diff --git a/CHANGELOG b/CHANGELOG index 40662472..78a7fa89 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -225,6 +225,10 @@ HISTORY ++ Bugfixes: + 2010-06-24: Simon Goldschmidt + * api(_lib).c/.h, api_msg.c/.h, sockets.c/.h: Fixed bug #10088: Correctly + implemented shutdown at socket level. + 2010-06-21: Simon Goldschmidt * pbuf.c/.h, ip_frag.c/.h, opt.h, memp_std.h: Fixed bug #29361 (ip_frag has problems with zero-copy DMA MACs) by adding custom pbufs and implementing diff --git a/src/api/api_lib.c b/src/api/api_lib.c index 9336a380..04f6d306 100644 --- a/src/api/api_lib.c +++ b/src/api/api_lib.c @@ -600,13 +600,14 @@ netconn_write(struct netconn *conn, const void *dataptr, size_t size, u8_t apifl } /** - * Close a TCP netconn (doesn't delete it). + * Close ot shutdown a TCP netconn (doesn't delete it). * - * @param conn the TCP netconn to close + * @param conn the TCP netconn to close or shutdown + * @param how fully close or only shutdown one side? * @return ERR_OK if the netconn was closed, any other err_t on error */ -err_t -netconn_close(struct netconn *conn) +static err_t +netconn_close_shutdown(struct netconn *conn, u8_t how) { struct api_msg msg; err_t err; @@ -615,6 +616,8 @@ netconn_close(struct netconn *conn) msg.function = do_close; msg.msg.conn = conn; + /* shutting down both ends is the same as closing */ + msg.msg.msg.sd.shut = how; /* because of the LWIP_TCPIP_CORE_LOCKING implementation of do_close, don't use TCPIP_APIMSG here */ err = tcpip_apimsg(&msg); @@ -623,6 +626,31 @@ netconn_close(struct netconn *conn) return err; } +/** + * Close a TCP netconn (doesn't delete it). + * + * @param conn the TCP netconn to close + * @return ERR_OK if the netconn was closed, any other err_t on error + */ +err_t +netconn_close(struct netconn *conn) +{ + /* shutting down both ends is the same as closing */ + return netconn_close_shutdown(conn, NETCONN_SHUT_RDWR); +} + +/** + * Shut down one or both sides of a TCP netconn (doesn't delete it). + * + * @param conn the TCP netconn to shut down + * @return ERR_OK if the netconn was closed, any other err_t on error + */ +err_t +netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx) +{ + return netconn_close_shutdown(conn, (shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0)); +} + #if LWIP_IGMP /** * Join multicast groups for UDP netconns. diff --git a/src/api/api_msg.c b/src/api/api_msg.c index b40cb126..4e7c4d81 100644 --- a/src/api/api_msg.c +++ b/src/api/api_msg.c @@ -729,6 +729,7 @@ static void do_close_internal(struct netconn *conn) { err_t err; + u8_t shut, shut_rx, shut_tx, close; LWIP_ASSERT("invalid conn", (conn != NULL)); LWIP_ASSERT("this is for tcp netconns only", (conn->type == NETCONN_TCP)); @@ -736,20 +737,38 @@ do_close_internal(struct netconn *conn) LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL)); LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL); + shut = conn->current_msg->msg.sd.shut; + shut_rx = shut & NETCONN_SHUT_RD; + shut_tx = shut & NETCONN_SHUT_WR; + /* shutting down both ends is the same as closing */ + close = shut == NETCONN_SHUT_RDWR; + /* Set back some callback pointers */ - tcp_arg(conn->pcb.tcp, NULL); + if (close) { + tcp_arg(conn->pcb.tcp, NULL); + } if (conn->pcb.tcp->state == LISTEN) { tcp_accept(conn->pcb.tcp, NULL); } else { - tcp_recv(conn->pcb.tcp, NULL); - tcp_accept(conn->pcb.tcp, NULL); /* some callbacks have to be reset if tcp_close is not successful */ - tcp_sent(conn->pcb.tcp, NULL); - tcp_poll(conn->pcb.tcp, NULL, 4); - tcp_err(conn->pcb.tcp, NULL); + if (shut_rx) { + tcp_recv(conn->pcb.tcp, NULL); + tcp_accept(conn->pcb.tcp, NULL); + } + if (shut_tx) { + tcp_sent(conn->pcb.tcp, NULL); + } + if (close) { + tcp_poll(conn->pcb.tcp, NULL, 4); + tcp_err(conn->pcb.tcp, NULL); + } } /* Try to close the connection */ - err = tcp_close(conn->pcb.tcp); + if (shut == NETCONN_SHUT_RDWR) { + err = tcp_close(conn->pcb.tcp); + } else { + err = tcp_shutdown(conn->pcb.tcp, shut & NETCONN_SHUT_RD, shut & NETCONN_SHUT_WR); + } if (err == ERR_OK) { /* Closing succeeded */ conn->current_msg->err = ERR_OK; @@ -759,9 +778,15 @@ do_close_internal(struct netconn *conn) conn->pcb.tcp = NULL; /* Trigger select() in socket layer. Make sure everybody notices activity on the connection, error first! */ - API_EVENT(conn, NETCONN_EVT_ERROR, 0); - API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); - API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + if (close) { + API_EVENT(conn, NETCONN_EVT_ERROR, 0); + } + if (shut_rx) { + API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0); + } + if (shut_tx) { + API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0); + } /* wake up the application task */ sys_sem_signal(&conn->op_completed); } else { @@ -772,6 +797,7 @@ do_close_internal(struct netconn *conn) tcp_poll(conn->pcb.tcp, poll_tcp, 4); tcp_err(conn->pcb.tcp, err_tcp); tcp_arg(conn->pcb.tcp, conn); + /* don't restore recv callback: we don't want to receive any more data */ } /* If closing didn't succeed, we get called again either from poll_tcp or from sent_tcp */ @@ -1401,15 +1427,22 @@ do_close(struct api_msg_msg *msg) LWIP_ASSERT("msg->conn->type == NETCONN_TCP", msg->conn->type == NETCONN_TCP); msg->err = ERR_INPROGRESS; } else if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) { - /* Drain and delete mboxes */ - netconn_drain(msg->conn); - LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && - msg->conn->write_offset == 0); - msg->conn->state = NETCONN_CLOSE; - msg->conn->current_msg = msg; - do_close_internal(msg->conn); - /* for tcp netconns, do_close_internal ACKs the message */ - return; + if ((msg->msg.sd.shut != NETCONN_SHUT_RDWR) && (msg->conn->state == NETCONN_LISTEN)) { + /* LISTEN doesn't support half shutdown */ + msg->err = ERR_CONN; + } else { + if (msg->msg.sd.shut & NETCONN_SHUT_RD) { + /* Drain and delete mboxes */ + netconn_drain(msg->conn); + } + LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && + msg->conn->write_offset == 0); + msg->conn->state = NETCONN_CLOSE; + msg->conn->current_msg = msg; + do_close_internal(msg->conn); + /* for tcp netconns, do_close_internal ACKs the message */ + return; + } } else #endif /* LWIP_TCP */ { diff --git a/src/api/sockets.c b/src/api/sockets.c index 7cefbd4c..e4b78f96 100644 --- a/src/api/sockets.c +++ b/src/api/sockets.c @@ -1344,9 +1344,42 @@ event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len) int lwip_shutdown(int s, int how) { - LWIP_UNUSED_ARG(how); + struct lwip_sock *sock; + err_t err; + u8_t shut_rx = 0, shut_tx = 0; + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how)); - return lwip_close(s); /* XXX temporary hack until proper implementation */ + + sock = get_socket(s); + if (!sock) { + return -1; + } + + if (sock->conn != NULL) { + if (netconn_type(sock->conn) != NETCONN_TCP) { + sock_set_errno(sock, EOPNOTSUPP); + return EOPNOTSUPP; + } + } else { + sock_set_errno(sock, ENOTCONN); + return ENOTCONN; + } + + if (how == SHUT_RD) { + shut_rx = 1; + } else if (how == SHUT_WR) { + shut_tx = 1; + } else if(how == SHUT_RDWR) { + shut_rx = 1; + shut_tx = 1; + } else { + sock_set_errno(sock, EINVAL); + return EINVAL; + } + err = netconn_shutdown(sock->conn, shut_rx, shut_tx); + + sock_set_errno(sock, err_to_errno(err)); + return (err == ERR_OK ? 0 : -1); } static int diff --git a/src/include/lwip/api.h b/src/include/lwip/api.h index 2e6561e0..9add7e89 100644 --- a/src/include/lwip/api.h +++ b/src/include/lwip/api.h @@ -234,6 +234,7 @@ err_t netconn_send(struct netconn *conn, struct netbuf *buf); err_t netconn_write(struct netconn *conn, const void *dataptr, size_t size, u8_t apiflags); err_t netconn_close(struct netconn *conn); +err_t netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx); #if LWIP_IGMP err_t netconn_join_leave_group(struct netconn *conn, ip_addr_t *multiaddr, diff --git a/src/include/lwip/api_msg.h b/src/include/lwip/api_msg.h index aeec5a80..ccb1015f 100644 --- a/src/include/lwip/api_msg.h +++ b/src/include/lwip/api_msg.h @@ -48,6 +48,10 @@ extern "C" { #endif +#define NETCONN_SHUT_RD 1 +#define NETCONN_SHUT_WR 2 +#define NETCONN_SHUT_RDWR 3 + /* IP addresses and port numbers are expected to be in * the same byte order as in the corresponding pcb. */ @@ -89,6 +93,10 @@ struct api_msg_msg { struct { u32_t len; } r; + /** used for do_close (/shutdown) */ + struct { + u8_t shut; + } sd; #if LWIP_IGMP /** used for do_join_leave_group */ struct { @@ -144,6 +152,7 @@ void do_recv ( struct api_msg_msg *msg); void do_write ( struct api_msg_msg *msg); void do_getaddr ( struct api_msg_msg *msg); void do_close ( struct api_msg_msg *msg); +void do_shutdown ( struct api_msg_msg *msg); #if LWIP_IGMP void do_join_leave_group( struct api_msg_msg *msg); #endif /* LWIP_IGMP */ diff --git a/src/include/lwip/sockets.h b/src/include/lwip/sockets.h index ff956120..ccc012db 100644 --- a/src/include/lwip/sockets.h +++ b/src/include/lwip/sockets.h @@ -279,6 +279,12 @@ typedef struct ip_mreq { #define O_NDELAY 1 /* same as O_NONBLOCK, for compatibility */ #endif +#ifndef SHUT_RD + #define SHUT_RD 1 + #define SHUT_WR 2 + #define SHUT_RDWR 3 +#endif + /* FD_SET used for lwip_select */ #ifndef FD_SET #undef FD_SETSIZE