From ad9e6b1fa409603f9a3acaaf970c005ede8c52f1 Mon Sep 17 00:00:00 2001 From: Gerhard Rieger Date: Tue, 31 Mar 2015 18:21:45 +0200 Subject: [PATCH] Fixed bind with abstract unix domain sockets (Linux) --- CHANGES | 4 ++ test.sh | 70 ++++++++++++++++++++++++++----- xio-unix.c | 119 +++++++++++++++++++++++++++++++---------------------- xio.h | 3 ++ xioopts.c | 12 ++++-- 5 files changed, 145 insertions(+), 63 deletions(-) diff --git a/CHANGES b/CHANGES index e793bb3..4d6d07b 100644 --- a/CHANGES +++ b/CHANGES @@ -162,6 +162,10 @@ corrections: docu mentions option so-bindtodev but correct name is so-bindtodevice. Thanks to Jim Zimmerman for reporting. + Bind with ABSTRACT commands used non-abstract namespace (Linux). + Test: ABSTRACT_BIND + Thanks to Denis Shatov for reporting this bug. + porting: Red Hat issue 1020203: configure checks fail with some compilers. Use case: clang diff --git a/test.sh b/test.sh index ae1b1a5..e6c3dac 100755 --- a/test.sh +++ b/test.sh @@ -9005,28 +9005,78 @@ rc2="$?" i=0; while [ ! -s "$tf" -a "$i" -lt 10 ]; do usleep 100000; i=$((i+1)); done kill "$pid1" 2>/dev/null; wait if [ "$rc2" -ne 0 ]; then - $PRINTF "$FAILED: $TRACE $SOCAT:\n" - echo "$CMD1 &" - echo "$CMD2" - cat "${te}1" - cat "${te}2" + $PRINTF "$FAILED: $TRACE $SOCAT:\n" + echo "$CMD1 &" + echo "$CMD2" + cat "${te}1" + cat "${te}2" numFAIL=$((numFAIL+1)) listFAIL="$listFAIL $N" elif ! echo "$da" |diff - "$tf" >"$tdiff"; then - $PRINTF "$FAILED\n" - cat "$tdiff" + $PRINTF "$FAILED\n" + echo "$CMD1 &" + cat "${te}1" + cat "$tdiff" numFAIL=$((numFAIL+1)) listFAIL="$listFAIL $N" else - $PRINTF "$OK\n" - if [ -n "$debug" ]; then cat $te; fi - numOK=$((numOK+1)) + $PRINTF "$OK\n" + if [ -n "$debug" ]; then cat $te; fi + numOK=$((numOK+1)) fi fi ;; # NUMCOND, feats esac N=$((N+1)) +# bind with Linux abstract UNIX domain addresses bound to filesystem socket +# instead of abstract namespace +NAME=ABSTRACT_BIND +case "$TESTS" in +*%$N%*|*%functions%*|*%bugs%*|*%socket%*|*%unix%*|*%abstract%*|*%$NAME%*) +TEST="$NAME: abstract bind" +# open an abstract client address with bind option, bind to the target socket. +# send a datagram. +# when socat outputs the datagram it got the test succeeded +if ! eval $NUMCOND; then :; +elif [ "$UNAME" != Linux ]; then + $PRINTF "test $F_n $TEST... ${YELLOW}only on Linux${NORMAL}\n" $N + numCANT=$((numCANT+1)) +else +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +tdiff="$td/test$N.diff" +ts1="$td/test$N.sock1" +da="test$N $(date) $RANDOM" +CMD1="$TRACE $SOCAT $opts - ABSTRACT-SENDTO:$ts1,bind=$ts1" +printf "test $F_n $TEST... " $N +echo "$da" |$CMD1 >$tf 2>"${te}1" +rc1=$? +if [ $rc1 -ne 0 ]; then + $PRINTF "$FAILED\n" + echo "$CMD1" >&2 + echo "rc=$rc1" >&2 + cat "${te}1" >&2 + numFAIL=$((numFAIL+1)) + listFAIL="$listFAIL $N" +elif echo "$da" |diff -q - $tf; then + $PRINTF "$OK\n" + numOK=$((numOK+1)) +else + $PRINTF "$FAILED\n" + echo "$CMD1" >&2 + cat "${te}1" >&2 + echo "$da" |diff - "$tf" >&2 + numFAIL=$((numFAIL+1)) + listFAIL="$listFAIL $N" +fi +fi # NUMCOND + ;; +esac +PORT=$((PORT+1)) +N=$((N+1)) + + NAME=OPENSSLREAD # socat determined availability of data using select(). With openssl, the # following situation might occur: diff --git a/xio-unix.c b/xio-unix.c index d8c0a98..13d520b 100644 --- a/xio-unix.c +++ b/xio-unix.c @@ -73,13 +73,13 @@ static const struct xioaddr_endpoint_desc xioendpoint_abstract_client1 = { XIOA const union xioaddr_desc *xioaddrs_abstract_client[] = { (union xioaddr_desc *)&xioendpoint_abstract_client1, NULL }; #endif /* WITH_ABSTRACT_UNIXSOCKET */ -const struct optdesc xioopt_unix_tightsocklen = { "unix-tightsocklen", "tightsocklen", OPT_UNIX_TIGHTSOCKLEN, GROUP_SOCK_UNIX, PH_INIT, TYPE_BOOL, OFUNC_SPEC, 0, 0 }; +const struct optdesc xioopt_unix_tightsocklen = { "unix-tightsocklen", "tightsocklen", OPT_UNIX_TIGHTSOCKLEN, GROUP_SOCK_UNIX, PH_PREBIND, TYPE_BOOL, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.un.tight), XIO_SIZEOF(para.socket.un.tight) }; /* fills the socket address struct and returns its effective length. abstract is usually 0; != 0 generates an abstract socket address on Linux. tight!=0 calculates the resulting length from the path length, not from the - structures length; this is more common. + structures length; this is more common (see option unix-tightsocklen) the struct need not be initialized when calling this function. */ socklen_t @@ -140,7 +140,6 @@ static int xioopen_unix_listen(int argc, const char *argv[], struct opt *opts, i int protocol = 0; struct sockaddr_un us; socklen_t uslen; - bool tight = true; struct opt *opts0 = NULL; pid_t pid = Getpid(); bool opt_unlink_early = false; @@ -152,11 +151,10 @@ static int xioopen_unix_listen(int argc, const char *argv[], struct opt *opts, i argv[0], argc-1); return STAT_NORETRY; } - name = argv[1]; + + xfd->para.socket.un.tight = true; retropt_socket_pf(opts, &pf); - retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight); - uslen = xiosetunix(pf, &us, name, abstract, tight); if (!(ABSTRACT && abstract)) { /* only for non abstract because abstract do not work in file system */ @@ -167,7 +165,10 @@ static int xioopen_unix_listen(int argc, const char *argv[], struct opt *opts, i if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY; applyopts(-1, opts, PH_INIT); applyopts_named(name, opts, PH_EARLY); /* umask! */ + applyopts_offset(xfd, opts); applyopts(-1, opts, PH_EARLY); + + uslen = xiosetunix(pf, &us, name, abstract, xfd->para.socket.un.tight); if (!(ABSTRACT && abstract)) { if (opt_unlink_early) { @@ -229,8 +230,7 @@ static int xioopen_unix_connect(int argc, const char *argv[], struct opt *opts, int socktype = SOCK_STREAM; int protocol = 0; struct sockaddr_un them, us; - socklen_t themlen, uslen; - bool tight = true; + socklen_t themlen, uslen = sizeof(us); bool needbind = false; bool opt_unlink_close = false; int result; @@ -241,18 +241,23 @@ static int xioopen_unix_connect(int argc, const char *argv[], struct opt *opts, return STAT_NORETRY; } - xfd->howtoshut = XIOSHUT_DOWN; - name = argv[1]; + + xfd->para.socket.un.tight = true; retropt_socket_pf(opts, &pf); - retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight); - themlen = xiosetunix(pf, &them, name, abstract, tight); + xfd->howtoshut = XIOSHUT_DOWN; + if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY; + applyopts(-1, opts, PH_INIT); + applyopts_offset(xfd, opts); + applyopts(-1, opts, PH_EARLY); + + themlen = xiosetunix(pf, &them, name, abstract, xfd->para.socket.un.tight); if (!(ABSTRACT && abstract)) { /* only for non abstract because abstract do not work in file system */ retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); } - if (retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen, 0, 0, 0) - != STAT_NOACTION) { + if (retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen, + (abstract<<1)|xfd->para.socket.un.tight, 0, 0) == STAT_OK) { needbind = true; } @@ -263,10 +268,6 @@ static int xioopen_unix_connect(int argc, const char *argv[], struct opt *opts, xfd->opt_unlink_close = true; } - if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1; - applyopts(-1, opts, PH_INIT); - applyopts(-1, opts, PH_EARLY); - if ((result = xioopen_connect(xfd, needbind?(struct sockaddr *)&us:NULL, uslen, @@ -295,8 +296,7 @@ static int xioopen_unix_sendto(int argc, const char *argv[], struct opt *opts, i int socktype = SOCK_DGRAM; int protocol = 0; union sockaddr_union us; - socklen_t uslen; - bool tight = true; + socklen_t uslen = sizeof(us); bool needbind = false; bool opt_unlink_close = false; int result; @@ -312,10 +312,13 @@ static int xioopen_unix_sendto(int argc, const char *argv[], struct opt *opts, i retropt_int(opts, OPT_SO_TYPE, &socktype); - retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight); name = argv[1]; + + xfd->para.socket.un.tight = true; retropt_socket_pf(opts, &pf); - xfd->salen = xiosetunix(pf, &xfd->peersa.un, name, abstract, tight); + applyopts_offset(xfd, opts); + + xfd->salen = xiosetunix(pf, &xfd->peersa.un, name, abstract, xfd->para.socket.un.tight); if (!(ABSTRACT && abstract)) { /* only for non abstract because abstract do not work in file system */ @@ -324,8 +327,8 @@ static int xioopen_unix_sendto(int argc, const char *argv[], struct opt *opts, i xfd->dtype = XIODATA_RECVFROM; - if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, 0, 0, 0) - != STAT_NOACTION) { + if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, + (abstract<<1)| xfd->para.socket.un.tight, 0, 0) == STAT_OK) { needbind = true; } @@ -363,7 +366,6 @@ int xioopen_unix_recvfrom(int argc, const char *argv[], struct opt *opts, int protocol = 0; struct sockaddr_un us; socklen_t uslen; - bool tight = true; bool needbind = true; bool opt_unlink_early = false; bool opt_unlink_close = true; @@ -373,16 +375,16 @@ int xioopen_unix_recvfrom(int argc, const char *argv[], struct opt *opts, argv[0], argc-1); return STAT_NORETRY; } - name = argv[1]; - retropt_socket_pf(opts, &pf); - retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight); - uslen = xiosetunix(pf, &us, name, abstract, tight); + xfd->para.socket.un.tight = true; + retropt_socket_pf(opts, &pf); retropt_int(opts, OPT_SO_TYPE, &socktype); - retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen, - 1, 0, 0); + if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY; + applyopts(-1, opts, PH_INIT); + applyopts_named(name, opts, PH_EARLY); /* umask! */ + applyopts_offset(xfd, opts); retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early); retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); @@ -391,6 +393,15 @@ int xioopen_unix_recvfrom(int argc, const char *argv[], struct opt *opts, retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early); retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); } + applyopts(-1, opts, PH_EARLY); + + uslen = xiosetunix(pf, &us, name, abstract, xfd->para.socket.un.tight); + +#if 0 + if (retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen, + (abstract<<1)|xfd->para.socket.un.tight, 0, 0) == STAT_OK) { + } +#endif if (!(ABSTRACT && abstract)) { if (opt_unlink_early) { @@ -444,7 +455,6 @@ int xioopen_unix_recv(int argc, const char *argv[], struct opt *opts, int protocol = 0; union sockaddr_union us; socklen_t uslen; - bool tight = true; bool opt_unlink_early = false; bool opt_unlink_close = true; int result; @@ -454,21 +464,33 @@ int xioopen_unix_recv(int argc, const char *argv[], struct opt *opts, argv[0], argc-1); return STAT_NORETRY; } - name = argv[1]; - retropt_socket_pf(opts, &pf); - retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight); - uslen = xiosetunix(pf, &us.un, name, abstract, tight); -#if 1 /*!!! why bind option? */ - retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, 1, 0, 0); -#endif + xfd->para.socket.un.tight = true; + retropt_socket_pf(opts, &pf); + if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY; + applyopts(-1, opts, PH_INIT); + applyopts_named(name, opts, PH_EARLY); /* umask! */ + applyopts_offset(xfd, opts); if (!(ABSTRACT && abstract)) { /* only for non abstract because abstract do not work in file system */ retropt_bool(opts, OPT_UNLINK_EARLY, &opt_unlink_early); retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); + } + applyopts(-1, opts, PH_EARLY); + + uslen = xiosetunix(pf, &us.un, name, abstract, xfd->para.socket.un.tight); + +#if 0 + if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, + (abstract<<1)|xfd->para.socket.un.tight, 0, 0) + == STAT_OK) { + } +#endif + + if (!(ABSTRACT && abstract)) { if (opt_unlink_early) { if (Unlink(name) < 0) { if (errno == ENOENT) { @@ -531,27 +553,26 @@ _xioopen_unix_client(xiosingle_t *xfd, int xioflags, unsigned groups, int socktype = 0; /* to be determined by server socket type */ int protocol = 0; union sockaddr_union them, us; - socklen_t themlen, uslen; - bool tight = true; + socklen_t themlen, uslen = sizeof(us); bool needbind = false; bool opt_unlink_close = false; struct opt *opts0; int result; - if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1; - applyopts(-1, opts, PH_INIT); - + xfd->para.socket.un.tight = true; retropt_socket_pf(opts, &pf); + if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY; + applyopts(-1, opts, PH_INIT); + applyopts_offset(xfd, opts); + applyopts(-1, opts, PH_EARLY); - retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight); - themlen = xiosetunix(pf, &them.un, name, abstract, tight); - + themlen = xiosetunix(pf, &them.un, name, abstract, xfd->para.socket.un.tight); if (!(ABSTRACT && abstract)) { /* only for non abstract because abstract do not work in file system */ retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); } - - if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, 0, 0, 0) + if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, + (abstract<<1)|xfd->para.socket.un.tight, 0, 0) != STAT_NOACTION) { needbind = true; } diff --git a/xio.h b/xio.h index 3335a51..56caf95 100644 --- a/xio.h +++ b/xio.h @@ -394,6 +394,9 @@ typedef struct single { char *hosts_deny_table; #endif } ip; + struct { + bool tight; + } un; #endif /* _WITH_IP4 || _WITH_IP6 */ } socket; #endif /* _WITH_SOCKET */ diff --git a/xioopts.c b/xioopts.c index 81bb45c..029f864 100644 --- a/xioopts.c +++ b/xioopts.c @@ -2808,7 +2808,7 @@ int retropt_string(struct opt *opts, int optcode, char **result) { #if _WITH_SOCKET -/* looks for an bind option and, if found, overwrites the complete contents of +/* looks for a bind option and, if found, overwrites the complete contents of sa with the appropriate value(s). returns STAT_OK if option exists and could be resolved, STAT_NORETRY if option exists but had error, @@ -2821,7 +2821,10 @@ int retropt_bind(struct opt *opts, struct sockaddr *sa, socklen_t *salen, int feats, /* TCP etc: 1..address allowed, - 3..address and port allowed */ + 3..address and port allowed + UNIX (or'd): 1..tight + 2..abstract + */ unsigned long res_opts0, unsigned long res_opts1) { const char portsep[] = ":"; const char *ends[] = { portsep, NULL }; @@ -2889,9 +2892,10 @@ int retropt_bind(struct opt *opts, #if WITH_UNIX case AF_UNIX: { - bool tight = false; + bool abstract = (feats&2); + bool tight = (feats&1); struct sockaddr_un *s_un = (struct sockaddr_un *)sa; - *salen = xiosetunix(af, s_un, bindname, false, tight); + *salen = xiosetunix(af, s_un, bindname, abstract, tight); } break; #endif /* WITH_UNIX */