Preferred IP version sorts getaddrionf() results

This commit is contained in:
Gerhard Rieger 2023-11-05 13:59:14 +01:00
parent 2d282f5608
commit 277f0d755d
16 changed files with 331 additions and 146 deletions

22
CHANGES
View file

@ -75,14 +75,22 @@ Features:
Tests: TRY_ADDRS_4 TRY_ADDRS_4_6 Tests: TRY_ADDRS_4 TRY_ADDRS_4_6
Feature recommended by Anand Buddhdev. Feature recommended by Anand Buddhdev.
configure option --with-default-ipv allows to specify at build time if configure option --enable-default-ipv allows to specify at build time if
IPv4, IPv6, or none of these is the preferred default. IPv4, IPv6, or none of these is the preferred default; this is related
to environment variables SOCAT_PREFERRED_RESOLVE_IP and
SOCAT_DEFAULT_LISTEN_IP, and to Socat option -4, -6.
Furthermore, mechanism of IPv4 vs.IPv6 selection has been reworked.
When no IP version is preferred by these mechanism, passive Socat
addresses (LISTEN, RECV, RECVFROM) default to IPv6 because it might
support both versions (but checkout option ipv6-v6only).
For client addresses, when one of these mechanisms applies and name
resolution gives addresses of both IP versions, the addresses of the
preferred versions are tried first.
Socat options -4 and -6 have been reworked. New option ai-addrconfig sets or unsets the AI_ADDRCONFIG flag of the
Tests: TCP_ENV6 TCP_DASH6 resolver to prevent name resolution to address families that are not
available in the network configuration. Default value is 1 in case the
New option ai-addrconfig disables name resolution to protocol families resolver does not get an address family hint.
that are not configured on the computer (e.g. IPv6)
Flag AI_PASSIVE is now automatically applied for LISTEN, RECV, and Flag AI_PASSIVE is now automatically applied for LISTEN, RECV, and
RECVFROM type addresses, and with bind option. In addition to its RECVFROM type addresses, and with bind option. In addition to its

View file

@ -723,7 +723,7 @@
#undef WITH_MSGLEVEL #undef WITH_MSGLEVEL
#undef WITH_DEFAULT_IPV /* default IP version: undef, "4", "6" */ #undef WITH_DEFAULT_IPV /* default IP version: "0", "4", "6" */
#define BUILD_DATE __DATE__ " " __TIME__ #define BUILD_DATE __DATE__ " " __TIME__

View file

@ -821,12 +821,12 @@ AC_ARG_ENABLE(msglevel, [ --enable-msglevel=N set max verbosity to debug,in
[AC_DEFINE(WITH_MSGLEVEL,0) AC_MSG_RESULT(debug)]) [AC_DEFINE(WITH_MSGLEVEL,0) AC_MSG_RESULT(debug)])
AC_MSG_CHECKING(default IP version) AC_MSG_CHECKING(default IP version)
AC_ARG_ENABLE(ip-version, [ --enable-ip-version=N set default IP version to undef, "4", "6"], AC_ARG_ENABLE(default-ipv, [ --enable-default-ipv=N set default/preferred IP version to "0" (none), "4", "6"],
[case "$enableval" in [case "$enableval" in
"") AC_DEFINE(WITH_DEFAULT_IPV, 0) AC_MSG_RESULT("0");; "") AC_DEFINE(WITH_DEFAULT_IPV, '0') AC_MSG_RESULT("0");;
4) AC_DEFINE(WITH_DEFAULT_IPV, '4') AC_MSG_RESULT("4");; 4) AC_DEFINE(WITH_DEFAULT_IPV, '4') AC_MSG_RESULT("4");;
6) AC_DEFINE(WITH_DEFAULT_IPV, '6') AC_MSG_RESULT("6");; 6) AC_DEFINE(WITH_DEFAULT_IPV, '6') AC_MSG_RESULT("6");;
*) AC_DEFINE(WITH_DEFAULT_IPV, 0) AC_MSG_RESULT("0");; *) AC_DEFINE(WITH_DEFAULT_IPV, '0') AC_MSG_RESULT("0");;
esac], esac],
[AC_DEFINE(WITH_DEFAULT_IPV, '0') AC_MSG_RESULT("0")]) [AC_DEFINE(WITH_DEFAULT_IPV, '0') AC_MSG_RESULT("0")])

View file

@ -2244,7 +2244,7 @@ label(OPTION_AI_ADDRCONFIG)
dit(bf(tt(ai-addrconfig[=0|1]))), dit(bf(tt(addrconfig[=0|1]))) dit(bf(tt(ai-addrconfig[=0|1]))), dit(bf(tt(addrconfig[=0|1])))
Sets or unsets the AI_ADDRCONFIG flag to prevent name resolution to address Sets or unsets the AI_ADDRCONFIG flag to prevent name resolution to address
families that are not configured (e.g. IPv6). Default value is 1 in case the families that are not configured (e.g. IPv6). Default value is 1 in case the
resolver does not get an address family hint. resolver does not get an address family hint from Socat address or defaults.
dit(bf(tt(ai-passive[=0|1]))), dit(bf(tt(passive[=0|1]))) dit(bf(tt(ai-passive[=0|1]))), dit(bf(tt(passive[=0|1])))
Sets of unsets the AI_ADDRCONFIG flag for code(getaddrinfo)) calls. Default Sets of unsets the AI_ADDRCONFIG flag for code(getaddrinfo)) calls. Default
is 1 for LISTEN, RECV, and RECVFROM type addresses, and with is 1 for LISTEN, RECV, and RECVFROM type addresses, and with

