Reworked domain name resolution, centralized IPv4/IPv6 sorting

This commit is contained in:
Gerhard Rieger 2024-08-21 20:53:15 +02:00
parent 127280088c
commit ec0e1ca20c
10 changed files with 258 additions and 183 deletions

View file

@ -46,6 +46,8 @@ Corrections:
Thanks to Heinrich Schuchardt from Canonical for reporting and sending Thanks to Heinrich Schuchardt from Canonical for reporting and sending
a patch. a patch.
Reworked domain name resolution, centralized IPv4/IPv6 sorting.
Features: Features:
Total inactivity timeout option -T 0 now means 0.0 seconds; up to Total inactivity timeout option -T 0 now means 0.0 seconds; up to
version 1.8.0.0 it meant no total inactivity timeout. version 1.8.0.0 it meant no total inactivity timeout.

89
test.sh
View file

@ -4969,6 +4969,7 @@ EOF
#0 if ! sed 's/.*\r//g' "$tpo" |diff -q "$tr" - >/dev/null 2>&1; then #0 if ! sed 's/.*\r//g' "$tpo" |diff -q "$tr" - >/dev/null 2>&1; then
#0 if ! sed 's/.*'"$($ECHO '\r\c')"'/</g' "$tpo" |diff -q "$tr" - >/dev/null 2>&1; then #0 if ! sed 's/.*'"$($ECHO '\r\c')"'/</g' "$tpo" |diff -q "$tr" - >/dev/null 2>&1; then
kill $pid 2>/dev/null # necc on OpenBSD
wait wait
if ! tr "$($ECHO '\r \c')" "% " <$tpo |sed 's/%$//g' |sed 's/.*%//g' |diff "$tr" - >"$tdiff" 2>&1; then if ! tr "$($ECHO '\r \c')" "% " <$tpo |sed 's/%$//g' |sed 's/.*%//g' |diff "$tr" - >"$tdiff" 2>&1; then
$PRINTF "$FAILED: $TRACE $SOCAT:\n" $PRINTF "$FAILED: $TRACE $SOCAT:\n"
@ -4985,7 +4986,6 @@ else
numOK=$((numOK+1)) numOK=$((numOK+1))
listOK="$listOK $N" listOK="$listOK $N"
fi fi
kill $pid 2>/dev/null # necc on OpenBSD
wait wait
MICROS=$SAVEMICS MICROS=$SAVEMICS
TERM="$SAVETERM" TERM="$SAVETERM"
@ -19339,7 +19339,7 @@ elif ! cond=$(checkconds \
"" \ "" \
"" \ "" \
"" \ "" \
"IP4 TCP LISTEN STDIO UNIX" \ "IP4 TCP LISTEN STDIO UNIX SOCKS4" \
"TCP4-LISTEN PIPE STDIN STDOUT TCP4 UNIX UNIX-LISTEN" \ "TCP4-LISTEN PIPE STDIN STDOUT TCP4 UNIX UNIX-LISTEN" \
"so-reuseaddr" \ "so-reuseaddr" \
"tcp4 unix" ); then "tcp4 unix" ); then
@ -20234,7 +20234,7 @@ else
rc0=$? rc0=$?
if [ "$rc0" -ne 0 ]; then if [ "$rc0" -ne 0 ]; then
$PRINTF "$FAILED (rc0=$rc0)\n" $PRINTF "$FAILED (rc0=$rc0)\n"
echo "$CMD0 &" echo "$CMD0"
cat "${te}0" >&2 cat "${te}0" >&2
numFAIL=$((numFAIL+1)) numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N" listFAIL="$listFAIL $N"
@ -20257,6 +20257,89 @@ UDPLITE-SENDTO udplite4 PORT
IP-SENDTO ip4 PROTO IP-SENDTO ip4 PROTO
" "
# Test if CONNECT to a server name that resolves to IPv6 first and IPv4
# as second address, when binding to an IPv4 address, uses IPv4
# This failed in Socat 1.8.0.0
while read ADDR protov IPPORT _; do
if [ -z "$ADDR" ] || [[ "$ADDR" == \#* ]]; then continue; fi
FEATS=
ADDR_="$(echo $ADDR |tr - _)" # TCP_CONNECT
PROTO="${ADDR%%[-:]*}" # TCP
proto=$(tolower $PROTO) # tcp
FEATS="$FEATS $PROTO"
NAME="$(echo "V1800_${ADDR_}_CONNECT_6_4" |sed 's/:[.0-8]*//')"
case "$TESTS" in
*%$N%*|*%functions%*|*%bugs%*|*%ip4%*|*%$protov%*|*%$proto%*|*%socket%*|*%$NAME%*)
TEST="$NAME: test regression of $ADDR with IPv6,4 and binding to IPv4"
# Run an appropriate server address in background.
# Start a CONNECT command to (internal) test name localhost-6-4.dest-unreach.net
# and bind to an IPv4 address, connect, terminate immediately.
# When no error occurs the test succeeded.
if ! eval $NUMCOND; then :
elif ! cond=$(checkconds \
"" \
"" \
"" \
"$FEATS DEVTESTS IP4" \
"$ADDR GOPEN" \
"bind" \
"$protov" ); then
$PRINTF "test $F_n $TEST... ${YELLOW}$cond${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
namesCANT="$namesCANT $NAME"
else
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"
case X$IPPORT in
XPORT) newport $(tolower $PROTO); _PORT=$PORT ;;
XPROTO) echo "IPPROTO=\"$IPPROTO\""
_PORT=$IPPROTO ;;
esac
CMD0="$TRACE $SOCAT $opts ${ADDR%%-*}-LISTEN:$_PORT,pf=ip4 PIPE"
CMD1="$TRACE $SOCAT $opts /dev/null $ADDR:localhost-6-4.dest-unreach.net:$_PORT,bind=127.0.0.1"
printf "test $F_n $TEST... " $N
$CMD0 2>"${te}0" </dev/null &
pid0=$!
wait${protov}port $PORT 1
$CMD1 2>"${te}1" </dev/null
rc1=$?
kill $pid0 2>/dev/null; wait
if [ "$rc1" -ne 0 ]; then
$PRINTF "$FAILED (rc1=$rc1)\n"
echo "$CMD0 &"
cat "${te}0" >&2
echo "$CMD1"
cat "${te}1" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
else
$PRINTF "$OK\n"
if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
if [ "$DEBUG" ]; then cat "${te}0" >&2; fi
numOK=$((numOK+1))
listOK="$listOK $N"
fi
fi # NUMCOND
;;
esac
PORT=$((PORT+1))
N=$((N+1))
done <<<"
TCP-CONNECT tcp4 PORT
SCTP-CONNECT sctp4 PORT
DCCP-CONNECT dccp4 PORT
#PENSSL tcp4 PORT
#OCKS4:127.0.0.1 tcp4 PORT
#OCKS4A:127.0.0.1 tcp4 PORT
#OCKS5:127.0.0.1:1080 tcp4 PORT
#ROXY::127.0.0.1 tcp4 PORT
"
# end of common tests # end of common tests
################################################################################## ##################################################################################

