diff --git a/CHANGES b/CHANGES index 18cf2c3..a1294cc 100644 --- a/CHANGES +++ b/CHANGES @@ -152,6 +152,15 @@ Features: afterwards. Tests: UMASK_ON_CREATE UMASK_ON_SYSTEM + Added option unix-bind-tempname (bind-tempname) to allow UNIX (and + ABSTRACT) client addresses to bind to unique addresses even when + invoked in forked off sub processes. + Tests: UNIX_LISTEN_CONNECT_BIND_TEMPNAME UNIX_LISTEN_CLIENT_BIND_TEMPNAME + UNIX_RECVFROM_CLIENT_BIND_TEMPNAME UNIX_RECVFROM_SENDTO_BIND_TEMPNAME + ABSTRACT_LISTEN_CONNECT_BIND_TEMPNAME ABSTRACT_LISTEN_CLIENT_BIND_TEMPNAME + ABSTRACT_RECVFROM_CLIENT_BIND_TEMPNAME ABSTRACT_RECVFROM_SENDTO_BIND_TEMPNAME + Thanks to Kai Lüke for sending an initial patch. + Corrections: When a sub process (EXEC, SYSTEM) terminated with exit code other than 0, its last sent data might have been lost depending on timing of read/ diff --git a/Makefile.in b/Makefile.in index 3f0c1bf..3f8d86f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -22,7 +22,8 @@ srcdir = @srcdir@ VPATH = @srcdir@ CC = @CC@ -#CCOPT=-fcf-protection=none # for gdb on Ubuntu-20.04 +#CCOPT1=-no-pie -fstack-protector +#CCOPT=$(CCOPT1) -fcf-protection=none # for gdb on Ubuntu-20.04 CCOPTS = $(CCOPT) SYSDEFS = @SYSDEFS@ diff --git a/compat.h b/compat.h index 80618bb..bb7e2d8 100644 --- a/compat.h +++ b/compat.h @@ -831,6 +831,13 @@ typedef unsigned long T_sigset; # endif #endif +/* OpenBSD (at least 7.2) does better with this special setting */ +#if __FreeBSD__ || __OpenBSD__ +# define UNIX_TIGHTSOCKLEN false +#else +# define UNIX_TIGHTSOCKLEN true +#endif + /* Cygwin 1.3.22 has the prototypes, but not the type... */ #ifndef HAVE_TYPE_STAT64 # undef HAVE_STAT64 diff --git a/doc/socat.yo b/doc/socat.yo index 572d4a1..4746418 100644 --- a/doc/socat.yo +++ b/doc/socat.yo @@ -2136,8 +2136,8 @@ label(OPTION_BIND)dit(bf(tt(bind=))) Binds the socket to the given socket address using the code(bind()) system call. The form of is socket domain dependent: IP4 and IP6 allow the form [hostname|hostaddress][:(service|port)] (link(example)(EXAMPLE_OPTION_BIND_TCP4)), - unixdomain() sockets require link()(TYPE_FILENAME), - VSOCK allow the form [cid][:(port)]. + VSOCK allows the form [cid][:(port)].nl() + See also: link(unix-bind-tempname)(OPTION_UNIX_BIND_TEMPNAME) label(OPTION_CONNECT_TIMEOUT)dit(bf(tt(connect-timeout=))) Abort the connection attempt after [link(timeval)(TYPE_TIMEVAL)] with error status. @@ -2292,6 +2292,15 @@ label(GROUP_SOCK_UNIX)em(bf(UNIX option group)) These options apply to UNIX domain based addresses. startdit() +label(OPTION_UNIX_BIND_TEMPNAME)dit(bf(tt(bind-tempname[=/tmp/pre-XXXXXX], + unix-bind-tempname[=/tmp/pre-XXXXXX]))) + Binds to a random path or random address (on abstract namespace sockets). + This is useful with datagram client addresses (tt(SENDTO), or tt(CLIENT)) + that are opened in child processes forked off from a common + parent process where the child processes cannot have different bind options. + In the path code(X)'s get replaced with a random character sequence + similar to NOEXPAND(tempnam(3)). When no argument is given socat() takes a + default like code(/tmp/fileXXXXXX).nl() label(OPTION_UNIX_TIGHTSOCKLEN)dit(bf(tt(unix-tightsocklen[=(0|1)]))) On socket operations, pass a socket address length that does not include the whole code(struct sockaddr_un) record but (besides other components) only diff --git a/error.c b/error.c index 46d2714..d0a2a9e 100644 --- a/error.c +++ b/error.c @@ -128,6 +128,8 @@ static int diag_sock_pair(void) { fcntl(diag_sock_send, F_SETFL, O_NONBLOCK); fcntl(diag_sock_recv, F_SETFL, O_NONBLOCK); #endif + fcntl(diag_sock_send, F_SETFD, FD_CLOEXEC); + fcntl(diag_sock_recv, F_SETFD, FD_CLOEXEC); return 0; } diff --git a/procan.c b/procan.c index 8f08aa0..05e9b5a 100644 --- a/procan.c +++ b/procan.c @@ -156,13 +156,23 @@ int procan(FILE *outfile) { rlim.rlim_cur, rlim.rlim_max); } #endif + } + #ifdef SIZE_MAX - fprintf(outfile, "SIZE_MAX = %-24lu\n", SIZE_MAX); + fprintf(outfile, "SIZE_MAX = %-24lu\n", SIZE_MAX); +#endif +#ifdef P_tmpdir + fprintf(outfile, "P_tmpdir = \"%s\"\n", P_tmpdir); +#endif +#ifdef L_tmpnam + fprintf(outfile, "L_tmpnam = %u\n", L_tmpnam); +#endif +#ifdef TMP_MAX + fprintf(outfile, "TMP_MAX = %d\n", TMP_MAX); #endif #ifdef PIPE_BUF - fprintf(outfile, "PIPE_BUF = %-24d\n", PIPE_BUF); + fprintf(outfile, "PIPE_BUF = %-24d\n", PIPE_BUF); #endif - } /* Name spaces */ { diff --git a/sycls.c b/sycls.c index 1e258c0..444d6ac 100644 --- a/sycls.c +++ b/sycls.c @@ -1146,6 +1146,7 @@ int Listen(int s, int backlog) { #if _WITH_SOCKET /* don't forget to handle EINTR when using Accept() ! */ int Accept(int s, struct sockaddr *addr, socklen_t *addrlen) { + char infobuff[256]; int result, _errno; fd_set accept_s; if (!diag_in_handler) diag_flush(); @@ -1155,15 +1156,14 @@ int Accept(int s, struct sockaddr *addr, socklen_t *addrlen) { return -1; } #if WITH_SYCLS - Debug3("accept(%d, %p, %p)", s, addr, addrlen); + sockaddr_info(addr, *addrlen, infobuff, sizeof(infobuff)); + Debug3("accept(%d, %p, %p)", s, infobuff, addrlen); #endif /* WITH_SYCLS */ result = accept(s, addr, addrlen); _errno = errno; if (!diag_in_handler) diag_flush(); #if WITH_SYCLS if (result >= 0) { - char infobuff[256]; - sockaddr_info(addr, *addrlen, infobuff, sizeof(infobuff)); Info5("accept(%d, {%d, %s}, "F_socklen") -> %d", s, addr->sa_family, sockaddr_info(addr, *addrlen, infobuff, sizeof(infobuff)), diff --git a/test.sh b/test.sh index e25013c..a742828 100755 --- a/test.sh +++ b/test.sh @@ -1462,6 +1462,11 @@ waitunixport () { waitfile "$1" "$2" "$3" } +# Not implemented +waitabstractport () { + relsleep 5 +} + # wait until a filesystem entry exists waitfile () { local crit=-e @@ -5610,7 +5615,7 @@ N=$((N+1)) # Test if Filan can determine UNIX domain socket in file system NAME=FILANSOCKET case "$TESTS" in -*%$N%*|*%filan%*|*%listen%*|*%$NAME%*) +*%$N%*|*%filan%*|*%unix%*|*%listen%*|*%$NAME%*) TEST="$NAME: capability to analyze named unix socket" # Run Filan on a listening UNIX domain socket. # When its output gives "socket" as type (2nd column), the test succeeded @@ -5701,7 +5706,7 @@ fi NAME=PTMXWAITSLAVE PTYTYPE=ptmx case "$TESTS" in -*%$N%*|*%functions%*|*%pty%*|*%$NAME%*) +*%$N%*|*%functions%*|*%pty%*|*%unix%*|*%listen%*|*%$NAME%*) TEST="$NAME: test if master pty ($PTYTYPE) waits for slave connection" if ! eval $NUMCOND; then :; else if ! feat=$(testfeats pty); then @@ -5722,7 +5727,7 @@ N=$((N+1)) NAME=OPENPTYWAITSLAVE PTYTYPE=openpty case "$TESTS" in -*%$N%*|*%functions%*|*%pty%*|*%$NAME%*) +*%$N%*|*%functions%*|*%pty%*|*%unix%*|*%listen%*|*%$NAME%*) TEST="$NAME: test if master pty ($PTYTYPE) waits for slave connection" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats pty); then @@ -7803,7 +7808,7 @@ N=$((N+1)) NAME=EXECENDCLOSE case "$TESTS" in -*%$N%*|*%functions%*|*%exec%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%exec%*|*%listen%*|*%unix%*|*%fork%*|*%$NAME%*) TEST="$NAME: end-close keeps EXEC child running" if ! eval $NUMCOND; then :; else tf="$td/test$N.stdout" @@ -7867,7 +7872,7 @@ printf "test $F_n $TEST... " $N # output for the PTY name { $CMD 2>"${te}"; echo $? >"$td/test$N.rc0"; } & waitfile "${te}" -psleep 0.1 +psleep 0.5 # 0.1 is too few for FreeBSD-10 PTY=$(grep "N PTY is " $te |sed 's/.*N PTY is //') [ -e "$PTY" ] && cat $PTY >/dev/null 2>/dev/null rc=$(cat "$td/test$N.rc0") @@ -9015,7 +9020,7 @@ N=$((N+1)) # process under some circumstances. NAME=EXECPTYKILL case "$TESTS" in -*%$N%*|*%functions%*|*%bugs%*|*%exec%*|*%pty%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%bugs%*|*%exec%*|*%pty%*|*%listen%*|*%unix%*|*%fork%*|*%$NAME%*) TEST="$NAME: exec:...,pty explicitely kills sub process" # we want to check if the exec'd sub process is killed in time # for this we have a shell script that generates a file after two seconds; @@ -14920,7 +14925,7 @@ for addr in exec system; do ADDR=$(echo $addr |tr a-z A-Z) NAME=${ADDR}SOCKETPAIRPACKETS case "$TESTS" in - *%$N%*|*%functions%*|*%exec%*|*%socketpair%*|*%packets%*|*%$NAME%*) + *%$N%*|*%functions%*|*%exec%*|*%socketpair%*|*%unix%*|*%dgram%*|*%packets%*|*%$NAME%*) TEST="$NAME: simple echo via $addr of cat with socketpair, keeping packet boundaries" # Start a Socat process with a UNIX datagram socket on the left side and with # a sub process connected via datagram socketpair that keeps packet boundaries @@ -16478,7 +16483,7 @@ N=$((N+1)) # Test the netns (net namespace) feature with EXEC and reset NAME=NETNS_EXEC case "$TESTS" in -*%$N%*|*%functions%*|*%root%*|*%namespace%*|*%netns%*|*%socket%*|*%$NAME%*) +*%$N%*|*%functions%*|*%root%*|*%namespace%*|*%netns%*|*%socket%*|*%abstract%*|*%dgram%*|*%$NAME%*) ns=socat-$$-test$N TEST="$NAME: option netns with EXEC (net namespace $ns)" # Start a simple server with option netns on localhost of a net namespace that @@ -17490,20 +17495,21 @@ else tdiff="$td/test$N.diff" tdebug="$td/test$N.debug" opwd=$(pwd) - CMD0="$TRACE $absSOCAT $opts -U SHELL:\"cat\ >$tc\",chdir=$td SYSTEM:pwd" + CMD0="$TRACE $absSOCAT $opts SHELL:\"cat\ >$tc\",chdir=$td SYSTEM:pwd" printf "test $F_n $TEST... " $N mkdir "$td/$tdd" pushd "$td/$tdd" >/dev/null eval "$CMD0" >/dev/null 2>"${te}0" rc0=$? popd >/dev/null + waitfile "$td/$tc" tpwd=$(find $td -name $tc -print); tpwd=${tpwd%/*} pwd2=$(cat $tpwd/$tc >$tdebug echo "Temporary pwd: $tpwd" >>$tdebug echo "Addr2 pwd: $pwd2" >>$tdebug if [ "$rc0" -ne 0 ]; then - $PRINTF "$FAILED\n" + $PRINTF "$FAILED (rc=$rc0)\n" echo "$CMD0 &" cat "${te}0" >&2 numFAIL=$((numFAIL+1)) @@ -17696,6 +17702,200 @@ esac N=$((N+1)) +while read _UNIX _SRV _CLI; do + if [ -z "$_UNIX" ] || [[ "$_UNIX" == \#* ]]; then continue; fi +SRV=${_UNIX}-$_SRV +CLI=${_UNIX}-$_CLI +CLI_=$(echo $CLI |tr x- x_) +PROTO=${_UNIX} +proto=$(tolower $PROTO) + +# Test the unix-bind-tempname option +NAME=${_UNIX}_${_SRV}_${_CLI}_BIND_TEMPNAME +case "$TESTS" in +*%$N%*|*%functions%*|*%$proto%*|*%socket%*|*%tempname%*|*%listen%*|*%fork%*|*%$NAME%*) +TEST="$NAME: Option unix-bind-tempname" +# Start a UNIX domain service with forking +# Start a TCP service with forking that relays to the UNIX domain service +# Open two concurrent client sessions to the TCP service. +# When both sessions work (in particular, when the UNIX domain service does not +# log "Transport endpoint is not connected" and the TCP service does not fail +# with "Address already in use"), the test succeeded. +if ! eval $NUMCOND; then :; +elif [[ $CLI_ =~ ABSTRACT-* ]] && ! feat=$(testfeats abstract-unixsocket); then + $PRINTF "test $F_n $TEST... ${YELLOW}$feat not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +else +ts="$td/test$N.sock" +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +tdiff="$td/test$N.diff" +da="test$N $(date) $RANDOM" +CMD0="$TRACE $SOCAT $opts -lp server $SRV:${ts}0,fork PIPE" +# Using this command would show the principal problem: UNIX (and ABSTRACT) +# datagram clients do not internally bind to a defined address and thus cannot +# receive replies. Applies to all(?) Linux, (some)FreeBSD, (some)Solaris, others +# not tried +#CMD1="$TRACE $SOCAT $opts -lp bind-tempname TCP4-LISTEN:$PORT,reuseaddr,fork $CLI:${ts}0" +# Attempt to bind the datagram client to some address works, but only for a +# single client; when multiple clients are forked they conflict +# The following command is the solution: option unix-bind-tempname generates +# random names (like tempnam(2)) for binding the datagram client socket; +# creating the XXXXXX file makes sure that the (non abstract) clients cannot +# erronously bind there (part of the test) +CMD1="$TRACE $SOCAT $opts -lp bind-tempname TCP4-LISTEN:$PORT,reuseaddr,fork $CLI:${ts}0,bind=${ts}1" +touch ${ts}1.XXXXXX; CMD1="$TRACE $SOCAT $opts -lp tempname TCP4-LISTEN:$PORT,reuseaddr,fork $CLI:${ts}0,bind-tempname=${ts}1.XXXXXX" +CMD2="$TRACE $SOCAT $opts -lp client - TCP4-CONNECT:$LOCALHOST:$PORT" +printf "test $F_n $TEST... " $N +$CMD0 2>"${te}0" & +pid0=$! +wait${proto}port ${ts}0 1 +$CMD1 2>"${te}1" & +pid1=$! +waittcp4port $PORT 1 +{ echo "$da a"; sleep 2; } |$CMD2 >"${tf}2a" 2>"${te}2a" & +pid2a=$! +sleep 1 +echo "$da b" |$CMD2 >"${tf}2b" 2>"${te}2b" +rc2b=$? +sleep 1 +kill $pid0 $pid1 $pid2a 2>/dev/null; wait +if [ $rc2b -ne 0 ]; then + $PRINTF "$FAILED\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1 &" + cat "${te}1" >&2 + echo "$CMD2 &" + cat "${te}2a" >&2 + echo "$CMD2" + cat "${te}2b" >&2 + numFAIL=$((numFAIL+1)) + listFAIL="$listFAIL $N" +elif ! echo "$da a" |diff - ${tf}2a >${tdiff}2a; then + $PRINTF "$FAILED (phase a)\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1 &" + cat "${te}1" >&2 + echo "$CMD2" + cat "${te}2a" >&2 + echo "diff a:" >&2 + cat ${tdiff}2a >&2 + numFAIL=$((numFAIL+1)) + listFAIL="$listFAIL $N" +elif ! echo "$da b" |diff - ${tf}2b >${tdiff}2b; then + $PRINTF "$FAILED\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1 &" + cat "${te}1" >&2 + echo "$CMD2 &" + cat "${te}2a" >&2 + echo "$CMD2" + cat "${te}2b" >&2 + echo "diff b:" >&2 + cat ${tdiff}2b >&2 + numFAIL=$((numFAIL+1)) + listFAIL="$listFAIL $N" +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 + if [ "$VERBOSE" ]; then echo "$CMD2"; fi + if [ "$DEBUG" ]; then cat "${te}2a" >&2; fi + if [ "$VERBOSE" ]; then echo "$CMD2"; fi + if [ "$DEBUG" ]; then cat "${te}2b" >&2; fi + numOK=$((numOK+1)) +fi +fi # NUMCOND + ;; +esac +PORT=$((PORT+1)) +N=$((N+1)) + +done <<<" +UNIX LISTEN CONNECT +UNIX LISTEN CLIENT +UNIX RECVFROM CLIENT +UNIX RECVFROM SENDTO +ABSTRACT LISTEN CONNECT +ABSTRACT LISTEN CLIENT +ABSTRACT RECVFROM CLIENT +ABSTRACT RECVFROM SENDTO +" + +# Test if OS/libc is not prone to symlink attacks on UNIX bind() +NAME=TEMPNAME_SEC +case "$TESTS" in +*%$N%*|*%functions%*|*%bugs%*|*%socket%*|*%unix%*|*%dgram%*|*%security%*|*%$NAME%*) +TEST="$NAME: test if a symlink attack works against bind()" +# Create a symlink .sock2 pointing to non-existing .sock3 +# Start Socat with UNIX-SENDTO...,bind=.sock2 +# When .sock3 exists the test failed +if ! eval $NUMCOND; then :; else +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +ts1="$td/test$N.sock1" +ts2="$td/test$N.sock2" +ts3="$td/test$N.sock3" +tdiff="$td/test$N.diff" +da="test$N $(date) $RANDOM" +CMD0a="rm -f $ts3" +CMD0b="ln -s $ts3 $ts2" +CMD1="$TRACE $SOCAT $opts UNIX-SENDTO:$ts1,bind=$ts2 PIPE" +rc1=$? +printf "test $F_n $TEST... " $N +$CMD0a +$CMD0b +#echo; ls -l $ts2 $ts3 +$CMD1 2>"${te}1" & +pid1=$! +waitunixport $ts1 1 1 2>/dev/null +#res="$(ls -l $ts3 2>/dev/null)" +kill $pid1 2>/dev/null +if [ -e $ts3 ]; then + $PRINTF "$FAILED\n" + echo "symlink target has been created" >&2 + echo "$CMD0a" >&2 + cat "${te}0a" >&2 + echo "$CMD0b" >&2 + cat "${te}0b" >&2 + echo "$CMD1" >&2 + cat "${te}1" >&2 + numFAIL=$((numFAIL+1)) + listFAIL="$listFAIL $N" +elif ! grep -q " E " ${te}1; then + $PRINTF "$FAILED\n" + echo "Socat did not fail" + echo "$CMD0a" >&2 + cat "${te}0a" >&2 + echo "$CMD0b" >&2 + cat "${te}0b" >&2 + echo "$CMD1" >&2 + cat "${te}1" >&2 + numFAIL=$((numFAIL+1)) + listFAIL="$listFAIL $N" +else + $PRINTF "$OK\n" + if [ "$VERBOSE" ]; then echo "$CMD0a"; fi + if [ "$DEBUG" ]; then cat "${te}0a" >&2; fi + if [ "$VERBOSE" ]; then echo "$CMD0b"; fi + if [ "$DEBUG" ]; then cat "${te}0b" >&2; fi + if [ "$VERBOSE" ]; then echo "$CMD1"; fi + if [ "$DEBUG" ]; then cat "${te}1" >&2; fi + numOK=$((numOK+1)) +fi +fi # NUMCOND + ;; +esac +PORT=$((PORT+1)) +N=$((N+1)) + + # end of common tests ################################################################################## diff --git a/xio-gopen.c b/xio-gopen.c index 7d251c7..ec36580 100644 --- a/xio-gopen.c +++ b/xio-gopen.c @@ -62,7 +62,8 @@ static int xioopen_gopen( Info1("\"%s\" is a socket, connecting to it", filename); result = - _xioopen_unix_client(sfd, xioflags, addrdesc->groups, 0, opts, filename); + _xioopen_unix_client(sfd, xioflags, addrdesc->groups, 0, opts, + filename, addrdesc); if (result < 0) { return result; } diff --git a/xio-ip.c b/xio-ip.c index 9256d84..e1de0fa 100644 --- a/xio-ip.c +++ b/xio-ip.c @@ -244,7 +244,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\", {0x%02x,%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, @@ -438,7 +438,7 @@ int xioresolve(const char *node, const char *service, } aip = res; - if (ai_flags[0] & AI_PASSIVE && family == PF_UNSPEC) { + if (ai_flags != NULL && ai_flags[0] & AI_PASSIVE && family == PF_UNSPEC) { /* We select the first IPv6 address, if available, because this might accept IPv4 connections too */ while (aip != NULL) { diff --git a/xio-listen.c b/xio-listen.c index 64ac0e9..2e99833 100644 --- a/xio-listen.c +++ b/xio-listen.c @@ -212,7 +212,6 @@ int _xioopen_accept_fd( /* Under some circumstances (e.g., TCP listen on port 0) bind() fills empty fields that we want to know. */ - salen = sizeof(sa); if (Getsockname(sfd->fd, us, &uslen) < 0) { Warn4("getsockname(%d, %p, {%d}): %s", sfd->fd, &us, uslen, strerror(errno)); @@ -255,7 +254,6 @@ int _xioopen_accept_fd( pa = &_peername; la = &_sockname; - salen = sizeof(struct sockaddr); do { /*? int level = E_ERROR;*/ Notice1("listening on %s", sockaddr_info(us, uslen, lisname, sizeof(lisname))); @@ -304,6 +302,7 @@ int _xioopen_accept_fd( Exit(0); } } + salen = sizeof(sa); ps = Accept(sfd->fd, (struct sockaddr *)&sa, &salen); if (ps >= 0) { /*0 Info4("accept(%d, %p, {"F_Zu"}) -> %d", sfd->fd, &sa, salen, ps);*/ diff --git a/xio-socket.c b/xio-socket.c index d23d2ee..0a1af63 100644 --- a/xio-socket.c +++ b/xio-socket.c @@ -775,6 +775,10 @@ int xiogetpacketinfo(struct single *sfd, int fd) OFUNC_OFFSET, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC Does not fork, does not retry. + Alternate (alt) bind semantics are: + with IP sockets: lowport (selects randomly a free port from 640 to 1023) + with UNIX and abstract sockets: uses tmpname() to find a free file system + entry. returns 0 on success. */ int _xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen, @@ -1113,7 +1117,7 @@ int _xioopen_dgram_sendto(/* them is already in xfd->peersa */ /* waits for incoming packet, checks its source address and port. Depending on fork option, it may fork a subprocess. - Returns STAT_OK if a the packet was accepted; with fork option, this is already in + Returns STAT_OK if a packet was accepted; with fork option, this is already in a new subprocess! Other return values indicate a problem; this can happen in the master process or in a subprocess. @@ -1393,10 +1397,10 @@ int _xioopen_dgram_recv(struct single *sfd, int xioflags, #endif /* && (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */ if (xioparms.logopt == 'm') { - Info("starting recvfrom loop, switching to syslog"); + Info("starting recv loop, switching to syslog"); diag_set('y', xioparms.syslogfac); xioparms.logopt = 'y'; } else { - Info("starting recvfrom loop"); + Info("starting recv loop"); } return STAT_OK; @@ -2053,6 +2057,15 @@ xiosocketpair(struct opt *opts, int pf, int socktype, int proto, int sv[2]) { return result; } +/* Binds a socket to a socket address. Handles IP (internet protocol), UNIX + domain, Linux abstract UNIX domain. + The bind address us may be NULL in which case no bind() happens, except with + alt (on option unix-bind-tempname (bind-tempname)). + Alternate (atl) bind semantics are: + with IP sockets: lowport (selects randomly a free port from 640 to 1023) + with UNIX and abstract sockets: uses a method similar to tmpname() to + find a free file system entry. +*/ int xiobind( struct single *sfd, union sockaddr_union *us, @@ -2065,19 +2078,89 @@ int xiobind( char infobuff[256]; int result; + if (false /* for canonical reasons */) { + ; #if WITH_UNIX - if (pf == PF_UNIX && us != NULL) { - applyopts_named(us->un.sun_path, opts, PH_PREOPEN); - } + } else if (pf == PF_UNIX) { + if (alt && us != NULL) { + bool abstract = false; + char *usrname = NULL, *sockname; + +#if WITH_ABSTRACT_UNIXSOCKET + abstract = (us->un.sun_path[0] == '\0'); #endif - applyopts(sfd, sfd->fd, opts, PH_PREBIND); - applyopts(sfd, sfd->fd, opts, PH_BIND); + + if (uslen == ((char *)&us->un.sun_path-(char *)us)) { + usrname = NULL; + } else { +#if WITH_ABSTRACT_UNIXSOCKET + if (abstract) + usrname = strndup(us->un.sun_path+1, sizeof(us->un.sun_path)-1); + else +#endif + usrname = strndup(us->un.sun_path, sizeof(us->un.sun_path)); + if (usrname == NULL) { + int _errno = errno; + Error2("strndup(\"%s\", "F_Zu"): out of memory", + us->un.sun_path, sizeof(us->un.sun_path)); + errno = _errno; + return -1; + } + } + + do { /* loop over tempnam bind() attempts */ + sockname = xio_tempnam(usrname, abstract); + if (sockname == NULL) { + Error2("tempnam(\"%s\"): %s", usrname, strerror(errno)); + free(usrname); + return -1; + } + strncpy(us->un.sun_path+(abstract?1:0), sockname, sizeof(us->un.sun_path)); + uslen = sizeof(&((struct sockaddr_un *)0)->sun_path) + + Min(strlen(sockname), sizeof(us->un.sun_path)); /*?*/ + free(sockname); + + if (Bind(sfd->fd, (struct sockaddr *)us, uslen) < 0) { + Msg4(errno==EADDRINUSE?E_INFO:level, "bind(%d, {%s}, "F_Zd"): %s", + sfd->fd, sockaddr_info((struct sockaddr *)us, uslen, + infobuff, sizeof(infobuff)), + uslen, strerror(errno)); + if (errno != EADDRINUSE) { + free(usrname); + Close(sfd->fd); + return STAT_RETRYLATER; + } + } else { + break; /* could bind to path, good, continue past loop */ + } + } while (true); + free(usrname); + applyopts_named(us->un.sun_path, opts, PH_PREOPEN); + } else + + if (us != NULL) { + if (Bind(sfd->fd, &us->soa, uslen) < 0) { + Msg4(level, "bind(%d, {%s}, "F_Zd"): %s", + sfd->fd, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)), + uslen, strerror(errno)); + Close(sfd->fd); + return STAT_RETRYLATER; + } + applyopts_named(us->un.sun_path, opts, PH_PREOPEN); + } + + applyopts(sfd, sfd->fd, opts, PH_PREBIND); + applyopts(sfd, sfd->fd, opts, PH_BIND); +#endif /* WITH_UNIX */ + #if WITH_TCP || WITH_UDP - if (alt) { + } else if (alt) { union sockaddr_union sin, *sinp; unsigned short *port, i, N; div_t dv; + applyopts(sfd, sfd->fd, opts, PH_PREBIND); + applyopts(sfd, sfd->fd, opts, PH_BIND); /* prepare sockaddr for bind probing */ if (us) { sinp = us; @@ -2147,28 +2230,21 @@ int xiobind( 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(sfd->fd, &us->soa, uslen) < 0) { - Msg4(level, "bind(%d, {%s}, "F_Zd"): %s", - sfd->fd, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)), - uslen, strerror(errno)); - Close(sfd->fd); - return STAT_RETRYLATER; + } else { + applyopts(sfd, sfd->fd, opts, PH_PREBIND); + if (us) { + applyopts(sfd, sfd->fd, opts, PH_BIND); + if (Bind(sfd->fd, &us->soa, uslen) < 0) { + Msg4(level, "bind(%d, {%s}, "F_Zd"): %s", + sfd->fd, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)), + uslen, strerror(errno)); + Close(sfd->fd); + return STAT_RETRYLATER; + } } } -#if WITH_UNIX - if (pf == PF_UNIX && us != NULL) { - applyopts_named(us->un.sun_path, opts, PH_PASTOPEN); - } -#endif applyopts(sfd, -1, opts, PH_PASTBIND); return 0; diff --git a/xio-unix.c b/xio-unix.c index 4d7dfad..b1b3735 100644 --- a/xio-unix.c +++ b/xio-unix.c @@ -53,6 +53,7 @@ const struct addrdesc xioaddr_abstract_recv = { "ABSTRACT-RECV", 1+XIO_RD const struct addrdesc xioaddr_abstract_client = { "ABSTRACT-CLIENT", 1+XIO_RDWR, xioopen_unix_client, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_RETRY, 1, 0, 0 HELP(":") }; #endif /* WITH_ABSTRACT_UNIXSOCKET */ +const struct optdesc xioopt_unix_bind_tempname = { "unix-bind-tempname", "bind-tempname", OPT_UNIX_BIND_TEMPNAME, GROUP_SOCK_UNIX, PH_PREOPEN, TYPE_STRING_NULL, OFUNC_SPEC }; 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) }; @@ -90,7 +91,7 @@ xiosetunix(int pf, len = sizeof(struct sockaddr_un); } return len; - } + } else #endif /* WITH_ABSTRACT_UNIXSOCKET */ if ((pathlen = strlen(path)) > sizeof(saun->sun_path)) { @@ -139,7 +140,7 @@ static int xioopen_unix_listen( } name = argv[1]; - sfd->para.socket.un.tight = true; + sfd->para.socket.un.tight = UNIX_TIGHTSOCKLEN; retropt_socket_pf(opts, &pf); sfd->howtoend = END_SHUTDOWN; @@ -221,11 +222,13 @@ static int xioopen_unix_connect( struct sockaddr_un them, us; socklen_t themlen, uslen = sizeof(us); bool needbind = false; + bool needtemp = false; bool opt_unlink_close = (addrdesc->arg1/*abstract*/ != 1); bool dofork = false; struct opt *opts0; char infobuff[256]; int level; + char *opt_bind_tempname = NULL; int result; if (argc != 2) { @@ -234,7 +237,7 @@ static int xioopen_unix_connect( } name = argv[1]; - sfd->para.socket.un.tight = true; + sfd->para.socket.un.tight = UNIX_TIGHTSOCKLEN; retropt_socket_pf(opts, &pf); sfd->howtoend = END_SHUTDOWN; if (applyopts_single(sfd, opts, PH_INIT) < 0) return STAT_NORETRY; @@ -249,6 +252,7 @@ static int xioopen_unix_connect( /* 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, (addrdesc->arg1/*abstract*/<<1)|sfd->para.socket.un.tight, sfd->para.socket.ip.ai_flags) @@ -256,18 +260,25 @@ static int xioopen_unix_connect( needbind = true; } + if (retropt_string(opts, OPT_UNIX_BIND_TEMPNAME, &opt_bind_tempname) == 0) { + if (needbind) { + Error("do not use both options bind and unix-bind-tempnam"); + return -1; + } + needbind = true; + needtemp = true; + xiosetunix(pf, &us, opt_bind_tempname?opt_bind_tempname:"", + addrdesc->arg1/*abstract*/, sfd->para.socket.un.tight); + if (opt_bind_tempname == NULL && !addrdesc->arg1/*abstract*/) { + us.sun_path[0] = 0x01; /* mark as non abstract */ + } + } + 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 ((sfd->unlink_close = strdup(us.sun_path)) == NULL) { - Error1("strdup(\"%s\"): out of memory", name); - } - sfd->opt_unlink_close = true; - } - retropt_bool(opts, OPT_FORK, &dofork); opts0 = copyopts(opts, GROUP_ALL); @@ -288,7 +299,7 @@ static int xioopen_unix_connect( _xioopen_connect(sfd, needbind?(union sockaddr_union *)&us:NULL, uslen, (struct sockaddr *)&them, themlen, - opts, pf, socktype, protocol, false, level); + opts, pf, socktype, protocol, needtemp, level); if (result != 0) { char infobuff[256]; /* we caller must handle this */ @@ -382,10 +393,12 @@ static int xioopen_unix_sendto( int pf = PF_UNIX; int socktype = SOCK_DGRAM; int protocol = 0; - union sockaddr_union us; + struct sockaddr_un us; socklen_t uslen = sizeof(us); bool needbind = false; + bool needtemp = false; bool opt_unlink_close = (addrdesc->arg1/*abstract*/ != 1); + char *opt_bind_tempname = NULL; int result; if (argc != 2) { @@ -394,7 +407,7 @@ static int xioopen_unix_sendto( } name = argv[1]; - sfd->para.socket.un.tight = true; + sfd->para.socket.un.tight = UNIX_TIGHTSOCKLEN; retropt_socket_pf(opts, &pf); sfd->howtoend = END_SHUTDOWN; applyopts_offset(sfd, opts); @@ -408,13 +421,24 @@ static int xioopen_unix_sendto( sfd->dtype = XIODATA_RECVFROM; - if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, + if (retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen, (addrdesc->arg1/*abstract*/<<1)| sfd->para.socket.un.tight, sfd->para.socket.ip.ai_flags) - == STAT_OK) { + == STAT_OK) { needbind = true; } + if (retropt_string(opts, OPT_UNIX_BIND_TEMPNAME, &opt_bind_tempname) == 0) { + if (needbind) { + Error("do not use both options bind and bind-tempnam"); + return STAT_NORETRY; + } + needbind = true; + needtemp = true; + xiosetunix(pf, &us, opt_bind_tempname?opt_bind_tempname:"", + addrdesc->arg1/*abstract*/, sfd->para.socket.un.tight); + } + if (!needbind && (namedopt = searchopt(opts, GROUP_NAMED, 0, 0, 0))) { Error1("Option \"%s\" only with bind option", namedopt->desc->defname); @@ -426,14 +450,14 @@ static int xioopen_unix_sendto( result = _xioopen_dgram_sendto(needbind?(union sockaddr_union *)&us:NULL, uslen, opts, xioflags, sfd, addrdesc->groups, - pf, socktype, protocol, 0); + pf, socktype, protocol, needtemp); if (result != 0) { return result; } if (opt_unlink_close && needbind) { - if ((sfd->unlink_close = strndup(us.un.sun_path, sizeof(us.un.sun_path))) == NULL) { - Error2("strndup(\"%s\", "F_Zu"): out of memory", name, sizeof(us.un.sun_path)); + if ((sfd->unlink_close = strndup(us.sun_path, sizeof(us.sun_path))) == NULL) { + Error2("strndup(\"%s\", "F_Zu"): out of memory", name, sizeof(us.sun_path)); } sfd->opt_unlink_close = true; } @@ -468,7 +492,7 @@ int xioopen_unix_recvfrom( } name = argv[1]; - sfd->para.socket.un.tight = true; + sfd->para.socket.un.tight = UNIX_TIGHTSOCKLEN; retropt_socket_pf(opts, &pf); sfd->howtoend = END_NONE; if (applyopts_single(sfd, opts, PH_INIT) < 0) return STAT_NORETRY; @@ -554,7 +578,7 @@ static int xioopen_unix_recv( } name = argv[1]; - sfd->para.socket.un.tight = true; + sfd->para.socket.un.tight = UNIX_TIGHTSOCKLEN; retropt_socket_pf(opts, &pf); sfd->howtoend = END_SHUTDOWN; if (applyopts_single(sfd, opts, PH_INIT) < 0) return STAT_NORETRY; @@ -625,7 +649,7 @@ static int xioopen_unix_client( return _xioopen_unix_client(&xxfd->stream, xioflags, addrdesc->groups, - addrdesc->arg1/*abstract*/, opts, argv[1]); + addrdesc->arg1/*abstract*/, opts, argv[1], addrdesc); } /* establishes communication with an existing UNIX type socket. supports stream @@ -639,8 +663,15 @@ static int xioopen_unix_client( OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_CLOEXEC, OPT_USER, OPT_GROUP, ?OPT_FORK, */ int -_xioopen_unix_client(xiosingle_t *sfd, int xioflags, groups_t groups, - int abstract, struct opt *opts, const char *name) { +_xioopen_unix_client( + xiosingle_t *sfd, + int xioflags, + groups_t groups, + int abstract, + struct opt *opts, + const char *name, + const struct addrdesc *addrdesc) +{ const struct opt *namedopt; int pf = PF_UNIX; int socktype = 0; /* to be determined by server socket type */ @@ -648,11 +679,13 @@ _xioopen_unix_client(xiosingle_t *sfd, int xioflags, groups_t groups, union sockaddr_union them, us; socklen_t themlen, uslen = sizeof(us); bool needbind = false; + bool needtemp = false; bool opt_unlink_close = false; + char *opt_bind_tempname = NULL; struct opt *opts0; int result; - sfd->para.socket.un.tight = true; + sfd->para.socket.un.tight = UNIX_TIGHTSOCKLEN; retropt_socket_pf(opts, &pf); sfd->howtoend = END_SHUTDOWN; if (applyopts_single(sfd, opts, PH_INIT) < 0) return STAT_NORETRY; @@ -667,6 +700,7 @@ _xioopen_unix_client(xiosingle_t *sfd, int xioflags, groups_t groups, /* 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, (abstract<<1)|sfd->para.socket.un.tight, sfd->para.socket.ip.ai_flags) @@ -674,6 +708,15 @@ _xioopen_unix_client(xiosingle_t *sfd, int xioflags, groups_t groups, needbind = true; } + if (retropt_string(opts, OPT_UNIX_BIND_TEMPNAME, &opt_bind_tempname) == 0) { + if (needbind) { + Error("do not use both options bind and unix-bind-tempname"); + return STAT_NORETRY; + } + needbind = true; + needtemp = true; + } + if (!needbind && (namedopt = searchopt(opts, GROUP_NAMED, 0, 0, 0))) { Error1("Option \"%s\" only with bind option", namedopt->desc->defname); @@ -692,40 +735,57 @@ _xioopen_unix_client(xiosingle_t *sfd, int xioflags, groups_t groups, /* just a breakable block, helps to avoid goto */ do { /* sfd->dtype = DATA_STREAM; // is default */ + if (needtemp) + xiosetunix(pf, &us.un, opt_bind_tempname?opt_bind_tempname:"", + abstract, sfd->para.socket.un.tight); /* this function handles AF_UNIX with EPROTOTYPE specially for us */ if ((result = _xioopen_connect(sfd, needbind?&us:NULL, uslen, &them.soa, themlen, opts, pf, socktype?socktype:SOCK_STREAM, protocol, - false, E_INFO)) == 0) + needtemp, E_INFO)) == 0) break; - if (errno != EPROTOTYPE || socktype != 0) + if ((errno != EPROTOTYPE +#if WITH_ABSTRACT_UNIXSOCKET + && !(abstract && errno == ECONNREFUSED) +#endif + ) || socktype != 0) break; if (needbind) xio_unlink(us.un.sun_path, E_ERROR); dropopts2(opts, PH_INIT, PH_SPEC); opts = opts0; + if (needtemp) + xiosetunix(pf, &us.un, opt_bind_tempname?opt_bind_tempname:"", + abstract, sfd->para.socket.un.tight); socktype = SOCK_SEQPACKET; if ((result = _xioopen_connect(sfd, needbind?&us:NULL, uslen, (struct sockaddr *)&them, themlen, opts, pf, SOCK_SEQPACKET, protocol, - false, E_INFO)) == 0) + needtemp, E_INFO)) == 0) break; - if (errno != EPROTOTYPE && errno != EPROTONOSUPPORT/*AIX*/) + if (errno != EPROTOTYPE && errno != EPROTONOSUPPORT/*AIX*/ +#if WITH_ABSTRACT_UNIXSOCKET + && !(abstract && errno == ECONNREFUSED) +#endif + ) break; if (needbind) xio_unlink(us.un.sun_path, E_ERROR); dropopts2(opts, PH_INIT, PH_SPEC); opts = opts0; + if (needtemp) + xiosetunix(pf, &us.un, opt_bind_tempname?opt_bind_tempname:"", + abstract, sfd->para.socket.un.tight); sfd->peersa = them; - sfd->salen = sizeof(struct sockaddr_un); + sfd->salen = themlen; if ((result = _xioopen_dgram_sendto(needbind?&us:NULL, uslen, opts, xioflags, sfd, groups, - pf, SOCK_DGRAM, protocol, 0)) + pf, SOCK_DGRAM, protocol, needtemp)) == 0) { sfd->dtype = XIODATA_RECVFROM; break; @@ -733,7 +793,7 @@ _xioopen_unix_client(xiosingle_t *sfd, int xioflags, groups_t groups, } while (0); if (result != 0) { - Error2("UNIX-CLIENT:%s: %s", name, strerror(errno)); + Error3("%s: %s: %s", addrdesc->defname, name, strerror(errno)); if (needbind) xio_unlink(us.un.sun_path, E_ERROR); return result; @@ -767,4 +827,79 @@ xiosetsockaddrenv_unix(int idx, char *namebuff, size_t namelen, return 0; } +static const char tmpchars[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; +static size_t numchars = sizeof(tmpchars)-1; + +/* Simplyfied version of tempnam(). Uses the current directory when pathx is + not absolute. + Returns a malloc()'ed string with a probably free name, + or NULL when an error occurred */ +char *xio_tempnam( + const char *pathx, + bool donttry) /* for abstract, do not check if it exists */ +{ + int len; + char *X; /* begin of XXXXXX */ + unsigned int i = TMP_MAX; + unsigned int r1, r2; + uint64_t v; + char *patht; + char readl[PATH_MAX]; + int rlc; + + if (pathx == NULL || pathx[0] == '\0') + pathx = "/tmp/socat-bind.XXXXXX"; + + len = strlen(pathx); + if (len < 6 || strstr(pathx, "XXXXXX") == NULL) { + Warn1("xio_tempnam(\"%s\"): path pattern is not valid", pathx); + errno = EINVAL; + return NULL; + } + patht = strdup(pathx); + if (patht == NULL) { + Error1("strdup("F_Zu"): out of memory", strlen(pathx)); + return patht; + } + X = strstr(patht, "XXXXXX"); + + Debug1("xio_tempnam(\"%s\"): trying path names, suppressing stat() logs", + patht); + while (i > 0) { + r1 = random(); + r2 = random(); + v = r2*RAND_MAX + r1; + X[0] = tmpchars[v%numchars]; + v /= numchars; + X[1] = tmpchars[v%numchars]; + v /= numchars; + X[2] = tmpchars[v%numchars]; + v /= numchars; + X[3] = tmpchars[v%numchars]; + v /= numchars; + X[4] = tmpchars[v%numchars]; + v /= numchars; + X[5] = tmpchars[v%numchars]; + v /= numchars; + + if (donttry) + return patht; + + /* readlink() might be faster than lstat() */ + rlc = readlink(patht, readl, sizeof(readl)); + if (rlc < 0 && errno == ENOENT) + break; + + --i; + } + + if (i == 0) { + errno = EEXIST; + return NULL; + } + + return patht; +} + #endif /* WITH_UNIX */ diff --git a/xio-unix.h b/xio-unix.h index 049ad8d..a286825 100644 --- a/xio-unix.h +++ b/xio-unix.h @@ -18,6 +18,7 @@ extern const struct addrdesc xioaddr_abstract_recvfrom; extern const struct addrdesc xioaddr_abstract_recv; extern const struct addrdesc xioaddr_abstract_client; +extern const struct optdesc xioopt_unix_bind_tempname; extern const struct optdesc xioopt_unix_tightsocklen; extern socklen_t @@ -32,7 +33,8 @@ xiosetsockaddrenv_unix(int idx, char *namebuff, size_t namelen, struct sockaddr_un *sa, socklen_t salen, int ipproto); extern int -_xioopen_unix_client(xiosingle_t *xfd, int xioflags, groups_t groups, - int abstract, struct opt *opts, const char *name); +_xioopen_unix_client(xiosingle_t *xfd, int xioflags, groups_t groups, int abstract, struct opt *opts, const char *name, const struct addrdesc *addrdesc); + +extern char *xio_tempnam(const char *pathx, bool donttry); #endif /* !defined(__xio_unix_h_included) */ diff --git a/xiolayer.c b/xiolayer.c index 81c88e9..83b8839 100644 --- a/xiolayer.c +++ b/xiolayer.c @@ -48,7 +48,7 @@ int xio_chdir( free(tmp_dir); return -1; } - *orig_dir = Realloc(*orig_dir, strlen(*orig_dir+1)); + *orig_dir = Realloc(*orig_dir, strlen(*orig_dir)+1); if (Chdir(tmp_dir) < 0) { Error2("chdir(\"%s\"): %s", tmp_dir, strerror(errno)); diff --git a/xioopts.c b/xioopts.c index f582785..679fc6d 100644 --- a/xioopts.c +++ b/xioopts.c @@ -292,6 +292,7 @@ const struct optname optionnames[] = { IF_OPEN ("binary", &opt_o_binary) #endif IF_SOCKET ("bind", &opt_bind) + IF_UNIX ("bind-tempname", &xioopt_unix_bind_tempname) #ifdef SO_BINDTODEVICE IF_SOCKET ("bindtodevice", &opt_so_bindtodevice) #endif @@ -1880,6 +1881,7 @@ const struct optname optionnames[] = { IF_ANY ("uid-l", &opt_user_late) IF_NAMED ("umask", &opt_umask) IF_IP6 ("unicast-hops", &opt_ipv6_unicast_hops) + IF_UNIX ("unix-bind-tempname", &xioopt_unix_bind_tempname) IF_UNIX ("unix-tightsocklen", &xioopt_unix_tightsocklen) IF_NAMED ("unlink", &opt_unlink) IF_NAMED ("unlink-close", &opt_unlink_close) @@ -3223,6 +3225,7 @@ int retropt_bind(struct opt *opts, 3..address and port allowed UNIX (or'd): 1..tight 2..abstract + 4..templatename */ const int ai_flags[2]) { @@ -3313,7 +3316,17 @@ int retropt_bind(struct opt *opts, { bool abstract = (feats&2); bool tight = (feats&1); + bool templatename = (feats&4); struct sockaddr_un *s_un = (struct sockaddr_un *)sa; + if (templatename) { + int i = 0; + srandom(getpid()); + for (; i < strlen(bindname); i++) { + if (bindname[i] == 'X') { + bindname[i] = 'a' + (char) (random() % ('z' - 'a')); + } + } + } *salen = xiosetunix(af, s_un, bindname, abstract, tight); } break; diff --git a/xioopts.h b/xioopts.h index 1140f82..0ed8881 100644 --- a/xioopts.h +++ b/xioopts.h @@ -859,7 +859,8 @@ enum e_optcode { OPT_TUN_NAME, /* tun: tun0 */ OPT_TUN_TYPE, /* tun: tun|tap */ OPT_UMASK, - OPT_UNIX_TIGHTSOCKLEN, /* UNIX domain sockets */ + OPT_UNIX_BIND_TEMPNAME, /* UNIX domain sockets */ + OPT_UNIX_TIGHTSOCKLEN, OPT_UNLINK, OPT_UNLINK_CLOSE, OPT_UNLINK_EARLY,