diff --git a/CHANGES b/CHANGES index e69e89b..80ac1f2 100644 --- a/CHANGES +++ b/CHANGES @@ -75,14 +75,22 @@ Features: Tests: TRY_ADDRS_4 TRY_ADDRS_4_6 Feature recommended by Anand Buddhdev. - configure option --with-default-ipv allows to specify at build time if - IPv4, IPv6, or none of these is the preferred default. + configure option --enable-default-ipv allows to specify at build time if + 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. - Tests: TCP_ENV6 TCP_DASH6 - - New option ai-addrconfig disables name resolution to protocol families - that are not configured on the computer (e.g. IPv6) + New option ai-addrconfig sets or unsets the AI_ADDRCONFIG flag of the + resolver to prevent name resolution to address families that are not + available in the network configuration. Default value is 1 in case the + resolver does not get an address family hint. Flag AI_PASSIVE is now automatically applied for LISTEN, RECV, and RECVFROM type addresses, and with bind option. In addition to its diff --git a/config.h.in b/config.h.in index 6342635..44e8f19 100644 --- a/config.h.in +++ b/config.h.in @@ -723,7 +723,7 @@ #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__ diff --git a/configure.ac b/configure.ac index f5fae83..1388d59 100644 --- a/configure.ac +++ b/configure.ac @@ -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_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 - "") 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");; 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], [AC_DEFINE(WITH_DEFAULT_IPV, '0') AC_MSG_RESULT("0")]) diff --git a/doc/socat.yo b/doc/socat.yo index 8da8ac7..909fd77 100644 --- a/doc/socat.yo +++ b/doc/socat.yo @@ -2244,7 +2244,7 @@ label(OPTION_AI_ADDRCONFIG) 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 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]))) Sets of unsets the AI_ADDRCONFIG flag for code(getaddrinfo)) calls. Default is 1 for LISTEN, RECV, and RECVFROM type addresses, and with diff --git a/test.sh b/test.sh index dd80bc1..f459065 100755 --- a/test.sh +++ b/test.sh @@ -2483,20 +2483,29 @@ waittcp6port $tsl 1 echo "$da" |$CMD2 >>"$tf" 2>>"${te}2" if [ $? -ne 0 ]; then $PRINTF "$FAILED: $TRACE $SOCAT:\n" - echo "$CMD1 &" + echo "SOCAT_DEFAULT_LISTEN_IP=6 $CMD1 &" + cat "${te}1" >&2 echo "$CMD2" - cat "${te}1" "${te}2" + cat "${te}2" >&2 numFAIL=$((numFAIL+1)) listFAIL="$listFAIL $N" elif ! echo "$da" |diff - "$tf" >"$tdiff"; then - $PRINTF "$FAILED: diff:\n" - cat "$tdiff" + $PRINTF "$FAILED (diff):\n" + 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)) listFAIL="$listFAIL $N" else - $PRINTF "$OK\n" - if [ -n "$debug" ]; then cat "${te}1" "${te}2"; fi - numOK=$((numOK+1)) + $PRINTF "$OK\n" + 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)) fi kill $pid 2>/dev/null; wait fi @@ -3870,26 +3879,30 @@ tdiff="$td/test$N.diff" da="test$N $(date) $RANDOM"; da="$da$($ECHO '\r')" # we have a normal tcp echo listening - so the socks header must appear in answer newport tcp6 # provide free port number in $PORT -CMD2="$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" +CMD0="$TRACE $SOCAT $opts TCP6-L:$PORT,$REUSEADDR exec:\"./socks4echo.sh\"" +CMD1="$TRACE $SOCAT $opts - socks4:$LOCALHOST6:32.98.76.54:32109,socksport=$PORT",socksuser="nobody" printf "test $F_n $TEST... " $N -eval "$CMD2 2>\"${te}1\" &" +eval "$CMD0 2>\"${te}0\" &" pid=$! # background process id waittcp6port $PORT 1 -echo "$da" |$CMD >$tf 2>"${te}2" -if ! echo "$da" |diff - "$tf" >"$tdiff"; then +echo "$da" |$CMD1 >${tf}1 2>"${te}1" +if ! echo "$da" |diff - "${tf}1" >"$tdiff"; then $PRINTF "$FAILED: $TRACE $SOCAT:\n" - echo "$CMD2 &" - echo "$CMD" - cat "${te}1" - cat "${te}2" - cat "$tdiff" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + echo "// diff:" >&2 + cat "$tdiff" >&2 numFAIL=$((numFAIL+1)) listFAIL="$listFAIL $N" else - $PRINTF "$OK\n" - if [ -n "$debug" ]; then cat "${te}1" "${te}2"; fi - numOK=$((numOK+1)) + $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 + numOK=$((numOK+1)) fi kill $pid 2>/dev/null wait @@ -7885,24 +7898,34 @@ rc1=$? rc2=$? kill $pid0 2>/dev/null; wait if [ $rc1 != 0 -o $rc2 != 0 ]; then - $PRINTF "$FAILED\n" + $PRINTF "$FAILED (client(s) failed)\n" echo "$CMD0 &" + cat "${te}0" >&2 echo "$CMD1" - cat "${te}0" - cat "${te}1" - cat "${te}2" + cat "${te}1" >&2 + echo "$CMD1" + cat "${te}2" >&2 numFAIL=$((numFAIL+1)) listFAIL="$listFAIL $N" elif echo "$da" |diff - "${tf}" >"$tdiff"; then $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)) else - $PRINTF "$FAILED\n" + $PRINTF "$FAILED (diff)\n" echo "$CMD0 &" + cat "${te}0" >&2 echo "$CMD1" - cat "${te}0" - cat "${te}1" - cat "${tdiff}" + cat "${te}1" >&2 + echo "$CMD2" + cat "${te}2" >&2 + echo "// diff:" >&2 + cat "${tdiff}" >&2 numFAIL=$((numFAIL+1)) listFAIL="$listFAIL $N" fi diff --git a/xio-ip.c b/xio-ip.c index 71933e8..fb6c1bc 100644 --- a/xio-ip.c +++ b/xio-ip.c @@ -112,11 +112,11 @@ const struct optdesc opt_res_dnsrch = { "res-dnsrch", "dnsrch", OPT_RES_DN int xioinit_ip( - struct single *sfd, - int *pf) + int *pf, + char ipv) { if (*pf == PF_UNSPEC) { - switch (xioparms.preferred_ip) { + switch (ipv) { case '0': *pf = PF_UNSPEC; break; #if WITH_IP4 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 (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", hints.ai_flags, hints.ai_family, hints.ai_socktype, hints.ai_protocol, @@ -290,7 +290,7 @@ int xiogetaddrinfo(const char *node, const char *service, while (record) { char buff[256/*!*/]; 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; } #endif /* WITH_MSGLEVEL <= E_DEBUG */ @@ -425,7 +425,7 @@ void xiofreeaddrinfo(struct addrinfo *res) { /* A simple resolver interface that just returns one address, 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. */ 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]) { struct addrinfo *res = NULL; + struct addrinfo *aip; int rc; rc = xiogetaddrinfo(node, service, family, socktype, protocol, @@ -455,8 +456,23 @@ int xioresolve(const char *node, const char *service, 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?"\"":""); } - 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); return 0; } diff --git a/xio-ip.h b/xio-ip.h index b12d4d9..4c63f7a 100644 --- a/xio-ip.h +++ b/xio-ip.h @@ -42,7 +42,7 @@ extern const struct optdesc opt_res_defnames; extern const struct optdesc opt_res_stayopen; 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 void xiofreeaddrinfo(struct addrinfo *res); diff --git a/xio-ipapp.c b/xio-ipapp.c index 93f0e52..3c671da 100644 --- a/xio-ipapp.c +++ b/xio-ipapp.c @@ -38,11 +38,13 @@ int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts, int level; int result; + struct addrinfo **ai_sorted; + int i; + if (argc != 3) { Error2("%s: wrong number of parameters (%d instead of 2)", argv[0], argc-1); } - xioinit_ip(xfd, &pf); xfd->howtoend = END_SHUTDOWN; 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"); } + /* 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 */ - themp = themlist; - /* Loop over themlist */ + /* Loop over themlist - no, over ai_sorted */ result = STAT_RETRYLATER; + i = 0; + themp = ai_sorted[i++]; while (themp != NULL) { Notice1("opening connection to %s", sockaddr_info(themp->ai_addr, themp->ai_addrlen, infobuff, sizeof(infobuff))); #if WITH_RETRY - if (xfd->forever || xfd->retry || themp->ai_next != NULL) { + if (xfd->forever || xfd->retry || ai_sorted[i] != NULL) { level = E_INFO; } else #endif /* WITH_RETRY */ @@ -94,7 +110,7 @@ int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts, lowport, level); if (result == STAT_OK) break; - themp = themp->ai_next; + themp = ai_sorted[i++]; if (themp == NULL) { result = STAT_RETRYLATER; } @@ -114,7 +130,7 @@ int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts, } #endif /* WITH_RETRY */ default: - xiofreeaddrinfo(themlist); + free(ai_sorted); free(opts0);free(opts); return result; } @@ -131,7 +147,7 @@ int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts, if (xfd->forever || --xfd->retry) { Nanosleep(&xfd->intervall, NULL); continue; } - xiofreeaddrinfo(themlist); + free(ai_sorted); free(opts0); return STAT_RETRYLATER; } @@ -154,13 +170,14 @@ int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts, } } while (true); /* only "active" process breaks (master without fork, or child) */ + free(ai_sorted); xiofreeaddrinfo(themlist); if ((result = _xio_openlate(xfd, opts)) < 0) { free(opts0);free(opts); return result; } - free(opts0);free(opts); + free(opts0); free(opts); return 0; } @@ -191,12 +208,14 @@ int retropt_socket_pf(opts, pf); - if ((result = + if (hostname != NULL || portname != NULL) { + if ((result = xiogetaddrinfo(hostname, portname, *pf, socktype, protocol, themlist, ai_flags, res_opts)) != STAT_OK) { return STAT_NORETRY; /*! STAT_RETRYLATER? */ + } } 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); } - xioinit_ip(&xfd->stream, &pf); + xioinit_ip(&pf, xioparms.default_ip); if (pf == PF_UNSPEC) { #if WITH_IP4 && WITH_IP6 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 */ + +/* 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 */ diff --git a/xio-ipapp.h b/xio-ipapp.h index 1767fff..fa25c47 100644 --- a/xio-ipapp.h +++ b/xio-ipapp.h @@ -28,5 +28,6 @@ extern int xioopen_ipapp_listen(int argc, const char *argv[], struct opt *opts, groups_t groups, int socktype, 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 _xio_sort_ip_addresses(struct addrinfo *themlist, struct addrinfo **ai_sorted); #endif /* !defined(__xio_ipapp_h_included) */ diff --git a/xio-openssl.c b/xio-openssl.c index 42197b7..bcf8c2e 100644 --- a/xio-openssl.c +++ b/xio-openssl.c @@ -252,7 +252,9 @@ static int struct addrinfo *themlist, *themp; bool needbind = false; bool lowport = false; - int level; + int level = E_ERROR; + struct addrinfo **ai_sorted; + int i; SSL_CTX* ctx; bool opt_ver = true; /* verify peer certificate */ char *opt_cert = NULL; /* file name of client certificate */ @@ -280,7 +282,6 @@ static int return STAT_NORETRY; } - xioinit_ip(xfd, &pf); xfd->howtoend = END_SHUTDOWN; if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1; applyopts(-1, opts, PH_INIT); @@ -340,19 +341,34 @@ static int 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 */ -#if WITH_RETRY - if (xfd->forever || xfd->retry) { - level = E_INFO; - } else -#endif /* WITH_RETRY */ - level = E_ERROR; - - themp = themlist; - /* loop over themlist */ + /* Loop over ai_sorted list */ + i = 0; + themp = ai_sorted[i++]; 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 = _xioopen_connect(xfd, needbind?us:NULL, uslen, @@ -360,7 +376,7 @@ static int opts, pf?pf:themp->ai_addr->sa_family, socktype, ipproto, lowport, level); if (result == STAT_OK) break; - themp = themp->ai_next; + themp = ai_sorted[i++]; if (themp == NULL) { result = STAT_RETRYLATER; } @@ -378,16 +394,16 @@ static int --xfd->retry; continue; } - xiofreeaddrinfo(themlist); + free(ai_sorted); return STAT_NORETRY; #endif /* WITH_RETRY */ default: - xiofreeaddrinfo(themlist); + free(ai_sorted); return result; } /*! isn't this too early? */ if ((result = _xio_openlate(xfd, opts)) < 0) { - xiofreeaddrinfo(themlist); + free(ai_sorted); return result; } @@ -449,6 +465,7 @@ static int #endif /* WITH_RETRY */ break; } while (true); /* drop out on success */ + free(ai_sorted); xiofreeaddrinfo(themlist); openssl_conn_loginfo(xfd->para.openssl.ssl); @@ -572,7 +589,6 @@ static int return STAT_NORETRY; } - xioinit_ip(xfd, &pf); #if WITH_IP4 && WITH_IP6 switch (xioparms.default_ip) { case '4': pf = PF_INET; break; diff --git a/xio-proxy.c b/xio-proxy.c index 2753d70..b6b4c64 100644 --- a/xio-proxy.c +++ b/xio-proxy.c @@ -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; socklen_t uslen = sizeof(us_sa); struct addrinfo *themlist, *themp; + struct addrinfo **ai_sorted; + int i; const char *proxyname; char *proxyport = NULL; const char *targetname, *targetport; int ipproto = IPPROTO_TCP; @@ -110,7 +112,6 @@ static int xioopen_proxy_connect(int argc, const char *argv[], struct opt *opts, targetname = argv[2]; targetport = argv[3]; - xioinit_ip(xfd, &pf); xfd->howtoend = END_SHUTDOWN; if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1; 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, xfd->para.socket.ip.ai_flags, 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, &pf, ipproto, xfd->para.socket.ip.ai_flags, xfd->para.socket.ip.res_opts, &themlist, us, &uslen, &needbind, &lowport, socktype); - 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; + if (result != STAT_OK) + return result; + /* Count addrinfo entries */ themp = themlist; - /* Loop over 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 */ + 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 = _xioopen_connect(xfd, 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); if (result == STAT_OK) break; - themp = themp->ai_next; + themp = ai_sorted[i++]; if (themp == NULL) { result = STAT_RETRYLATER; } @@ -170,18 +192,19 @@ static int xioopen_proxy_connect(int argc, const char *argv[], struct opt *opts, #if WITH_RETRY case STAT_RETRYLATER: case STAT_RETRYNOW: - if (xfd->forever || xfd->retry--) { - if (result == STAT_RETRYLATER) Nanosleep(&xfd->intervall, NULL); + if (xfd->forever || xfd->retry) { + --xfd->retry; + if (result == STAT_RETRYLATER) + Nanosleep(&xfd->intervall, NULL); continue; } #endif /* WITH_RETRY */ default: + free(ai_sorted); xiofreeaddrinfo(themlist); return result; } } - xiofreeaddrinfo(themlist); - applyopts(xfd->fd, opts, PH_ALL); if ((result = _xio_openlate(xfd, opts)) < 0) return result; diff --git a/xio-rawip.c b/xio-rawip.c index a314a75..2613bfa 100644 --- a/xio-rawip.c +++ b/xio-rawip.c @@ -74,7 +74,7 @@ int xioopen_rawip_sendto(int argc, const char *argv[], struct opt *opts, 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, groups, &pf)) != STAT_OK) { return result; @@ -165,7 +165,7 @@ int xioopen_rawip_datagram(int argc, const char *argv[], struct opt *opts, return STAT_NORETRY; } - xioinit_ip(xfd, &pf); + xioinit_ip(&pf, xioparms.preferred_ip); if ((result = _xioopen_rawip_sendto(argv[1], argv[2], opts, xioflags, xxfd, groups, &pf)) != STAT_OK) { @@ -220,7 +220,7 @@ int xioopen_rawip_recvfrom(int argc, const char *argv[], struct opt *opts, return STAT_NORETRY; } - xioinit_ip(&xfd->stream, &pf); + xioinit_ip(&pf, xioparms.default_ip); if ((ipproto = strtoul(protname, &garbage, 0)) >= 256) { Error2("xioopen_rawip_recvfrom(\"%s\",,): protocol number exceeds 255 (%u)", protname, ipproto); @@ -284,7 +284,7 @@ int xioopen_rawip_recv(int argc, const char *argv[], struct opt *opts, return STAT_NORETRY; } - xioinit_ip(&xfd->stream, &pf); + xioinit_ip(&pf, xioparms.default_ip); if ((ipproto = strtoul(protname, &garbage, 0)) >= 256) { Error2("xioopen_rawip_recv(\"%s\",,): protocol number exceeds 255 (%u)", protname, ipproto); diff --git a/xio-socks.c b/xio-socks.c index 77e5312..26d49ec 100644 --- a/xio-socks.c +++ b/xio-socks.c @@ -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; socklen_t uslen = sizeof(us_sa); struct addrinfo *themlist, *themp; + struct addrinfo **ai_sorted; + int i; bool needbind = false; bool lowport = false; char infobuff[256]; @@ -72,7 +74,6 @@ static int xioopen_socks4_connect(int argc, const char *argv[], struct opt *opts targetname = argv[2]; targetport = argv[3]; - xioinit_ip(xfd, &pf); xfd->howtoend = END_SHUTDOWN; if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1; 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); result = _xioopen_socks4_prepare(targetport, opts, &socksport, sockhead, &buflen); - if (result != STAT_OK) return result; - 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); + if (result != STAT_OK) + return result; Notice5("opening connection to %s:%u via socks4 server %s:%s as user \"%s\"", targetname, ntohs(sockhead->port), 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 - if (xfd->forever || xfd->retry) { - level = E_INFO; - } else -#endif /* WITH_RETRY */ - level = E_ERROR; + level = E_INFO; + + 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); + + /* 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 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; } - themp = themlist; /* loop over themlist */ + i = 0; + themp = ai_sorted[i++]; while (themp != NULL) { Notice1("opening connection to %s", sockaddr_info(themp->ai_addr, themp->ai_addrlen, 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 */ - result = - _xioopen_connect(xfd, - needbind?us:NULL, sizeof(*us), - themp->ai_addr, themp->ai_addrlen, - opts, pf?pf:themp->ai_family, socktype, IPPROTO_TCP, lowport, level); - if (result == STAT_OK) - break; - themp = themp->ai_next; - if (themp == NULL) { - result = STAT_RETRYLATER; + result = + _xioopen_connect(xfd, + needbind?us:NULL, sizeof(*us), + themp->ai_addr, themp->ai_addrlen, + opts, pf?pf:themp->ai_family, socktype, IPPROTO_TCP, + lowport, level); + if (result == STAT_OK) + break; + themp = ai_sorted[i++]; + if (themp == NULL) { + result = STAT_RETRYLATER; + } } switch (result) { case STAT_OK: break; #if WITH_RETRY case STAT_RETRYLATER: case STAT_RETRYNOW: - if (xfd->forever || xfd->retry--) { - if (result == STAT_RETRYLATER) Nanosleep(&xfd->intervall, NULL); + if (xfd->forever || xfd->retry) { + --xfd->retry; + if (result == STAT_RETRYLATER) + Nanosleep(&xfd->intervall, NULL); continue; } #endif /* WITH_RETRY */ default: + free(ai_sorted); xiofreeaddrinfo(themlist); return result; } - } - xiofreeaddrinfo(themlist); - applyopts(xfd->fd, opts, PH_ALL); if ((result = _xio_openlate(xfd, opts)) < 0) return result; diff --git a/xio-udp.c b/xio-udp.c index aaba4d3..a6e31b2 100644 --- a/xio-udp.c +++ b/xio-udp.c @@ -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); } - xioinit_ip(&xfd->stream, &pf); + xioinit_ip(&pf, xioparms.default_ip); if (pf == PF_UNSPEC) { #if WITH_IP4 && WITH_IP6 switch (xioparms.default_ip) { @@ -354,7 +354,7 @@ int xioopen_udp_sendto(int argc, const char *argv[], struct opt *opts, return STAT_NORETRY; } - xioinit_ip(&xfd->stream, &pf); + //xioinit_ip(&pf, xioparms.preferred_ip); retropt_socket_pf(opts, &pf); if ((result = _xioopen_udp_sendto(argv[1], argv[2], opts, xioflags, xfd, groups, pf, socktype, ipproto)) @@ -451,7 +451,7 @@ int xioopen_udp_datagram(int argc, const char *argv[], struct opt *opts, return STAT_NORETRY; } - xioinit_ip(xfd, &pf); + //xioinit_ip(&pf, xioparms.preferred_ip); if ((hostname = strdup(argv[1])) == NULL) { Error1("strdup(\"%s\"): out of memory", argv[1]); return STAT_RETRYLATER; @@ -516,7 +516,7 @@ int xioopen_udp_recvfrom(int argc, const char *argv[], struct opt *opts, return STAT_NORETRY; } - xioinit_ip(&xfd->stream, &pf); + xioinit_ip(&pf, xioparms.default_ip); xfd->stream.howtoend = END_NONE; retropt_socket_pf(opts, &pf); if (pf == PF_UNSPEC) { @@ -541,8 +541,7 @@ int xioopen_udp_recvfrom(int argc, const char *argv[], struct opt *opts, if ((result = xioresolve(NULL, argv[1], pf, socktype, ipproto, &us, &uslen, - ai_flags2, - xfd->stream.para.socket.ip.res_opts)) + ai_flags2, xfd->stream.para.socket.ip.res_opts)) != STAT_OK) { return result; } @@ -602,7 +601,7 @@ int xioopen_udp_recv(int argc, const char *argv[], struct opt *opts, return STAT_NORETRY; } - xioinit_ip(&xfd->stream, &pf); + //xioinit_ip(&pf, xioparms.default_ip); retropt_socket_pf(opts, &pf); if (pf == PF_UNSPEC) { #if WITH_IP4 && WITH_IP6 diff --git a/xioinitialize.c b/xioinitialize.c index ec06c7b..6b7161a 100644 --- a/xioinitialize.c +++ b/xioinitialize.c @@ -95,9 +95,11 @@ int xioinitialize(void) { switch (preferred_ip[0]) { case '4': case '6': - xioparms.preferred_ip = preferred_ip[0]; break; + xioparms.preferred_ip = preferred_ip[0]; + break; default: - xioparms.preferred_ip = '0'; break; + xioparms.preferred_ip = '0'; + break; } } } diff --git a/xioopen.c b/xioopen.c index 680ad44..fa214af 100644 --- a/xioopen.c +++ b/xioopen.c @@ -386,9 +386,9 @@ xiofile_t *xioopen(const char *addr, /* address specification */ int xioflags) { xiofile_t *xfd; - if (xioinitialize() < 0) { - return NULL; - } + //if (xioinitialize() < 0) { + // return NULL; + //} Debug1("xioopen(\"%s\")", addr);