130
xio-ip.c
View file

@ -363,7 +363,7 @@ static int xioip_freeaddrinfo_devtests(
#endif /* WITH_DEVTESTS */ #endif /* WITH_DEVTESTS */
/* the ultimate(?) socat resolver function /* A socat resolver function
node: the address to be resolved; supported forms: node: the address to be resolved; supported forms:
1.2.3.4 (IPv4 address) 1.2.3.4 (IPv4 address)
[::2] (IPv6 address) [::2] (IPv6 address)
@ -376,7 +376,7 @@ static int xioip_freeaddrinfo_devtests(
res: a pointer to an uninitialized ptr var for the resulting socket address res: a pointer to an uninitialized ptr var for the resulting socket address
returns: STAT_OK, STAT_RETRYLATER, STAT_NORETRY, prints message returns: STAT_OK, STAT_RETRYLATER, STAT_NORETRY, prints message
*/ */
int xiogetaddrinfo(const char *node, const char *service, int _xiogetaddrinfo(const char *node, const char *service,
int family, int socktype, int protocol, int family, int socktype, int protocol,
struct addrinfo **res, const int ai_flags[2]) { struct addrinfo **res, const int ai_flags[2]) {
char *numnode = NULL; char *numnode = NULL;
@ -388,11 +388,11 @@ int xiogetaddrinfo(const char *node, const char *service,
#endif #endif
int error_num; int error_num;
Debug8("xiogetaddrinfo(node=\"%s\", service=\"%s\", family=%d, socktype=%d, protoco=%d, ai_flags={0x%04x/0x%04x} }, res=%p", Debug8("_xiogetaddrinfo(node=\"%s\", service=\"%s\", family=%d, socktype=%d, protoco=%d, ai_flags={0x%04x/0x%04x} }, res=%p",
node?node:"NULL", service?service:"NULL", family, socktype, protocol, node?node:"NULL", service?service:"NULL", family, socktype, protocol,
ai_flags?ai_flags[0]:0, ai_flags?ai_flags[1]:0, res); ai_flags?ai_flags[0]:0, ai_flags?ai_flags[1]:0, res);
if (service && service[0]=='\0') { if (service && service[0]=='\0') {
Error("xiogetaddrinfo(): empty port and service"); Error("_xiogetaddrinfo(): empty port and service");
return EAI_NONAME; return EAI_NONAME;
} }
@ -620,7 +620,7 @@ int xiogetaddrinfo(const char *node, const char *service,
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }
if (host->h_addrtype != family) { if (host->h_addrtype != family) {
Error2("xiogetaddrinfo(): \"%s\" does not resolve to %s", Error2("_xiogetaddrinfo(): \"%s\" does not resolve to %s",
node, family==PF_INET?"IP4":"IP6"); node, family==PF_INET?"IP4":"IP6");
} else { } else {
switch (family) { switch (family) {
@ -653,7 +653,97 @@ int xiogetaddrinfo(const char *node, const char *service,
return 0; return 0;
} }
void xiofreeaddrinfo(struct addrinfo *res) { /* Sort the records of an addrinfo list themp (as returned by getaddrinfo),
return the sorted list in the array ai_sorted (takes at most n entries
including the terminating NULL)
Returns 0 on success. */
int _xio_sort_ip_addresses(
struct addrinfo *themlist,
struct addrinfo **ai_sorted)
{
struct addrinfo *themp;
int i;
int ipv[3];
int ipi = 0;
/* Make a simple array of IP version preferences */
switch (xioparms.preferred_ip) {
case '0':
ipv[0] = PF_UNSPEC;
ipv[1] = -1;
break;
case '4':
ipv[0] = PF_INET;
ipv[1] = PF_INET6;
ipv[2] = -1;
break;
case '6':
ipv[0] = PF_INET6;
ipv[1] = PF_INET;
ipv[2] = -1;
break;
default:
Error("INTERNAL: undefined preferred_ip value");
return -1;
}
/* Create the sorted list */
ipi = 0;
i = 0;
while (ipv[ipi] >= 0) {
themp = themlist;
while (themp != NULL) {
if (ipv[ipi] == PF_UNSPEC) {
ai_sorted[i] = themp;
++i;
} else if (ipv[ipi] == themp->ai_family) {
ai_sorted[i] = themp;
++i;
}
themp = themp->ai_next;
}
++ipi;
}
ai_sorted[i] = NULL;
return 0;
}
/* Wrapper around _xiogetaddrinfo() (which is a wrapper arount getaddrinfo())
that sorts the results according to xioparms.preferred_ip when family is
AF_UNSPEC; it returns an array of record pointers instead of a list! */
int xiogetaddrinfo(const char *node, const char *service,
int family, int socktype, int protocol,
struct addrinfo ***ai_sorted, const int ai_flags[2]) {
struct addrinfo *res;
struct addrinfo **_ai_sorted;
struct addrinfo *aip;
int ain;
int rc;
rc = _xiogetaddrinfo(node, service, family, socktype, protocol, &res,
ai_flags);
if (rc != 0)
return rc;
/* Sort results - first, count records for mem allocation */
aip = res;
ain = 0;
while (aip != NULL) {
++ain;
aip = aip->ai_next;
}
_ai_sorted = Calloc((ain+2), sizeof(struct addrinfo *));
if (_ai_sorted == NULL)
return STAT_RETRYLATER;
/* Generate a list of addresses sorted by preferred ip version */
_xio_sort_ip_addresses(res, _ai_sorted);
_ai_sorted[ain+1] = res; /* save list past NULL for later freeing */
*ai_sorted = _ai_sorted;
return 0;
}
void _xiofreeaddrinfo(struct addrinfo *res) {
#if WITH_DEVTESTS #if WITH_DEVTESTS
if (!xioip_freeaddrinfo_devtests(res)) { if (!xioip_freeaddrinfo_devtests(res)) {
return; return;
@ -666,6 +756,20 @@ void xiofreeaddrinfo(struct addrinfo *res) {
#endif #endif
} }
void xiofreeaddrinfo(struct addrinfo **ai_sorted) {
int ain;
struct addrinfo *res;
/* Find the original *res from getaddrinfo past NULL */
ain = 0;
while (ai_sorted[ain] != NULL)
++ain;
res = ai_sorted[ain+1];
_xiofreeaddrinfo(res);
free(ai_sorted);
}
/* A simple resolver interface that just returns one address, /* A simple resolver interface that just returns one address,
the first found by calling xiogetaddrinfo(), but ev.respects preferred_ip; the first found by calling xiogetaddrinfo(), but ev.respects preferred_ip;
pf may be AF_INET, AF_INET6, or AF_UNSPEC; pf may be AF_INET, AF_INET6, or AF_UNSPEC;
@ -677,7 +781,7 @@ int xioresolve(const char *node, const char *service,
union sockaddr_union *addr, socklen_t *addrlen, union sockaddr_union *addr, socklen_t *addrlen,
const int ai_flags[2]) const int ai_flags[2])
{ {
struct addrinfo *res = NULL; struct addrinfo **res = NULL;
struct addrinfo *aip; struct addrinfo *aip;
int rc; int rc;
@ -699,17 +803,17 @@ int xioresolve(const char *node, const char *service,
xiofreeaddrinfo(res); xiofreeaddrinfo(res);
return STAT_NORETRY; return STAT_NORETRY;
} }
if (res->ai_addrlen > *addrlen) { if ((*res)->ai_addrlen > *addrlen) {
Error3("xioresolve(node=\"%s\", addrlen="F_socklen", ...): "F_socklen" bytes required", Error3("xioresolve(node=\"%s\", addrlen="F_socklen", ...): "F_socklen" bytes required",
node, *addrlen, res->ai_addrlen); node, *addrlen, (*res)->ai_addrlen);
xiofreeaddrinfo(res); xiofreeaddrinfo(res);
return STAT_NORETRY; return STAT_NORETRY;
} }
if (res->ai_next != NULL) { if ((*res)->ai_next != NULL) {
Info4("xioresolve(node=\"%s\", service=%s%s%s, ...): More than one address found", node?node:"NULL", service?"\"":"", service?service:"NULL", service?"\"":""); Info4("xioresolve(node=\"%s\", service=%s%s%s, ...): More than one address found", node?node:"NULL", service?"\"":"", service?service:"NULL", service?"\"":"");
} }
aip = res; aip = *res;
if (ai_flags != NULL && ai_flags[0] & AI_PASSIVE && pf == PF_UNSPEC) { if (ai_flags != NULL && ai_flags[0] & AI_PASSIVE && pf == PF_UNSPEC) {
/* We select the first IPv6 address, if available, /* We select the first IPv6 address, if available,
because this might accept IPv4 connections too */ because this might accept IPv4 connections too */
@ -719,7 +823,7 @@ int xioresolve(const char *node, const char *service,
aip = aip->ai_next; aip = aip->ai_next;
} }
if (aip == NULL) if (aip == NULL)
aip = res; aip = *res;
} else if (pf == PF_UNSPEC && xioparms.preferred_ip != '0') { } else if (pf == PF_UNSPEC && xioparms.preferred_ip != '0') {
int prefip = PF_UNSPEC; int prefip = PF_UNSPEC;
xioinit_ip(&prefip, xioparms.preferred_ip); xioinit_ip(&prefip, xioparms.preferred_ip);
@ -729,7 +833,7 @@ int xioresolve(const char *node, const char *service,
aip = aip->ai_next; aip = aip->ai_next;
} }
if (aip == NULL) if (aip == NULL)
aip = res; aip = *res;
} }
memcpy(addr, aip->ai_addr, aip->ai_addrlen); memcpy(addr, aip->ai_addr, aip->ai_addrlen);

View file

@ -49,8 +49,9 @@ extern const struct optdesc opt_res_nsaddr;
extern int xioinit_ip(int *pf, char ipv); extern int xioinit_ip(int *pf, char ipv);
extern int xiogetaddrinfo(const char *node, const char *service, int family, int socktype, int protocol, struct addrinfo **res, const int ai_flags[2]); extern int xiogetaddrinfo(const char *node, const char *service, int family, int socktype, int protocol, struct addrinfo ***ai_sorted, const int ai_flags[2]);
extern void xiofreeaddrinfo(struct addrinfo *res); extern void xiofreeaddrinfo(struct addrinfo **ai_sorted);
extern int _xio_sort_ip_addresses(struct addrinfo *themlist, struct addrinfo **ai_sorted);
extern int xioresolve(const char *node, const char *service, int family, int socktype, int protocol, union sockaddr_union *addr, socklen_t *addrlen, const int ai_flags[2]); extern int xioresolve(const char *node, const char *service, int family, int socktype, int protocol, union sockaddr_union *addr, socklen_t *addrlen, const int ai_flags[2]);
extern int xiolog_ancillary_ip(struct single *sfd, struct cmsghdr *cmsg, int *num, char *typbuff, int typlen, char *nambuff, int namlen, char *envbuff, int envlen, char *valbuff, int vallen); extern int xiolog_ancillary_ip(struct single *sfd, struct cmsghdr *cmsg, int *num, char *typbuff, int typlen, char *nambuff, int namlen, char *envbuff, int envlen, char *valbuff, int vallen);
extern int xiotype_ip_add_membership(char *token, const struct optname *ent, struct opt *opt); extern int xiotype_ip_add_membership(char *token, const struct optname *ent, struct opt *opt);

View file

@ -39,12 +39,11 @@ int xioopen_ipapp_connect(
int maxchildren = 0; int maxchildren = 0;
union sockaddr_union us_sa, *us = &us_sa; union sockaddr_union us_sa, *us = &us_sa;
socklen_t uslen = sizeof(us_sa); socklen_t uslen = sizeof(us_sa);
struct addrinfo *themlist, *themp; struct addrinfo **themarr, *themp;
char infobuff[256]; char infobuff[256];
bool needbind = false; bool needbind = false;
bool lowport = false; bool lowport = false;
int level; int level;
struct addrinfo **ai_sorted;
int i; int i;
int result; int result;
@ -78,7 +77,7 @@ int xioopen_ipapp_connect(
if (_xioopen_ipapp_prepare(opts, &opts0, hostname, portname, &pf, ipproto, if (_xioopen_ipapp_prepare(opts, &opts0, hostname, portname, &pf, ipproto,
sfd->para.socket.ip.ai_flags, sfd->para.socket.ip.ai_flags,
&themlist, us, &uslen, &needbind, &lowport, &themarr, us, &uslen, &needbind, &lowport,
socktype) != STAT_OK) { socktype) != STAT_OK) {
return STAT_NORETRY; return STAT_NORETRY;
} }
@ -94,33 +93,22 @@ int xioopen_ipapp_connect(
Info("starting connect loop"); Info("starting connect loop");
} }
/* Count addrinfo entries */
themp = themlist;
i = 0;
while (themp != NULL) {
++i;
themp = themp->ai_next;
}
ai_sorted = Calloc((i+1), sizeof(struct addrinfo *));
if (ai_sorted == NULL)
return STAT_RETRYLATER;
/* Generate a list of addresses sorted by preferred ip version */
_xio_sort_ip_addresses(themlist, ai_sorted);
do { /* loop over retries, and forks */ do { /* loop over retries, and forks */
/* Loop over themlist - no, over ai_sorted */ /* Loop over themarr (which had been "ai_sorted") */
result = STAT_RETRYLATER; result = STAT_RETRYLATER;
i = 0; i = 0;
themp = ai_sorted[i++]; themp = themarr[i++];
while (themp != NULL) { while (themp != NULL) {
Notice1("opening connection to %s", Notice1("opening connection to %s",
sockaddr_info(themp->ai_addr, themp->ai_addrlen, sockaddr_info(themp->ai_addr, themp->ai_addrlen,
infobuff, sizeof(infobuff))); infobuff, sizeof(infobuff)));
#if WITH_RETRY #if WITH_RETRY
if (sfd->forever || sfd->retry || ai_sorted[i] != NULL) { if (sfd->forever || sfd->retry) {
level = E_INFO; level = E_INFO;
} else if (themarr[i] != NULL) {
level = E_WARN;
} else } else
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
level = E_ERROR; level = E_ERROR;
@ -133,7 +121,7 @@ int xioopen_ipapp_connect(
lowport, level); lowport, level);
if (result == STAT_OK) if (result == STAT_OK)
break; break;
themp = ai_sorted[i++]; themp = themarr[i++];
if (themp == NULL) { if (themp == NULL) {
result = STAT_RETRYLATER; result = STAT_RETRYLATER;
} }
@ -153,7 +141,7 @@ int xioopen_ipapp_connect(
} }
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
default: default:
free(ai_sorted); xiofreeaddrinfo(themarr);
free(opts0);free(opts); free(opts0);free(opts);
return result; return result;
} }
@ -170,7 +158,7 @@ int xioopen_ipapp_connect(
if (sfd->forever || --sfd->retry) { if (sfd->forever || --sfd->retry) {
Nanosleep(&sfd->intervall, NULL); continue; Nanosleep(&sfd->intervall, NULL); continue;
} }
free(ai_sorted); xiofreeaddrinfo(themarr);
free(opts0); free(opts0);
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }
@ -197,8 +185,7 @@ int xioopen_ipapp_connect(
} }
} while (true); } while (true);
/* only "active" process breaks (master without fork, or child) */ /* only "active" process breaks (master without fork, or child) */
free(ai_sorted); xiofreeaddrinfo(themarr);
xiofreeaddrinfo(themlist);
if ((result = _xio_openlate(sfd, opts)) < 0) { if ((result = _xio_openlate(sfd, opts)) < 0) {
free(opts0);free(opts); free(opts0);free(opts);
@ -223,7 +210,7 @@ int
int *pf, int *pf,
int protocol, int protocol,
const int ai_flags[2], const int ai_flags[2],
struct addrinfo **themlist, struct addrinfo ***themarr,
union sockaddr_union *us, union sockaddr_union *us,
socklen_t *uslen, socklen_t *uslen,
bool *needbind, bool *needbind,
@ -236,7 +223,7 @@ int
if (hostname != NULL || portname != NULL) { if (hostname != NULL || portname != NULL) {
rc = xiogetaddrinfo(hostname, portname, *pf, socktype, protocol, rc = xiogetaddrinfo(hostname, portname, *pf, socktype, protocol,
themlist, ai_flags); themarr, ai_flags);
if (rc == EAI_AGAIN) { if (rc == EAI_AGAIN) {
Warn4("_xioopen_ipapp_prepare(node=\"%s\", service=\"%s\", pf=%d, ...): %s", Warn4("_xioopen_ipapp_prepare(node=\"%s\", service=\"%s\", pf=%d, ...): %s",
hostname?hostname:"NULL", portname?portname:"NULL", hostname?hostname:"NULL", portname?portname:"NULL",
@ -253,13 +240,13 @@ int
applyopts(NULL, -1, opts, PH_EARLY); applyopts(NULL, -1, opts, PH_EARLY);
/* 3 means: IP address AND port accepted */ /* 3 means: IP address AND port accepted */
if (retropt_bind(opts, (*pf!=PF_UNSPEC)?*pf:(*themlist)->ai_family, if (retropt_bind(opts, (*pf!=PF_UNSPEC)?*pf:(**themarr)->ai_family,
socktype, protocol, (struct sockaddr *)us, uslen, 3, socktype, protocol, (struct sockaddr *)us, uslen, 3,
ai_flags) ai_flags)
!= STAT_NOACTION) { != STAT_NOACTION) {
*needbind = true; *needbind = true;
} else { } else {
switch ((*pf!=PF_UNSPEC)?*pf:(*themlist)->ai_family) { switch ((*pf!=PF_UNSPEC)?*pf:(**themarr)->ai_family) {
#if WITH_IP4 #if WITH_IP4
case PF_INET: socket_in_init(&us->ip4); *uslen = sizeof(us->ip4); break; case PF_INET: socket_in_init(&us->ip4); *uslen = sizeof(us->ip4); break;
#endif /* WITH_IP4 */ #endif /* WITH_IP4 */
@ -271,7 +258,7 @@ int
} }
if (retropt_2bytes(opts, OPT_SOURCEPORT, &port) >= 0) { if (retropt_2bytes(opts, OPT_SOURCEPORT, &port) >= 0) {
switch ((*pf!=PF_UNSPEC)?*pf:(*themlist)->ai_family) { switch ((*pf!=PF_UNSPEC)?*pf:(**themarr)->ai_family) {
#if WITH_IP4 #if WITH_IP4
case PF_INET: us->ip4.sin_port = htons(port); break; case PF_INET: us->ip4.sin_port = htons(port); break;
#endif /* WITH_IP4 */ #endif /* WITH_IP4 */
@ -397,60 +384,4 @@ int xioopen_ipapp_listen(
} }
#endif /* WITH_IP4 && WITH_TCP && WITH_LISTEN */ #endif /* WITH_IP4 && WITH_TCP && WITH_LISTEN */
/* Sort the records of an addrinfo list themp (as returned by getaddrinfo),
return the sorted list in the array ai_sorted (takes at most n entries
including the terminating NULL)
Returns 0 on success. */
int _xio_sort_ip_addresses(
struct addrinfo *themlist,
struct addrinfo **ai_sorted)
{
struct addrinfo *themp;
int i;
int ipv[3];
int ipi = 0;
/* Make a simple array of IP version preferences */
switch (xioparms.preferred_ip) {
case '0':
ipv[0] = PF_UNSPEC;
ipv[1] = -1;
break;
case '4':
ipv[0] = PF_INET;
ipv[1] = PF_INET6;
ipv[2] = -1;
break;
case '6':
ipv[0] = PF_INET6;
ipv[1] = PF_INET;
ipv[2] = -1;
break;
default:
Error("INTERNAL: undefined preferred_ip value");
return -1;
}
/* Create the sorted list */
ipi = 0;
i = 0;
while (ipv[ipi] >= 0) {
themp = themlist;
while (themp != NULL) {
if (ipv[ipi] == PF_UNSPEC) {
ai_sorted[i] = themp;
++i;
} else if (ipv[ipi] == themp->ai_family) {
ai_sorted[i] = themp;
++i;
}
themp = themp->ai_next;
}
++ipi;
}
ai_sorted[i] = NULL;
return 0;
}
#endif /* WITH_TCP || WITH_UDP */ #endif /* WITH_TCP || WITH_UDP */

View file

@ -15,13 +15,12 @@ extern const struct optdesc opt_sourceport;
extern const struct optdesc opt_lowport; extern const struct optdesc opt_lowport;
extern int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, const struct addrdesc *addrdesc); extern int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, const struct addrdesc *addrdesc);
extern int _xioopen_ipapp_prepare(struct opt *opts, struct opt **opts0, const char *hostname, const char *portname, int *pf, int protocol, const int ai_flags[2], struct addrinfo **res, union sockaddr_union *us, socklen_t *uslen, bool *needbind, bool *lowport, int socktype); extern int _xioopen_ipapp_prepare(struct opt *opts, struct opt **opts0, const char *hostname, const char *portname, int *pf, int protocol, const int ai_flags[2], struct addrinfo ***themlist, union sockaddr_union *us, socklen_t *uslen, bool *needbind, bool *lowport, int socktype);
extern int _xioopen_ip4app_connect(const char *hostname, const char *portname, extern int _xioopen_ip4app_connect(const char *hostname, const char *portname,
struct single *xfd, struct single *xfd,
int socktype, int ipproto, void *protname, int socktype, int ipproto, void *protname,
struct opt *opts); struct opt *opts);
extern int xioopen_ipapp_listen(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, const struct addrdesc *addrdesc); extern int xioopen_ipapp_listen(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, const struct addrdesc *addrdesc);
extern int _xioopen_ipapp_listen_prepare(struct opt *opts, struct opt **opts0, const char *portname, int *pf, int ipproto, const int ai_flags[2], union sockaddr_union *us, socklen_t *uslen, int socktype); extern int _xioopen_ipapp_listen_prepare(struct opt *opts, struct opt **opts0, const char *portname, int *pf, int ipproto, const int ai_flags[2], union sockaddr_union *us, socklen_t *uslen, int socktype);
extern int _xio_sort_ip_addresses(struct addrinfo *themlist, struct addrinfo **ai_sorted);
#endif /* !defined(__xio_ipapp_h_included) */ #endif /* !defined(__xio_ipapp_h_included) */

View file

@ -241,11 +241,10 @@ static int xioopen_openssl_connect(
bool dofork = false; bool dofork = false;
union sockaddr_union us_sa, *us = &us_sa; union sockaddr_union us_sa, *us = &us_sa;
socklen_t uslen = sizeof(us_sa); socklen_t uslen = sizeof(us_sa);
struct addrinfo *themlist, *themp; struct addrinfo **themarr, *themp;
bool needbind = false; bool needbind = false;
bool lowport = false; bool lowport = false;
int level = E_ERROR; int level = E_ERROR;
struct addrinfo **ai_sorted;
int i; int i;
SSL_CTX* ctx; SSL_CTX* ctx;
bool opt_ver = true; /* verify peer certificate */ bool opt_ver = true; /* verify peer certificate */
@ -323,7 +322,7 @@ static int xioopen_openssl_connect(
result = result =
_xioopen_ipapp_prepare(opts, &opts0, hostname, portname, &pf, ipproto, _xioopen_ipapp_prepare(opts, &opts0, hostname, portname, &pf, ipproto,
sfd->para.socket.ip.ai_flags, sfd->para.socket.ip.ai_flags,
&themlist, us, &uslen, &themarr, us, &uslen,
&needbind, &lowport, socktype); &needbind, &lowport, socktype);
if (result != STAT_OK) return STAT_NORETRY; if (result != STAT_OK) return STAT_NORETRY;
@ -334,28 +333,15 @@ static int xioopen_openssl_connect(
Info("starting connect loop"); Info("starting connect loop");
} }
/* Count addrinfo entries */
themp = themlist;
i = 0;
while (themp != NULL) {
++i;
themp = themp->ai_next;
}
ai_sorted = Calloc((i+1), sizeof(struct addrinfo *));
if (ai_sorted == NULL)
return STAT_RETRYLATER;
/* Generate a list of addresses sorted by preferred ip version */
_xio_sort_ip_addresses(themlist, ai_sorted);
do { /* loop over failed connect and SSL handshake attempts */ do { /* loop over failed connect and SSL handshake attempts */
/* Loop over ai_sorted list */ /* Loop over themarr (which had been "ai_sorted") */
i = 0; i = 0;
themp = ai_sorted[i++]; themp = themarr[i++];
while (themp != NULL) { while (themp != NULL) {
#if WITH_RETRY #if WITH_RETRY
if (sfd->forever || sfd->retry || ai_sorted[i] != NULL) { if (sfd->forever || sfd->retry || themarr[i] != NULL) {
level = E_INFO; level = E_INFO;
} else } else
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
@ -369,7 +355,7 @@ static int xioopen_openssl_connect(
opts, pf?pf:themp->ai_addr->sa_family, socktype, ipproto, lowport, level); opts, pf?pf:themp->ai_addr->sa_family, socktype, ipproto, lowport, level);
if (result == STAT_OK) if (result == STAT_OK)
break; break;
themp = ai_sorted[i++]; themp = themarr[i++];
if (themp == NULL) { if (themp == NULL) {
result = STAT_RETRYLATER; result = STAT_RETRYLATER;
} }
@ -387,16 +373,16 @@ static int xioopen_openssl_connect(
--sfd->retry; --sfd->retry;
continue; continue;
} }
free(ai_sorted); xiofreeaddrinfo(themarr);
return STAT_NORETRY; return STAT_NORETRY;
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
default: default:
free(ai_sorted); xiofreeaddrinfo(themarr);
return result; return result;
} }
/*! isn't this too early? */ /*! isn't this too early? */
if ((result = _xio_openlate(sfd, opts)) < 0) { if ((result = _xio_openlate(sfd, opts)) < 0) {
free(ai_sorted); xiofreeaddrinfo(themarr);
return result; return result;
} }
@ -418,7 +404,7 @@ static int xioopen_openssl_connect(
} }
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
default: default:
xiofreeaddrinfo(themlist); xiofreeaddrinfo(themarr);
return STAT_NORETRY; return STAT_NORETRY;
} }
@ -437,7 +423,7 @@ static int xioopen_openssl_connect(
if (sfd->forever || --sfd->retry) { if (sfd->forever || --sfd->retry) {
Nanosleep(&sfd->intervall, NULL); continue; Nanosleep(&sfd->intervall, NULL); continue;
} }
xiofreeaddrinfo(themlist); xiofreeaddrinfo(themarr);
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }
@ -458,8 +444,7 @@ static int xioopen_openssl_connect(
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
break; break;
} while (true); /* drop out on success */ } while (true); /* drop out on success */
free(ai_sorted); xiofreeaddrinfo(themarr);
xiofreeaddrinfo(themlist);
openssl_conn_loginfo(sfd->para.openssl.ssl); openssl_conn_loginfo(sfd->para.openssl.ssl);

View file

@ -93,8 +93,7 @@ static int xioopen_proxy_connect(
int pf = PF_UNSPEC; int pf = PF_UNSPEC;
union sockaddr_union us_sa, *us = &us_sa; union sockaddr_union us_sa, *us = &us_sa;
socklen_t uslen = sizeof(us_sa); socklen_t uslen = sizeof(us_sa);
struct addrinfo *themlist, *themp; struct addrinfo **themarr, *themp;
struct addrinfo **ai_sorted;
int i; int i;
const char *proxyname; char *proxyport = NULL; const char *proxyname; char *proxyport = NULL;
const char *targetname, *targetport; const char *targetname, *targetport;
@ -146,32 +145,19 @@ static int xioopen_proxy_connect(
_xioopen_ipapp_prepare(opts, &opts0, proxyname, proxyport, _xioopen_ipapp_prepare(opts, &opts0, proxyname, proxyport,
&pf, ipproto, &pf, ipproto,
sfd->para.socket.ip.ai_flags, sfd->para.socket.ip.ai_flags,
&themlist, us, &uslen, &themarr, us, &uslen,
&needbind, &lowport, socktype); &needbind, &lowport, socktype);
if (result != STAT_OK) if (result != STAT_OK)
return result; return result;
/* Count addrinfo entries */
themp = themlist;
i = 0;
while (themp != NULL) {
++i;
themp = themp->ai_next;
}
ai_sorted = Calloc((i+1), sizeof(struct addrinfo *));
if (ai_sorted == NULL)
return STAT_RETRYLATER;
/* Generate a list of addresses sorted by preferred ip version */
_xio_sort_ip_addresses(themlist, ai_sorted);
/* Loop over themlist */ /* Loop over themlist */
i = 0; i = 0;
themp = ai_sorted[i++]; themp = themarr[i++];
while (themp != NULL) { while (themp != NULL) {
Notice4("opening connection to %s:%u via proxy %s:%s", Notice4("opening connection to %s:%u via proxy %s:%s",
proxyvars->targetaddr, proxyvars->targetport, proxyname, proxyport); proxyvars->targetaddr, proxyvars->targetport, proxyname, proxyport);
#if WITH_RETRY #if WITH_RETRY
if (sfd->forever || sfd->retry || ai_sorted[i] != NULL) { if (sfd->forever || sfd->retry || themarr[i] != NULL) {
level = E_INFO; level = E_INFO;
} else } else
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
@ -184,7 +170,7 @@ static int xioopen_proxy_connect(
opts, pf?pf:themp->ai_family, socktype, IPPROTO_TCP, lowport, level); opts, pf?pf:themp->ai_family, socktype, IPPROTO_TCP, lowport, level);
if (result == STAT_OK) if (result == STAT_OK)
break; break;
themp = ai_sorted[i++]; themp = themarr[i++];
if (themp == NULL) { if (themp == NULL) {
result = STAT_RETRYLATER; result = STAT_RETRYLATER;
} }
@ -201,12 +187,11 @@ static int xioopen_proxy_connect(
} }
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
default: default:
free(ai_sorted); xiofreeaddrinfo(themarr);
xiofreeaddrinfo(themlist);
return result; return result;
} }
} }
xiofreeaddrinfo(themlist); xiofreeaddrinfo(themarr);
applyopts(sfd, -1, opts, PH_ALL); applyopts(sfd, -1, opts, PH_ALL);
if ((result = _xio_openlate(sfd, opts)) < 0) if ((result = _xio_openlate(sfd, opts)) < 0)

View file

@ -55,8 +55,7 @@ static int xioopen_socks4_connect(
bool dofork = false; bool dofork = false;
union sockaddr_union us_sa, *us = &us_sa; union sockaddr_union us_sa, *us = &us_sa;
socklen_t uslen = sizeof(us_sa); socklen_t uslen = sizeof(us_sa);
struct addrinfo *themlist, *themp; struct addrinfo **themarr, *themp;
struct addrinfo **ai_sorted;
int i; int i;
bool needbind = false; bool needbind = false;
bool lowport = false; bool lowport = false;
@ -103,22 +102,9 @@ static int xioopen_socks4_connect(
_xioopen_ipapp_prepare(opts, &opts0, sockdname, socksport, _xioopen_ipapp_prepare(opts, &opts0, sockdname, socksport,
&pf, ipproto, &pf, ipproto,
sfd->para.socket.ip.ai_flags, sfd->para.socket.ip.ai_flags,
&themlist, us, &uslen, &themarr, us, &uslen,
&needbind, &lowport, socktype); &needbind, &lowport, socktype);
/* Count addrinfo entries */
themp = themlist;
i = 0;
while (themp != NULL) {
++i;
themp = themp->ai_next;
}
ai_sorted = Calloc((i+1), sizeof(struct addrinfo *));
if (ai_sorted == NULL)
return STAT_RETRYLATER;
/* Generate a list of addresses sorted by preferred ip version */
_xio_sort_ip_addresses(themlist, ai_sorted);
/* we try to resolve the target address _before_ connecting to the socks /* we try to resolve the target address _before_ connecting to the socks
server: this avoids unnecessary socks connects and timeouts */ server: this avoids unnecessary socks connects and timeouts */
result = result =
@ -139,15 +125,15 @@ static int xioopen_socks4_connect(
return result; return result;
} }
/* loop over themlist */ /* loop over themarr */
i = 0; i = 0;
themp = ai_sorted[i++]; themp = themarr[i++];
while (themp != NULL) { while (themp != NULL) {
Notice1("opening connection to %s", Notice1("opening connection to %s",
sockaddr_info(themp->ai_addr, themp->ai_addrlen, sockaddr_info(themp->ai_addr, themp->ai_addrlen,
infobuff, sizeof(infobuff))); infobuff, sizeof(infobuff)));
#if WITH_RETRY #if WITH_RETRY
if (sfd->forever || sfd->retry || ai_sorted[i] != NULL) { if (sfd->forever || sfd->retry || themarr[i] != NULL) {
level = E_INFO; level = E_INFO;
} else } else
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
@ -161,7 +147,7 @@ static int xioopen_socks4_connect(
opts, pf?pf:themp->ai_family, socktype, IPPROTO_TCP, lowport, level); opts, pf?pf:themp->ai_family, socktype, IPPROTO_TCP, lowport, level);
if (result == STAT_OK) if (result == STAT_OK)
break; break;
themp = ai_sorted[i++]; themp = themarr[i++];
if (themp == NULL) if (themp == NULL)
result = STAT_RETRYLATER; result = STAT_RETRYLATER;
} }
@ -178,11 +164,10 @@ static int xioopen_socks4_connect(
} }
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
default: default:
free(ai_sorted); xiofreeaddrinfo(themarr);
xiofreeaddrinfo(themlist);
return result; return result;
} }
xiofreeaddrinfo(themlist); xiofreeaddrinfo(themarr);
applyopts(sfd, -1, opts, PH_ALL); applyopts(sfd, -1, opts, PH_ALL);
if ((result = _xio_openlate(sfd, opts)) < 0) if ((result = _xio_openlate(sfd, opts)) < 0)

View file

@ -512,7 +512,7 @@ static int xioopen_socks5(
const char *socks_server, *target_name, *target_port, *socks_port; const char *socks_server, *target_name, *target_port, *socks_port;
union sockaddr_union us_sa, *us = &us_sa; union sockaddr_union us_sa, *us = &us_sa;
socklen_t uslen = sizeof(us_sa); socklen_t uslen = sizeof(us_sa);
struct addrinfo *themlist, *themp; struct addrinfo **themarr, *themp;
bool needbind = false; bool needbind = false;
bool lowport = false; bool lowport = false;
char infobuff[256]; char infobuff[256];
@ -542,7 +542,7 @@ static int xioopen_socks5(
result = _xioopen_ipapp_prepare(opts, &opts0, socks_server, socks_port, result = _xioopen_ipapp_prepare(opts, &opts0, socks_server, socks_port,
&pf, ipproto, &pf, ipproto,
sfd->para.socket.ip.ai_flags, sfd->para.socket.ip.ai_flags,
&themlist, us, &uslen, &themarr, us, &uslen,
&needbind, &lowport, socktype); &needbind, &lowport, socktype);
Notice2("connecting to socks5 server %s:%s", Notice2("connecting to socks5 server %s:%s",
@ -557,8 +557,8 @@ static int xioopen_socks5(
} }
#endif #endif
/* loop over themlist */ /* loop over themarr */
themp = themlist; themp = themarr[0];
while (themp != NULL) { while (themp != NULL) {
Notice1("opening connection to %s", Notice1("opening connection to %s",
sockaddr_info(themp->ai_addr, themp->ai_addrlen, sockaddr_info(themp->ai_addr, themp->ai_addrlen,
@ -584,11 +584,11 @@ static int xioopen_socks5(
} }
#endif #endif
default: default:
xiofreeaddrinfo(themlist); xiofreeaddrinfo(themarr);
return result; return result;
} }
} }
xiofreeaddrinfo(themlist); xiofreeaddrinfo(themarr);
applyopts(sfd, -1, opts, PH_ALL); applyopts(sfd, -1, opts, PH_ALL);
if ((result = _xio_openlate(sfd, opts)) < 0) if ((result = _xio_openlate(sfd, opts)) < 0)