Corrected UNIX client NAMED options to work on bind address

This commit is contained in:
Gerhard Rieger 2020-12-27 12:39:48 +01:00
parent 5570bf4d62
commit d9d320cb47
13 changed files with 232 additions and 45 deletions

View file

@ -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

View file

@ -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

View file

@ -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()

88
test.sh
View file

@ -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

View file

@ -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) {

View file

@ -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) {

View file

@ -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) {

View file

@ -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 */

View file

@ -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,

View file

@ -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) {

View file

@ -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) {

View file

@ -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 <groups> */
struct opt *copyopts(const struct opt *opts, unsigned int groups) {

View file

@ -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);