Commit f3440d33 authored by Philip Withnall's avatar Philip Withnall Committed by Olivier Crête

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.
parent 78ec8485
...@@ -457,6 +457,7 @@ struct _PseudoTcpSocketPrivate { ...@@ -457,6 +457,7 @@ struct _PseudoTcpSocketPrivate {
PseudoTcpCallbacks callbacks; PseudoTcpCallbacks callbacks;
Shutdown shutdown; /* only used if !support_fin_ack */ Shutdown shutdown; /* only used if !support_fin_ack */
gboolean shutdown_reads;
gint error; gint error;
// TCB data // TCB data
...@@ -1116,7 +1117,8 @@ pseudo_tcp_socket_recv(PseudoTcpSocket *self, char * buffer, size_t len) ...@@ -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. */ /* Received a FIN from the peer, so return 0. RFC 793, §3.5, Case 2. */
if (priv->support_fin_ack && 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; return 0;
} }
...@@ -1191,22 +1193,51 @@ pseudo_tcp_socket_close(PseudoTcpSocket *self, gboolean force) ...@@ -1191,22 +1193,51 @@ pseudo_tcp_socket_close(PseudoTcpSocket *self, gboolean force)
{ {
PseudoTcpSocketPrivate *priv = self->priv; 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"); 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. */ /* FIN-ACK--only stuff below here. */
if (!priv->support_fin_ack) { if (!priv->support_fin_ack) {
priv->shutdown = force ? SD_FORCEFUL : SD_GRACEFUL; priv->shutdown = SD_GRACEFUL;
return; return;
} }
/* Forced closure by sending an RST segment. RFC 1122, §4.2.2.13. */ /* What needs shutting down? */
if (force) { switch (how) {
closedown (self, ECONNABORTED, CLOSEDOWN_LOCAL); 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; return;
} }
/* Unforced closure. */ /* Unforced write closure. */
switch (priv->state) { switch (priv->state) {
case TCP_LISTEN: case TCP_LISTEN:
case TCP_SYN_SENT: case TCP_SYN_SENT:
......
...@@ -189,6 +189,24 @@ typedef enum { ...@@ -189,6 +189,24 @@ typedef enum {
WR_FAIL WR_FAIL
} PseudoTcpWriteResult; } 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: * PseudoTcpCallbacks:
* @user_data: A user defined pointer to be passed to the callbacks * @user_data: A user defined pointer to be passed to the callbacks
...@@ -314,12 +332,16 @@ gint pseudo_tcp_socket_send(PseudoTcpSocket *self, const char * buffer, ...@@ -314,12 +332,16 @@ gint pseudo_tcp_socket_send(PseudoTcpSocket *self, const char * buffer,
* @self: The #PseudoTcpSocket object. * @self: The #PseudoTcpSocket object.
* @force: %TRUE to close the socket forcefully, %FALSE to close it gracefully * @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 * Close the socket for sending. If @force is set to %FALSE, the socket will
* finish sending pending data before closing. * 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 * The socket will be closed in both directions – sending and receiving – and
* of the connection — until that point, pseudo_tcp_socket_recv() can still be * any pending received data must be read before calling this function, by
* called to receive data from the peer. * 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.
* *
<note> <note>
<para> <para>
...@@ -336,6 +358,24 @@ gint pseudo_tcp_socket_send(PseudoTcpSocket *self, const char * buffer, ...@@ -336,6 +358,24 @@ gint pseudo_tcp_socket_send(PseudoTcpSocket *self, const char * buffer,
*/ */
void pseudo_tcp_socket_close(PseudoTcpSocket *self, gboolean force); 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: * pseudo_tcp_socket_get_error:
......
...@@ -286,11 +286,13 @@ PseudoTcpState ...@@ -286,11 +286,13 @@ PseudoTcpState
PseudoTcpWriteResult PseudoTcpWriteResult
PseudoTcpCallbacks PseudoTcpCallbacks
PseudoTcpDebugLevel PseudoTcpDebugLevel
PseudoTcpShutdown
pseudo_tcp_socket_new pseudo_tcp_socket_new
pseudo_tcp_socket_connect pseudo_tcp_socket_connect
pseudo_tcp_socket_recv pseudo_tcp_socket_recv
pseudo_tcp_socket_send pseudo_tcp_socket_send
pseudo_tcp_socket_close pseudo_tcp_socket_close
pseudo_tcp_socket_shutdown
pseudo_tcp_socket_is_closed pseudo_tcp_socket_is_closed
pseudo_tcp_socket_is_closed_remotely pseudo_tcp_socket_is_closed_remotely
pseudo_tcp_socket_get_error pseudo_tcp_socket_get_error
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment