diff --git a/CHANGES b/CHANGES index 0e4c1d1..2d5eb5a 100644 --- a/CHANGES +++ b/CHANGES @@ -27,6 +27,10 @@ Corrections: The rawer option failed because it tried to clear CREAD. Test: RAWER + UDP-SEND and UPD-SENDTO with option lowport always bound to port 1 + instead of a free port in range 640..1023 + Test: UDP_LOWPORT + Porting: OpenSSL, at least 1.1 on Ubuntu, crashed with SIGSEGV under certain conditions: client connection to server with certificate with empty diff --git a/test.sh b/test.sh index 56e82a1..5fee7c9 100755 --- a/test.sh +++ b/test.sh @@ -15496,6 +15496,47 @@ esac PORT=$((PORT+1)) N=$((N+1)) +# Up to 1.7.4.3 there was a bug with the lowport option: +# Active addresses UDP-SEND, UDP-SENDTO always bound to port 1 instead of +# 640..1023 +NAME=UDP_LOWPORT +case "$TESTS" in +*%$N%*|*%functions%*|*%bugs%*|*%socket%*|*%$NAME%*) +TEST="$NAME: UDP4-SEND with lowport" +# Run Socat with UDP4-SEND:...,lowport and full logging and check the +# parameters of bind() call. It port is in the range 640..1023 the test +# succeeded. +if ! eval $NUMCOND; then :; else +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +tdiff="$td/test$N.diff" +da="test$N $(date) $RANDOM" +CMD="$TRACE $SOCAT $opts -d -d -d -d /dev/null UDP4-SENDTO:$LOCALHOST:$PORT,lowport" +printf "test $F_n $TEST... " $N +$CMD >/dev/null 2>"${te}" +rc1=$? +LOWPORT=$(grep 'D bind(.*:' $te |sed 's/.*:\([0-9][0-9]*\),.*/\1/') +#echo "LOWPORT=\"$LOWPORT\"" >&2 +#type socat >&2 +if [[ $LOWPORT =~ [0-9][0-9]* ]] && [ "$LOWPORT" -ge 640 -a "$LOWPORT" -le 1023 ]; then + $PRINTF "$OK\n" + if [ "$VERBOSE" ]; then + echo "$CMD" >&2 + fi + numOK=$((numOK+1)) +else + $PRINTF "$FAILED\n" + echo "$CMD" >&2 + cat "${te}" >&2 + numFAIL=$((numFAIL+1)) + listFAIL="$listFAIL $N" +fi +fi # NUMCOND + ;; +esac +PORT=$((PORT+1)) +N=$((N+1)) + # end of common tests ################################################################################## diff --git a/xio-interface.c b/xio-interface.c index ee20186..475bfd1 100644 --- a/xio-interface.c +++ b/xio-interface.c @@ -69,7 +69,7 @@ int _xioopen_interface(const char *ifname, return _xioopen_dgram_sendto(needbind?&us:NULL, uslen, - opts, xioflags, xfd, groups, pf, socktype, 0); + opts, xioflags, xfd, groups, pf, socktype, 0, 0); } static diff --git a/xio-rawip.c b/xio-rawip.c index a53ad15..15747c0 100644 --- a/xio-rawip.c +++ b/xio-rawip.c @@ -143,7 +143,7 @@ int _xioopen_rawip_sendto(const char *hostname, const char *protname, } return _xioopen_dgram_sendto(needbind?&us:NULL, uslen, - opts, xioflags, xfd, groups, *pf, socktype, ipproto); + opts, xioflags, xfd, groups, *pf, socktype, ipproto, 0); } diff --git a/xio-socket.c b/xio-socket.c index 3d55837..f02b3e2 100644 --- a/xio-socket.c +++ b/xio-socket.c @@ -63,6 +63,14 @@ xiolog_ancillary_socket(struct cmsghdr *cmsg, int *num, char *nambuff, int namlen, char *envbuff, int envlen, char *valbuff, int vallen); +static int xiobind( + struct single *xfd, + union sockaddr_union *us, + size_t uslen, + struct opt *opts, + int pf, + bool alt, + int level); #if WITH_GENERICSOCKET @@ -457,7 +465,7 @@ int _xioopen_socket_sendto(const char *pfname, const char *type, return _xioopen_dgram_sendto(needbind?&us:NULL, uslen, - opts, xioflags, xfd, groups, pf, socktype, proto); + opts, xioflags, xfd, groups, pf, socktype, proto, 0); } @@ -775,111 +783,9 @@ int _xioopen_connect(struct single *xfd, union sockaddr_union *us, size_t uslen, applyopts_cloexec(xfd->fd, opts); -#if WITH_UNIX - if (pf == PF_UNIX && us != NULL) { - applyopts_named(us->un.sun_path, opts, PH_PREOPEN); + if (xiobind(xfd, us, uslen, opts, pf, alt, level) < 0) { + return -1; } -#endif - applyopts(xfd->fd, opts, PH_PREBIND); - applyopts(xfd->fd, opts, PH_BIND); -#if WITH_TCP || WITH_UDP - if (alt) { - union sockaddr_union sin, *sinp; - unsigned short *port, i, N; - div_t dv; - - /* prepare sockaddr for bind probing */ - if (us) { - sinp = us; - } else { - if (them->sa_family == AF_INET) { - socket_in_init(&sin.ip4); -#if WITH_IP6 - } else { - socket_in6_init(&sin.ip6); -#endif - } - sinp = &sin; - } - if (them->sa_family == AF_INET) { - port = &sin.ip4.sin_port; -#if WITH_IP6 - } else if (them->sa_family == AF_INET6) { - port = &sin.ip6.sin6_port; -#endif - } else { - port = 0; /* just to make compiler happy */ - } - /* combine random+step variant to quickly find a free port when only - few are in use, and certainly find a free port in defined time even - if there are almost all in use */ - /* dirt 1: having tcp/udp code in socket function */ - /* dirt 2: using a time related system call for init of random */ - { - /* generate a random port, with millisecond random init */ -#if 0 - struct timeb tb; - ftime(&tb); - srandom(tb.time*1000+tb.millitm); -#else - struct timeval tv; - struct timezone tz; - tz.tz_minuteswest = 0; - tz.tz_dsttime = 0; - if ((result = Gettimeofday(&tv, &tz)) < 0) { - Warn2("gettimeofday(%p, {0,0}): %s", &tv, strerror(errno)); - } - srandom(tv.tv_sec*1000000+tv.tv_usec); -#endif - } - dv = div(random(), IPPORT_RESERVED-XIO_IPPORT_LOWER); - i = N = XIO_IPPORT_LOWER + dv.rem; - do { /* loop over lowport bind() attempts */ - *port = htons(i); - if (Bind(xfd->fd, &sinp->soa, sizeof(*sinp)) < 0) { - Msg4(errno==EADDRINUSE?E_INFO:level, - "bind(%d, {%s}, "F_Zd"): %s", xfd->fd, - sockaddr_info(&sinp->soa, sizeof(*sinp), infobuff, sizeof(infobuff)), - sizeof(*sinp), strerror(errno)); - if (errno != EADDRINUSE) { - Close(xfd->fd); - return STAT_RETRYLATER; - } - } else { - break; /* could bind to port, good, continue past loop */ - } - --i; if (i < XIO_IPPORT_LOWER) i = IPPORT_RESERVED-1; - if (i == N) { - Msg(level, "no low port available"); - /*errno = EADDRINUSE; still assigned */ - Close(xfd->fd); - return STAT_RETRYLATER; - } - } while (i != N); - } else -#endif /* WITH_TCP || WITH_UDP */ - - if (us) { -#if WITH_UNIX - if (pf == PF_UNIX && us != NULL) { - applyopts_named(us->un.sun_path, opts, PH_PREOPEN); - } -#endif - if (Bind(xfd->fd, &us->soa, uslen) < 0) { - Msg4(level, "bind(%d, {%s}, "F_Zd"): %s", - xfd->fd, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)), - uslen, strerror(errno)); - Close(xfd->fd); - return STAT_RETRYLATER; - } - } -#if WITH_UNIX - if (pf == PF_UNIX && us != NULL) { - applyopts_named(us->un.sun_path, opts, PH_PASTOPEN); - } -#endif - - applyopts(xfd->fd, opts, PH_PASTBIND); applyopts(xfd->fd, opts, PH_CONNECT); @@ -1116,7 +1022,7 @@ int _xioopen_dgram_sendto(/* them is already in xfd->peersa */ union sockaddr_union *us, socklen_t uslen, struct opt *opts, int xioflags, xiosingle_t *xfd, unsigned groups, - int pf, int socktype, int ipproto) { + int pf, int socktype, int ipproto, bool alt) { int level = E_ERROR; union sockaddr_union la; socklen_t lalen = sizeof(la); char infobuff[256]; @@ -1138,30 +1044,9 @@ int _xioopen_dgram_sendto(/* them is already in xfd->peersa */ applyopts_cloexec(xfd->fd, opts); -#if WITH_UNIX - if (pf == PF_UNIX && us != NULL) { - applyopts_named(us->un.sun_path, opts, PH_PREOPEN); + if (xiobind(xfd, us, uslen, opts, pf, alt, level) < 0) { + return -1; } -#endif - applyopts(xfd->fd, opts, PH_PREBIND); - applyopts(xfd->fd, opts, PH_BIND); - - if (us) { - if (Bind(xfd->fd, &us->soa, uslen) < 0) { - Msg4(level, "bind(%d, {%s}, "F_socklen"): %s", - xfd->fd, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)), - uslen, strerror(errno)); - Close(xfd->fd); - return STAT_RETRYLATER; - } - } -#if WITH_UNIX - if (pf == PF_UNIX && us != NULL) { - applyopts_named(us->un.sun_path, opts, PH_PASTOPEN); - } -#endif - - applyopts(xfd->fd, opts, PH_PASTBIND); /*applyopts(xfd->fd, opts, PH_CONNECT);*/ @@ -1333,25 +1218,16 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, applyopts_cloexec(xfd->fd, opts); - applyopts(xfd->fd, opts, PH_PREBIND); - applyopts(xfd->fd, opts, PH_BIND); - if ((us != NULL) && Bind(xfd->fd, us, uslen) < 0) { - Msg4(level, "bind(%d, {%s}, "F_socklen"): %s", xfd->fd, - sockaddr_info(us, uslen, infobuff, sizeof(infobuff)), uslen, - strerror(errno)); - Close(xfd->fd); - return STAT_RETRYLATER; + if (xiobind(xfd, (union sockaddr_union *)us, uslen, + opts, pf, 0, level) < 0) { + return -1; } + applyopts(xfd->fd, opts, PH_PASTBIND); + #if WITH_UNIX if (pf == AF_UNIX && us != NULL) { applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_FD); - } -#endif - - applyopts(xfd->fd, opts, PH_PASTBIND); -#if WITH_UNIX - if (pf == AF_UNIX && us != NULL) { /*applyopts_early(((struct sockaddr_un *)us)->sun_path, opts);*/ applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_EARLY); applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_PREOPEN); @@ -1592,7 +1468,6 @@ int _xioopen_dgram_recv(struct single *xfd, int xioflags, struct opt *opts, int pf, int socktype, int proto, int level) { char *rangename; - char infobuff[256]; if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY; @@ -1605,26 +1480,13 @@ int _xioopen_dgram_recv(struct single *xfd, int xioflags, applyopts_cloexec(xfd->fd, opts); - applyopts(xfd->fd, opts, PH_PREBIND); - applyopts(xfd->fd, opts, PH_BIND); - if ((us != NULL) && Bind(xfd->fd, us, uslen) < 0) { - Msg4(level, "bind(%d, {%s}, "F_socklen"): %s", xfd->fd, - sockaddr_info(us, uslen, infobuff, sizeof(infobuff)), uslen, - strerror(errno)); - Close(xfd->fd); - return STAT_RETRYLATER; + if (xiobind(xfd, (union sockaddr_union *)us, uslen, opts, pf, 0, level) < 0) { + return -1; } #if WITH_UNIX if (pf == AF_UNIX && us != NULL) { applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_FD); - } -#endif - - applyopts_single(xfd, opts, PH_PASTBIND); - applyopts(xfd->fd, opts, PH_PASTBIND); -#if WITH_UNIX - if (pf == AF_UNIX && us != NULL) { /*applyopts_early(((struct sockaddr_un *)us)->sun_path, opts);*/ applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_EARLY); applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_PREOPEN); @@ -2262,3 +2124,124 @@ xiosocketpair(struct opt *opts, int pf, int socktype, int proto, int sv[2]) { } return result; } + +int xiobind( + struct single *xfd, + union sockaddr_union *us, + size_t uslen, + struct opt *opts, + int pf, + bool alt, + int level) +{ + char infobuff[256]; + int result; + +#if WITH_UNIX + if (pf == PF_UNIX && us != NULL) { + applyopts_named(us->un.sun_path, opts, PH_PREOPEN); + } +#endif + applyopts(xfd->fd, opts, PH_PREBIND); + applyopts(xfd->fd, opts, PH_BIND); +#if WITH_TCP || WITH_UDP + if (alt) { + union sockaddr_union sin, *sinp; + unsigned short *port, i, N; + div_t dv; + + /* prepare sockaddr for bind probing */ + if (us) { + sinp = us; + } else { + if (pf == AF_INET) { + socket_in_init(&sin.ip4); +#if WITH_IP6 + } else { + socket_in6_init(&sin.ip6); +#endif + } + sinp = &sin; + } + if (pf == AF_INET) { + port = &sin.ip4.sin_port; +#if WITH_IP6 + } else if (pf == AF_INET6) { + port = &sin.ip6.sin6_port; +#endif + } else { + port = 0; /* just to make compiler happy */ + } + /* combine random+step variant to quickly find a free port when only + few are in use, and certainly find a free port in defined time even + if there are almost all in use */ + /* dirt 1: having tcp/udp code in socket function */ + /* dirt 2: using a time related system call for init of random */ + { + /* generate a random port, with millisecond random init */ +#if 0 + struct timeb tb; + ftime(&tb); + srandom(tb.time*1000+tb.millitm); +#else + struct timeval tv; + struct timezone tz; + tz.tz_minuteswest = 0; + tz.tz_dsttime = 0; + if ((result = Gettimeofday(&tv, &tz)) < 0) { + Warn2("gettimeofday(%p, {0,0}): %s", &tv, strerror(errno)); + } + srandom(tv.tv_sec*1000000+tv.tv_usec); +#endif + } + /* Note: IPPORT_RESERVED is from includes, 1024 */ + dv = div(random(), IPPORT_RESERVED-XIO_IPPORT_LOWER); + i = N = XIO_IPPORT_LOWER + dv.rem; + do { /* loop over lowport bind() attempts */ + *port = htons(i); + if (Bind(xfd->fd, &sinp->soa, sizeof(*sinp)) < 0) { + Msg4(errno==EADDRINUSE?E_INFO:level, + "bind(%d, {%s}, "F_Zd"): %s", xfd->fd, + sockaddr_info(&sinp->soa, sizeof(*sinp), infobuff, sizeof(infobuff)), + sizeof(*sinp), strerror(errno)); + if (errno != EADDRINUSE) { + Close(xfd->fd); + return STAT_RETRYLATER; + } + } else { + break; /* could bind to port, good, continue past loop */ + } + --i; if (i < XIO_IPPORT_LOWER) i = IPPORT_RESERVED-1; + if (i == N) { + Msg(level, "no low port available"); + /*errno = EADDRINUSE; still assigned */ + Close(xfd->fd); + return STAT_RETRYLATER; + } + } while (i != N); + } else +#endif /* WITH_TCP || WITH_UDP */ + + if (us) { +#if WITH_UNIX + if (pf == PF_UNIX && us != NULL) { + applyopts_named(us->un.sun_path, opts, PH_PREOPEN); + } +#endif + if (Bind(xfd->fd, &us->soa, uslen) < 0) { + Msg4(level, "bind(%d, {%s}, "F_Zd"): %s", + xfd->fd, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)), + uslen, strerror(errno)); + Close(xfd->fd); + return STAT_RETRYLATER; + } + } +#if WITH_UNIX + if (pf == PF_UNIX && us != NULL) { + applyopts_named(us->un.sun_path, opts, PH_PASTOPEN); + } +#endif + + applyopts(xfd->fd, opts, PH_PASTBIND); + return 0; +} diff --git a/xio-socket.h b/xio-socket.h index 1a612a8..2898ec1 100644 --- a/xio-socket.h +++ b/xio-socket.h @@ -102,7 +102,7 @@ int _xioopen_dgram_sendto(/* them is already in xfd->peersa */ union sockaddr_union *us, socklen_t uslen, struct opt *opts, int xioflags, xiosingle_t *xfd, unsigned groups, - int pf, int socktype, int ipproto); + int pf, int socktype, int ipproto, bool alt); extern int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, struct sockaddr *us, socklen_t uslen, diff --git a/xio-udp.c b/xio-udp.c index 433ef77..27e4f18 100644 --- a/xio-udp.c +++ b/xio-udp.c @@ -410,26 +410,12 @@ int _xioopen_udp_sendto(const char *hostname, const char *servname, } retropt_bool(opts, OPT_LOWPORT, &xfd->para.socket.ip.lowport); - if (xfd->para.socket.ip.lowport) { - switch (pf) { -#if WITH_IP4 - case PF_INET: - /*!!! this is buggy */ - us.ip4.sin_port = htons(xfd->para.socket.ip.lowport); break; -#endif -#if WITH_IP6 - case PF_INET6: - /*!!! this is buggy */ - us.ip6.sin6_port = htons(xfd->para.socket.ip.lowport); break; -#endif - } - needbind = true; - } xfd->dtype = XIODATA_RECVFROM; return _xioopen_dgram_sendto(needbind?&us:NULL, uslen, opts, xioflags, xfd, groups, - pf, socktype, ipproto); + pf, socktype, ipproto, + xfd->para.socket.ip.lowport); } diff --git a/xio-unix.c b/xio-unix.c index e4b054b..84b6df2 100644 --- a/xio-unix.c +++ b/xio-unix.c @@ -411,7 +411,7 @@ static int xioopen_unix_sendto(int argc, const char *argv[], struct opt *opts, i return _xioopen_dgram_sendto(needbind?&us:NULL, uslen, opts, xioflags, xfd, groups, - pf, socktype, protocol); + pf, socktype, protocol, 0); } @@ -690,7 +690,7 @@ _xioopen_unix_client(xiosingle_t *xfd, int xioflags, unsigned groups, if ((result = _xioopen_dgram_sendto(needbind?&us:NULL, uslen, opts, xioflags, xfd, groups, - pf, SOCK_DGRAM, protocol)) + pf, SOCK_DGRAM, protocol, 0)) == 0) { xfd->dtype = XIODATA_RECVFROM; break;