Fixed bug #10088: Correctly implemented shutdown at socket level.

This commit is contained in:
goldsimon 2010-06-24 19:33:14 +00:00
parent f61b80ca6a
commit 6929a786aa
7 changed files with 139 additions and 25 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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