diff --git a/include/proto/proto_tcp.h b/include/proto/proto_tcp.h index 2cf3be18c..b93b37145 100644 --- a/include/proto/proto_tcp.h +++ b/include/proto/proto_tcp.h @@ -24,7 +24,6 @@ #include #include -#include #include int tcp_event_accept(int fd); @@ -32,6 +31,9 @@ int tcpv4_bind_socket(int fd, int flags, struct sockaddr_in *local, struct socka void tcpv4_add_listener(struct listener *listener); void tcpv6_add_listener(struct listener *listener); int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen); +int tcpv4_connect_server(struct stream_interface *si, + struct proxy *be, struct server *srv, + struct sockaddr *srv_addr, struct sockaddr *cli_addr); int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit); int acl_fetch_rdp_cookie(struct proxy *px, struct session *l4, void *l7, int dir, struct acl_expr *expr, struct acl_test *test); diff --git a/include/types/fd.h b/include/types/fd.h index 0c631b1d6..a50d076c3 100644 --- a/include/types/fd.h +++ b/include/types/fd.h @@ -29,7 +29,6 @@ #include #include -#include #include /* different possible states for the fd */ diff --git a/include/types/stream_interface.h b/include/types/stream_interface.h index f97aa624d..7789323fd 100644 --- a/include/types/stream_interface.h +++ b/include/types/stream_interface.h @@ -72,6 +72,9 @@ enum { #define SI_FL_CAP_SPLICE (SI_FL_CAP_SPLTCP) +struct server; +struct proxy; + struct stream_interface { unsigned int state; /* SI_ST* */ unsigned int prev_state;/* SI_ST*, copy of previous state */ @@ -79,6 +82,8 @@ struct stream_interface { int fd; /* file descriptor for a stream driver when known */ unsigned int flags; unsigned int exp; /* wake up time for connect, queue, turn-around, ... */ + int (*connect)(struct stream_interface *, struct proxy *, struct server *, + struct sockaddr *, struct sockaddr *); /* connect function if any */ void (*shutr)(struct stream_interface *); /* shutr function */ void (*shutw)(struct stream_interface *); /* shutw function */ void (*chk_rcv)(struct stream_interface *);/* chk_rcv function */ diff --git a/src/backend.c b/src/backend.c index baa301a55..7a6b7b8f4 100644 --- a/src/backend.c +++ b/src/backend.c @@ -18,8 +18,6 @@ #include #include -#include - #include #include #include @@ -32,16 +30,11 @@ #include #include #include -#include -#include -#include -#include #include #include #include #include #include -#include #include static inline void fwrr_remove_from_tree(struct server *s); @@ -1791,7 +1784,7 @@ int assign_server_and_queue(struct session *s) */ int connect_server(struct session *s) { - int fd, err; + int err; if (!(s->flags & SN_ADDR_SET)) { err = assign_server_address(s); @@ -1799,234 +1792,16 @@ int connect_server(struct session *s) return SN_ERR_INTERNAL; } - if ((fd = s->req->cons->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { - qfprintf(stderr, "Cannot get a server socket.\n"); - - if (errno == ENFILE) - send_log(s->be, LOG_EMERG, - "Proxy %s reached system FD limit at %d. Please check system tunables.\n", - s->be->id, maxfd); - else if (errno == EMFILE) - send_log(s->be, LOG_EMERG, - "Proxy %s reached process FD limit at %d. Please check 'ulimit-n' and restart.\n", - s->be->id, maxfd); - else if (errno == ENOBUFS || errno == ENOMEM) - send_log(s->be, LOG_EMERG, - "Proxy %s reached system memory limit at %d sockets. Please check system tunables.\n", - s->be->id, maxfd); - /* this is a resource error */ - return SN_ERR_RESOURCE; - } - - if (fd >= global.maxsock) { - /* do not log anything there, it's a normal condition when this option - * is used to serialize connections to a server ! - */ - Alert("socket(): not enough free sockets. Raise -n argument. Giving up.\n"); - close(fd); - return SN_ERR_PRXCOND; /* it is a configuration limit */ - } - - if ((fcntl(fd, F_SETFL, O_NONBLOCK)==-1) || - (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) == -1)) { - qfprintf(stderr,"Cannot set client socket to non blocking mode.\n"); - close(fd); + if (!s->req->cons->connect) return SN_ERR_INTERNAL; - } - if (s->be->options & PR_O_TCP_SRV_KA) - setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(one)); + err = s->req->cons->connect(s->req->cons, s->be, s->srv, + (struct sockaddr *)&s->srv_addr, + (struct sockaddr *)&s->cli_addr); - if (s->be->options & PR_O_TCP_NOLING) - setsockopt(fd, SOL_SOCKET, SO_LINGER, (struct linger *) &nolinger, sizeof(struct linger)); + if (err != SN_ERR_NONE) + return err; - /* allow specific binding : - * - server-specific at first - * - proxy-specific next - */ - if (s->srv != NULL && s->srv->state & SRV_BIND_SRC) { - struct sockaddr_in *remote = NULL; - int ret, flags = 0; - -#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY) - switch (s->srv->state & SRV_TPROXY_MASK) { - case SRV_TPROXY_ADDR: - remote = (struct sockaddr_in *)&s->srv->tproxy_addr; - flags = 3; - break; - case SRV_TPROXY_CLI: - flags |= 2; - /* fall through */ - case SRV_TPROXY_CIP: - /* FIXME: what can we do if the client connects in IPv6 ? */ - flags |= 1; - remote = (struct sockaddr_in *)&s->cli_addr; - break; - } -#endif -#ifdef SO_BINDTODEVICE - /* Note: this might fail if not CAP_NET_RAW */ - if (s->srv->iface_name) - setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, s->srv->iface_name, s->srv->iface_len + 1); -#endif - - if (s->srv->sport_range) { - int attempts = 10; /* should be more than enough to find a spare port */ - struct sockaddr_in src; - - ret = 1; - src = s->srv->source_addr; - - do { - /* note: in case of retry, we may have to release a previously - * allocated port, hence this loop's construct. - */ - port_range_release_port(fdtab[fd].port_range, fdtab[fd].local_port); - fdtab[fd].port_range = NULL; - - if (!attempts) - break; - attempts--; - - fdtab[fd].local_port = port_range_alloc_port(s->srv->sport_range); - if (!fdtab[fd].local_port) - break; - - fdtab[fd].port_range = s->srv->sport_range; - src.sin_port = htons(fdtab[fd].local_port); - - ret = tcpv4_bind_socket(fd, flags, &src, remote); - } while (ret != 0); /* binding NOK */ - } - else { - ret = tcpv4_bind_socket(fd, flags, &s->srv->source_addr, remote); - } - - if (ret) { - port_range_release_port(fdtab[fd].port_range, fdtab[fd].local_port); - fdtab[fd].port_range = NULL; - close(fd); - - if (ret == 1) { - Alert("Cannot bind to source address before connect() for server %s/%s. Aborting.\n", - s->be->id, s->srv->id); - send_log(s->be, LOG_EMERG, - "Cannot bind to source address before connect() for server %s/%s.\n", - s->be->id, s->srv->id); - } else { - Alert("Cannot bind to tproxy source address before connect() for server %s/%s. Aborting.\n", - s->be->id, s->srv->id); - send_log(s->be, LOG_EMERG, - "Cannot bind to tproxy source address before connect() for server %s/%s.\n", - s->be->id, s->srv->id); - } - return SN_ERR_RESOURCE; - } - } - else if (s->be->options & PR_O_BIND_SRC) { - struct sockaddr_in *remote = NULL; - int ret, flags = 0; - -#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY) - switch (s->be->options & PR_O_TPXY_MASK) { - case PR_O_TPXY_ADDR: - remote = (struct sockaddr_in *)&s->be->tproxy_addr; - flags = 3; - break; - case PR_O_TPXY_CLI: - flags |= 2; - /* fall through */ - case PR_O_TPXY_CIP: - /* FIXME: what can we do if the client connects in IPv6 ? */ - flags |= 1; - remote = (struct sockaddr_in *)&s->cli_addr; - break; - } -#endif -#ifdef SO_BINDTODEVICE - /* Note: this might fail if not CAP_NET_RAW */ - if (s->be->iface_name) - setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, s->be->iface_name, s->be->iface_len + 1); -#endif - ret = tcpv4_bind_socket(fd, flags, &s->be->source_addr, remote); - if (ret) { - close(fd); - if (ret == 1) { - Alert("Cannot bind to source address before connect() for proxy %s. Aborting.\n", - s->be->id); - send_log(s->be, LOG_EMERG, - "Cannot bind to source address before connect() for proxy %s.\n", - s->be->id); - } else { - Alert("Cannot bind to tproxy source address before connect() for proxy %s. Aborting.\n", - s->be->id); - send_log(s->be, LOG_EMERG, - "Cannot bind to tproxy source address before connect() for proxy %s.\n", - s->be->id); - } - return SN_ERR_RESOURCE; - } - } - -#if defined(TCP_QUICKACK) && defined(SOL_TCP) - /* disabling tcp quick ack now allows the first request to leave the - * machine with the first ACK. We only do this if there are pending - * data in the buffer. - */ - if ((s->be->options2 & PR_O2_SMARTCON) && s->req->send_max) - setsockopt(fd, SOL_TCP, TCP_QUICKACK, (char *) &zero, sizeof(zero)); -#endif - - if ((connect(fd, (struct sockaddr *)&s->srv_addr, sizeof(s->srv_addr)) == -1) && - (errno != EINPROGRESS) && (errno != EALREADY) && (errno != EISCONN)) { - - if (errno == EAGAIN || errno == EADDRINUSE) { - char *msg; - if (errno == EAGAIN) /* no free ports left, try again later */ - msg = "no free ports"; - else - msg = "local address already in use"; - - qfprintf(stderr,"Cannot connect: %s.\n",msg); - port_range_release_port(fdtab[fd].port_range, fdtab[fd].local_port); - fdtab[fd].port_range = NULL; - close(fd); - send_log(s->be, LOG_EMERG, - "Connect() failed for server %s/%s: %s.\n", - s->be->id, s->srv->id, msg); - return SN_ERR_RESOURCE; - } else if (errno == ETIMEDOUT) { - //qfprintf(stderr,"Connect(): ETIMEDOUT"); - port_range_release_port(fdtab[fd].port_range, fdtab[fd].local_port); - fdtab[fd].port_range = NULL; - close(fd); - return SN_ERR_SRVTO; - } else { - // (errno == ECONNREFUSED || errno == ENETUNREACH || errno == EACCES || errno == EPERM) - //qfprintf(stderr,"Connect(): %d", errno); - port_range_release_port(fdtab[fd].port_range, fdtab[fd].local_port); - fdtab[fd].port_range = NULL; - close(fd); - return SN_ERR_SRVCL; - } - } - - fdtab[fd].owner = s->req->cons; - fdtab[fd].state = FD_STCONN; /* connection in progress */ - fdtab[fd].flags = FD_FL_TCP | FD_FL_TCP_NODELAY; - fdtab[fd].cb[DIR_RD].f = &stream_sock_read; - fdtab[fd].cb[DIR_RD].b = s->rep; - fdtab[fd].cb[DIR_WR].f = &stream_sock_write; - fdtab[fd].cb[DIR_WR].b = s->req; - - fdtab[fd].peeraddr = (struct sockaddr *)&s->srv_addr; - fdtab[fd].peerlen = sizeof(s->srv_addr); - - fd_insert(fd); - EV_FD_SET(fd, DIR_WR); /* for connect status */ - - s->req->cons->state = SI_ST_CON; - s->req->cons->flags |= SI_FL_CAP_SPLTCP; /* TCP supports splicing */ if (s->srv) { s->flags |= SN_CURR_SESS; s->srv->cur_sess++; @@ -2036,7 +1811,6 @@ int connect_server(struct session *s) s->be->lbprm.server_take_conn(s->srv); } - s->req->cons->exp = tick_add_ifset(now_ms, s->be->timeout.connect); return SN_ERR_NONE; /* connection is OK */ } diff --git a/src/client.c b/src/client.c index 6210cdbe0..cb1172754 100644 --- a/src/client.c +++ b/src/client.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -179,6 +180,7 @@ int event_accept(int fd) { s->si[0].err_type = SI_ET_NONE; s->si[0].err_loc = NULL; s->si[0].owner = t; + s->si[0].connect = NULL; s->si[0].shutr = stream_sock_shutr; s->si[0].shutw = stream_sock_shutw; s->si[0].chk_rcv = stream_sock_chk_rcv; @@ -191,6 +193,7 @@ int event_accept(int fd) { s->si[1].err_type = SI_ET_NONE; s->si[1].err_loc = NULL; s->si[1].owner = t; + s->si[1].connect = tcpv4_connect_server; s->si[1].shutr = stream_sock_shutr; s->si[1].shutw = stream_sock_shutw; s->si[1].chk_rcv = stream_sock_chk_rcv; diff --git a/src/proto_tcp.c b/src/proto_tcp.c index 0091464dc..6f368da98 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -38,11 +38,14 @@ #include #include +#include #include #include #include #include +#include +#include #include #include #include @@ -173,6 +176,263 @@ int tcpv4_bind_socket(int fd, int flags, struct sockaddr_in *local, struct socka return 0; } + +/* + * This function initiates a connection to the server assigned to this session + * (s->srv, s->srv_addr). It will assign a server if none is assigned yet. + * It can return one of : + * - SN_ERR_NONE if everything's OK + * - SN_ERR_SRVTO if there are no more servers + * - SN_ERR_SRVCL if the connection was refused by the server + * - SN_ERR_PRXCOND if the connection has been limited by the proxy (maxconn) + * - SN_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...) + * - SN_ERR_INTERNAL for any other purely internal errors + * Additionnally, in the case of SN_ERR_RESOURCE, an emergency log will be emitted. + */ +int tcpv4_connect_server(struct stream_interface *si, + struct proxy *be, struct server *srv, + struct sockaddr *srv_addr, struct sockaddr *cli_addr) +{ + int fd; + + if ((fd = si->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { + qfprintf(stderr, "Cannot get a server socket.\n"); + + if (errno == ENFILE) + send_log(be, LOG_EMERG, + "Proxy %s reached system FD limit at %d. Please check system tunables.\n", + be->id, maxfd); + else if (errno == EMFILE) + send_log(be, LOG_EMERG, + "Proxy %s reached process FD limit at %d. Please check 'ulimit-n' and restart.\n", + be->id, maxfd); + else if (errno == ENOBUFS || errno == ENOMEM) + send_log(be, LOG_EMERG, + "Proxy %s reached system memory limit at %d sockets. Please check system tunables.\n", + be->id, maxfd); + /* this is a resource error */ + return SN_ERR_RESOURCE; + } + + if (fd >= global.maxsock) { + /* do not log anything there, it's a normal condition when this option + * is used to serialize connections to a server ! + */ + Alert("socket(): not enough free sockets. Raise -n argument. Giving up.\n"); + close(fd); + return SN_ERR_PRXCOND; /* it is a configuration limit */ + } + + if ((fcntl(fd, F_SETFL, O_NONBLOCK)==-1) || + (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) == -1)) { + qfprintf(stderr,"Cannot set client socket to non blocking mode.\n"); + close(fd); + return SN_ERR_INTERNAL; + } + + if (be->options & PR_O_TCP_SRV_KA) + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(one)); + + if (be->options & PR_O_TCP_NOLING) + setsockopt(fd, SOL_SOCKET, SO_LINGER, (struct linger *) &nolinger, sizeof(struct linger)); + + /* allow specific binding : + * - server-specific at first + * - proxy-specific next + */ + if (srv != NULL && srv->state & SRV_BIND_SRC) { + struct sockaddr_in *remote = NULL; + int ret, flags = 0; + +#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY) + switch (srv->state & SRV_TPROXY_MASK) { + case SRV_TPROXY_ADDR: + remote = (struct sockaddr_in *)&srv->tproxy_addr; + flags = 3; + break; + case SRV_TPROXY_CLI: + if (cli_addr) + flags |= 2; + /* fall through */ + case SRV_TPROXY_CIP: + /* FIXME: what can we do if the client connects in IPv6 ? */ + if (cli_addr) + flags |= 1; + remote = (struct sockaddr_in *)cli_addr; + break; + } +#endif +#ifdef SO_BINDTODEVICE + /* Note: this might fail if not CAP_NET_RAW */ + if (srv->iface_name) + setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, srv->iface_name, srv->iface_len + 1); +#endif + + if (srv->sport_range) { + int attempts = 10; /* should be more than enough to find a spare port */ + struct sockaddr_in src; + + ret = 1; + src = srv->source_addr; + + do { + /* note: in case of retry, we may have to release a previously + * allocated port, hence this loop's construct. + */ + port_range_release_port(fdtab[fd].port_range, fdtab[fd].local_port); + fdtab[fd].port_range = NULL; + + if (!attempts) + break; + attempts--; + + fdtab[fd].local_port = port_range_alloc_port(srv->sport_range); + if (!fdtab[fd].local_port) + break; + + fdtab[fd].port_range = srv->sport_range; + src.sin_port = htons(fdtab[fd].local_port); + + ret = tcpv4_bind_socket(fd, flags, &src, remote); + } while (ret != 0); /* binding NOK */ + } + else { + ret = tcpv4_bind_socket(fd, flags, &srv->source_addr, remote); + } + + if (ret) { + port_range_release_port(fdtab[fd].port_range, fdtab[fd].local_port); + fdtab[fd].port_range = NULL; + close(fd); + + if (ret == 1) { + Alert("Cannot bind to source address before connect() for server %s/%s. Aborting.\n", + be->id, srv->id); + send_log(be, LOG_EMERG, + "Cannot bind to source address before connect() for server %s/%s.\n", + be->id, srv->id); + } else { + Alert("Cannot bind to tproxy source address before connect() for server %s/%s. Aborting.\n", + be->id, srv->id); + send_log(be, LOG_EMERG, + "Cannot bind to tproxy source address before connect() for server %s/%s.\n", + be->id, srv->id); + } + return SN_ERR_RESOURCE; + } + } + else if (be->options & PR_O_BIND_SRC) { + struct sockaddr_in *remote = NULL; + int ret, flags = 0; + +#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY) + switch (be->options & PR_O_TPXY_MASK) { + case PR_O_TPXY_ADDR: + remote = (struct sockaddr_in *)&be->tproxy_addr; + flags = 3; + break; + case PR_O_TPXY_CLI: + if (cli_addr) + flags |= 2; + /* fall through */ + case PR_O_TPXY_CIP: + /* FIXME: what can we do if the client connects in IPv6 ? */ + if (cli_addr) + flags |= 1; + remote = (struct sockaddr_in *)cli_addr; + break; + } +#endif +#ifdef SO_BINDTODEVICE + /* Note: this might fail if not CAP_NET_RAW */ + if (be->iface_name) + setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, be->iface_name, be->iface_len + 1); +#endif + ret = tcpv4_bind_socket(fd, flags, &be->source_addr, remote); + if (ret) { + close(fd); + if (ret == 1) { + Alert("Cannot bind to source address before connect() for proxy %s. Aborting.\n", + be->id); + send_log(be, LOG_EMERG, + "Cannot bind to source address before connect() for proxy %s.\n", + be->id); + } else { + Alert("Cannot bind to tproxy source address before connect() for proxy %s. Aborting.\n", + be->id); + send_log(be, LOG_EMERG, + "Cannot bind to tproxy source address before connect() for proxy %s.\n", + be->id); + } + return SN_ERR_RESOURCE; + } + } + +#if defined(TCP_QUICKACK) && defined(SOL_TCP) + /* disabling tcp quick ack now allows the first request to leave the + * machine with the first ACK. We only do this if there are pending + * data in the buffer. + */ + if ((be->options2 & PR_O2_SMARTCON) && si->ob->send_max) + setsockopt(fd, SOL_TCP, TCP_QUICKACK, (char *) &zero, sizeof(zero)); +#endif + + if ((connect(fd, (struct sockaddr *)srv_addr, sizeof(struct sockaddr_in)) == -1) && + (errno != EINPROGRESS) && (errno != EALREADY) && (errno != EISCONN)) { + + if (errno == EAGAIN || errno == EADDRINUSE) { + char *msg; + if (errno == EAGAIN) /* no free ports left, try again later */ + msg = "no free ports"; + else + msg = "local address already in use"; + + qfprintf(stderr,"Cannot connect: %s.\n",msg); + port_range_release_port(fdtab[fd].port_range, fdtab[fd].local_port); + fdtab[fd].port_range = NULL; + close(fd); + send_log(be, LOG_EMERG, + "Connect() failed for server %s/%s: %s.\n", + be->id, srv->id, msg); + return SN_ERR_RESOURCE; + } else if (errno == ETIMEDOUT) { + //qfprintf(stderr,"Connect(): ETIMEDOUT"); + port_range_release_port(fdtab[fd].port_range, fdtab[fd].local_port); + fdtab[fd].port_range = NULL; + close(fd); + return SN_ERR_SRVTO; + } else { + // (errno == ECONNREFUSED || errno == ENETUNREACH || errno == EACCES || errno == EPERM) + //qfprintf(stderr,"Connect(): %d", errno); + port_range_release_port(fdtab[fd].port_range, fdtab[fd].local_port); + fdtab[fd].port_range = NULL; + close(fd); + return SN_ERR_SRVCL; + } + } + + fdtab[fd].owner = si; + fdtab[fd].state = FD_STCONN; /* connection in progress */ + fdtab[fd].flags = FD_FL_TCP | FD_FL_TCP_NODELAY; + fdtab[fd].cb[DIR_RD].f = &stream_sock_read; + fdtab[fd].cb[DIR_RD].b = si->ib; + fdtab[fd].cb[DIR_WR].f = &stream_sock_write; + fdtab[fd].cb[DIR_WR].b = si->ob; + + fdtab[fd].peeraddr = (struct sockaddr *)srv_addr; + fdtab[fd].peerlen = sizeof(struct sockaddr_in); + + fd_insert(fd); + EV_FD_SET(fd, DIR_WR); /* for connect status */ + + si->state = SI_ST_CON; + si->flags |= SI_FL_CAP_SPLTCP; /* TCP supports splicing */ + si->exp = tick_add_ifset(now_ms, be->timeout.connect); + + return SN_ERR_NONE; /* connection is OK */ +} + + /* This function tries to bind a TCPv4/v6 listener. It may return a warning or * an error message in if the message is at most bytes long * (including '\0'). The return value is composed from ERR_ABORT, ERR_WARN, diff --git a/src/proto_uxst.c b/src/proto_uxst.c index a05399227..8c1f2c56a 100644 --- a/src/proto_uxst.c +++ b/src/proto_uxst.c @@ -460,6 +460,7 @@ int uxst_event_accept(int fd) { s->si[0].err_type = SI_ET_NONE; s->si[0].err_loc = NULL; s->si[0].owner = t; + s->si[0].connect = NULL; s->si[0].shutr = stream_sock_shutr; s->si[0].shutw = stream_sock_shutw; s->si[0].chk_rcv = stream_sock_chk_rcv; @@ -472,6 +473,7 @@ int uxst_event_accept(int fd) { s->si[1].err_type = SI_ET_NONE; s->si[1].err_loc = NULL; s->si[1].owner = t; + s->si[1].connect = NULL; s->si[1].shutr = stream_sock_shutr; s->si[1].shutw = stream_sock_shutw; s->si[1].chk_rcv = stream_sock_chk_rcv;