diff --git a/CHANGES b/CHANGES index ffaefe0..28ab3e5 100644 --- a/CHANGES +++ b/CHANGES @@ -45,6 +45,13 @@ Corrections: POSIXMQ-* addresses. POSIXMQ addresses are no longer experimental. + With version 1.8.0.0, and with 1.8.0.1 and option -0, the following + command failed: + socat UDP-LISTEN:1234,fork,reuseaddr,bind=127.0.0.1 - + Message: "E xioopen_ipdgram_listen(): unknown address family 0": + Thanks to Brian Woo for reporting this issue. + Test: UDP_LISTEN_BIND4 + Features: POSIXMQ-RECV now takes option o-nonblock; this, in combination with -T, makes it possible to terminate Socat in case the queue is empty. diff --git a/test.sh b/test.sh index f321bc0..d6ce591 100755 --- a/test.sh +++ b/test.sh @@ -3231,18 +3231,20 @@ echo "$da" |$CMD2 >>"$tf" 2>>"${te}2" rc2=$? kill $pid1 2>/dev/null; wait if [ $rc2 -ne 0 ]; then - $PRINTF "$FAILED: $TRACE $SOCAT:\n" + $PRINTF "$FAILED (rc2=$rc2)\n" echo "$CMD1 &" + cat "${te}1" >&2 echo "$CMD2" - cat "${te}1" "${te}2" + cat "${te}2" >&2 failed elif ! echo "$da" |diff - "$tf" >"$tdiff"; then - $PRINTF "$FAILED\n" + $PRINTF "$FAILED (diff)\n" echo "$CMD1 &" - cat "${te}1" + cat "${te}1" >&2 echo "$CMD2" - cat "${te}2" - cat "$tdiff" + cat "${te}2" >&2 + echo "// diff:" >&2 + cat "$tdiff" >&2 failed else $PRINTF "$OK\n" @@ -3279,14 +3281,20 @@ echo "$da" |$CMD2 >>"$tf" 2>>"${te}2" rc2=$? kill $pid1 2>/dev/null; wait if [ $rc2 -ne 0 ]; then - $PRINTF "$FAILED: $TRACE $SOCAT:\n" + $PRINTF "$FAILED (rc2=$rc2)\n" echo "$CMD1 &" + cat "${te}1" >&2 echo "$CMD2" - cat "${te}1" "${te}2" + cat "${te}2" >&2 failed elif ! echo "$da" |diff - "$tf" >"$tdiff"; then - $PRINTF "$FAILED\n" - cat "$tdiff" + $PRINTF "$FAILED (diff)\n" + echo "$CMD1 &" + cat "${te}1" >&2 + echo "$CMD2" + cat "${te}2" >&2 + echo "// diff:" >&2 + cat "$tdiff" >&2 failed else $PRINTF "$OK\n" @@ -3540,8 +3548,7 @@ elif ! cond=$(checkconds \ "ignoreeof" \ "" ); then $PRINTF "test $F_n $TEST... ${YELLOW}$cond${NORMAL}\n" $N - numCANT=$((numCANT+1)) - listCANT="$listCANT $N" + cant else ti="$td/test$N.file" tf="$td/test$N.stdout" @@ -3593,8 +3600,7 @@ elif ! cond=$(checkconds \ "ignoreeof" \ "" ); then $PRINTF "test $F_n $TEST... ${YELLOW}$cond${NORMAL}\n" $N - numCANT=$((numCANT+1)) - listCANT="$listCANT $N" + cant else # Let Socat read from an empty file, this would terminate immediately due to # EOF. Wait for more than one second, then append data to the file; when Socat @@ -13046,9 +13052,7 @@ elif ! cond=$(checkconds \ "ipv6-join-group" \ "udp6" ); then $PRINTF "test $F_n $TEST... ${YELLOW}$cond${NORMAL}\n" $N - numCANT=$((numCANT+1)) - listCANT="$listCANT $N" - namesCANT="$namesCANT $NAME" + cant else tf="$td/test$N.stdout" te="$td/test$N.stderr" @@ -19278,6 +19282,7 @@ N=$((N+1)) # Below tests introduced with 1.8.0.3 (or later) + # Test the SOCKS5-CONNECT and SOCKS5-LISTEN addresses with IPv4 for SUFFIX in CONNECT LISTEN; do @@ -19353,6 +19358,71 @@ N=$((N+1)) done # CONNECT LISTEN +# Test UDP-LISTEN with bind to IPv4 address; this failed with Socat version +# 1.8.0.0 +NAME=UDP_LISTEN_BIND4 +case "$TESTS" in +*%$N%*|*%functions%*|*%bugs%*|*%socket%*|*%ip4%*|*%udp%*|*%udp4%*|*%listen%*|*%$NAME%*) +TEST="$NAME: Test UDP-LISTEN with bind to IPv4 addr" +# Start a listener with UDP-LISTEN and bind to 127.0.0.1; when it starts +# without error and even processes data the test succeeded +if ! eval $NUMCOND; then : +# Remove unneeded checks, adapt lists of the remaining ones +elif ! cond=$(checkconds \ + "" \ + "" \ + "" \ + "IP4 UDP LISTEN STDIO PIPE" \ + "UDP-LISTEN PIPE STDIO UDP" \ + "bind" \ + "udp4" ); then + $PRINTF "test $F_n $TEST... ${YELLOW}$cond${NORMAL}\n" $N + cant +else + tf="$td/test$N.stdout" + te="$td/test$N.stderr" + tdiff="$td/test$N.diff" + da="test$N $(date) $RANDOM" + newport udp4 + CMD0="$TRACE $SOCAT $opts UDP-LISTEN:$PORT,bind=$LOCALHOST4 PIPE" + CMD1="$TRACE $SOCAT $opts - UDP-CONNECT:$LOCALHOST4:$PORT" + printf "test $F_n $TEST... " $N + $CMD0 >/dev/null 2>"${te}0" & + pid0=$! + waitudp4port $PORT 1 + echo "$da" |$CMD1 >"${tf}1" 2>"${te}1" + 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 + fail + elif ! echo "$da" |diff - "${tf}1" >$tdiff; then + $PRINTF "$FAILED (diff)\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + echo "// diff:" >&2 + cat "$tdiff" >&2 + fail + else + $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 + ok + fi +fi # NUMCOND + ;; +esac +N=$((N+1)) + + # end of common tests ################################################################################## diff --git a/xio-socket.c b/xio-socket.c index 89045d1..dbbde71 100644 --- a/xio-socket.c +++ b/xio-socket.c @@ -262,7 +262,7 @@ static int xioopen_socket_connect( sfd->dtype = XIOREAD_STREAM|XIOWRITE_STREAM; socket_init(0, &us); - if (retropt_bind(opts, 0 /*pf*/, socktype, proto, (struct sockaddr *)&us, &uslen, 3, + if (retropt_bind(opts, pf, socktype, proto, (struct sockaddr *)&us, &uslen, -1, #if _WITH_IP4 || _WITH_IP6 sfd->para.socket.ip.ai_flags #else diff --git a/xio-udp.c b/xio-udp.c index ff5a9e1..d74f670 100644 --- a/xio-udp.c +++ b/xio-udp.c @@ -277,8 +277,10 @@ int xioopen_ipdgram_listen( int pf = addrdesc->arg1; int ipproto = addrdesc->arg2; union sockaddr_union us; + int bind_rc; int socktype = SOCK_DGRAM; socklen_t uslen; + int result; if (argc != 2) { xio_syntax(argv[0], 1, argc-1, addrdesc->syntax); @@ -295,12 +297,31 @@ int xioopen_ipdgram_listen( applyopts(sfd, -1, opts, PH_INIT); uslen = socket_init(pf, &us); - retropt_bind(opts, pf, socktype, ipproto, + bind_rc = retropt_bind(opts, pf, socktype, ipproto, (struct sockaddr *)&us, &uslen, 1, xfd->stream.para.socket.ip.ai_flags); + if (bind_rc == STAT_NORETRY) + return STAT_NORETRY; + if (pf == PF_UNSPEC && bind_rc == STAT_OK) + pf = us.soa.sa_family; if (false) { ; +#if WITH_IP4 || WITH_IP6 + } else if (pf == PF_UNSPEC && bind_rc == STAT_NOACTION) { + int ai_flags[2]; + ai_flags[0] = sfd->para.socket.ip.ai_flags[0]; + ai_flags[1] = sfd->para.socket.ip.ai_flags[1]; + if (!(ai_flags[1] & AI_PASSIVE)) + ai_flags[0] |= AI_PASSIVE; + result = + xioresolve(NULL, portname, pf, socktype, ipproto, &us, &uslen, ai_flags); + if (result != STAT_OK) { + Error("error resolving bind option"); + return STAT_NORETRY; + } + pf = us.soa.sa_family; +#endif /* WITH_IP4 || WITH_IP6*/ #if WITH_IP4 } else if (pf == PF_INET) { us.ip4.sin_port = parseport(portname, ipproto); diff --git a/xioopts.c b/xioopts.c index b11cdb5..3781529 100644 --- a/xioopts.c +++ b/xioopts.c @@ -3258,7 +3258,8 @@ int retropt_bind(struct opt *opts, int ipproto, struct sockaddr *sa, socklen_t *salen, - int feats, /* TCP etc: 1..address allowed, + int feats, /* -1..generic addr spec + TCP etc: 1..address allowed, 3..address and port allowed UNIX (or'd): 1..tight 2..abstract @@ -3282,10 +3283,13 @@ int retropt_bind(struct opt *opts, } bindp = bindname; - switch (af) { +#if WITH_IP4 && WITH_IP6 + /* Try to derive address family from string */ + if (af == AF_UNSPEC && bindname[0] == '[') + af = AF_INET6; +#endif /* WITH_IP4 && WITH_IP6 */ - case AF_UNSPEC: - { + if (feats == -1) { size_t p = 0; dalan(bindname, (uint8_t *)sa->sa_data, &p, *salen-sizeof(sa->sa_family), 'i'); *salen = p + sizeof(sa->sa_family); @@ -3297,10 +3301,13 @@ int retropt_bind(struct opt *opts, #if HAVE_STRUCT_SOCKADDR_SALEN sa->sa_len = *salen; #endif - } - break; + return STAT_OK; + } + + switch (af) { #if WITH_IP4 || WITH_IP6 || WITH_VSOCK + case AF_UNSPEC: #if WITH_VSOCK case AF_VSOCK: #endif @@ -3336,7 +3343,7 @@ int retropt_bind(struct opt *opts, ai_flags2[0] = ai_flags[0]; ai_flags2[1] = ai_flags[1]; if (!(ai_flags2[1] & AI_PASSIVE)) - ai_flags2[0] |= AI_PASSIVE; + ai_flags2[0] |= AI_PASSIVE; if ((result = xioresolve(hostname[0]!='\0'?hostname:NULL, portp,