From f3440d331602cff99437781c6eb18b2703778aeb Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 8 Aug 2014 11:07:17 +0100 Subject: [PATCH] pseudotcp: Add pseudo_tcp_socket_shutdown() support This is analogous to the UNIX shutdown() function, allowing either or both sides of a pseudo-TCP connection to be shut down. --- agent/pseudotcp.c | 45 ++++++++++++++++++++++---- agent/pseudotcp.h | 50 ++++++++++++++++++++++++++--- docs/reference/libnice/libnice-sections.txt | 2 ++ 3 files changed, 85 insertions(+), 12 deletions(-) diff --git a/agent/pseudotcp.c b/agent/pseudotcp.c index d3a124d..a786f36 100644 --- a/agent/pseudotcp.c +++ b/agent/pseudotcp.c @@ -457,6 +457,7 @@ struct _PseudoTcpSocketPrivate { PseudoTcpCallbacks callbacks; Shutdown shutdown; /* only used if !support_fin_ack */ + gboolean shutdown_reads; gint error; // TCB data @@ -1116,7 +1117,8 @@ pseudo_tcp_socket_recv(PseudoTcpSocket *self, char * buffer, size_t len) /* Received a FIN from the peer, so return 0. RFC 793, §3.5, Case 2. */ if (priv->support_fin_ack && - pseudo_tcp_state_has_received_fin (priv->state)) { + (priv->shutdown_reads || + pseudo_tcp_state_has_received_fin (priv->state))) { return 0; } @@ -1191,22 +1193,51 @@ pseudo_tcp_socket_close(PseudoTcpSocket *self, gboolean force) { PseudoTcpSocketPrivate *priv = self->priv; - DEBUG (PSEUDO_TCP_DEBUG_VERBOSE, "Closing socket %p : %s", self, + DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Closing socket %p %s", self, force ? "forcefully" : "gracefully"); + /* Forced closure by sending an RST segment. RFC 1122, §4.2.2.13. */ + if (force && priv->state != TCP_CLOSED) { + closedown (self, ECONNABORTED, CLOSEDOWN_LOCAL); + return; + } + + /* Fall back to shutdown(). */ + pseudo_tcp_socket_shutdown (self, PSEUDO_TCP_SHUTDOWN_RDWR); +} + +void +pseudo_tcp_socket_shutdown (PseudoTcpSocket *self, PseudoTcpShutdown how) +{ + PseudoTcpSocketPrivate *priv = self->priv; + + DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Shutting down socket %p: %u", self, how); + /* FIN-ACK--only stuff below here. */ if (!priv->support_fin_ack) { - priv->shutdown = force ? SD_FORCEFUL : SD_GRACEFUL; + priv->shutdown = SD_GRACEFUL; return; } - /* Forced closure by sending an RST segment. RFC 1122, §4.2.2.13. */ - if (force) { - closedown (self, ECONNABORTED, CLOSEDOWN_LOCAL); + /* What needs shutting down? */ + switch (how) { + case PSEUDO_TCP_SHUTDOWN_RD: + case PSEUDO_TCP_SHUTDOWN_RDWR: + priv->shutdown_reads = TRUE; + break; + case PSEUDO_TCP_SHUTDOWN_WR: + /* Handled below. */ + break; + default: + DEBUG (PSEUDO_TCP_DEBUG_NORMAL, "Invalid shutdown method: %u.", how); + break; + } + + if (how == PSEUDO_TCP_SHUTDOWN_RD) { return; } - /* Unforced closure. */ + /* Unforced write closure. */ switch (priv->state) { case TCP_LISTEN: case TCP_SYN_SENT: diff --git a/agent/pseudotcp.h b/agent/pseudotcp.h index 3ab6fc4..71aa2ef 100644 --- a/agent/pseudotcp.h +++ b/agent/pseudotcp.h @@ -190,6 +190,24 @@ typedef enum { } PseudoTcpWriteResult; /** + * PseudoTcpShutdown: + * @PSEUDO_TCP_SHUTDOWN_RD: Shut down the local reader only + * @PSEUDO_TCP_SHUTDOWN_WR: Shut down the local writer only + * @PSEUDO_TCP_SHUTDOWN_RDWR: Shut down both reading and writing + * + * Options for which parts of a connection to shut down when calling + * pseudo_tcp_socket_shutdown(). These correspond to the values passed to POSIX + * shutdown(). + * + * Since: UNRELEASED + */ +typedef enum { + PSEUDO_TCP_SHUTDOWN_RD, + PSEUDO_TCP_SHUTDOWN_WR, + PSEUDO_TCP_SHUTDOWN_RDWR, +} PseudoTcpShutdown; + +/** * PseudoTcpCallbacks: * @user_data: A user defined pointer to be passed to the callbacks * @PseudoTcpOpened: The #PseudoTcpSocket is now connected @@ -314,12 +332,16 @@ gint pseudo_tcp_socket_send(PseudoTcpSocket *self, const char * buffer, * @self: The #PseudoTcpSocket object. * @force: %TRUE to close the socket forcefully, %FALSE to close it gracefully * - * Close the socket for sending. IF @force is set to %FALSE, the socket will - * finish sending pending data before closing. + * Close the socket for sending. If @force is set to %FALSE, the socket will + * finish sending pending data before closing. If it is set to %TRUE, the socket + * will discard pending data and close the connection immediately (sending a TCP + * RST segment). * - * The socket will only be fully closed once the peer has also closed their end - * of the connection — until that point, pseudo_tcp_socket_recv() can still be - * called to receive data from the peer. + * The socket will be closed in both directions – sending and receiving – and + * any pending received data must be read before calling this function, by + * calling pseudo_tcp_socket_recv() until it blocks. If any pending data is in + * the receive buffer when pseudo_tcp_socket_close() is called, a TCP RST + * segment will be sent to the peer to notify it of the data loss. * @@ -336,6 +358,24 @@ gint pseudo_tcp_socket_send(PseudoTcpSocket *self, const char * buffer, */ void pseudo_tcp_socket_close(PseudoTcpSocket *self, gboolean force); +/** + * pseudo_tcp_socket_shutdown: + * @self: The #PseudoTcpSocket object. + * @how: The directions of the connection to shut down. + * + * Shut down sending, receiving, or both on the socket, depending on the value + * of @how. The behaviour of pseudo_tcp_socket_send() and + * pseudo_tcp_socket_recv() will immediately change after this function returns + * (depending on the value of @how), though the socket may continue to process + * network traffic in the background even if sending or receiving data is + * forbidden. + * + * This is equivalent to the POSIX shutdown() function. Setting @how to + * %PSEUDO_TCP_SHUTDOWN_RDWR is equivalent to calling pseudo_tcp_socket_close(). + * + * Since: UNRELEASED + */ +void pseudo_tcp_socket_shutdown (PseudoTcpSocket *self, PseudoTcpShutdown how); /** * pseudo_tcp_socket_get_error: diff --git a/docs/reference/libnice/libnice-sections.txt b/docs/reference/libnice/libnice-sections.txt index c755fbd..dd185f0 100644 --- a/docs/reference/libnice/libnice-sections.txt +++ b/docs/reference/libnice/libnice-sections.txt @@ -286,11 +286,13 @@ PseudoTcpState PseudoTcpWriteResult PseudoTcpCallbacks PseudoTcpDebugLevel +PseudoTcpShutdown pseudo_tcp_socket_new pseudo_tcp_socket_connect pseudo_tcp_socket_recv pseudo_tcp_socket_send pseudo_tcp_socket_close +pseudo_tcp_socket_shutdown pseudo_tcp_socket_is_closed pseudo_tcp_socket_is_closed_remotely pseudo_tcp_socket_get_error -- 2.7.4