79
test.sh
View file

@ -2483,20 +2483,29 @@ waittcp6port $tsl 1
echo "$da" |$CMD2 >>"$tf" 2>>"${te}2" echo "$da" |$CMD2 >>"$tf" 2>>"${te}2"
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
$PRINTF "$FAILED: $TRACE $SOCAT:\n" $PRINTF "$FAILED: $TRACE $SOCAT:\n"
echo "$CMD1 &" echo "SOCAT_DEFAULT_LISTEN_IP=6 $CMD1 &"
cat "${te}1" >&2
echo "$CMD2" echo "$CMD2"
cat "${te}1" "${te}2" cat "${te}2" >&2
numFAIL=$((numFAIL+1)) numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N" listFAIL="$listFAIL $N"
elif ! echo "$da" |diff - "$tf" >"$tdiff"; then elif ! echo "$da" |diff - "$tf" >"$tdiff"; then
$PRINTF "$FAILED: diff:\n" $PRINTF "$FAILED (diff):\n"
cat "$tdiff" echo "SOCAT_DEFAULT_LISTEN_IP=6 $CMD1 &"
cat "${te}1" >&2
echo "$CMD2"
cat "${te}2" >&2
echo "// diff:" >&2
cat "$tdiff" >&2
numFAIL=$((numFAIL+1)) numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N" listFAIL="$listFAIL $N"
else else
$PRINTF "$OK\n" $PRINTF "$OK\n"
if [ -n "$debug" ]; then cat "${te}1" "${te}2"; fi if [ "$VERBOSE" ]; then echo "$CMD1 &"; fi
numOK=$((numOK+1)) if [ "$DEBUG" ]; then cat "${te}1" >&2; fi
if [ "$VERBOSE" ]; then echo "$CMD2"; fi
if [ "$DEBUG" ]; then cat "${te}2" >&2; fi
numOK=$((numOK+1))
fi fi
kill $pid 2>/dev/null; wait kill $pid 2>/dev/null; wait
fi fi
@ -3870,26 +3879,30 @@ tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"; da="$da$($ECHO '\r')" da="test$N $(date) $RANDOM"; da="$da$($ECHO '\r')"
# we have a normal tcp echo listening - so the socks header must appear in answer # we have a normal tcp echo listening - so the socks header must appear in answer
newport tcp6 # provide free port number in $PORT newport tcp6 # provide free port number in $PORT
CMD2="$TRACE $SOCAT $opts TCP6-L:$PORT,$REUSEADDR exec:\"./socks4echo.sh\"" CMD0="$TRACE $SOCAT $opts TCP6-L:$PORT,$REUSEADDR exec:\"./socks4echo.sh\""
CMD="$TRACE $SOCAT $opts - socks4:$LOCALHOST6:32.98.76.54:32109,socksport=$PORT",socksuser="nobody" CMD1="$TRACE $SOCAT $opts - socks4:$LOCALHOST6:32.98.76.54:32109,socksport=$PORT",socksuser="nobody"
printf "test $F_n $TEST... " $N printf "test $F_n $TEST... " $N
eval "$CMD2 2>\"${te}1\" &" eval "$CMD0 2>\"${te}0\" &"
pid=$! # background process id pid=$! # background process id
waittcp6port $PORT 1 waittcp6port $PORT 1
echo "$da" |$CMD >$tf 2>"${te}2" echo "$da" |$CMD1 >${tf}1 2>"${te}1"
if ! echo "$da" |diff - "$tf" >"$tdiff"; then if ! echo "$da" |diff - "${tf}1" >"$tdiff"; then
$PRINTF "$FAILED: $TRACE $SOCAT:\n" $PRINTF "$FAILED: $TRACE $SOCAT:\n"
echo "$CMD2 &" echo "$CMD0 &"
echo "$CMD" cat "${te}0" >&2
cat "${te}1" echo "$CMD1"
cat "${te}2" cat "${te}1" >&2
cat "$tdiff" echo "// diff:" >&2
cat "$tdiff" >&2
numFAIL=$((numFAIL+1)) numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N" listFAIL="$listFAIL $N"
else else
$PRINTF "$OK\n" $PRINTF "$OK\n"
if [ -n "$debug" ]; then cat "${te}1" "${te}2"; fi if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
numOK=$((numOK+1)) if [ "$DEBUG" ]; then cat "${te}0" >&2; fi
if [ "$VERBOSE" ]; then echo "$CMD1"; fi
if [ "$DEBUG" ]; then cat "${te}1" >&2; fi
numOK=$((numOK+1))
fi fi
kill $pid 2>/dev/null kill $pid 2>/dev/null
wait wait
@ -7885,24 +7898,34 @@ rc1=$?
rc2=$? rc2=$?
kill $pid0 2>/dev/null; wait kill $pid0 2>/dev/null; wait
if [ $rc1 != 0 -o $rc2 != 0 ]; then if [ $rc1 != 0 -o $rc2 != 0 ]; then
$PRINTF "$FAILED\n" $PRINTF "$FAILED (client(s) failed)\n"
echo "$CMD0 &" echo "$CMD0 &"
cat "${te}0" >&2
echo "$CMD1" echo "$CMD1"
cat "${te}0" cat "${te}1" >&2
cat "${te}1" echo "$CMD1"
cat "${te}2" cat "${te}2" >&2
numFAIL=$((numFAIL+1)) numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N" listFAIL="$listFAIL $N"
elif echo "$da" |diff - "${tf}" >"$tdiff"; then elif echo "$da" |diff - "${tf}" >"$tdiff"; then
$PRINTF "$OK\n" $PRINTF "$OK\n"
if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
if [ "$DEBUG" ]; then cat "${te}0" >&2; fi
if [ "$VERBOSE" ]; then echo "$CMD1"; fi
if [ "$DEBUG" ]; then cat "${te}1" >&2; fi
if [ "$VERBOSE" ]; then echo "$CMD2"; fi
if [ "$DEBUG" ]; then cat "${te}2" >&2; fi
numOK=$((numOK+1)) numOK=$((numOK+1))
else else
$PRINTF "$FAILED\n" $PRINTF "$FAILED (diff)\n"
echo "$CMD0 &" echo "$CMD0 &"
cat "${te}0" >&2
echo "$CMD1" echo "$CMD1"
cat "${te}0" cat "${te}1" >&2
cat "${te}1" echo "$CMD2"
cat "${tdiff}" cat "${te}2" >&2
echo "// diff:" >&2
cat "${tdiff}" >&2
numFAIL=$((numFAIL+1)) numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N" listFAIL="$listFAIL $N"
fi fi

