mirror of
https://repo.or.cz/socat.git
synced 2024-12-22 15:32:35 +00:00
SO_REUSEADDR per default on TCP LISTEN; so-reuseaddr=
This commit is contained in:
parent
b5640dd707
commit
2a9623d61c
9 changed files with 714 additions and 242 deletions
4
CHANGES
4
CHANGES
|
@ -66,6 +66,10 @@ Features:
|
|||
With this option Socat restores the VLAN tag.
|
||||
Feature inspired by Zhao Dong.
|
||||
|
||||
Socket option SO_REUSEADDR is now automatically applied to TCP LISTEN
|
||||
addresses. reuseaddr= restores the old behaviour.
|
||||
Tests: TCP4_REUSEADDR OPENSSL_6_REUSEADDR REUSEADDR_NULL
|
||||
|
||||
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/
|
||||
|
|
29
doc/socat.yo
29
doc/socat.yo
|
@ -586,7 +586,7 @@ label(ADDRESS_OPENSSL_LISTEN)dit(bf(tt(OPENSSL-LISTEN:<port>)))
|
|||
link(range)(OPTION_RANGE),
|
||||
link(tcpwrap)(OPTION_TCPWRAPPERS),
|
||||
link(su)(OPTION_SUBSTUSER),
|
||||
link(reuseaddr)(OPTION_REUSEADDR),
|
||||
link(reuseaddr)(OPTION_SO_REUSEADDR),
|
||||
link(retry)(OPTION_RETRY)nl()
|
||||
See also:
|
||||
link(OPENSSL)(ADDRESS_OPENSSL_CONNECT),
|
||||
|
@ -652,7 +652,7 @@ label(ADDRESS_OPENSSL_DTLS_SERVER)dit(bf(tt(OPENSSL-DTLS-SERVER:<port>)))
|
|||
link(range)(OPTION_RANGE),
|
||||
link(tcpwrap)(OPTION_TCPWRAPPERS),
|
||||
link(su)(OPTION_SUBSTUSER),
|
||||
link(reuseaddr)(OPTION_REUSEADDR),
|
||||
link(reuseaddr)(OPTION_SO_REUSEADDR),
|
||||
link(retry)(OPTION_RETRY)nl()
|
||||
link(rcvtimeo)(OPTION_SO_RCVTIMOE)nl()
|
||||
See also:
|
||||
|
@ -788,7 +788,7 @@ label(ADDRESS_SCTP_LISTEN)dit(bf(tt(SCTP-LISTEN:<port>)))
|
|||
link(sctp-maxseg)(OPTION_SCTP_MAXSEG),
|
||||
link(sctp-nodelay)(OPTION_SCTP_NODELAY),
|
||||
link(su)(OPTION_SUBSTUSER),
|
||||
link(reuseaddr)(OPTION_REUSEADDR),
|
||||
link(reuseaddr)(OPTION_SO_REUSEADDR),
|
||||
link(retry)(OPTION_RETRY)nl()
|
||||
See also:
|
||||
link(SCTP4-LISTEN)(ADDRESS_SCTP4_LISTEN),
|
||||
|
@ -1053,7 +1053,7 @@ label(ADDRESS_TCP_LISTEN)dit(bf(tt(TCP-LISTEN:<port>)))
|
|||
link(accept-timeout)(OPTION_ACCEPT_TIMEOUT),
|
||||
link(mss)(OPTION_MSS),
|
||||
link(su)(OPTION_SUBSTUSER),
|
||||
link(reuseaddr)(OPTION_REUSEADDR),
|
||||
link(reuseaddr)(OPTION_SO_REUSEADDR),
|
||||
link(retry)(OPTION_RETRY),
|
||||
link(cool-write)(OPTION_COOL_WRITE)nl()
|
||||
See also:
|
||||
|
@ -1413,7 +1413,7 @@ label(ADDRESS_VSOCK_LISTEN)dit(bf(tt(VSOCK-LISTEN:<port>)))
|
|||
link(max-children)(OPTION_MAX_CHILDREN),
|
||||
link(backlog)(OPTION_BACKLOG),
|
||||
link(su)(OPTION_SUBSTUSER),
|
||||
link(reuseaddr)(OPTION_REUSEADDR),
|
||||
link(reuseaddr)(OPTION_SO_REUSEADDR),
|
||||
link(retry)(OPTION_RETRY)nl()
|
||||
See also:
|
||||
link(VSOCK-CONNECT)(ADDRESS_VSOCK_CONNECT)
|
||||
|
@ -2004,9 +2004,16 @@ label(OPTION_SO_RCVTIMOE)dit(bf(tt(so-rcvtimeo=<time>, rcvtimeo=<time>)))
|
|||
label(OPTION_SO_SNDTIMOE)dit(bf(tt(so-sndtimeo=<time>, sndtimeo=<time>)))
|
||||
Like link(so-recvtimeo)(OPTION_SO_RCVTIMOE), but for code(send). Not usecase
|
||||
known.
|
||||
label(OPTION_REUSEADDR)dit(bf(tt(reuseaddr)))
|
||||
label(OPTION_RCVLOWAT)dit(bf(tt(rcvlowat=<bytes>)))
|
||||
Specifies the minimum number of received bytes [link(int)(TYPE_INT)] until
|
||||
the socket layer will pass the buffered data to socat().
|
||||
label(OPTION_SO_REUSEADDR)dit(bf(tt(reuseaddr[=[0|1]])))
|
||||
Allows other sockets to bind to an address even if parts of it (e.g. the
|
||||
local port) are already in use by socat() (link(example)(EXAMPLE_OPTION_REUSEADDR)).
|
||||
local port) are already in use by socat().nl()
|
||||
With version 1.7.5, this socket option is set automatically for TCP LISTEN
|
||||
addresses. If you prefer the system default (no related
|
||||
tt(setsockopt(...SO_REUSEADDR...)) call at all), use form tt(reuseaddr=).nl()
|
||||
(link(example)(EXAMPLE_OPTION_SO_REUSEADDR)).
|
||||
label(OPTION_SNDBUF)dit(bf(tt(sndbuf=<bytes>)))
|
||||
Sets the size of the send buffer after the code(socket()) call to
|
||||
<bytes> [link(int)(TYPE_INT)].
|
||||
|
@ -3295,7 +3302,7 @@ connection comes in, accepts it, then connects to the remote host
|
|||
a second connection.
|
||||
|
||||
label(EXAMPLE_OPTION_BIND_TCP4)
|
||||
label(EXAMPLE_OPTION_REUSEADDR)
|
||||
label(EXAMPLE_OPTION_SO_REUSEADDR)
|
||||
label(EXAMPLE_OPTION_FORK)
|
||||
label(EXAMPLE_OPTION_SUBSTUSER)
|
||||
label(EXAMPLE_OPTION_RANGE)
|
||||
|
@ -3319,7 +3326,7 @@ link(fork)(OPTION_FORK)'ing a new
|
|||
process after each code(accept()). It provides a little security by
|
||||
link(su)(OPTION_SUBSTUSER)'ing to user
|
||||
nobody after forking; it only permits connections from the private 10 network
|
||||
(link(range)(OPTION_RANGE)); due to link(reuseaddr)(OPTION_REUSEADDR), it
|
||||
(link(range)(OPTION_RANGE)); due to link(reuseaddr)(OPTION_SO_REUSEADDR), it
|
||||
allows immediate restart after master process's termination, even if some child
|
||||
sockets are not completely shut down.
|
||||
With link(-lmlocal2)(option_lm), socat logs to stderr until successfully
|
||||
|
@ -3512,7 +3519,7 @@ implements a simple network based message collector.
|
|||
For each client connecting to port 3334, a new child process is generated (option link(fork)(OPTION_FORK)).
|
||||
All data sent by the clients are link(append)(OPTION_APPEND)'ed to the file /tmp/in.log.
|
||||
If the file does not exist, socat link(creat)(OPTION_O_CREAT)'s it.
|
||||
Option link(reuseaddr)(OPTION_REUSEADDR) allows immediate restart of the server
|
||||
Option link(reuseaddr)(OPTION_SO_REUSEADDR) allows immediate restart of the server
|
||||
process.
|
||||
|
||||
COMMENT(
|
||||
|
@ -3523,7 +3530,7 @@ For each client connecting to port 3335, a new child process is generated
|
|||
(option link(fork)(OPTION_FORK)).
|
||||
The contents of the file /tmp/motd is sent to each client.
|
||||
Messages sent by clients result in an error due to option link(rdonly)(OPTION_RDONLY).
|
||||
Option link(reuseaddr)(OPTION_REUSEADDR) allows immediate restart of the server
|
||||
Option link(reuseaddr)(OPTION_SO_REUSEADDR) allows immediate restart of the server
|
||||
process.
|
||||
)
|
||||
COMMENT(
|
||||
|
|
|
@ -153,7 +153,10 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl
|
|||
applyopts_offset(xfd, opts);
|
||||
applyopts_cloexec(xfd->fd, opts);
|
||||
|
||||
/* Phase prebind */
|
||||
xiosock_reuseaddr(xfd->fd, proto, opts);
|
||||
applyopts(xfd->fd, opts, PH_PREBIND);
|
||||
|
||||
applyopts(xfd->fd, opts, PH_BIND);
|
||||
if (Bind(xfd->fd, (struct sockaddr *)us, uslen) < 0) {
|
||||
Msg4(level, "bind(%d, {%s}, "F_socklen"): %s", xfd->fd,
|
||||
|
|
36
xio-socket.c
36
xio-socket.c
|
@ -97,7 +97,7 @@ const struct optdesc opt_so_debug = { "so-debug", "debug", OPT_SO_DEBUG,
|
|||
const struct optdesc opt_so_acceptconn={ "so-acceptconn","acceptconn",OPT_SO_ACCEPTCONN,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_ACCEPTCONN};
|
||||
#endif /* SO_ACCEPTCONN */
|
||||
const struct optdesc opt_so_broadcast= { "so-broadcast", "broadcast", OPT_SO_BROADCAST,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_BROADCAST};
|
||||
const struct optdesc opt_so_reuseaddr= { "so-reuseaddr", "reuseaddr", OPT_SO_REUSEADDR,GROUP_SOCKET, PH_PREBIND, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_REUSEADDR};
|
||||
const struct optdesc opt_so_reuseaddr= { "so-reuseaddr", "reuseaddr", OPT_SO_REUSEADDR,GROUP_SOCKET, PH_PREBIND, TYPE_INT_NULL, OFUNC_SOCKOPT, SOL_SOCKET, SO_REUSEADDR};
|
||||
const struct optdesc opt_so_keepalive= { "so-keepalive", "keepalive", OPT_SO_KEEPALIVE,GROUP_SOCKET, PH_FD, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_KEEPALIVE};
|
||||
#if HAVE_STRUCT_LINGER
|
||||
const struct optdesc opt_so_linger = { "so-linger", "linger", OPT_SO_LINGER, GROUP_SOCKET, PH_PASTSOCKET, TYPE_LINGER,OFUNC_SOCKOPT,SOL_SOCKET, SO_LINGER };
|
||||
|
@ -2153,3 +2153,37 @@ int xiobind(
|
|||
applyopts(xfd->fd, opts, PH_PASTBIND);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Handles the SO_REUSEADDR socket option for TCP LISTEN addresses depending on
|
||||
Socat option so-reuseaddr:
|
||||
Option not applied: set it to 1
|
||||
Option applied with a value: set it to the value
|
||||
Option applied eith empty value "so-reuseaddr=": do not call setsockopt() for
|
||||
SO_REUSEADDR
|
||||
Return 0 on success, or -1 with errno when an error occurred.
|
||||
*/
|
||||
int xiosock_reuseaddr(int fd, int ipproto, struct opt *opts)
|
||||
{
|
||||
union integral val;
|
||||
union integral notnull;
|
||||
int _errno;
|
||||
|
||||
val.u_int = 0;
|
||||
notnull.u_bool = false;
|
||||
if (ipproto == IPPROTO_TCP) {
|
||||
val.u_int = 1;
|
||||
notnull.u_bool = true;
|
||||
}
|
||||
retropt_2integrals(opts, OPT_SO_REUSEADDR, &val, ¬null);
|
||||
if (notnull.u_bool) {
|
||||
if (Setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val.u_int, sizeof(int))
|
||||
!= 0) {
|
||||
_errno = errno;
|
||||
Error4("setsockopt(%d, SOL_SOCKET, SO_REUSEADDR, { %d }, "F_Zu"): %s",
|
||||
fd, val.u_int, sizeof(val.u_int), strerror(errno));
|
||||
errno = _errno;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -135,5 +135,6 @@ extern int
|
|||
xiosocket(struct opt *opts, int pf, int socktype, int proto, int level);
|
||||
extern int
|
||||
xiosocketpair(struct opt *opts, int pf, int socktype, int proto, int sv[2]);
|
||||
extern int xiosock_reuseaddr(int fd, int ipproto, struct opt *opts);
|
||||
|
||||
#endif /* !defined(__xio_socket_h_included) */
|
||||
|
|
14
xio-udp.c
14
xio-udp.c
|
@ -133,24 +133,30 @@ int _xioopen_ipdgram_listen(struct single *sfd,
|
|||
while (true) { /* we loop with fork or prohibited packets */
|
||||
/* now wait for some packet on this datagram socket, get its sender
|
||||
address, connect there, and return */
|
||||
int reuseaddr = dofork;
|
||||
union integral notnull;
|
||||
union integral reuseaddr;
|
||||
int doreuseaddr = (dofork != 0);
|
||||
char infobuff[256];
|
||||
union sockaddr_union _sockname;
|
||||
union sockaddr_union *la = &_sockname; /* local address */
|
||||
|
||||
reuseaddr.u_int = dofork;
|
||||
|
||||
if ((sfd->fd = xiosocket(opts, pf, socktype, ipproto, E_ERROR)) < 0) {
|
||||
return STAT_RETRYLATER;
|
||||
}
|
||||
doreuseaddr |= (retropt_int(opts, OPT_SO_REUSEADDR, &reuseaddr) >= 0);
|
||||
doreuseaddr |= (retropt_2integrals(opts, OPT_SO_REUSEADDR,
|
||||
&reuseaddr, ¬null) >= 0);
|
||||
applyopts(sfd->fd, opts, PH_PASTSOCKET);
|
||||
|
||||
/* SO_REUSEADDR handling of UDP sockets is helpful on Solaris */
|
||||
if (doreuseaddr) {
|
||||
if (Setsockopt(sfd->fd, opt_so_reuseaddr.major,
|
||||
opt_so_reuseaddr.minor, &reuseaddr, sizeof(reuseaddr))
|
||||
opt_so_reuseaddr.minor, &reuseaddr.u_int, sizeof(reuseaddr.u_int))
|
||||
< 0) {
|
||||
Warn6("setsockopt(%d, %d, %d, {%d}, "F_Zd"): %s",
|
||||
sfd->fd, opt_so_reuseaddr.major,
|
||||
opt_so_reuseaddr.minor, reuseaddr, sizeof(reuseaddr),
|
||||
opt_so_reuseaddr.minor, reuseaddr.u_int, sizeof(reuseaddr.u_int),
|
||||
strerror(errno));
|
||||
}
|
||||
}
|
||||
|
|
57
xioopts.c
57
xioopts.c
|
@ -2094,6 +2094,21 @@ int parseopts_table(const char **a, groups_t groups, struct opt **opts,
|
|||
Info2("setting option \"%s\" to %d", ent->desc->defname,
|
||||
(*opts)[i].value.u_int);
|
||||
break;
|
||||
case TYPE_INT_NULL:
|
||||
(*opts)[i].value2.u_bool = true;
|
||||
if (assign && token[0] != '\0') {
|
||||
char *rest;
|
||||
(*opts)[i].value.u_int = Strtoul(token, &rest, 0, a0);
|
||||
} else if (assign) {
|
||||
(*opts)[i].value2.u_bool = false; /* NULL / no value */
|
||||
Info1("setting option \"%s\" to NULL", ent->desc->defname);
|
||||
break;
|
||||
} else {
|
||||
(*opts)[i].value.u_int = 1;
|
||||
}
|
||||
Info2("setting option \"%s\" to %d", ent->desc->defname,
|
||||
(*opts)[i].value.u_int);
|
||||
break;
|
||||
case TYPE_BOOL:
|
||||
if (!assign) {
|
||||
(*opts)[i].value.u_bool = 1;
|
||||
|
@ -2924,7 +2939,7 @@ int retropt_int(struct opt *opts, int optcode, int *result) {
|
|||
case TYPE_INT: *result = opt->value.u_int; break;
|
||||
case TYPE_STRING: *result = strtol(opt->value.u_string, &rest, 0);
|
||||
if (*rest != '\0') {
|
||||
Error1("retropts: trailing garbage in numerical arg of option \"%s\"",
|
||||
Error1("retropts_int(): trailing garbage in numerical arg of option \"%s\"",
|
||||
opt->desc->defname);
|
||||
}
|
||||
break;
|
||||
|
@ -2941,6 +2956,36 @@ int retropt_int(struct opt *opts, int optcode, int *result) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* Looks for the first option of type <optcode>. If the option is found,
|
||||
this function stores its int value in *result, "consumes" the
|
||||
option, and returns 0.
|
||||
If the option is not found, *result is not modified, and -1 is returned. */
|
||||
int retropt_2integrals(struct opt *opts, int optcode,
|
||||
union integral *value1, union integral *value2)
|
||||
{
|
||||
struct opt *opt = opts;
|
||||
|
||||
while (opt->desc != ODESC_END) {
|
||||
if (opt->desc != ODESC_DONE && opt->desc->optcode == optcode) {
|
||||
switch (opt->desc->type) {
|
||||
case TYPE_INT_NULL:
|
||||
/* ...and many more types */
|
||||
*value1 = opt->value;
|
||||
*value2 = opt->value2;
|
||||
break;
|
||||
default: Error2("cannot convert type %d of option %s to int/NULL",
|
||||
opt->desc->type, opt->desc->defname);
|
||||
opt->desc = ODESC_ERROR;
|
||||
return -1;
|
||||
}
|
||||
opt->desc = ODESC_DONE;
|
||||
return 0;
|
||||
}
|
||||
++opt;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Looks for the first option of type <optcode>. If the option is found,
|
||||
this function stores its unsigned int value in *result, "consumes" the
|
||||
option, and returns 0.
|
||||
|
@ -3335,6 +3380,16 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
|
|||
opt->desc = ODESC_ERROR; ++opt; continue;
|
||||
}
|
||||
break;
|
||||
case TYPE_INT_NULL:
|
||||
if (opt->value2.u_bool &&
|
||||
Setsockopt(fd, opt->desc->major, opt->desc->minor,
|
||||
&opt->value.u_int, sizeof(int)) < 0) {
|
||||
Error6("setsockopt(%d, %d, %d, {%d}, "F_Zu"): %s",
|
||||
fd, opt->desc->major, opt->desc->minor,
|
||||
opt->value.u_int, sizeof(int), strerror(errno));
|
||||
opt->desc = ODESC_ERROR; ++opt; continue;
|
||||
}
|
||||
break;
|
||||
case TYPE_LONG:
|
||||
if (Setsockopt(fd, opt->desc->major, opt->desc->minor,
|
||||
&opt->value.u_long, sizeof(long)) < 0) {
|
||||
|
|
|
@ -27,6 +27,7 @@ enum e_types {
|
|||
TYPE_BYTE, /* unsigned char */
|
||||
|
||||
TYPE_INT, /* int */
|
||||
TYPE_INT_NULL, /* int, or opt= for no action (instead of default) */
|
||||
TYPE_LONG, /* long */
|
||||
TYPE_STRING, /* char * */
|
||||
TYPE_NAME = TYPE_STRING,
|
||||
|
@ -125,7 +126,7 @@ enum e_func {
|
|||
groups. to keep the search for options simple, we allow each option to
|
||||
belong to at most one group only. (we have a dummy GROUP_NONE for those
|
||||
that don't want to belong to any...)
|
||||
The caller of parseopts() specifies per bitpatter a set of groups where it
|
||||
The caller of parseopts() specifies per bit pattern a set of groups where it
|
||||
accepts options from.
|
||||
*/
|
||||
|
||||
|
@ -946,6 +947,7 @@ extern int retropt_bool(struct opt *opts, int optcode, bool *result);
|
|||
extern int retropt_short(struct opt *opts, int optcode, short *result);
|
||||
extern int retropt_ushort(struct opt *opts, int optcode, unsigned short *result);
|
||||
extern int retropt_int(struct opt *opts, int optcode, int *result);
|
||||
extern int retropt_2integrals(struct opt *opts, int optcode, union integral *value1, union integral *value2);
|
||||
extern int retropt_uint(struct opt *opts, int optcode, unsigned int *result);
|
||||
extern int retropt_long(struct opt *opts, int optcode, long *result);
|
||||
extern int retropt_ulong(struct opt *opts, int optcode, unsigned long *result);
|
||||
|
|
Loading…
Reference in a new issue