From d9d320cb47acd2d1e93301485b39fdb26f7897e8 Mon Sep 17 00:00:00 2001 From: Gerhard Rieger Date: Sun, 27 Dec 2020 12:39:48 +0100 Subject: [PATCH] Corrected UNIX client NAMED options to work on bind address --- CHANGES | 5 +++ DEVELOPMENT | 20 ++++++++++++ doc/socat.yo | 9 +++--- test.sh | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++- xio-ipapp.c | 2 +- xio-openssl.c | 2 +- xio-proxy.c | 2 +- xio-socket.c | 79 ++++++++++++++++++++++++++++++--------------- xio-socket.h | 4 +-- xio-socks.c | 2 +- xio-unix.c | 37 ++++++++++++++++------ xioopts.c | 25 +++++++++++++++ xioopts.h | 2 ++ 13 files changed, 232 insertions(+), 45 deletions(-) diff --git a/CHANGES b/CHANGES index fc7a3fd..fc3d98f 100644 --- a/CHANGES +++ b/CHANGES @@ -34,6 +34,11 @@ Corrections: pselect() system call. Thanks to Fulvio Scapin for reporting this issue. + UNIX domain client addresses applied file system entry options (group + NAMED) to the server socket instead of the client (bind) socket entry. + Tests: UNIX_SENDTO_UNLINK UNIX_CONNECT_UNLINK + Thanks to Nico Williams for reporting this major issue. + Porting: In gcc version 10 the default changed from -fcommon to -fno-common. Consequently, linking filan and procan failed with error diff --git a/DEVELOPMENT b/DEVELOPMENT index 99a82c6..caf88cd 100644 --- a/DEVELOPMENT +++ b/DEVELOPMENT @@ -157,6 +157,26 @@ PH_LATE FD is ready, before start of data loop PH_LATE2 FD is ready, dropping privileges +Passive UNIX socket addresses; this is a mix of socket phases and file system phases: +PH_INIT retrieving info from original state +PH_EARLY before any other processing +PH_PRESOCKET before socket call +PH_SOCKET for socket call +PH_PASTSOCKET after socket call +PH_FD soon after FD creation or identification +PH_PREOPEN before file creation/opening +PH_PREBIND before socket bind() +PH_BIND during socket bind() +PH_PASTOPEN past file creation/opening +PH_PASTBIND past socket bind(), not used up to 1.7.3.4 +PH_PRECONNECT before connect() +PH_CONNECT during connect() +PH_PASTCONNECT after connect() +PH_CONNECTED phase common with listen +PH_LATE FD is ready, before start of data loop +PH_LATE2 FD is ready, dropping privileges + + FD addresses: PH_INIT retrieving info from original state diff --git a/doc/socat.yo b/doc/socat.yo index a64997c..23783b1 100644 --- a/doc/socat.yo +++ b/doc/socat.yo @@ -1541,6 +1541,8 @@ startdit()enddit()nl() label(GROUP_NAMED)em(bf(NAMED option group)) These options work on file system entries.nl() +Please note that, with UNIX domain client addresses, this means the bind entry, +not the target/peer entry.nl() See also options link(user)(OPTION_USER), link(group)(OPTION_GROUP), and link(mode)(OPTION_MODE). @@ -1575,12 +1577,11 @@ label(OPTION_UNLINK_LATE)dit(bf(tt(unlink-late))) label(OPTION_UNLINK_CLOSE)dit(bf(tt(unlink-close))) Removes the addresses file system entry when closing the address. For link(named pipes)(ADDRESS_NAMED_PIPE), - link(listening unix domain sockets)(ADDRESS_UNIX_LISTEN), + link(UNIX domain sockets)(ADDRESS_UNIX_LISTEN), and the link(symbolic links)(OPTION_SYMBOLIC_LINK) of link(pty addresses)(ADDRESS_PTY), the default is 1; for link(created files)(ADDRESS_CREAT), - link(opened files)(ADDRESS_OPEN), - link(generic opened files)(ADDRESS_GOPEN), and - link(client unix domain sockets)(ADDRESS_UNIX_CONNECT) the default is 0. + link(opened files)(ADDRESS_OPEN), and + link(generic opened files)(ADDRESS_GOPEN) the default is 0. enddit() startdit()enddit()nl() diff --git a/test.sh b/test.sh index 25cbae0..7a63352 100755 --- a/test.sh +++ b/test.sh @@ -13573,7 +13573,6 @@ esac PORT=$((PORT+1)) N=$((N+1)) - # Test if unbalanced quoting in Socat addresses is detected NAME=UNBALANCED_QUOTE case "$TESTS" in @@ -13703,6 +13702,93 @@ esac N=$((N+1)) +# test if option unlink-close removes the bind socket file +NAME=UNIX_SENDTO_UNLINK +case "$TESTS" in +*%$N%*|*%functions%*|*%bugs%*|*%socket%*|*%unix%*|*%$NAME%*) +TEST="$NAME: Option unlink-close with UNIX sendto socket" +# Have a recv socket with option unlink-close=0 +# and a sendto socket with option unlink-close=1 +# Expected beavior: the recv socket is kept, the +# sendto/bind socket is removed +if ! eval $NUMCOND; then :; else +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +uns="$td/test$N.server" +unc="$td/test$N.client" +tdiff="$td/test$N.diff" +da="test$N $(date) $RANDOM" +CMD0="$TRACE $SOCAT $opts -u UNIX-RECV:$uns,unlink-close=0 GOPEN:$tf" +CMD1="$TRACE $SOCAT $opts - UNIX-SENDTO:$uns,bind=$unc,unlink-close=1" +printf "test $F_n $TEST... " $N +$CMD0 >/dev/null 2>"${te}0" & +pid0=$! +waitunixport $uns 1 +echo "$da" |$CMD1 >"${tf}1" 2>"${te}1" +rc1=$? +kill $pid0 2>/dev/null; wait +if test -S $uns && ! test -S $unc; then + $PRINTF "$OK\n" + numOK=$((numOK+1)) +else + $PRINTF "$FAILED\n" + echo "$CMD0 &" + echo "$CMD1" + ls -ld $uns $unc + cat "${te}0" + cat "${te}1" + numFAIL=$((numFAIL+1)) + listFAIL="$listFAIL $N" +fi +fi # NUMCOND + ;; +esac +N=$((N+1)) + +# test if option unlink-close removes the bind socket file +NAME=UNIX_CONNECT_UNLINK +case "$TESTS" in +*%$N%*|*%functions%*|*%bugs%*|*%socket%*|*%unix%*|*%$NAME%*) +TEST="$NAME: Option unlink-close with UNIX connect socket" +# Have a listen socket with option unlink-close=0 +# and a connect socket with option unlink-close=1 +# Expected beavior: the listen socket entry is kept, the +# connect/bind socket is removed +if ! eval $NUMCOND; then :; else +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +uns="$td/test$N.server" +unc="$td/test$N.client" +tdiff="$td/test$N.diff" +da="test$N $(date) $RANDOM" +CMD0="$TRACE $SOCAT $opts -u UNIX-LISTEN:$uns,unlink-close=0 GOPEN:$tf" +CMD1="$TRACE $SOCAT $opts - UNIX-CONNECT:$uns,bind=$unc,unlink-close=1" +printf "test $F_n $TEST... " $N +$CMD0 >/dev/null 2>"${te}0" & +pid0=$! +waitunixport $uns 1 +echo "$da" |$CMD1 >"${tf}1" 2>"${te}1" +rc1=$? +kill $pid0 2>/dev/null; wait +if test -S $uns && ! test -S $unc; then + $PRINTF "$OK\n" + numOK=$((numOK+1)) +else + $PRINTF "$FAILED\n" + echo "$CMD0 &" + echo "$CMD1" + ls -ld $uns $unc + cat "${te}0" + cat "${te}1" + numFAIL=$((numFAIL+1)) + listFAIL="$listFAIL $N" +fi +fi # NUMCOND + ;; +esac +N=$((N+1)) + + ################################################################################## #================================================================================= # here come tests that might affect your systems integrity. Put normal tests diff --git a/xio-ipapp.c b/xio-ipapp.c index 8a0f057..96011a3 100644 --- a/xio-ipapp.c +++ b/xio-ipapp.c @@ -79,7 +79,7 @@ int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts, result = _xioopen_connect(xfd, - needbind?(struct sockaddr *)us:NULL, uslen, + needbind?us:NULL, uslen, (struct sockaddr *)them, themlen, opts, pf, socktype, ipproto, lowport, level); switch (result) { diff --git a/xio-openssl.c b/xio-openssl.c index 0710f1d..118f335 100644 --- a/xio-openssl.c +++ b/xio-openssl.c @@ -278,7 +278,7 @@ static int /* this cannot fork because we retrieved fork option above */ result = _xioopen_connect(xfd, - needbind?(struct sockaddr *)us:NULL, uslen, + needbind?us:NULL, uslen, (struct sockaddr *)them, themlen, opts, pf, socktype, ipproto, lowport, level); switch (result) { diff --git a/xio-proxy.c b/xio-proxy.c index 59198ed..6e780a6 100644 --- a/xio-proxy.c +++ b/xio-proxy.c @@ -149,7 +149,7 @@ static int xioopen_proxy_connect(int argc, const char *argv[], struct opt *opts, result = _xioopen_connect(xfd, - needbind?(struct sockaddr *)us:NULL, sizeof(*us), + needbind?us:NULL, sizeof(*us), (struct sockaddr *)them, themlen, opts, pf, socktype, IPPROTO_TCP, lowport, level); switch (result) { diff --git a/xio-socket.c b/xio-socket.c index 2e33fb2..5f682bb 100644 --- a/xio-socket.c +++ b/xio-socket.c @@ -264,7 +264,7 @@ int xioopen_socket_connect(int argc, const char *argv[], struct opt *opts, if ((result = xioopen_connect(xfd, - needbind?(struct sockaddr *)&us:NULL, uslen, + needbind?&us:NULL, uslen, (struct sockaddr *)&them, themlen, opts, pf, socktype, proto, false)) != 0) { return result; @@ -337,7 +337,7 @@ int xioopen_socket_listen(int argc, const char *argv[], struct opt *opts, if ((result = xioopen_listen(xfd, xioflags, - (struct sockaddr *)&us, uslen, + &us.soa, uslen, opts, opts0, 0/*instead of pf*/, socktype, proto)) != STAT_OK) return result; @@ -702,7 +702,7 @@ int xioopen_socket_datagram(int argc, const char *argv[], struct opt *opts, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC returns 0 on success. */ -int _xioopen_connect(struct single *xfd, struct sockaddr *us, size_t uslen, +int _xioopen_connect(struct single *xfd, union sockaddr_union *us, size_t uslen, struct sockaddr *them, size_t themlen, struct opt *opts, int pf, int socktype, int protocol, bool alt, int level) { @@ -713,6 +713,10 @@ int _xioopen_connect(struct single *xfd, struct sockaddr *us, size_t uslen, int _errno; int result; + if (pf == PF_UNIX && us != NULL) { + applyopts_named(us->un.sun_path, opts, PH_EARLY); + } + if ((xfd->fd = xiosocket(opts, pf, socktype, protocol, level)) < 0) { return STAT_RETRYLATER; } @@ -723,6 +727,9 @@ int _xioopen_connect(struct single *xfd, struct sockaddr *us, size_t uslen, applyopts_cloexec(xfd->fd, opts); + if (pf == PF_UNIX && us != NULL) { + applyopts_named(us->un.sun_path, opts, PH_PREOPEN); + } applyopts(xfd->fd, opts, PH_PREBIND); applyopts(xfd->fd, opts, PH_BIND); #if WITH_TCP || WITH_UDP @@ -733,7 +740,7 @@ int _xioopen_connect(struct single *xfd, struct sockaddr *us, size_t uslen, /* prepare sockaddr for bind probing */ if (us) { - sinp = (union sockaddr_union *)us; + sinp = us; } else { if (them->sa_family == AF_INET) { socket_in_init(&sin.ip4); @@ -779,7 +786,7 @@ int _xioopen_connect(struct single *xfd, struct sockaddr *us, size_t uslen, i = N = XIO_IPPORT_LOWER + dv.rem; do { /* loop over lowport bind() attempts */ *port = htons(i); - if (Bind(xfd->fd, (struct sockaddr *)sinp, sizeof(*sinp)) < 0) { + 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)), @@ -803,14 +810,20 @@ int _xioopen_connect(struct single *xfd, struct sockaddr *us, size_t uslen, #endif /* WITH_TCP || WITH_UDP */ if (us) { - if (Bind(xfd->fd, us, uslen) < 0) { + if (pf == PF_UNIX && us != NULL) { + applyopts_named(us->un.sun_path, opts, PH_PREOPEN); + } + if (Bind(xfd->fd, &us->soa, uslen) < 0) { Msg4(level, "bind(%d, {%s}, "F_Zd"): %s", - xfd->fd, sockaddr_info(us, uslen, infobuff, sizeof(infobuff)), + xfd->fd, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)), uslen, strerror(errno)); Close(xfd->fd); return STAT_RETRYLATER; } } + if (pf == PF_UNIX && us != NULL) { + applyopts_named(us->un.sun_path, opts, PH_PASTOPEN); + } applyopts(xfd->fd, opts, PH_PASTBIND); @@ -822,7 +835,7 @@ int _xioopen_connect(struct single *xfd, struct sockaddr *us, size_t uslen, Fcntl_l(xfd->fd, F_SETFL, fcntl_flags|O_NONBLOCK); } - result = Connect(xfd->fd, (struct sockaddr *)them, themlen); + result = Connect(xfd->fd, them, themlen); _errno = errno; la.soa.sa_family = them->sa_family; lalen = sizeof(la); if (Getsockname(xfd->fd, &la.soa, &lalen) < 0) { @@ -909,6 +922,9 @@ int _xioopen_connect(struct single *xfd, struct sockaddr *us, size_t uslen, applyopts_fchown(xfd->fd, opts); /* OPT_USER, OPT_GROUP */ applyopts(xfd->fd, opts, PH_CONNECTED); + if (pf == PF_UNIX && us != NULL) { + applyopts_named(us->un.sun_path, opts, PH_LATE); + } applyopts(xfd->fd, opts, PH_LATE); return STAT_OK; @@ -925,7 +941,7 @@ int _xioopen_connect(struct single *xfd, struct sockaddr *us, size_t uslen, OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC returns 0 on success. */ -int xioopen_connect(struct single *xfd, struct sockaddr *us, size_t uslen, +int xioopen_connect(struct single *xfd, union sockaddr_union *us, size_t uslen, struct sockaddr *them, size_t themlen, struct opt *opts, int pf, int socktype, int protocol, bool alt) { @@ -1033,6 +1049,10 @@ int _xioopen_dgram_sendto(/* them is already in xfd->peersa */ union sockaddr_union la; socklen_t lalen = sizeof(la); char infobuff[256]; + if (pf == PF_UNIX && us != NULL) { + applyopts_named(us->un.sun_path, opts, PH_EARLY); + } + if ((xfd->fd = xiosocket(opts, pf, socktype, ipproto, level)) < 0) { return STAT_RETRYLATER; } @@ -1044,18 +1064,24 @@ int _xioopen_dgram_sendto(/* them is already in xfd->peersa */ applyopts_cloexec(xfd->fd, opts); + if (pf == PF_UNIX && us != NULL) { + applyopts_named(us->un.sun_path, opts, PH_PREOPEN); + } applyopts(xfd->fd, opts, PH_PREBIND); applyopts(xfd->fd, opts, PH_BIND); if (us) { - if (Bind(xfd->fd, (struct sockaddr *)us, uslen) < 0) { + if (Bind(xfd->fd, &us->soa, uslen) < 0) { Msg4(level, "bind(%d, {%s}, "F_socklen"): %s", - xfd->fd, sockaddr_info((struct sockaddr *)us, uslen, infobuff, sizeof(infobuff)), + xfd->fd, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)), uslen, strerror(errno)); Close(xfd->fd); return STAT_RETRYLATER; } } + if (pf == PF_UNIX && us != NULL) { + applyopts_named(us->un.sun_path, opts, PH_PASTOPEN); + } applyopts(xfd->fd, opts, PH_PASTBIND); @@ -1068,6 +1094,9 @@ int _xioopen_dgram_sendto(/* them is already in xfd->peersa */ applyopts_fchown(xfd->fd, opts); applyopts(xfd->fd, opts, PH_CONNECTED); + if (pf == PF_UNIX && us != NULL) { + applyopts_named(us->un.sun_path, opts, PH_LATE); + } applyopts(xfd->fd, opts, PH_LATE); /* xfd->dtype = DATA_RECVFROM; *//* no, the caller must set this (ev _SKIPIP) */ @@ -1221,7 +1250,7 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, applyopts(xfd->fd, opts, PH_PREBIND); applyopts(xfd->fd, opts, PH_BIND); - if ((us != NULL) && Bind(xfd->fd, (struct sockaddr *)us, uslen) < 0) { + 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)); @@ -1362,7 +1391,7 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, palen = msgh.msg_namelen; Notice1("receiving packet from %s"/*"src"*/, - sockaddr_info((struct sockaddr *)pa, palen, peername, sizeof(peername))/*, + sockaddr_info(&pa->soa, palen, peername, sizeof(peername))/*, sockaddr_info(&la->soa, sockname, sizeof(sockname))*/); xiodopacketinfo(&msgh, true, true); @@ -1374,7 +1403,7 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, continue; } Info1("permitting packet from %s", - sockaddr_info((struct sockaddr *)pa, palen, + sockaddr_info(&pa->soa, palen, infobuff, sizeof(infobuff))); /* set the env vars describing the local and remote sockets */ @@ -1482,7 +1511,7 @@ int _xioopen_dgram_recv(struct single *xfd, int xioflags, applyopts(xfd->fd, opts, PH_PREBIND); applyopts(xfd->fd, opts, PH_BIND); - if ((us != NULL) && Bind(xfd->fd, (struct sockaddr *)us, uslen) < 0) { + 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)); @@ -1727,12 +1756,12 @@ int xiocheckpeer(xiosingle_t *xfd, if (xiocheckrange(pa, &xfd->para.socket.range) < 0) { char infobuff[256]; Warn1("refusing connection from %s due to range option", - sockaddr_info((struct sockaddr *)pa, 0, + sockaddr_info(&pa->soa, 0, infobuff, sizeof(infobuff))); return -1; } Info1("permitting connection from %s due to range option", - sockaddr_info((struct sockaddr *)pa, 0, + sockaddr_info(&pa->soa, 0, infobuff, sizeof(infobuff))); } #endif /* WITH_IP4 */ @@ -1744,7 +1773,7 @@ int xiocheckpeer(xiosingle_t *xfd, if (pa->soa.sa_family == AF_INET && ntohs(((struct sockaddr_in *)pa)->sin_port) != xfd->para.socket.ip.sourceport) { Warn1("refusing connection from %s due to wrong sourceport", - sockaddr_info((struct sockaddr *)pa, 0, + sockaddr_info(&pa->soa, 0, infobuff, sizeof(infobuff))); return -1; } @@ -1753,20 +1782,20 @@ int xiocheckpeer(xiosingle_t *xfd, if (pa->soa.sa_family == AF_INET6 && ntohs(((struct sockaddr_in6 *)pa)->sin6_port) != xfd->para.socket.ip.sourceport) { Warn1("refusing connection from %s due to wrong sourceport", - sockaddr_info((struct sockaddr *)pa, 0, + sockaddr_info(&pa->soa, 0, infobuff, sizeof(infobuff))); return -1; } #endif /* WITH_IP6 */ Info1("permitting connection from %s due to sourceport option", - sockaddr_info((struct sockaddr *)pa, 0, + sockaddr_info(&pa->soa, 0, infobuff, sizeof(infobuff))); } else if (xfd->para.socket.ip.lowport) { if (pa == NULL) { return -1; } if (pa->soa.sa_family == AF_INET && ntohs(((struct sockaddr_in *)pa)->sin_port) >= IPPORT_RESERVED) { Warn1("refusing connection from %s due to lowport option", - sockaddr_info((struct sockaddr *)pa, 0, + sockaddr_info(&pa->soa, 0, infobuff, sizeof(infobuff))); return -1; } @@ -1775,13 +1804,13 @@ int xiocheckpeer(xiosingle_t *xfd, ntohs(((struct sockaddr_in6 *)pa)->sin6_port) >= IPPORT_RESERVED) { Warn1("refusing connection from %s due to lowport option", - sockaddr_info((struct sockaddr *)pa, 0, + sockaddr_info(&pa->soa, 0, infobuff, sizeof(infobuff))); return -1; } #endif /* WITH_IP6 */ Info1("permitting connection from %s due to lowport option", - sockaddr_info((struct sockaddr *)pa, 0, + sockaddr_info(&pa->soa, 0, infobuff, sizeof(infobuff))); } #endif /* WITH_TCP || WITH_UDP */ @@ -1791,12 +1820,12 @@ int xiocheckpeer(xiosingle_t *xfd, if (result < 0) { char infobuff[256]; Warn1("refusing connection from %s due to tcpwrapper option", - sockaddr_info((struct sockaddr *)pa, 0, + sockaddr_info(&pa->soa, 0, infobuff, sizeof(infobuff))); return -1; } else if (result > 0) { Info1("permitting connection from %s due to tcpwrapper option", - sockaddr_info((struct sockaddr *)pa, 0, + sockaddr_info(&pa->soa, 0, infobuff, sizeof(infobuff))); } #endif /* (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */ diff --git a/xio-socket.h b/xio-socket.h index 7d18b24..76b1bf0 100644 --- a/xio-socket.h +++ b/xio-socket.h @@ -74,13 +74,13 @@ char *xiogetifname(int ind, char *val, int ins); extern int retropt_socket_pf(struct opt *opts, int *pf); extern int xioopen_connect(struct single *fd, - struct sockaddr *us, size_t uslen, + union sockaddr_union *us, size_t uslen, struct sockaddr *them, size_t themlen, struct opt *opts, int pf, int socktype, int protocol, bool alt); extern int _xioopen_connect(struct single *fd, - struct sockaddr *us, size_t uslen, + union sockaddr_union *us, size_t uslen, struct sockaddr *them, size_t themlen, struct opt *opts, int pf, int socktype, int protocol, diff --git a/xio-socks.c b/xio-socks.c index aa1070e..0af5cda 100644 --- a/xio-socks.c +++ b/xio-socks.c @@ -126,7 +126,7 @@ static int xioopen_socks4_connect(int argc, const char *argv[], struct opt *opts /* this cannot fork because we retrieved fork option above */ result = _xioopen_connect (xfd, - needbind?(struct sockaddr *)us:NULL, sizeof(*us), + needbind?us:NULL, sizeof(*us), (struct sockaddr *)them, themlen, opts, pf, socktype, IPPROTO_TCP, lowport, level); switch (result) { diff --git a/xio-unix.c b/xio-unix.c index 2e3c5a5..0c1dead 100644 --- a/xio-unix.c +++ b/xio-unix.c @@ -210,13 +210,14 @@ static int xioopen_unix_connect(int argc, const char *argv[], struct opt *opts, /* we expect the form: filename */ const char *name; struct single *xfd = &xxfd->stream; + const struct opt *namedopt; int pf = PF_UNIX; int socktype = SOCK_STREAM; int protocol = 0; struct sockaddr_un them, us; socklen_t themlen, uslen = sizeof(us); bool needbind = false; - bool opt_unlink_close = false; + bool opt_unlink_close = true; int result; if (argc != 2) { @@ -235,6 +236,7 @@ static int xioopen_unix_connect(int argc, const char *argv[], struct opt *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); @@ -244,8 +246,13 @@ static int xioopen_unix_connect(int argc, const char *argv[], struct opt *opts, needbind = true; } - if (opt_unlink_close) { - if ((xfd->unlink_close = strdup(name)) == NULL) { + if (!needbind && + (namedopt = searchopt(opts, GROUP_NAMED, 0, 0, 0))) { + Error1("Option \"%s\" only with bind option", namedopt->desc->defname); + } + + if (opt_unlink_close && needbind) { + if ((xfd->unlink_close = strdup(us.sun_path)) == NULL) { Error1("strdup(\"%s\"): out of memory", name); } xfd->opt_unlink_close = true; @@ -253,7 +260,7 @@ static int xioopen_unix_connect(int argc, const char *argv[], struct opt *opts, if ((result = xioopen_connect(xfd, - needbind?(struct sockaddr *)&us:NULL, uslen, + needbind?(union sockaddr_union *)&us:NULL, uslen, (struct sockaddr *)&them, themlen, opts, pf, socktype, protocol, false)) != 0) { return result; @@ -269,13 +276,14 @@ static int xioopen_unix_sendto(int argc, const char *argv[], struct opt *opts, i /* we expect the form: filename */ const char *name; xiosingle_t *xfd = &xxfd->stream; + const struct opt *namedopt; int pf = PF_UNIX; int socktype = SOCK_DGRAM; int protocol = 0; union sockaddr_union us; socklen_t uslen = sizeof(us); bool needbind = false; - bool opt_unlink_close = false; + bool opt_unlink_close = true; if (argc != 2) { Error2("%s: wrong number of parameters (%d instead of 1)", @@ -303,8 +311,13 @@ static int xioopen_unix_sendto(int argc, const char *argv[], struct opt *opts, i needbind = true; } - if (opt_unlink_close) { - if ((xfd->unlink_close = strdup(name)) == NULL) { + if (!needbind && + (namedopt = searchopt(opts, GROUP_NAMED, 0, 0, 0))) { + Error1("Option \"%s\" only with bind option", namedopt->desc->defname); + } + + if (opt_unlink_close && needbind) { + if ((xfd->unlink_close = strdup(us.un.sun_path)) == NULL) { Error1("strdup(\"%s\"): out of memory", name); } xfd->opt_unlink_close = true; @@ -511,6 +524,7 @@ static int xioopen_unix_client(int argc, const char *argv[], struct opt *opts, i int _xioopen_unix_client(xiosingle_t *xfd, int xioflags, unsigned groups, int abstract, struct opt *opts, const char *name) { + const struct opt *namedopt; int pf = PF_UNIX; int socktype = 0; /* to be determined by server socket type */ int protocol = 0; @@ -540,6 +554,11 @@ _xioopen_unix_client(xiosingle_t *xfd, int xioflags, unsigned groups, needbind = true; } + if (!needbind && + (namedopt = searchopt(opts, GROUP_NAMED, 0, 0, 0))) { + Error1("Option \"%s\" only with bind option", namedopt->desc->defname); + } + if (opt_unlink_close) { if ((xfd->unlink_close = strdup(name)) == NULL) { Error1("strdup(\"%s\"): out of memory", name); @@ -553,8 +572,8 @@ _xioopen_unix_client(xiosingle_t *xfd, int xioflags, unsigned groups, /* xfd->dtype = DATA_STREAM; // is default */ if ((result = xioopen_connect(xfd, - needbind?(struct sockaddr *)&us:NULL, uslen, - (struct sockaddr *)&them, themlen, + needbind?&us:NULL, uslen, + &them.soa, themlen, opts, pf, socktype?socktype:SOCK_STREAM, protocol, false)) != 0) { if (errno == EPROTOTYPE) { diff --git a/xioopts.c b/xioopts.c index 048877e..4cda110 100644 --- a/xioopts.c +++ b/xioopts.c @@ -2514,6 +2514,31 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts, return 0; } + +/* look for an option with the given properties + return a pointer to the first matching valid option in the list + Returns NULL when no matching option found */ +const struct opt *searchopt(const struct opt *opts, unsigned int groups, enum e_phase from, enum e_phase to, + enum e_func func) { + int i; + + if (!opts) return NULL; + + /* remember: struct opt are in an array */ + i = 0; + while (opts[i].desc != ODESC_END) { + if (opts[i].desc != ODESC_DONE && + (groups == 0 || (groups && (opts[i].desc->group&groups))) && + (from == 0 || (from <= opts[i].desc->phase)) && + (to == 0 || (opts[i].desc->phase <= to)) && + (func == 0 || (opts[i].desc->func == func))) { + return &opts[i]; + } + ++i; + } + return NULL; +} + /* copy the already parsed options for repeated application, but only those matching groups ANY and */ struct opt *copyopts(const struct opt *opts, unsigned int groups) { diff --git a/xioopts.h b/xioopts.h index f50cce3..3e2127f 100644 --- a/xioopts.h +++ b/xioopts.h @@ -932,6 +932,8 @@ extern int parseopts(const char **a, unsigned int groups, struct opt **opts); extern int parseopts_table(const char **a, unsigned int groups, struct opt **opts, const struct optname optionnames[], size_t optionnum); +extern const struct opt *searchopt(const struct opt *opts, unsigned int groups, enum e_phase from, enum e_phase to, + enum e_func func); extern struct opt *copyopts(const struct opt *opts, unsigned int groups); extern struct opt *moveopts(struct opt *opts, unsigned int groups); extern int leftopts(const struct opt *opts);