Fixed bind with abstract unix domain sockets (Linux)

This commit is contained in:
Gerhard Rieger 2015-03-31 18:21:45 +02:00
parent f01ed154b6
commit ad9e6b1fa4
5 changed files with 145 additions and 63 deletions

View file

@ -162,6 +162,10 @@ corrections:
docu mentions option so-bindtodev but correct name is so-bindtodevice. docu mentions option so-bindtodev but correct name is so-bindtodevice.
Thanks to Jim Zimmerman for reporting. 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: porting:
Red Hat issue 1020203: configure checks fail with some compilers. Red Hat issue 1020203: configure checks fail with some compilers.
Use case: clang Use case: clang

50
test.sh
View file

@ -9014,6 +9014,8 @@ if [ "$rc2" -ne 0 ]; then
listFAIL="$listFAIL $N" listFAIL="$listFAIL $N"
elif ! echo "$da" |diff - "$tf" >"$tdiff"; then elif ! echo "$da" |diff - "$tf" >"$tdiff"; then
$PRINTF "$FAILED\n" $PRINTF "$FAILED\n"
echo "$CMD1 &"
cat "${te}1"
cat "$tdiff" cat "$tdiff"
numFAIL=$((numFAIL+1)) numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N" listFAIL="$listFAIL $N"
@ -9027,6 +9029,54 @@ esac
N=$((N+1)) 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 NAME=OPENSSLREAD
# socat determined availability of data using select(). With openssl, the # socat determined availability of data using select(). With openssl, the
# following situation might occur: # following situation might occur:

View file

