mirror of
https://repo.or.cz/socat.git
synced 2025-01-09 06:22:33 +00:00
UDP-RECVFROM with fork sometimes terminated - handle EINTR on recvmsg()
This commit is contained in:
parent
9502d092a2
commit
45d87df2fd
3 changed files with 40 additions and 25 deletions
5
CHANGES
5
CHANGES
|
@ -4,6 +4,11 @@ Corrections:
|
||||||
terminating \0 Byte was written behind the last position.
|
terminating \0 Byte was written behind the last position.
|
||||||
Thanks to Martin Liška for sending the address sanitizer report.
|
Thanks to Martin Liška for sending the address sanitizer report.
|
||||||
|
|
||||||
|
UDP-RECVFROM with fork sometimes terminated when multiple packets
|
||||||
|
arrived. This issue was introduced with a bug fix in version 1.7.4.0.
|
||||||
|
Reason was not handling EAGAIN on recvmsg().
|
||||||
|
Thanks to Jamie McQuillan for reporting this issue.
|
||||||
|
|
||||||
Porting:
|
Porting:
|
||||||
OpenSSL, at least 1.1 on Ubuntu, crashed with SIGSEGV under certain
|
OpenSSL, at least 1.1 on Ubuntu, crashed with SIGSEGV under certain
|
||||||
conditions: client connection to server with certificate with empty
|
conditions: client connection to server with certificate with empty
|
||||||
|
|
39
xio-socket.c
39
xio-socket.c
|
@ -693,6 +693,7 @@ int xioopen_socket_datagram(int argc, const char *argv[], struct opt *opts,
|
||||||
|
|
||||||
#endif /* WITH_GENERICSOCKET */
|
#endif /* WITH_GENERICSOCKET */
|
||||||
|
|
||||||
|
/* EINTR not handled specially */
|
||||||
int xiogetpacketinfo(int fd)
|
int xiogetpacketinfo(int fd)
|
||||||
{
|
{
|
||||||
#if defined(MSG_ERRQUEUE)
|
#if defined(MSG_ERRQUEUE)
|
||||||
|
@ -1245,6 +1246,7 @@ void xiosigaction_hasread(int signum
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (pid == xio_waitingfor) {
|
if (pid == xio_waitingfor) {
|
||||||
|
xio_waitingfor = 0; /* so this child will not set hashappened again */
|
||||||
xio_hashappened = true;
|
xio_hashappened = true;
|
||||||
xio_childstatus = WEXITSTATUS(status);
|
xio_childstatus = WEXITSTATUS(status);
|
||||||
Debug("xiosigaction_hasread() ->");
|
Debug("xiosigaction_hasread() ->");
|
||||||
|
@ -1284,6 +1286,7 @@ void xiosigaction_hasread(int signum
|
||||||
PH_INIT, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_EARLY, PH_PREOPEN, PH_FD,
|
PH_INIT, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_EARLY, PH_PREOPEN, PH_FD,
|
||||||
PH_CONNECTED, PH_LATE, PH_LATE2
|
PH_CONNECTED, PH_LATE, PH_LATE2
|
||||||
OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, cloexec, OPT_RANGE, tcpwrap
|
OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, cloexec, OPT_RANGE, tcpwrap
|
||||||
|
EINTR is not handled specially.
|
||||||
*/
|
*/
|
||||||
int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags,
|
int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags,
|
||||||
struct sockaddr *us, socklen_t uslen,
|
struct sockaddr *us, socklen_t uslen,
|
||||||
|
@ -1413,6 +1416,7 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags,
|
||||||
socklen_t palen = sizeof(_peername); /* peer address size */
|
socklen_t palen = sizeof(_peername); /* peer address size */
|
||||||
char ctrlbuff[1024]; /* ancillary messages */
|
char ctrlbuff[1024]; /* ancillary messages */
|
||||||
struct msghdr msgh = {0};
|
struct msghdr msgh = {0};
|
||||||
|
int rc;
|
||||||
|
|
||||||
socket_init(pf, pa);
|
socket_init(pf, pa);
|
||||||
|
|
||||||
|
@ -1423,6 +1427,7 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags,
|
||||||
drop = true;
|
drop = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Info("Recvfrom: Checking/waiting for next packet");
|
||||||
/* loop until select()/poll() returns valid */
|
/* loop until select()/poll() returns valid */
|
||||||
do {
|
do {
|
||||||
struct pollfd readfd;
|
struct pollfd readfd;
|
||||||
|
@ -1455,15 +1460,15 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags,
|
||||||
#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
|
#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
|
||||||
msgh.msg_controllen = sizeof(ctrlbuff);
|
msgh.msg_controllen = sizeof(ctrlbuff);
|
||||||
#endif
|
#endif
|
||||||
if (xiogetpacketsrc(xfd->fd,
|
while ((rc = xiogetpacketsrc(xfd->fd,
|
||||||
&msgh,
|
&msgh,
|
||||||
MSG_PEEK
|
MSG_PEEK
|
||||||
#ifdef MSG_TRUNC
|
#ifdef MSG_TRUNC
|
||||||
|MSG_TRUNC
|
|MSG_TRUNC
|
||||||
#endif
|
#endif
|
||||||
) < 0) {
|
)) < 0 &&
|
||||||
return STAT_RETRYLATER;
|
errno == EINTR) ;
|
||||||
}
|
if (rc < 0) return STAT_RETRYLATER;
|
||||||
palen = msgh.msg_namelen;
|
palen = msgh.msg_namelen;
|
||||||
|
|
||||||
Notice1("receiving packet from %s"/*"src"*/,
|
Notice1("receiving packet from %s"/*"src"*/,
|
||||||
|
@ -1534,24 +1539,21 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* server: continue loop with listen */
|
|
||||||
xio_waitingfor = pid;
|
xio_waitingfor = pid;
|
||||||
|
|
||||||
|
do {
|
||||||
#if HAVE_PSELECT
|
#if HAVE_PSELECT
|
||||||
{
|
{
|
||||||
struct timespec timeout = { LONG_MAX, 0 };
|
struct timespec timeout = { LONG_MAX, 0 };
|
||||||
Pselect(0, NULL, NULL, NULL, &timeout, &oldset);
|
Pselect(0, NULL, NULL, NULL, &timeout, &oldset);
|
||||||
Sigprocmask(SIG_SETMASK, &oldset, NULL);
|
Sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||||
}
|
}
|
||||||
#else /* ! HAVE_PSELECT */
|
#else /* ! HAVE_PSELECT */
|
||||||
/* now we are ready to handle signals */
|
/* now we are ready to handle signals */
|
||||||
Sigprocmask(SIG_SETMASK, &oldset, NULL);
|
Sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||||
|
Sleep(1); /* any signal speeds up return */
|
||||||
while (!xio_hashappened) {
|
|
||||||
Sleep(1); /* any signal speeds up return */
|
|
||||||
}
|
|
||||||
#endif /* ! HAVE_PSELECT */
|
#endif /* ! HAVE_PSELECT */
|
||||||
xio_waitingfor = 0; /* so this child will not set hashappened again */
|
} while (!xio_hashappened) ;
|
||||||
xio_hashappened = false;
|
xio_hashappened = false;
|
||||||
|
|
||||||
if (xio_childstatus != 0) {
|
if (xio_childstatus != 0) {
|
||||||
|
@ -1676,9 +1678,12 @@ int retropt_socket_pf(struct opt *opts, int *pf) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* this function calls recvmsg(..., MSG_PEEK, ...) to obtain information about
|
/* This function calls recvmsg(..., MSG_PEEK, ...) to obtain information about
|
||||||
the arriving packet. in msgh the msg_name pointer must refer to an (empty)
|
the arriving packet, thus it does not "consume" the packet.
|
||||||
sockaddr storage. */
|
In msgh the msg_name pointer must refer to an (empty) sockaddr storage.
|
||||||
|
Returns STAT_OK on success, or STAT_RETRYLATER when an error occurred,
|
||||||
|
including EINTR.
|
||||||
|
*/
|
||||||
int xiogetpacketsrc(int fd, struct msghdr *msgh, int flags) {
|
int xiogetpacketsrc(int fd, struct msghdr *msgh, int flags) {
|
||||||
char peekbuff[1];
|
char peekbuff[1];
|
||||||
#if HAVE_STRUCT_IOVEC
|
#if HAVE_STRUCT_IOVEC
|
||||||
|
|
21
xioread.c
21
xioread.c
|
@ -119,6 +119,7 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) {
|
||||||
socklen_t fromlen = sizeof(from);
|
socklen_t fromlen = sizeof(from);
|
||||||
char infobuff[256];
|
char infobuff[256];
|
||||||
char ctrlbuff[1024]; /* ancillary messages */
|
char ctrlbuff[1024]; /* ancillary messages */
|
||||||
|
int rc;
|
||||||
|
|
||||||
msgh.msg_name = &from;
|
msgh.msg_name = &from;
|
||||||
msgh.msg_namelen = fromlen;
|
msgh.msg_namelen = fromlen;
|
||||||
|
@ -128,14 +129,16 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) {
|
||||||
#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
|
#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
|
||||||
msgh.msg_controllen = sizeof(ctrlbuff);
|
msgh.msg_controllen = sizeof(ctrlbuff);
|
||||||
#endif
|
#endif
|
||||||
if (xiogetpacketsrc(pipe->fd, &msgh,
|
|
||||||
|
while ((rc = xiogetpacketsrc(pipe->fd, &msgh,
|
||||||
MSG_PEEK
|
MSG_PEEK
|
||||||
#ifdef MSG_TRUNC
|
#ifdef MSG_TRUNC
|
||||||
|MSG_TRUNC
|
|MSG_TRUNC
|
||||||
#endif
|
#endif
|
||||||
) < 0) {
|
)) < 0 &&
|
||||||
return -1;
|
errno == EINTR) ;
|
||||||
}
|
if (rc < 0) return -1;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
bytes =
|
bytes =
|
||||||
Recvfrom(pipe->fd, buff, bufsiz, 0, &from.soa, &fromlen);
|
Recvfrom(pipe->fd, buff, bufsiz, 0, &from.soa, &fromlen);
|
||||||
|
@ -319,6 +322,7 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) {
|
||||||
char infobuff[256];
|
char infobuff[256];
|
||||||
struct msghdr msgh = {0};
|
struct msghdr msgh = {0};
|
||||||
char ctrlbuff[1024]; /* ancillary messages */
|
char ctrlbuff[1024]; /* ancillary messages */
|
||||||
|
int rc;
|
||||||
|
|
||||||
socket_init(pipe->para.socket.la.soa.sa_family, &from);
|
socket_init(pipe->para.socket.la.soa.sa_family, &from);
|
||||||
/* get source address */
|
/* get source address */
|
||||||
|
@ -330,14 +334,15 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) {
|
||||||
#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
|
#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN
|
||||||
msgh.msg_controllen = sizeof(ctrlbuff);
|
msgh.msg_controllen = sizeof(ctrlbuff);
|
||||||
#endif
|
#endif
|
||||||
if (xiogetpacketsrc(pipe->fd, &msgh,
|
while ((rc = xiogetpacketsrc(pipe->fd, &msgh,
|
||||||
MSG_PEEK
|
MSG_PEEK
|
||||||
#ifdef MSG_TRUNC
|
#ifdef MSG_TRUNC
|
||||||
|MSG_TRUNC
|
|MSG_TRUNC
|
||||||
#endif
|
#endif
|
||||||
) < 0) {
|
)) < 0 &&
|
||||||
return -1;
|
errno == EINTR) ;
|
||||||
}
|
if (rc < 0) return -1;
|
||||||
|
|
||||||
xiodopacketinfo(&msgh, true, false);
|
xiodopacketinfo(&msgh, true, false);
|
||||||
if (xiocheckpeer(pipe, &from, &pipe->para.socket.la) < 0) {
|
if (xiocheckpeer(pipe, &from, &pipe->para.socket.la) < 0) {
|
||||||
Recvfrom(pipe->fd, buff, bufsiz, 0, &from.soa, &fromlen); /* drop */
|
Recvfrom(pipe->fd, buff, bufsiz, 0, &from.soa, &fromlen); /* drop */
|
||||||
|
|
Loading…
Reference in a new issue