mirror of
https://github.com/lwip-tcpip/lwip.git
synced 2024-10-01 04:12:07 +00:00
Fixed bug #10088: Correctly implemented shutdown at socket level.
This commit is contained in:
parent
f61b80ca6a
commit
6929a786aa
@ -225,6 +225,10 @@ HISTORY
|
|||||||
|
|
||||||
++ Bugfixes:
|
++ 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
|
2010-06-21: Simon Goldschmidt
|
||||||
* pbuf.c/.h, ip_frag.c/.h, opt.h, memp_std.h: Fixed bug #29361 (ip_frag has
|
* 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
|
problems with zero-copy DMA MACs) by adding custom pbufs and implementing
|
||||||
|
@ -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
|
* @return ERR_OK if the netconn was closed, any other err_t on error
|
||||||
*/
|
*/
|
||||||
err_t
|
static err_t
|
||||||
netconn_close(struct netconn *conn)
|
netconn_close_shutdown(struct netconn *conn, u8_t how)
|
||||||
{
|
{
|
||||||
struct api_msg msg;
|
struct api_msg msg;
|
||||||
err_t err;
|
err_t err;
|
||||||
@ -615,6 +616,8 @@ netconn_close(struct netconn *conn)
|
|||||||
|
|
||||||
msg.function = do_close;
|
msg.function = do_close;
|
||||||
msg.msg.conn = conn;
|
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,
|
/* because of the LWIP_TCPIP_CORE_LOCKING implementation of do_close,
|
||||||
don't use TCPIP_APIMSG here */
|
don't use TCPIP_APIMSG here */
|
||||||
err = tcpip_apimsg(&msg);
|
err = tcpip_apimsg(&msg);
|
||||||
@ -623,6 +626,31 @@ netconn_close(struct netconn *conn)
|
|||||||
return err;
|
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
|
#if LWIP_IGMP
|
||||||
/**
|
/**
|
||||||
* Join multicast groups for UDP netconns.
|
* Join multicast groups for UDP netconns.
|
||||||
|
@ -729,6 +729,7 @@ static void
|
|||||||
do_close_internal(struct netconn *conn)
|
do_close_internal(struct netconn *conn)
|
||||||
{
|
{
|
||||||
err_t err;
|
err_t err;
|
||||||
|
u8_t shut, shut_rx, shut_tx, close;
|
||||||
|
|
||||||
LWIP_ASSERT("invalid conn", (conn != NULL));
|
LWIP_ASSERT("invalid conn", (conn != NULL));
|
||||||
LWIP_ASSERT("this is for tcp netconns only", (conn->type == NETCONN_TCP));
|
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("pcb already closed", (conn->pcb.tcp != NULL));
|
||||||
LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != 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 */
|
/* 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) {
|
if (conn->pcb.tcp->state == LISTEN) {
|
||||||
tcp_accept(conn->pcb.tcp, NULL);
|
tcp_accept(conn->pcb.tcp, NULL);
|
||||||
} else {
|
} 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 */
|
/* some callbacks have to be reset if tcp_close is not successful */
|
||||||
tcp_sent(conn->pcb.tcp, NULL);
|
if (shut_rx) {
|
||||||
tcp_poll(conn->pcb.tcp, NULL, 4);
|
tcp_recv(conn->pcb.tcp, NULL);
|
||||||
tcp_err(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 */
|
/* 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) {
|
if (err == ERR_OK) {
|
||||||
/* Closing succeeded */
|
/* Closing succeeded */
|
||||||
conn->current_msg->err = ERR_OK;
|
conn->current_msg->err = ERR_OK;
|
||||||
@ -759,9 +778,15 @@ do_close_internal(struct netconn *conn)
|
|||||||
conn->pcb.tcp = NULL;
|
conn->pcb.tcp = NULL;
|
||||||
/* Trigger select() in socket layer. Make sure everybody notices activity
|
/* Trigger select() in socket layer. Make sure everybody notices activity
|
||||||
on the connection, error first! */
|
on the connection, error first! */
|
||||||
API_EVENT(conn, NETCONN_EVT_ERROR, 0);
|
if (close) {
|
||||||
API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
|
API_EVENT(conn, NETCONN_EVT_ERROR, 0);
|
||||||
API_EVENT(conn, NETCONN_EVT_SENDPLUS, 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 */
|
/* wake up the application task */
|
||||||
sys_sem_signal(&conn->op_completed);
|
sys_sem_signal(&conn->op_completed);
|
||||||
} else {
|
} else {
|
||||||
@ -772,6 +797,7 @@ do_close_internal(struct netconn *conn)
|
|||||||
tcp_poll(conn->pcb.tcp, poll_tcp, 4);
|
tcp_poll(conn->pcb.tcp, poll_tcp, 4);
|
||||||
tcp_err(conn->pcb.tcp, err_tcp);
|
tcp_err(conn->pcb.tcp, err_tcp);
|
||||||
tcp_arg(conn->pcb.tcp, conn);
|
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
|
/* If closing didn't succeed, we get called again either
|
||||||
from poll_tcp or from sent_tcp */
|
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);
|
LWIP_ASSERT("msg->conn->type == NETCONN_TCP", msg->conn->type == NETCONN_TCP);
|
||||||
msg->err = ERR_INPROGRESS;
|
msg->err = ERR_INPROGRESS;
|
||||||
} else if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) {
|
} else if ((msg->conn->pcb.tcp != NULL) && (msg->conn->type == NETCONN_TCP)) {
|
||||||
/* Drain and delete mboxes */
|
if ((msg->msg.sd.shut != NETCONN_SHUT_RDWR) && (msg->conn->state == NETCONN_LISTEN)) {
|
||||||
netconn_drain(msg->conn);
|
/* LISTEN doesn't support half shutdown */
|
||||||
LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
|
msg->err = ERR_CONN;
|
||||||
msg->conn->write_offset == 0);
|
} else {
|
||||||
msg->conn->state = NETCONN_CLOSE;
|
if (msg->msg.sd.shut & NETCONN_SHUT_RD) {
|
||||||
msg->conn->current_msg = msg;
|
/* Drain and delete mboxes */
|
||||||
do_close_internal(msg->conn);
|
netconn_drain(msg->conn);
|
||||||
/* for tcp netconns, do_close_internal ACKs the message */
|
}
|
||||||
return;
|
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
|
} else
|
||||||
#endif /* LWIP_TCP */
|
#endif /* LWIP_TCP */
|
||||||
{
|
{
|
||||||
|
@ -1344,9 +1344,42 @@ event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
|
|||||||
int
|
int
|
||||||
lwip_shutdown(int s, int how)
|
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));
|
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
|
static int
|
||||||
|
@ -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,
|
err_t netconn_write(struct netconn *conn, const void *dataptr, size_t size,
|
||||||
u8_t apiflags);
|
u8_t apiflags);
|
||||||
err_t netconn_close(struct netconn *conn);
|
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
|
#if LWIP_IGMP
|
||||||
err_t netconn_join_leave_group(struct netconn *conn, ip_addr_t *multiaddr,
|
err_t netconn_join_leave_group(struct netconn *conn, ip_addr_t *multiaddr,
|
||||||
|
@ -48,6 +48,10 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#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
|
/* IP addresses and port numbers are expected to be in
|
||||||
* the same byte order as in the corresponding pcb.
|
* the same byte order as in the corresponding pcb.
|
||||||
*/
|
*/
|
||||||
@ -89,6 +93,10 @@ struct api_msg_msg {
|
|||||||
struct {
|
struct {
|
||||||
u32_t len;
|
u32_t len;
|
||||||
} r;
|
} r;
|
||||||
|
/** used for do_close (/shutdown) */
|
||||||
|
struct {
|
||||||
|
u8_t shut;
|
||||||
|
} sd;
|
||||||
#if LWIP_IGMP
|
#if LWIP_IGMP
|
||||||
/** used for do_join_leave_group */
|
/** used for do_join_leave_group */
|
||||||
struct {
|
struct {
|
||||||
@ -144,6 +152,7 @@ void do_recv ( struct api_msg_msg *msg);
|
|||||||
void do_write ( struct api_msg_msg *msg);
|
void do_write ( struct api_msg_msg *msg);
|
||||||
void do_getaddr ( struct api_msg_msg *msg);
|
void do_getaddr ( struct api_msg_msg *msg);
|
||||||
void do_close ( struct api_msg_msg *msg);
|
void do_close ( struct api_msg_msg *msg);
|
||||||
|
void do_shutdown ( struct api_msg_msg *msg);
|
||||||
#if LWIP_IGMP
|
#if LWIP_IGMP
|
||||||
void do_join_leave_group( struct api_msg_msg *msg);
|
void do_join_leave_group( struct api_msg_msg *msg);
|
||||||
#endif /* LWIP_IGMP */
|
#endif /* LWIP_IGMP */
|
||||||
|
@ -279,6 +279,12 @@ typedef struct ip_mreq {
|
|||||||
#define O_NDELAY 1 /* same as O_NONBLOCK, for compatibility */
|
#define O_NDELAY 1 /* same as O_NONBLOCK, for compatibility */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef SHUT_RD
|
||||||
|
#define SHUT_RD 1
|
||||||
|
#define SHUT_WR 2
|
||||||
|
#define SHUT_RDWR 3
|
||||||
|
#endif
|
||||||
|
|
||||||
/* FD_SET used for lwip_select */
|
/* FD_SET used for lwip_select */
|
||||||
#ifndef FD_SET
|
#ifndef FD_SET
|
||||||
#undef FD_SETSIZE
|
#undef FD_SETSIZE
|
||||||
|
Loading…
Reference in New Issue
Block a user