@ -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 }; const union xioaddr_desc *xioaddrs_abstract_client[] = { (union xioaddr_desc *)&xioendpoint_abstract_client1, NULL };
#endif /* WITH_ABSTRACT_UNIXSOCKET */ #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. /* fills the socket address struct and returns its effective length.
abstract is usually 0; != 0 generates an abstract socket address on Linux. 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 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. the struct need not be initialized when calling this function.
*/ */
socklen_t socklen_t
@ -140,7 +140,6 @@ static int xioopen_unix_listen(int argc, const char *argv[], struct opt *opts, i
int protocol = 0; int protocol = 0;
struct sockaddr_un us; struct sockaddr_un us;
socklen_t uslen; socklen_t uslen;
bool tight = true;
struct opt *opts0 = NULL; struct opt *opts0 = NULL;
pid_t pid = Getpid(); pid_t pid = Getpid();
bool opt_unlink_early = false; 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); argv[0], argc-1);
return STAT_NORETRY; return STAT_NORETRY;
} }
name = argv[1]; name = argv[1];
xfd->para.socket.un.tight = true;
retropt_socket_pf(opts, &pf); retropt_socket_pf(opts, &pf);
retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight);
uslen = xiosetunix(pf, &us, name, abstract, tight);
if (!(ABSTRACT && abstract)) { if (!(ABSTRACT && abstract)) {
/* only for non abstract because abstract do not work in file system */ /* only for non abstract because abstract do not work in file system */
@ -167,8 +165,11 @@ 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; if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY;
applyopts(-1, opts, PH_INIT); applyopts(-1, opts, PH_INIT);
applyopts_named(name, opts, PH_EARLY); /* umask! */ applyopts_named(name, opts, PH_EARLY); /* umask! */
applyopts_offset(xfd, opts);
applyopts(-1, opts, PH_EARLY); applyopts(-1, opts, PH_EARLY);
uslen = xiosetunix(pf, &us, name, abstract, xfd->para.socket.un.tight);
if (!(ABSTRACT && abstract)) { if (!(ABSTRACT && abstract)) {
if (opt_unlink_early) { if (opt_unlink_early) {
if (Unlink(name) < 0) { if (Unlink(name) < 0) {
@ -229,8 +230,7 @@ static int xioopen_unix_connect(int argc, const char *argv[], struct opt *opts,
int socktype = SOCK_STREAM; int socktype = SOCK_STREAM;
int protocol = 0; int protocol = 0;
struct sockaddr_un them, us; struct sockaddr_un them, us;
socklen_t themlen, uslen; socklen_t themlen, uslen = sizeof(us);
bool tight = true;
bool needbind = false; bool needbind = false;
bool opt_unlink_close = false; bool opt_unlink_close = false;
int result; int result;
@ -241,18 +241,23 @@ static int xioopen_unix_connect(int argc, const char *argv[], struct opt *opts,
return STAT_NORETRY; return STAT_NORETRY;
} }
xfd->howtoshut = XIOSHUT_DOWN;
name = argv[1]; name = argv[1];
xfd->para.socket.un.tight = true;
retropt_socket_pf(opts, &pf); retropt_socket_pf(opts, &pf);
retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight); xfd->howtoshut = XIOSHUT_DOWN;
themlen = xiosetunix(pf, &them, name, abstract, tight); 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)) { if (!(ABSTRACT && abstract)) {
/* only for non abstract because abstract do not work in file system */ /* only for non abstract because abstract do not work in file system */
retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
} }
if (retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen, 0, 0, 0) if (retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen,
!= STAT_NOACTION) { (abstract<<1)|xfd->para.socket.un.tight, 0, 0) == STAT_OK) {
needbind = true; needbind = true;
} }
@ -263,10 +268,6 @@ static int xioopen_unix_connect(int argc, const char *argv[], struct opt *opts,
xfd->opt_unlink_close = true; 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 = if ((result =
xioopen_connect(xfd, xioopen_connect(xfd,
needbind?(struct sockaddr *)&us:NULL, uslen, 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 socktype = SOCK_DGRAM;
int protocol = 0; int protocol = 0;
union sockaddr_union us; union sockaddr_union us;
socklen_t uslen; socklen_t uslen = sizeof(us);
bool tight = true;
bool needbind = false; bool needbind = false;
bool opt_unlink_close = false; bool opt_unlink_close = false;
int result; 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_int(opts, OPT_SO_TYPE, &socktype);
retropt_bool(opts, OPT_UNIX_TIGHTSOCKLEN, &tight);
name = argv[1]; name = argv[1];
xfd->para.socket.un.tight = true;
retropt_socket_pf(opts, &pf); 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)) { if (!(ABSTRACT && abstract)) {
/* only for non abstract because abstract do not work in file system */ /* 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; xfd->dtype = XIODATA_RECVFROM;
if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, 0, 0, 0) if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen,
!= STAT_NOACTION) { (abstract<<1)| xfd->para.socket.un.tight, 0, 0) == STAT_OK) {
needbind = true; needbind = true;
} }
@ -363,7 +366,6 @@ int xioopen_unix_recvfrom(int argc, const char *argv[], struct opt *opts,
int protocol = 0; int protocol = 0;
struct sockaddr_un us; struct sockaddr_un us;
socklen_t uslen; socklen_t uslen;
bool tight = true;
bool needbind = true; bool needbind = true;
bool opt_unlink_early = false; bool opt_unlink_early = false;
bool opt_unlink_close = true; 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); argv[0], argc-1);
return STAT_NORETRY; return STAT_NORETRY;
} }
name = argv[1]; 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_int(opts, OPT_SO_TYPE, &socktype);
retropt_bind(opts, pf, socktype, protocol, (struct sockaddr *)&us, &uslen, if (applyopts_single(xfd, opts, PH_INIT) < 0) return STAT_NORETRY;
1, 0, 0); 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_EARLY, &opt_unlink_early);
retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); 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_EARLY, &opt_unlink_early);
retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); 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 (!(ABSTRACT && abstract)) {
if (opt_unlink_early) { if (opt_unlink_early) {
@ -444,7 +455,6 @@ int xioopen_unix_recv(int argc, const char *argv[], struct opt *opts,
int protocol = 0; int protocol = 0;
union sockaddr_union us; union sockaddr_union us;
socklen_t uslen; socklen_t uslen;
bool tight = true;
bool opt_unlink_early = false; bool opt_unlink_early = false;
bool opt_unlink_close = true; bool opt_unlink_close = true;
int result; int result;
@ -454,21 +464,33 @@ int xioopen_unix_recv(int argc, const char *argv[], struct opt *opts,
argv[0], argc-1); argv[0], argc-1);
return STAT_NORETRY; return STAT_NORETRY;
} }
name = argv[1]; 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? */ xfd->para.socket.un.tight = true;
retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, 1, 0, 0); retropt_socket_pf(opts, &pf);
#endif 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)) { if (!(ABSTRACT && abstract)) {
/* only for non abstract because abstract do not work in file system */ /* 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_EARLY, &opt_unlink_early);
retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); 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 (opt_unlink_early) {
if (Unlink(name) < 0) { if (Unlink(name) < 0) {
if (errno == ENOENT) { 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 socktype = 0; /* to be determined by server socket type */
int protocol = 0; int protocol = 0;
union sockaddr_union them, us; union sockaddr_union them, us;
socklen_t themlen, uslen; socklen_t themlen, uslen = sizeof(us);
bool tight = true;
bool needbind = false; bool needbind = false;
bool opt_unlink_close = false; bool opt_unlink_close = false;
struct opt *opts0; struct opt *opts0;
int result; int result;
if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1; xfd->para.socket.un.tight = true;
applyopts(-1, opts, PH_INIT);
retropt_socket_pf(opts, &pf); 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, xfd->para.socket.un.tight);
themlen = xiosetunix(pf, &them.un, name, abstract, tight);
if (!(ABSTRACT && abstract)) { if (!(ABSTRACT && abstract)) {
/* only for non abstract because abstract do not work in file system */ /* only for non abstract because abstract do not work in file system */
retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close); retropt_bool(opts, OPT_UNLINK_CLOSE, &opt_unlink_close);
} }
if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen,
if (retropt_bind(opts, pf, socktype, protocol, &us.soa, &uslen, 0, 0, 0) (abstract<<1)|xfd->para.socket.un.tight, 0, 0)
!= STAT_NOACTION) { != STAT_NOACTION) {
needbind = true; needbind = true;
} }

3
xio.h
View file

@ -394,6 +394,9 @@ typedef struct single {
char *hosts_deny_table; char *hosts_deny_table;
#endif #endif
} ip; } ip;
struct {
bool tight;
} un;
#endif /* _WITH_IP4 || _WITH_IP6 */ #endif /* _WITH_IP4 || _WITH_IP6 */
} socket; } socket;
#endif /* _WITH_SOCKET */ #endif /* _WITH_SOCKET */

View file

@ -2808,7 +2808,7 @@ int retropt_string(struct opt *opts, int optcode, char **result) {
#if _WITH_SOCKET #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). sa with the appropriate value(s).
returns STAT_OK if option exists and could be resolved, returns STAT_OK if option exists and could be resolved,
STAT_NORETRY if option exists but had error, STAT_NORETRY if option exists but had error,
@ -2821,7 +2821,10 @@ int retropt_bind(struct opt *opts,
struct sockaddr *sa, struct sockaddr *sa,
socklen_t *salen, socklen_t *salen,
int feats, /* TCP etc: 1..address allowed, 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) { unsigned long res_opts0, unsigned long res_opts1) {
const char portsep[] = ":"; const char portsep[] = ":";
const char *ends[] = { portsep, NULL }; const char *ends[] = { portsep, NULL };
@ -2889,9 +2892,10 @@ int retropt_bind(struct opt *opts,
#if WITH_UNIX #if WITH_UNIX
case AF_UNIX: case AF_UNIX:
{ {
bool tight = false; bool abstract = (feats&2);
bool tight = (feats&1);
struct sockaddr_un *s_un = (struct sockaddr_un *)sa; 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; break;
#endif /* WITH_UNIX */ #endif /* WITH_UNIX */