View file

@ -112,11 +112,11 @@ const struct optdesc opt_res_dnsrch = { "res-dnsrch", "dnsrch", OPT_RES_DN
int xioinit_ip( int xioinit_ip(
struct single *sfd, int *pf,
int *pf) char ipv)
{ {
if (*pf == PF_UNSPEC) { if (*pf == PF_UNSPEC) {
switch (xioparms.preferred_ip) { switch (ipv) {
case '0': *pf = PF_UNSPEC; break; case '0': *pf = PF_UNSPEC; break;
#if WITH_IP4 #if WITH_IP4
case '4': *pf = PF_INET; break; case '4': *pf = PF_INET; break;
@ -254,7 +254,7 @@ int xiogetaddrinfo(const char *node, const char *service,
} }
if (error_num == EAI_SERVICE && protocol != 0) { if (error_num == EAI_SERVICE && protocol != 0) {
if (hints.ai_protocol == 0) { if (hints.ai_protocol == 0) {
Error7("getaddrinfo\"%s\", \"%s\", {%d,%d,%d,%d}, {}): %s", Error7("getaddrinfo\"%s\", \"%s\", {0x%02x,%d,%d,%d}, {}): %s",
node?node:"NULL", service?service:"NULL", node?node:"NULL", service?service:"NULL",
hints.ai_flags, hints.ai_family, hints.ai_flags, hints.ai_family,
hints.ai_socktype, hints.ai_protocol, hints.ai_socktype, hints.ai_protocol,
@ -290,7 +290,7 @@ int xiogetaddrinfo(const char *node, const char *service,
while (record) { while (record) {
char buff[256/*!*/]; char buff[256/*!*/];
sockaddr_info(record->ai_addr, record->ai_addrlen, buff, sizeof(buff)); sockaddr_info(record->ai_addr, record->ai_addrlen, buff, sizeof(buff));
Debug5("getaddrinfo() -> flags=%d family=%d socktype=%d protocol=%d addr=%s", record->ai_flags, record->ai_family, record->ai_socktype, record->ai_protocol, buff); Debug5("getaddrinfo() -> flags=0x%02x family=%d socktype=%d protocol=%d addr=%s", record->ai_flags, record->ai_family, record->ai_socktype, record->ai_protocol, buff);
record = record->ai_next; record = record->ai_next;
} }
#endif /* WITH_MSGLEVEL <= E_DEBUG */ #endif /* WITH_MSGLEVEL <= E_DEBUG */
@ -425,7 +425,7 @@ void xiofreeaddrinfo(struct addrinfo *res) {
/* A simple resolver interface that just returns one address, /* A simple resolver interface that just returns one address,
the first found by calling xiogetaddrinfo(). the first found by calling xiogetaddrinfo().
family may be AF_INET, AF_INET6, or AF_UNSPEC. family may be AF_INET, AF_INET6, or AF_UNSPEC;
Returns -1 when an error occurred or when no result found. Returns -1 when an error occurred or when no result found.
*/ */
int xioresolve(const char *node, const char *service, int xioresolve(const char *node, const char *service,
@ -434,6 +434,7 @@ int xioresolve(const char *node, const char *service,
const int ai_flags[2], const unsigned long res_opts[2]) const int ai_flags[2], const unsigned long res_opts[2])
{ {
struct addrinfo *res = NULL; struct addrinfo *res = NULL;
struct addrinfo *aip;
int rc; int rc;
rc = xiogetaddrinfo(node, service, family, socktype, protocol, rc = xiogetaddrinfo(node, service, family, socktype, protocol,
@ -455,8 +456,23 @@ int xioresolve(const char *node, const char *service,
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?"\"":"");
} }
memcpy(addr, res->ai_addr, res->ai_addrlen);
*addrlen = res->ai_addrlen; aip = res;
if (ai_flags[0] & AI_PASSIVE && family == PF_UNSPEC) {
/* We select the first IPv6 address, if available,
because this might accept IPv4 connections too */
struct addrinfo *aip = res;
while (aip != NULL) {
if (aip->ai_family == PF_INET6)
break;
aip = aip->ai_next;
}
if (aip == NULL)
aip = res;
}
memcpy(addr, aip->ai_addr, aip->ai_addrlen);
*addrlen = aip->ai_addrlen;
xiofreeaddrinfo(res); xiofreeaddrinfo(res);
return 0; return 0;
} }

View file

@ -42,7 +42,7 @@ extern const struct optdesc opt_res_defnames;
extern const struct optdesc opt_res_stayopen; extern const struct optdesc opt_res_stayopen;
extern const struct optdesc opt_res_dnsrch; extern const struct optdesc opt_res_dnsrch;
extern int xioinit_ip(struct single *sfd, int *pf); 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], const unsigned long res_opts[2]); extern int xiogetaddrinfo(const char *node, const char *service, int family, int socktype, int protocol, struct addrinfo **res, const int ai_flags[2], const unsigned long res_opts[2]);
extern void xiofreeaddrinfo(struct addrinfo *res); extern void xiofreeaddrinfo(struct addrinfo *res);

View file

@ -38,11 +38,13 @@ int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts,
int level; int level;
int result; int result;
struct addrinfo **ai_sorted;
int i;
if (argc != 3) { if (argc != 3) {
Error2("%s: wrong number of parameters (%d instead of 2)", argv[0], argc-1); Error2("%s: wrong number of parameters (%d instead of 2)", argv[0], argc-1);
} }
xioinit_ip(xfd, &pf);
xfd->howtoend = END_SHUTDOWN; xfd->howtoend = END_SHUTDOWN;
if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1; if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
@ -69,18 +71,32 @@ int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts,
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 */
themp = themlist; /* Loop over themlist - no, over ai_sorted */
/* Loop over themlist */
result = STAT_RETRYLATER; result = STAT_RETRYLATER;
i = 0;
themp = ai_sorted[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 (xfd->forever || xfd->retry || themp->ai_next != NULL) { if (xfd->forever || xfd->retry || ai_sorted[i] != NULL) {
level = E_INFO; level = E_INFO;
} else } else
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
@ -94,7 +110,7 @@ int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts,
lowport, level); lowport, level);
if (result == STAT_OK) if (result == STAT_OK)
break; break;
themp = themp->ai_next; themp = ai_sorted[i++];
if (themp == NULL) { if (themp == NULL) {
result = STAT_RETRYLATER; result = STAT_RETRYLATER;
} }
@ -114,7 +130,7 @@ int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts,
} }
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
default: default:
xiofreeaddrinfo(themlist); free(ai_sorted);
free(opts0);free(opts); free(opts0);free(opts);
return result; return result;
} }
@ -131,7 +147,7 @@ int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts,
if (xfd->forever || --xfd->retry) { if (xfd->forever || --xfd->retry) {
Nanosleep(&xfd->intervall, NULL); continue; Nanosleep(&xfd->intervall, NULL); continue;
} }
xiofreeaddrinfo(themlist); free(ai_sorted);
free(opts0); free(opts0);
return STAT_RETRYLATER; return STAT_RETRYLATER;
} }
@ -154,13 +170,14 @@ int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts,
} }
} 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(themlist); xiofreeaddrinfo(themlist);
if ((result = _xio_openlate(xfd, opts)) < 0) { if ((result = _xio_openlate(xfd, opts)) < 0) {
free(opts0);free(opts); free(opts0);free(opts);
return result; return result;
} }
free(opts0);free(opts); free(opts0); free(opts);
return 0; return 0;
} }
@ -191,12 +208,14 @@ int
retropt_socket_pf(opts, pf); retropt_socket_pf(opts, pf);
if ((result = if (hostname != NULL || portname != NULL) {
if ((result =
xiogetaddrinfo(hostname, portname, xiogetaddrinfo(hostname, portname,
*pf, socktype, protocol, *pf, socktype, protocol,
themlist, ai_flags, res_opts)) themlist, ai_flags, res_opts))
!= STAT_OK) { != STAT_OK) {
return STAT_NORETRY; /*! STAT_RETRYLATER? */ return STAT_NORETRY; /*! STAT_RETRYLATER? */
}
} }
applyopts(-1, opts, PH_EARLY); applyopts(-1, opts, PH_EARLY);
@ -299,7 +318,7 @@ int xioopen_ipapp_listen(int argc, const char *argv[], struct opt *opts,
Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1); Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1);
} }
xioinit_ip(&xfd->stream, &pf); xioinit_ip(&pf, xioparms.default_ip);
if (pf == PF_UNSPEC) { if (pf == PF_UNSPEC) {
#if WITH_IP4 && WITH_IP6 #if WITH_IP4 && WITH_IP6
switch (xioparms.default_ip) { switch (xioparms.default_ip) {
@ -338,4 +357,60 @@ int xioopen_ipapp_listen(int argc, const char *argv[], struct opt *opts,
} }
#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

@ -28,5 +28,6 @@ extern int xioopen_ipapp_listen(int argc, const char *argv[], struct opt *opts,
groups_t groups, int socktype, groups_t groups, int socktype,
int ipproto, int protname); int ipproto, int protname);
extern int _xioopen_ipapp_listen_prepare(struct opt *opts, struct opt **opts0, const char *portname, int *pf, int ipproto, const int ai_flags[2], const unsigned long res_opts[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], const unsigned long res_opts[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

@ -252,7 +252,9 @@ static int
struct addrinfo *themlist, *themp; struct addrinfo *themlist, *themp;
bool needbind = false; bool needbind = false;
bool lowport = false; bool lowport = false;
int level; int level = E_ERROR;
struct addrinfo **ai_sorted;
int i;
SSL_CTX* ctx; SSL_CTX* ctx;
bool opt_ver = true; /* verify peer certificate */ bool opt_ver = true; /* verify peer certificate */
char *opt_cert = NULL; /* file name of client certificate */ char *opt_cert = NULL; /* file name of client certificate */
@ -280,7 +282,6 @@ static int
return STAT_NORETRY; return STAT_NORETRY;
} }
xioinit_ip(xfd, &pf);
xfd->howtoend = END_SHUTDOWN; xfd->howtoend = END_SHUTDOWN;
if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1; if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
applyopts(-1, opts, PH_INIT); applyopts(-1, opts, PH_INIT);
@ -340,19 +341,34 @@ static int
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 */
#if WITH_RETRY /* Loop over ai_sorted list */
if (xfd->forever || xfd->retry) { i = 0;
level = E_INFO; themp = ai_sorted[i++];
} else
#endif /* WITH_RETRY */
level = E_ERROR;
themp = themlist;
/* loop over themlist */
while (themp != NULL) { while (themp != NULL) {
/* This cannot fork because we retrieved fork option above */
#if WITH_RETRY
if (xfd->forever || xfd->retry || ai_sorted[i] != NULL) {
level = E_INFO;
} else
#endif /* WITH_RETRY */
level = E_ERROR;
/* This cannot fork because we retrieved fork option above */
result = result =
_xioopen_connect(xfd, _xioopen_connect(xfd,
needbind?us:NULL, uslen, needbind?us:NULL, uslen,
@ -360,7 +376,7 @@ static int
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 = themp->ai_next; themp = ai_sorted[i++];
if (themp == NULL) { if (themp == NULL) {
result = STAT_RETRYLATER; result = STAT_RETRYLATER;
} }
@ -378,16 +394,16 @@ static int
--xfd->retry; --xfd->retry;
continue; continue;
} }
xiofreeaddrinfo(themlist); free(ai_sorted);
return STAT_NORETRY; return STAT_NORETRY;
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
default: default:
xiofreeaddrinfo(themlist); free(ai_sorted);
return result; return result;
} }
/*! isn't this too early? */ /*! isn't this too early? */
if ((result = _xio_openlate(xfd, opts)) < 0) { if ((result = _xio_openlate(xfd, opts)) < 0) {
xiofreeaddrinfo(themlist); free(ai_sorted);
return result; return result;
} }
@ -449,6 +465,7 @@ static int
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
break; break;
} while (true); /* drop out on success */ } while (true); /* drop out on success */
free(ai_sorted);
xiofreeaddrinfo(themlist); xiofreeaddrinfo(themlist);
openssl_conn_loginfo(xfd->para.openssl.ssl); openssl_conn_loginfo(xfd->para.openssl.ssl);
@ -572,7 +589,6 @@ static int
return STAT_NORETRY; return STAT_NORETRY;
} }
xioinit_ip(xfd, &pf);
#if WITH_IP4 && WITH_IP6 #if WITH_IP4 && WITH_IP6
switch (xioparms.default_ip) { switch (xioparms.default_ip) {
case '4': pf = PF_INET; break; case '4': pf = PF_INET; break;

View file

@ -93,6 +93,8 @@ static int xioopen_proxy_connect(int argc, const char *argv[], struct opt *opts,
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 *themlist, *themp;
struct addrinfo **ai_sorted;
int i;
const char *proxyname; char *proxyport = NULL; const char *proxyname; char *proxyport = NULL;
const char *targetname, *targetport; const char *targetname, *targetport;
int ipproto = IPPROTO_TCP; int ipproto = IPPROTO_TCP;
@ -110,7 +112,6 @@ static int xioopen_proxy_connect(int argc, const char *argv[], struct opt *opts,
targetname = argv[2]; targetname = argv[2];
targetport = argv[3]; targetport = argv[3];
xioinit_ip(xfd, &pf);
xfd->howtoend = END_SHUTDOWN; xfd->howtoend = END_SHUTDOWN;
if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1; if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
applyopts(-1, opts, PH_INIT); applyopts(-1, opts, PH_INIT);
@ -128,32 +129,53 @@ static int xioopen_proxy_connect(int argc, const char *argv[], struct opt *opts,
result = _xioopen_proxy_prepare(proxyvars, opts, targetname, targetport, result = _xioopen_proxy_prepare(proxyvars, opts, targetname, targetport,
xfd->para.socket.ip.ai_flags, xfd->para.socket.ip.ai_flags,
xfd->para.socket.ip.res_opts); xfd->para.socket.ip.res_opts);
if (result != STAT_OK) return result; if (result != STAT_OK)
return result;
result = Notice4("opening connection to %s:%u via proxy %s:%s",
proxyvars->targetaddr, proxyvars->targetport, proxyname, proxyport);
i = 0;
do { /* loop over retries (failed connect and proxy-request attempts) */
level = E_INFO;
result =
_xioopen_ipapp_prepare(opts, &opts0, proxyname, proxyport, _xioopen_ipapp_prepare(opts, &opts0, proxyname, proxyport,
&pf, ipproto, &pf, ipproto,
xfd->para.socket.ip.ai_flags, xfd->para.socket.ip.ai_flags,
xfd->para.socket.ip.res_opts, xfd->para.socket.ip.res_opts,
&themlist, us, &uslen, &themlist, us, &uslen,
&needbind, &lowport, socktype); &needbind, &lowport, socktype);
if (result != STAT_OK) return result; if (result != STAT_OK)
return result;
Notice4("opening connection to %s:%u via proxy %s:%s",
proxyvars->targetaddr, proxyvars->targetport, proxyname, proxyport);
do { /* loop over failed connect and proxy connect attempts */
#if WITH_RETRY
if (xfd->forever || xfd->retry) {
level = E_INFO;
} else
#endif /* WITH_RETRY */
level = E_ERROR;
/* Count addrinfo entries */
themp = themlist; themp = themlist;
/* Loop over themlist */ i = 0;
while (themp != NULL) { 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 */
i = 0;
themp = ai_sorted[i++];
while (themp != NULL) {
Notice4("opening connection to %s:%u via proxy %s:%s",
proxyvars->targetaddr, proxyvars->targetport, proxyname, proxyport);
#if WITH_RETRY
if (xfd->forever || xfd->retry || ai_sorted[i] != NULL) {
level = E_INFO;
} else
#endif /* WITH_RETRY */
level = E_ERROR;
result = result =
_xioopen_connect(xfd, _xioopen_connect(xfd,
needbind?us:NULL, sizeof(*us), needbind?us:NULL, sizeof(*us),
@ -161,7 +183,7 @@ static int xioopen_proxy_connect(int argc, const char *argv[], struct opt *opts,
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 = themp->ai_next; themp = ai_sorted[i++];
if (themp == NULL) { if (themp == NULL) {
result = STAT_RETRYLATER; result = STAT_RETRYLATER;
} }
@ -170,18 +192,19 @@ static int xioopen_proxy_connect(int argc, const char *argv[], struct opt *opts,
#if WITH_RETRY #if WITH_RETRY
case STAT_RETRYLATER: case STAT_RETRYLATER:
case STAT_RETRYNOW: case STAT_RETRYNOW:
if (xfd->forever || xfd->retry--) { if (xfd->forever || xfd->retry) {
if (result == STAT_RETRYLATER) Nanosleep(&xfd->intervall, NULL); --xfd->retry;
if (result == STAT_RETRYLATER)
Nanosleep(&xfd->intervall, NULL);
continue; continue;
} }
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
default: default:
free(ai_sorted);
xiofreeaddrinfo(themlist); xiofreeaddrinfo(themlist);
return result; return result;
} }
} }
xiofreeaddrinfo(themlist);
applyopts(xfd->fd, opts, PH_ALL);
if ((result = _xio_openlate(xfd, opts)) < 0) if ((result = _xio_openlate(xfd, opts)) < 0)
return result; return result;

View file

@ -74,7 +74,7 @@ int xioopen_rawip_sendto(int argc, const char *argv[], struct opt *opts,
return STAT_NORETRY; return STAT_NORETRY;
} }
xioinit_ip(&xxfd->stream, &pf); xioinit_ip(&pf, xioparms.preferred_ip);
if ((result = _xioopen_rawip_sendto(argv[1], argv[2], opts, xioflags, xxfd, if ((result = _xioopen_rawip_sendto(argv[1], argv[2], opts, xioflags, xxfd,
groups, &pf)) != STAT_OK) { groups, &pf)) != STAT_OK) {
return result; return result;
@ -165,7 +165,7 @@ int xioopen_rawip_datagram(int argc, const char *argv[], struct opt *opts,
return STAT_NORETRY; return STAT_NORETRY;
} }
xioinit_ip(xfd, &pf); xioinit_ip(&pf, xioparms.preferred_ip);
if ((result = if ((result =
_xioopen_rawip_sendto(argv[1], argv[2], opts, xioflags, xxfd, _xioopen_rawip_sendto(argv[1], argv[2], opts, xioflags, xxfd,
groups, &pf)) != STAT_OK) { groups, &pf)) != STAT_OK) {
@ -220,7 +220,7 @@ int xioopen_rawip_recvfrom(int argc, const char *argv[], struct opt *opts,
return STAT_NORETRY; return STAT_NORETRY;
} }
xioinit_ip(&xfd->stream, &pf); xioinit_ip(&pf, xioparms.default_ip);
if ((ipproto = strtoul(protname, &garbage, 0)) >= 256) { if ((ipproto = strtoul(protname, &garbage, 0)) >= 256) {
Error2("xioopen_rawip_recvfrom(\"%s\",,): protocol number exceeds 255 (%u)", Error2("xioopen_rawip_recvfrom(\"%s\",,): protocol number exceeds 255 (%u)",
protname, ipproto); protname, ipproto);
@ -284,7 +284,7 @@ int xioopen_rawip_recv(int argc, const char *argv[], struct opt *opts,
return STAT_NORETRY; return STAT_NORETRY;
} }
xioinit_ip(&xfd->stream, &pf); xioinit_ip(&pf, xioparms.default_ip);
if ((ipproto = strtoul(protname, &garbage, 0)) >= 256) { if ((ipproto = strtoul(protname, &garbage, 0)) >= 256) {
Error2("xioopen_rawip_recv(\"%s\",,): protocol number exceeds 255 (%u)", Error2("xioopen_rawip_recv(\"%s\",,): protocol number exceeds 255 (%u)",
protname, ipproto); protname, ipproto);

View file

@ -54,6 +54,8 @@ static int xioopen_socks4_connect(int argc, const char *argv[], struct opt *opts
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 *themlist, *themp;
struct addrinfo **ai_sorted;
int i;
bool needbind = false; bool needbind = false;
bool lowport = false; bool lowport = false;
char infobuff[256]; char infobuff[256];
@ -72,7 +74,6 @@ static int xioopen_socks4_connect(int argc, const char *argv[], struct opt *opts
targetname = argv[2]; targetname = argv[2];
targetport = argv[3]; targetport = argv[3];
xioinit_ip(xfd, &pf);
xfd->howtoend = END_SHUTDOWN; xfd->howtoend = END_SHUTDOWN;
if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1; if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
applyopts(-1, opts, PH_INIT); applyopts(-1, opts, PH_INIT);
@ -82,28 +83,39 @@ static int xioopen_socks4_connect(int argc, const char *argv[], struct opt *opts
retropt_bool(opts, OPT_FORK, &dofork); retropt_bool(opts, OPT_FORK, &dofork);
result = _xioopen_socks4_prepare(targetport, opts, &socksport, sockhead, &buflen); result = _xioopen_socks4_prepare(targetport, opts, &socksport, sockhead, &buflen);
if (result != STAT_OK) return result; if (result != STAT_OK)
result = return result;
_xioopen_ipapp_prepare(opts, &opts0, sockdname, socksport,
&pf, ipproto,
xfd->para.socket.ip.ai_flags,
xfd->para.socket.ip.res_opts,
&themlist, us, &uslen,
&needbind, &lowport, socktype);
Notice5("opening connection to %s:%u via socks4 server %s:%s as user \"%s\"", Notice5("opening connection to %s:%u via socks4 server %s:%s as user \"%s\"",
targetname, targetname,
ntohs(sockhead->port), ntohs(sockhead->port),
sockdname, socksport, sockhead->userid); sockdname, socksport, sockhead->userid);
do { /* loop over failed connect and socks-request attempts */ i = 0;
do { /* loop over retries (failed connect and socks-request attempts) */
#if WITH_RETRY level = E_INFO;
if (xfd->forever || xfd->retry) {
level = E_INFO; result =
} else _xioopen_ipapp_prepare(opts, &opts0, sockdname, socksport,
#endif /* WITH_RETRY */ &pf, ipproto,
level = E_ERROR; xfd->para.socket.ip.ai_flags,
xfd->para.socket.ip.res_opts,
&themlist, us, &uslen,
&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 */
@ -124,41 +136,51 @@ static int xioopen_socks4_connect(int argc, const char *argv[], struct opt *opts
return result; return result;
} }
themp = themlist;
/* loop over themlist */ /* loop over themlist */
i = 0;
themp = ai_sorted[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 (xfd->forever || xfd->retry || ai_sorted[i] != NULL) {
level = E_INFO;
} else
#endif /* WITH_RETRY */
level = E_ERROR;
/* this cannot fork because we retrieved fork option above */ /* this cannot fork because we retrieved fork option above */
result = result =
_xioopen_connect(xfd, _xioopen_connect(xfd,
needbind?us:NULL, sizeof(*us), needbind?us:NULL, sizeof(*us),
themp->ai_addr, themp->ai_addrlen, themp->ai_addr, themp->ai_addrlen,
opts, pf?pf:themp->ai_family, socktype, IPPROTO_TCP, lowport, level); opts, pf?pf:themp->ai_family, socktype, IPPROTO_TCP,
if (result == STAT_OK) lowport, level);
break; if (result == STAT_OK)
themp = themp->ai_next; break;
if (themp == NULL) { themp = ai_sorted[i++];
result = STAT_RETRYLATER; if (themp == NULL) {
result = STAT_RETRYLATER;
}
} }
switch (result) { switch (result) {
case STAT_OK: break; case STAT_OK: break;
#if WITH_RETRY #if WITH_RETRY
case STAT_RETRYLATER: case STAT_RETRYLATER:
case STAT_RETRYNOW: case STAT_RETRYNOW:
if (xfd->forever || xfd->retry--) { if (xfd->forever || xfd->retry) {
if (result == STAT_RETRYLATER) Nanosleep(&xfd->intervall, NULL); --xfd->retry;
if (result == STAT_RETRYLATER)
Nanosleep(&xfd->intervall, NULL);
continue; continue;
} }
#endif /* WITH_RETRY */ #endif /* WITH_RETRY */
default: default:
free(ai_sorted);
xiofreeaddrinfo(themlist); xiofreeaddrinfo(themlist);
return result; return result;
} }
}
xiofreeaddrinfo(themlist);
applyopts(xfd->fd, opts, PH_ALL);
if ((result = _xio_openlate(xfd, opts)) < 0) if ((result = _xio_openlate(xfd, opts)) < 0)
return result; return result;

View file

@ -297,7 +297,7 @@ int xioopen_ipdgram_listen(int argc, const char *argv[], struct opt *opts,
Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1); Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1);
} }
xioinit_ip(&xfd->stream, &pf); xioinit_ip(&pf, xioparms.default_ip);
if (pf == PF_UNSPEC) { if (pf == PF_UNSPEC) {
#if WITH_IP4 && WITH_IP6 #if WITH_IP4 && WITH_IP6
switch (xioparms.default_ip) { switch (xioparms.default_ip) {
@ -354,7 +354,7 @@ int xioopen_udp_sendto(int argc, const char *argv[], struct opt *opts,
return STAT_NORETRY; return STAT_NORETRY;
} }
xioinit_ip(&xfd->stream, &pf); //xioinit_ip(&pf, xioparms.preferred_ip);
retropt_socket_pf(opts, &pf); retropt_socket_pf(opts, &pf);
if ((result = _xioopen_udp_sendto(argv[1], argv[2], opts, xioflags, xfd, if ((result = _xioopen_udp_sendto(argv[1], argv[2], opts, xioflags, xfd,
groups, pf, socktype, ipproto)) groups, pf, socktype, ipproto))
@ -451,7 +451,7 @@ int xioopen_udp_datagram(int argc, const char *argv[], struct opt *opts,
return STAT_NORETRY; return STAT_NORETRY;
} }
xioinit_ip(xfd, &pf); //xioinit_ip(&pf, xioparms.preferred_ip);
if ((hostname = strdup(argv[1])) == NULL) { if ((hostname = strdup(argv[1])) == NULL) {
Error1("strdup(\"%s\"): out of memory", argv[1]); Error1("strdup(\"%s\"): out of memory", argv[1]);
return STAT_RETRYLATER; return STAT_RETRYLATER;
@ -516,7 +516,7 @@ int xioopen_udp_recvfrom(int argc, const char *argv[], struct opt *opts,
return STAT_NORETRY; return STAT_NORETRY;
} }
xioinit_ip(&xfd->stream, &pf); xioinit_ip(&pf, xioparms.default_ip);
xfd->stream.howtoend = END_NONE; xfd->stream.howtoend = END_NONE;
retropt_socket_pf(opts, &pf); retropt_socket_pf(opts, &pf);
if (pf == PF_UNSPEC) { if (pf == PF_UNSPEC) {
@ -541,8 +541,7 @@ int xioopen_udp_recvfrom(int argc, const char *argv[], struct opt *opts,
if ((result = if ((result =
xioresolve(NULL, argv[1], pf, socktype, ipproto, &us, &uslen, xioresolve(NULL, argv[1], pf, socktype, ipproto, &us, &uslen,
ai_flags2, ai_flags2, xfd->stream.para.socket.ip.res_opts))
xfd->stream.para.socket.ip.res_opts))
!= STAT_OK) { != STAT_OK) {
return result; return result;
} }
@ -602,7 +601,7 @@ int xioopen_udp_recv(int argc, const char *argv[], struct opt *opts,
return STAT_NORETRY; return STAT_NORETRY;
} }
xioinit_ip(&xfd->stream, &pf); //xioinit_ip(&pf, xioparms.default_ip);
retropt_socket_pf(opts, &pf); retropt_socket_pf(opts, &pf);
if (pf == PF_UNSPEC) { if (pf == PF_UNSPEC) {
#if WITH_IP4 && WITH_IP6 #if WITH_IP4 && WITH_IP6

View file

@ -95,9 +95,11 @@ int xioinitialize(void) {
switch (preferred_ip[0]) { switch (preferred_ip[0]) {
case '4': case '4':
case '6': case '6':
xioparms.preferred_ip = preferred_ip[0]; break; xioparms.preferred_ip = preferred_ip[0];
break;
default: default:
xioparms.preferred_ip = '0'; break; xioparms.preferred_ip = '0';
break;
} }
} }
} }

View file

@ -386,9 +386,9 @@ xiofile_t *xioopen(const char *addr, /* address specification */
int xioflags) { int xioflags) {
xiofile_t *xfd; xiofile_t *xfd;
if (xioinitialize() < 0) { //if (xioinitialize() < 0) {
return NULL; // return NULL;
} //}
Debug1("xioopen(\"%s\")", addr); Debug1("xioopen(\"%s\")", addr);