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

pseudotcp: Add a PseudoTcpSocket:support-fin-ack property

This allows FIN–ACK support to be disabled entirely. This is mostly for
testing purposes, since TCP_OPT_FIN_ACK is negotiated when establishing
the connection, and is disabled if the other side doesn’t support it.

This includes an interoperability test.
parent 111301a0
......@@ -529,6 +529,7 @@ enum
PROP_NO_DELAY,
PROP_RCV_BUF,
PROP_SND_BUF,
PROP_SUPPORT_FIN_ACK,
LAST_PROPERTY
};
......@@ -644,6 +645,26 @@ pseudo_tcp_socket_class_init (PseudoTcpSocketClass *cls)
"Send Buffer size",
1, G_MAXUINT, DEFAULT_SND_BUF_SIZE,
G_PARAM_READWRITE| G_PARAM_STATIC_STRINGS));
/**
* PseudoTcpSocket:support-fin-ack:
*
* Whether to support the FIN–ACK extension to the pseudo-TCP protocol for
* this socket. The extension is only compatible with other libnice pseudo-TCP
* stacks, and not with Jingle pseudo-TCP stacks. If enabled, support is
* negotiatied on connection setup, so it is safe for a #PseudoTcpSocket with
* support enabled to be used with one with it disabled, or with a Jingle
* pseudo-TCP socket which doesn’t support it at all.
*
* Support is enabled by default.
*
* Since: UNRELEASED
*/
g_object_class_install_property (object_class, PROP_SUPPORT_FIN_ACK,
g_param_spec_boolean ("support-fin-ack", "Support FIN–ACK",
"Whether to enable the optional FIN–ACK support.",
TRUE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
}
......@@ -677,6 +698,9 @@ pseudo_tcp_socket_get_property (GObject *object,
case PROP_SND_BUF:
g_value_set_uint (value, self->priv->sbuf_len);
break;
case PROP_SUPPORT_FIN_ACK:
g_value_set_boolean (value, self->priv->support_fin_ack);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
......@@ -715,6 +739,9 @@ pseudo_tcp_socket_set_property (GObject *object,
g_return_if_fail (self->priv->state == TCP_LISTEN);
resize_send_buffer (self, g_value_get_uint (value));
break;
case PROP_SUPPORT_FIN_ACK:
self->priv->support_fin_ack = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
......
......@@ -193,14 +193,22 @@ write_packet (PseudoTcpSocket *sock, const gchar *buffer, guint32 len,
static void
create_sockets (Data *data)
create_sockets (Data *data, gboolean support_fin_ack)
{
PseudoTcpCallbacks cbs = {
data, opened, readable, writable, closed, write_packet
};
data->left = pseudo_tcp_socket_new (0, &cbs);
data->right = pseudo_tcp_socket_new (0, &cbs);
data->left = g_object_new (PSEUDO_TCP_SOCKET_TYPE,
"conversation", 0,
"callbacks", &cbs,
"support-fin-ack", support_fin_ack,
NULL);
data->right = g_object_new (PSEUDO_TCP_SOCKET_TYPE,
"conversation", 0,
"callbacks", &cbs,
"support-fin-ack", support_fin_ack,
NULL);
g_debug ("Left: %p, right: %p", data->left, data->right);
......@@ -435,7 +443,7 @@ close_socket (PseudoTcpSocket *socket)
static void
establish_connection (Data *data)
{
create_sockets (data);
create_sockets (data, TRUE);
pseudo_tcp_socket_connect (data->left);
expect_syn_sent (data);
forward_segment_ltr (data);
......@@ -992,6 +1000,65 @@ pseudotcp_close_rst_afterwards (void)
data_clear (&data);
}
/* Check that two pseudo-TCP sockets interact correctly even if FIN–ACK support
* is disabled on one of them. */
static void
pseudotcp_compatibility (void)
{
Data data = { 0, };
guint8 buf[100];
guint64 timeout;
/* Establish a connection. Note the sequence numbers should start at 4 this
* time, rather than the 7 in other tests, because the FIN–ACK option should
* not be being sent. */
create_sockets (&data, FALSE);
pseudo_tcp_socket_connect (data.left);
expect_segment (data.left, data.left_sent, 0, 0, 4, FLAG_SYN);
forward_segment_ltr (&data);
expect_segment (data.right, data.right_sent, 0, 4, 4, FLAG_SYN);
forward_segment_rtl (&data);
increment_time_both (&data, 110);
expect_ack (data.left, data.left_sent, 4, 4);
forward_segment_ltr (&data);
expect_sockets_connected (&data);
/* Close it. Sending shouldn’t fail. */
pseudo_tcp_socket_close (data.left, FALSE);
g_assert (!pseudo_tcp_socket_is_closed (data.left));
g_assert_cmpint (pseudo_tcp_socket_send (data.left, "foo", 3), ==, 3);
g_assert_cmpint (pseudo_tcp_socket_recv (data.left, (char *) buf, sizeof (buf)), ==, -1);
g_assert_cmpint (pseudo_tcp_socket_get_error (data.left), ==, EWOULDBLOCK);
expect_data (data.left, data.left_sent, 4, 4, 3);
forward_segment_ltr (&data);
increment_time_both (&data, 100);
expect_ack (data.right, data.right_sent, 4, 7);
forward_segment_rtl (&data);
/* Advance the timers; now the LHS should be closed, as the RHS has ACKed all
* outstanding data. */
increment_time_both (&data, 50);
g_assert (!pseudo_tcp_socket_get_next_clock (data.left, &timeout));
/* Check the RHS can be closed after receiving the data just sent. */
g_assert_cmpint (pseudo_tcp_socket_recv (data.right, (char *) buf, sizeof (buf)), ==, 3);
g_assert_cmpint (pseudo_tcp_socket_recv (data.right, (char *) buf, sizeof (buf)), ==, -1);
g_assert_cmpint (pseudo_tcp_socket_get_error (data.right), ==, EWOULDBLOCK);
pseudo_tcp_socket_close (data.right, FALSE);
g_assert (!pseudo_tcp_socket_get_next_clock (data.right, &timeout));
expect_sockets_closed (&data);
data_clear (&data);
}
int
main (int argc, char *argv[])
{
......@@ -1058,6 +1125,9 @@ main (int argc, char *argv[])
g_test_add_func ("/pseudotcp/close/rst-afterwards",
pseudotcp_close_rst_afterwards);
g_test_add_func ("/pseudotcp/compatibility",
pseudotcp_compatibility);
g_test_run ();
return 0;
......
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