[PATCH 7/7] TFTPFS: Add more tests

Frank Kuehndel frank.kuehndel at embedded-brains.de
Fri Jun 3 15:22:46 UTC 2022


From: Frank Kühndel <frank.kuehndel at embedded-brains.de>

---
 testsuites/fstests/tftpfs/init.c | 3397 ++++++++++++++++++++++++++++++
 1 file changed, 3397 insertions(+)

diff --git a/testsuites/fstests/tftpfs/init.c b/testsuites/fstests/tftpfs/init.c
index a7ef03cf74..0c6df5fa5c 100644
--- a/testsuites/fstests/tftpfs/init.c
+++ b/testsuites/fstests/tftpfs/init.c
@@ -171,6 +171,47 @@ static const T_fixture fixture_default_options = {
   .initial_context = &tftp_context
 };
 
+static void setup_large_blocksize( void *context )
+{
+  tftp_test_context *ctx = context;
+  _Tftp_Reset();
+  ctx->fd0 = -1;
+  ctx->tftp_handle = NULL;
+  mount_tftp_fs(
+    tftpfs_mount_point,
+    "verbose,blocksize=" RTEMS_XSTRING(LARGE_BLOCK_SIZE) ",windowsize=1"
+  );
+}
+
+static const T_fixture fixture_large_blocksize = {
+  .setup = setup_large_blocksize,
+  .stop = NULL,
+  .teardown = teardown,
+  .scope = NULL,
+  .initial_context = &tftp_context
+};
+
+static void setup_small_opt_size( void *context )
+{
+  tftp_test_context *ctx = context;
+  _Tftp_Reset();
+  ctx->fd0 = -1;
+  ctx->tftp_handle = NULL;
+  mount_tftp_fs(
+    tftpfs_mount_point,
+    "blocksize=" RTEMS_XSTRING(SMALL_BLOCK_SIZE)
+    ",windowsize=" RTEMS_XSTRING(SMALL_WINDOW_SIZE)
+  );
+}
+
+static const T_fixture fixture_small_opt_size = {
+  .setup = setup_small_opt_size,
+  .stop = NULL,
+  .teardown = teardown,
+  .scope = NULL,
+  .initial_context = &tftp_context
+};
+
 static void setup_mount_point( void *context )
 {
   int result;
@@ -240,6 +281,15 @@ static uint8_t get_file_content( size_t pos )
   }
 }
 
+/*
+ * Produce bad file content.
+ */
+static uint8_t get_bad_file_content( size_t pos )
+{
+  static const char buf[] = "BAD!";
+  return (uint8_t) buf[ pos % strlen( buf ) ];
+}
+
 static const char *create_tftpfs_path(
   const char *sever_addr,
   const char *file_name
@@ -3836,6 +3886,3353 @@ T_TEST_CASE_FIXTURE( write_simple_file, &fixture_rfc1350 )
 }
 #endif /* ENABLE_ALL_TESTS */
 
+#if ENABLE_ALL_TESTS
+/*
+ * Write a file to the server using only RFC1350.
+ * As response to the first DATA packet, the server sends an error packet.
+ * Tests:
+ *   * The code supports requests without options (RFC1350 only).
+ *   * The code supports the use of an IPv4 address instead of a server name.
+ *   * The first packet is sent to standard port 69 of server.
+ *   * All other packets are sent to the port used for this connection.
+ *   * The first data packet is full.
+ *   * The second packet from the server is an error packet.
+ *   * The TFTP client ends the connection after receiving an error packet.
+ *   * The test writes a file to the file system with a call to write()
+ *     for each byte.
+ */
+T_TEST_CASE_FIXTURE( write_simple_file_disk_full, &fixture_rfc1350 )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_written;
+  uint16_t block_num = 0;
+  size_t pos_in_file = 0;
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_wrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    NO_BLOCK_SIZE_OPTION,
+    NO_WINDOW_SIZE_OPTION,
+    true
+  );
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_send_data(
+    TFTP_FIRST_FD,
+    block_num,
+    pos_in_file,
+    TFTP_RFC1350_BLOCK_SIZE,
+    get_file_content,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  pos_in_file += TFTP_RFC1350_BLOCK_SIZE;
+  _Tftp_Add_interaction_recv_error(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_ERROR_CODE_DISK_FULL,
+    "disk full",
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_written = write_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    pos_in_file, /* Size of file */
+    1, /* Bytes written per call to write() */
+    &ctx->fd0
+  );
+  T_eq_int( errno, ENOSPC );
+  T_eq_int( bytes_written, pos_in_file - 1 );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Write a file to the server using only RFC1350.
+ * The file is one and a half data packet long.
+ * The server sends a malformed packet.
+ * Tests:
+ *   * The code supports requests without options (RFC1350 only).
+ *   * The code supports the use of an IPv4 address instead of a server name.
+ *   * The first packet is sent to standard port 69 of server.
+ *   * All other packets are sent to the port used for this connection.
+ *   * The first ACK to the WRQ packet is malformed.
+ *     It is only one byte long.
+ *   * The client sends an error upon the reception of a malformed packet
+ *     and terminates the file transfer.
+ */
+T_TEST_CASE_FIXTURE( write_file_malformed_ack_1, &fixture_rfc1350 )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_written;
+  size_t pos_in_file = 0;
+  static const uint8_t packet_too_short_1[] = { 0x04 };
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_wrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    NO_BLOCK_SIZE_OPTION,
+    NO_WINDOW_SIZE_OPTION,
+    true
+  );
+  _Tftp_Add_interaction_recv_raw(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    sizeof( packet_too_short_1 ), /* Malformed ACK packet */
+    packet_too_short_1,
+    true
+  );
+  _Tftp_Add_interaction_send_error(
+    TFTP_FIRST_FD,
+    TFTP_ERROR_CODE_ILLEGAL,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_written = write_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    pos_in_file, /* Size of file */
+    17, /* Bytes written per call to write() */
+    &ctx->fd0
+  );
+  T_eq_int( bytes_written, -1 );
+  T_eq_int( errno, EPROTO );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Write a file to the server using only RFC1350.
+ * The file is one and a half data packet long.
+ * The server sends a malformed packet.
+ * Tests:
+ *   * The code supports requests without options (RFC1350 only).
+ *   * The code supports the use of an IPv4 address instead of a server name.
+ *   * The first packet is sent to standard port 69 of server.
+ *   * All other packets are sent to the port used for this connection.
+ *   * The first ACK to the WRQ packet is malformed.
+ *     It is only two bytes long.
+ *   * The client sends an error upon the reception of a malformed packet
+ *     and terminates the file transfer.
+ */
+T_TEST_CASE_FIXTURE( write_file_malformed_ack_2, &fixture_rfc1350 )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_written;
+  size_t pos_in_file = 0;
+  static const uint8_t packet_too_short_2[] = { 0x00, 0x04 };
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_wrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    NO_BLOCK_SIZE_OPTION,
+    NO_WINDOW_SIZE_OPTION,
+    true
+  );
+  _Tftp_Add_interaction_recv_raw(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    sizeof( packet_too_short_2 ), /* Malformed ACK packet */
+    packet_too_short_2,
+    true
+  );
+  _Tftp_Add_interaction_send_error(
+    TFTP_FIRST_FD,
+    TFTP_ERROR_CODE_ILLEGAL,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_written = write_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    pos_in_file, /* Size of file */
+    17, /* Bytes written per call to write() */
+    &ctx->fd0
+  );
+  T_eq_int( bytes_written, -1 );
+  T_eq_int( errno, EPROTO );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Write a file to the server using only RFC1350.
+ * The file is one and a half data packet long.
+ * The server sends a malformed packet.
+ * Tests:
+ *   * The code supports requests without options (RFC1350 only).
+ *   * The code supports the use of an IPv4 address instead of a server name.
+ *   * The first packet is sent to standard port 69 of server.
+ *   * All other packets are sent to the port used for this connection.
+ *   * The first ACK to the WRQ packet is malformed.
+ *     It is only three bytes long.
+ *   * The client sends an error upon the reception of a malformed packet
+ *     and terminates the file transfer.
+ */
+T_TEST_CASE_FIXTURE( write_file_malformed_ack_3, &fixture_rfc1350 )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_written;
+  size_t pos_in_file = 0;
+  static const uint8_t packet_too_short_3[] = { 0x00, 0x04, 0x00 };
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_wrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    NO_BLOCK_SIZE_OPTION,
+    NO_WINDOW_SIZE_OPTION,
+    true
+  );
+  _Tftp_Add_interaction_recv_raw(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    sizeof( packet_too_short_3 ), /* Malformed ACK packet */
+    packet_too_short_3,
+    true
+  );
+  _Tftp_Add_interaction_send_error(
+    TFTP_FIRST_FD,
+    TFTP_ERROR_CODE_ILLEGAL,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_written = write_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    pos_in_file, /* Size of file */
+    17, /* Bytes written per call to write() */
+    &ctx->fd0
+  );
+  T_eq_int( bytes_written, -1 );
+  T_eq_int( errno, EPROTO );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Write a file to the server using only RFC1350.
+ * The file is one and a half data packet long.
+ * The server sends a malformed packet.
+ * Tests:
+ *   * The code supports requests without options (RFC1350 only).
+ *   * The code supports the use of an IPv4 address instead of a server name.
+ *   * The first packet is sent to standard port 69 of server.
+ *   * All other packets are sent to the port used for this connection.
+ *   * The first ACK to the WRQ packet is malformed.
+ *     The packet contains an illegal op code.
+ *   * The client sends an error upon the reception of a malformed packet
+ *     and terminates the file transfer.
+ */
+T_TEST_CASE_FIXTURE( write_file_illegal_opcode_1, &fixture_rfc1350 )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_written;
+  size_t pos_in_file = 0;
+  static const uint8_t packet_illegal_opcode_1[] = { 0x00, 0xFF, 0x00, 0x00 };
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_wrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    NO_BLOCK_SIZE_OPTION,
+    NO_WINDOW_SIZE_OPTION,
+    true
+  );
+  _Tftp_Add_interaction_recv_raw(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    sizeof( packet_illegal_opcode_1 ), /* Malformed ACK packet */
+    packet_illegal_opcode_1,
+    true
+  );
+  _Tftp_Add_interaction_send_error(
+    TFTP_FIRST_FD,
+    TFTP_ERROR_CODE_ILLEGAL,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_written = write_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    pos_in_file, /* Size of file */
+    17, /* Bytes written per call to write() */
+    &ctx->fd0
+  );
+  T_eq_int( bytes_written, -1 );
+  T_eq_int( errno, EPROTO );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Write a file to the server using only RFC1350.
+ * The server sends a malformed packet.
+ * Tests:
+ *   * The code supports requests without options (RFC1350 only).
+ *   * The code supports the use of an IPv4 address instead of a server name.
+ *   * The first packet is sent to standard port 69 of server.
+ *   * All other packets are sent to the port used for this connection.
+ *   * A malformed ACK packet is received by the TFTP client.
+ *     The packet is only three bytes long.
+ *   * The client sends an error upon the reception of a malformed packet
+ *     and terminates the file transfer.
+ */
+T_TEST_CASE_FIXTURE( write_short_file_malformed_ACK_1, &fixture_rfc1350 )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_written;
+  uint16_t block_num = 0;
+  size_t pos_in_file = 0;
+  static const uint8_t packet_too_short_3[] = { 0x00, 0x04, 0x00 };
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_wrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    NO_BLOCK_SIZE_OPTION,
+    NO_WINDOW_SIZE_OPTION,
+    true
+  );
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_send_data(
+    TFTP_FIRST_FD,
+    block_num,
+    pos_in_file,
+    TFTP_RFC1350_BLOCK_SIZE,
+    get_file_content,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_recv_raw(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    sizeof( packet_too_short_3 ), /* Malformed ACK packet */
+    packet_too_short_3,
+    true
+  );
+  _Tftp_Add_interaction_send_error(
+    TFTP_FIRST_FD,
+    TFTP_ERROR_CODE_ILLEGAL,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_written = write_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    2 * TFTP_RFC1350_BLOCK_SIZE, /* Size of file */
+    17, /* Bytes written per call to write() */
+    &ctx->fd0
+  );
+  T_eq_int( errno, EPROTO );
+  T_eq_int( bytes_written, 510 );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Write a file to the server using only RFC1350.
+ * The server sends a malformed packet.
+ * Tests:
+ *   * The code supports requests without options (RFC1350 only).
+ *   * The code supports the use of an IPv4 address instead of a server name.
+ *   * The first packet is sent to standard port 69 of server.
+ *   * All other packets are sent to the port used for this connection.
+ *   * A malformed ACK packet is received by the TFTP client after the first
+ *     DATA packet has been exchanged successfully.
+ *     The packet is only one byte long.
+ *   * The client sends an error upon the reception of a malformed packet
+ *     and terminates the file transfer.
+ */
+T_TEST_CASE_FIXTURE( write_short_file_malformed_ACK_2, &fixture_rfc1350 )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_written;
+  uint16_t block_num = 0;
+  size_t pos_in_file = 0;
+  static const uint8_t packet_too_short_1[] = { 0x04 };
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_wrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    NO_BLOCK_SIZE_OPTION,
+    NO_WINDOW_SIZE_OPTION,
+    true
+  );
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_send_data(
+    TFTP_FIRST_FD,
+    block_num,
+    pos_in_file,
+    TFTP_RFC1350_BLOCK_SIZE,
+    get_file_content,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  pos_in_file += TFTP_RFC1350_BLOCK_SIZE;
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_send_data(
+    TFTP_FIRST_FD,
+    block_num,
+    pos_in_file,
+    TFTP_RFC1350_BLOCK_SIZE / 4,
+    get_file_content,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  pos_in_file += TFTP_RFC1350_BLOCK_SIZE / 4;
+  _Tftp_Add_interaction_recv_raw(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    sizeof( packet_too_short_1 ), /* Malformed ACK packet */
+    packet_too_short_1,
+    true
+  );
+  _Tftp_Add_interaction_send_error(
+    TFTP_FIRST_FD,
+    TFTP_ERROR_CODE_ILLEGAL,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_written = write_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    pos_in_file, /* Size of file */
+    17, /* Bytes written per call to write() */
+    &ctx->fd0
+  );
+  T_eq_int( errno, EPROTO );
+  T_eq_int( bytes_written, -1 );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Write a file to the server using only RFC1350.
+ * The server sends a malformed packet.
+ * Tests:
+ *   * The code supports requests without options (RFC1350 only).
+ *   * The code supports the use of an IPv4 address instead of a server name.
+ *   * The first packet is sent to standard port 69 of server.
+ *   * All other packets are sent to the port used for this connection.
+ *   * A malformed ACK packet is received by the TFTP client after the first
+ *     DATA packet has been exchanged successfully.
+ *     The packet contains an illegal op code.
+ *   * The client sends an error upon the reception of a malformed packet
+ *     and terminates the file transfer.
+ */
+T_TEST_CASE_FIXTURE( write_short_file_malformed_opcode, &fixture_rfc1350 )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_written;
+  uint16_t block_num = 0;
+  size_t pos_in_file = 0;
+  static const uint8_t packet_illegal_opcode_2[] = { 0x04, 0x00, 0x00, 0x01 };
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_wrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    NO_BLOCK_SIZE_OPTION,
+    NO_WINDOW_SIZE_OPTION,
+    true
+  );
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_send_data(
+    TFTP_FIRST_FD,
+    block_num,
+    pos_in_file,
+    TFTP_RFC1350_BLOCK_SIZE,
+    get_file_content,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_recv_raw(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    sizeof( packet_illegal_opcode_2 ), /* Malformed ACK packet */
+    packet_illegal_opcode_2,
+    true
+  );
+  _Tftp_Add_interaction_send_error(
+    TFTP_FIRST_FD,
+    TFTP_ERROR_CODE_ILLEGAL,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_written = write_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    2 * TFTP_RFC1350_BLOCK_SIZE, /* Size of file */
+    17, /* Bytes written per call to write() */
+    &ctx->fd0
+  );
+  T_eq_int( errno, EPROTO );
+  T_eq_int( bytes_written, 510 );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Write a file to the server using only RFC1350.
+ * The file is two and a half data packet long.
+ * The server sends packets with wrong block numbers.
+ * Tests:
+ *   * The code supports requests without options (RFC1350 only).
+ *   * The code supports the use of an IPv4 address instead of a server name.
+ *   * The first packet is sent to standard port 69 of server.
+ *   * All other packets are sent to the port used for this connection.
+ *   * The first ACK packet contains a wrong block number.
+ *   * The client repeats the WRQ upon reception of an ACK with
+ *     an too high block number.
+ *   * The client uses a short time out for waiting on the answer of the
+ *     first WRQ or DATA packets.
+ *   * The client uses a long time out for waiting on the answer of
+ *     repeated WRQ or DATA packets.
+ *   * The first DATA packet is full.
+ *   * The second DATA packet signals the end of transfer.
+ *   * The client handles DATA packets with wrong block numbers
+ *     appropriately.
+ *   * The test writes a file to the file system with calls to write() of
+ *     exactly block size.
+ */
+T_TEST_CASE_FIXTURE( write_short_file_bad_block_numbers, &fixture_rfc1350 )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_written;
+  uint16_t block_num = 0;
+  size_t pos_in_file = 0;
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_wrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    NO_BLOCK_SIZE_OPTION,
+    NO_WINDOW_SIZE_OPTION,
+    true
+  );
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num + 1, /* Wrong block number */
+    true
+  );
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_send_data(
+    TFTP_FIRST_FD,
+    block_num,
+    pos_in_file,
+    TFTP_RFC1350_BLOCK_SIZE,
+    get_file_content,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num - 1, /* Wrong block number */
+    true
+  );
+  pos_in_file += TFTP_RFC1350_BLOCK_SIZE;
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_send_data(
+    TFTP_FIRST_FD,
+    block_num,
+    pos_in_file,
+    TFTP_RFC1350_BLOCK_SIZE,
+    get_file_content,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num - 2, /* Wrong block number */
+    true
+  );
+  pos_in_file += TFTP_RFC1350_BLOCK_SIZE;
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_send_data(
+    TFTP_FIRST_FD,
+    block_num,
+    pos_in_file,
+    TFTP_RFC1350_BLOCK_SIZE / 4,
+    get_file_content,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num - 2, /* Wrong block number */
+    true
+  );
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num + 1, /* Wrong block number */
+    true
+  );
+  pos_in_file += TFTP_RFC1350_BLOCK_SIZE / 4;
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_written = write_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    pos_in_file, /* Size of file */
+    TFTP_RFC1350_BLOCK_SIZE, /* Bytes written per call to write() */
+    &ctx->fd0
+  );
+  T_eq_int( bytes_written, pos_in_file );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Write a file to the server using only RFC1350.
+ * The file is one data packet long.
+ * The client receives a stray packet from an unknown server (wrong TID).
+ * Directly afterwards the expected ACK packet is lost and
+ * the client must retransmit the original DATA packet.
+ * Tests:
+ *   * The code supports requests without options (RFC1350 only).
+ *   * The code supports the use of an IPv4 address instead of a server name.
+ *   * The first packet is sent to standard port 69 of server.
+ *   * All other packets are sent to the port used for this connection.
+ *   * The client uses a short time out for waiting on the answer of the
+ *     first DATA packet.
+ *   * The client uses a long time out for waiting on the answer of
+ *     the repeated DATA.
+ *   * Upon reception of a packet with a wrong TID, the client sends
+ *     an ERROR message with code 5 but does not terminate the current
+ *     transfer.
+ *   * When re-transmitting the an packet, the data is intact (i.e.
+ *     not corrupted by the reception of any packet in-between).
+ */
+T_TEST_CASE_FIXTURE( write_one_block_file_stray_packets, &fixture_rfc1350 )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_written;
+  uint16_t block_num = 0;
+  size_t pos_in_file = 0;
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_wrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    NO_BLOCK_SIZE_OPTION,
+    NO_WINDOW_SIZE_OPTION,
+    true
+  );
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_send_data(
+    TFTP_FIRST_FD,
+    block_num,
+    pos_in_file,
+    TFTP_RFC1350_BLOCK_SIZE,
+    get_file_content,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_recv_data(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT + 1, /* Stray packet with wrong server TID */
+    tftpfs_ipv4_loopback,
+    block_num,
+    0,
+    TFTP_RFC1350_BLOCK_SIZE / 2, /* Number of bytes transferred */
+    get_bad_file_content,
+    true
+  );
+  _Tftp_Add_interaction_send_error(
+    TFTP_FIRST_FD,
+    TFTP_ERROR_CODE_UNKNOWN_ID,
+    SERV_PORT + 1,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_recv_nothing(
+    TFTP_FIRST_FD, /* Timeout: No packet received within timeout period */
+    FIRST_TIMEOUT_MILLISECONDS
+  );
+  _Tftp_Add_interaction_send_data( /* Retransmission of the DATA packet */
+    TFTP_FIRST_FD,
+    block_num,
+    pos_in_file,
+    TFTP_RFC1350_BLOCK_SIZE,
+    get_file_content,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  pos_in_file += TFTP_RFC1350_BLOCK_SIZE;
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_send_data(
+    TFTP_FIRST_FD,
+    block_num,
+    pos_in_file,
+    0, /* Number of bytes */
+    get_file_content,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  pos_in_file += 0;
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_written = write_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    pos_in_file, /* Size of file */
+    TFTP_RFC1350_BLOCK_SIZE, /* Bytes written per call to write() */
+    &ctx->fd0
+  );
+  T_eq_int( bytes_written, pos_in_file );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Read a file from the server using option to increase the block size.
+ * The file is one data packet long.  No timeouts, packet loss, ...
+ * Tests:
+ *   * Only the blksize option appears in RRQ and OACK.
+ *   * The client uses a block size which is larger than the default size.
+ *   * The first data packet is full.
+ *   * The second data packet is empty and signals the end of the transfer.
+ *   * Client handles the empty data packet correctly as
+ *     end of file indicator.
+ *   * The test reads a file from the file system in chunks of block size.
+ */
+T_TEST_CASE_FIXTURE( read_file_one_large_block, &fixture_large_blocksize )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_read;
+  uint16_t block_num = 0;
+  size_t pos_in_file = 0;
+  const char options[] =
+    TFTP_OPTION_BLKSIZE "\0"
+    RTEMS_XSTRING( LARGE_BLOCK_SIZE );
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_rrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    LARGE_BLOCK_SIZE,
+    NO_WINDOW_SIZE_OPTION,
+    true
+  );
+  _Tftp_Add_interaction_recv_oack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    options,
+    sizeof( options ),
+    true
+  );
+  _Tftp_Add_interaction_send_ack(
+    TFTP_FIRST_FD,
+    block_num++,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_recv_data(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num,
+    pos_in_file,
+    LARGE_BLOCK_SIZE, /* Number of bytes transferred */
+    get_file_content,
+    true
+  );
+  pos_in_file += LARGE_BLOCK_SIZE;
+  _Tftp_Add_interaction_send_ack(
+    TFTP_FIRST_FD,
+    block_num++,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_recv_data(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num,
+    pos_in_file,
+    0, /* Number of bytes transferred */
+    get_file_content,
+    true
+  );
+  _Tftp_Add_interaction_send_ack(
+    TFTP_FIRST_FD,
+    block_num++,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_read = read_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    LARGE_BLOCK_SIZE, /* Bytes read per call to read() */
+    SIZE_MAX,
+    &ctx->fd0
+  );
+  T_eq_int( bytes_read, pos_in_file );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Try to read a file from the server using a file name too large for a RRQ.
+ * Tests:
+ *   * The client rejects an attempt to open a file with a too long
+ *     file name is with an error.
+ */
+T_TEST_CASE_FIXTURE( read_too_long_file_name, &fixture_default_options )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_read;
+  char buffer[TFTP_RFC1350_BLOCK_SIZE -
+              strlen( TFTP_MODE_OCTET ) - 1 - 5];
+  int len;
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  len = sizeof( buffer ) - strlen( tftpfs_mount_point ) -
+        strlen( tftpfs_ipv4_loopback ) - 2 - 4;
+  len = snprintf(
+    buffer,
+    sizeof( buffer ),
+    "%s/%s:%0*d",
+    tftpfs_mount_point,
+    tftpfs_ipv4_loopback,
+    len,
+    123
+  );
+  T_quiet_gt_int( len, 0 );
+  T_quiet_lt_int( len, (int) sizeof( buffer ) );
+
+  bytes_read = read_tftp_file(
+    buffer,
+    TFTP_DEFAULT_BLOCK_SIZE, /* Bytes read per call to read() */
+    SIZE_MAX,
+    &ctx->fd0
+  );
+  T_eq_int( bytes_read, 0 );
+  T_eq_int( errno, ENAMETOOLONG );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Read a file using options but the server sends a DATA packet.
+ * The file is one byte long.  No timeouts, packet loss, ...
+ * Tests:
+ *   * The client uses windowsize and blksize option in the RRQ.
+ *   * For the data transfer the client uses the RFC1350 option values
+ *     because the server responded with a DATA packet.
+ *   * The whole package sequence behaves as if only RFC1350 was used.
+ *   * The first data packet contains a single byte and signals the end of the transfer.
+ *   * The test reads a file from the file system in chunks of half block size.
+ */
+T_TEST_CASE_FIXTURE( read_file_DATA_instead_of_OACK, &fixture_default_options )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_read;
+  uint16_t block_num = 1;
+  size_t pos_in_file = 0;
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_rrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_DEFAULT_BLOCK_SIZE,
+    TFTP_DEFAULT_WINDOW_SIZE,
+    true
+  );
+  _Tftp_Add_interaction_recv_data(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num,
+    pos_in_file,
+    1, /* Number of bytes transferred */
+    get_file_content,
+    true
+  );
+  pos_in_file += 1;
+  _Tftp_Add_interaction_send_ack(
+    TFTP_FIRST_FD,
+    block_num++,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_read = read_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    TFTP_DEFAULT_BLOCK_SIZE / 2, /* Bytes read per call to read() */
+    SIZE_MAX,
+    &ctx->fd0
+  );
+  T_eq_int( bytes_read, pos_in_file );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Read a file using RFC1350 but the server sends an OACK packet.
+ * Tests:
+ *   * The code supports requests without options (RFC1350 only).
+ *   * The server wrongly responds with an OACK which contains no options.
+ *   * The client sends an error upon reception of an unexpected packet.
+ */
+T_TEST_CASE_FIXTURE( read_tiny_file_OACK_instead_of_DATA, &fixture_rfc1350 )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_read;
+  const char options[] = {};
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_rrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    NO_BLOCK_SIZE_OPTION,
+    NO_WINDOW_SIZE_OPTION,
+    true
+  );
+  _Tftp_Add_interaction_recv_oack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    options,
+    sizeof( options ),
+    true
+  );
+  _Tftp_Add_interaction_send_error(
+    TFTP_FIRST_FD,
+    TFTP_ERROR_CODE_ILLEGAL,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_read = read_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    1, /* Bytes read per call to read() */
+    SIZE_MAX,
+    &ctx->fd0
+  );
+  T_eq_int( bytes_read, 0 );
+  T_eq_int( errno, EPROTO );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Read a file from the server using the default options.
+ * The file is 18 and a half data packet long.  No timeouts, packet loss, ...
+ * Tests:
+ *   * The client uses the default options
+ *     (windowsize = 8 and blocksize = 1456).
+ *   * The server send the options in the same order as the client did
+ *     send them.
+ *   * The ninetenth data packet signals the end of transfer.
+ *   * The test reads a file from the file system in chunks of 2000 bytes.
+ */
+T_TEST_CASE_FIXTURE( read_file_with_default_options, &fixture_default_options )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int i;
+  int bytes_read;
+  uint16_t block_num = 0;
+  size_t pos_in_file = 0;
+  const char options[] =
+    TFTP_OPTION_BLKSIZE "\0"
+    RTEMS_XSTRING( TFTP_DEFAULT_BLOCK_SIZE ) "\0"
+    TFTP_OPTION_WINDOWSIZE"\0"
+    RTEMS_XSTRING( TFTP_DEFAULT_WINDOW_SIZE );
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_rrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_DEFAULT_BLOCK_SIZE,
+    TFTP_DEFAULT_WINDOW_SIZE,
+    true
+  );
+  _Tftp_Add_interaction_recv_oack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    options,
+    sizeof( options ),
+    true
+  );
+  _Tftp_Add_interaction_send_ack(
+    TFTP_FIRST_FD,
+    block_num,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  while ( block_num < 16 ) {
+    for ( i = 0; i < TFTP_DEFAULT_WINDOW_SIZE; ++i ) {
+      _Tftp_Add_interaction_recv_data(
+        TFTP_FIRST_FD,
+        FIRST_TIMEOUT_MILLISECONDS,
+        SERV_PORT,
+        tftpfs_ipv4_loopback,
+        ++block_num,
+        pos_in_file,
+        TFTP_DEFAULT_BLOCK_SIZE, /* Number of bytes transferred */
+        get_file_content,
+        true
+      );
+      pos_in_file += TFTP_DEFAULT_BLOCK_SIZE;
+    }
+    _Tftp_Add_interaction_send_ack(
+      TFTP_FIRST_FD,
+      block_num,
+      SERV_PORT,
+      tftpfs_ipv4_loopback,
+      true
+    );
+  }
+  _Tftp_Add_interaction_recv_data(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    ++block_num,
+    pos_in_file,
+    TFTP_DEFAULT_BLOCK_SIZE, /* Number of bytes transferred */
+    get_file_content,
+    true
+  );
+  pos_in_file += TFTP_DEFAULT_BLOCK_SIZE;
+  _Tftp_Add_interaction_recv_data(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    ++block_num,
+    pos_in_file,
+    TFTP_DEFAULT_BLOCK_SIZE, /* Number of bytes transferred */
+    get_file_content,
+    true
+  );
+  pos_in_file += TFTP_DEFAULT_BLOCK_SIZE;
+  _Tftp_Add_interaction_recv_data(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    ++block_num,
+    pos_in_file,
+    TFTP_DEFAULT_BLOCK_SIZE / 2, /* Number of bytes transferred */
+    get_file_content,
+    true
+  );
+  pos_in_file += TFTP_DEFAULT_BLOCK_SIZE / 2;
+  _Tftp_Add_interaction_send_ack(
+    TFTP_FIRST_FD,
+    block_num,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_read = read_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    2000, /* Bytes read per call to read() */
+    SIZE_MAX,
+    &ctx->fd0
+  );
+  T_eq_int( bytes_read, pos_in_file );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Read a file following exactly the scenario in RFC 7440.
+ * This test uses window size and block size options.  There are lost packets.
+ * Tests:
+ *   * The client uses of non-default options
+ *     (windowsize = 4 and blocksize = 12).
+ *   * Test the scenario included in RFC 7440.
+ *   * When a packet from the server is lost (client receives DATA packet with
+ *     a too high block number), the client sends an ACK for the last package
+ *     received in the correct sequence.
+ *   * The client ignores duplicated packets (with block numbers it has
+ *     already processed).
+ *   * The data of the file ends exactly at a window size border (i.e.
+ *     after the window a single empty DATA packet is sent by the server).
+ *   * The test reads a file from the file system in chunks of 10 bytes.
+ */
+T_TEST_CASE_FIXTURE( read_file_rfc7440_scenario, &fixture_small_opt_size )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int i;
+  int bytes_read;
+  uint16_t block_num = 0;
+  size_t pos_in_file = 0;
+  const char options[] =
+    TFTP_OPTION_WINDOWSIZE"\0"
+    RTEMS_XSTRING( SMALL_WINDOW_SIZE ) "\0"
+    TFTP_OPTION_BLKSIZE "\0"
+    RTEMS_XSTRING( SMALL_BLOCK_SIZE );
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_rrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    SMALL_BLOCK_SIZE,
+    SMALL_WINDOW_SIZE,
+    true
+  );
+  _Tftp_Add_interaction_recv_oack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    options,
+    sizeof( options ),
+    true
+  );
+  _Tftp_Add_interaction_send_ack(
+    TFTP_FIRST_FD,
+    block_num,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  for ( i = 0; i < SMALL_WINDOW_SIZE; ++i ) {
+    _Tftp_Add_interaction_recv_data(
+      TFTP_FIRST_FD,
+      FIRST_TIMEOUT_MILLISECONDS,
+      SERV_PORT,
+      tftpfs_ipv4_loopback,
+      ++block_num,
+      pos_in_file,
+      SMALL_BLOCK_SIZE, /* Number of bytes transferred */
+      get_file_content,
+      true
+    );
+    pos_in_file += SMALL_BLOCK_SIZE;
+  }
+  _Tftp_Add_interaction_send_ack(
+    TFTP_FIRST_FD,
+    block_num,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_recv_data(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    ++block_num,
+    pos_in_file,
+    SMALL_BLOCK_SIZE, /* Number of bytes transferred */
+    get_file_content,
+    true
+  );
+  pos_in_file += SMALL_BLOCK_SIZE;
+  _Tftp_Add_interaction_recv_data(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num + 2, /* Error: One packet from the server has been lost */
+    pos_in_file,
+    SMALL_BLOCK_SIZE, /* Number of bytes transferred */
+    get_file_content,
+    true
+  );
+  _Tftp_Add_interaction_send_ack(
+    TFTP_FIRST_FD,
+    block_num,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  for ( i = 0; i < SMALL_WINDOW_SIZE; ++i ) {
+    _Tftp_Add_interaction_recv_data(
+      TFTP_FIRST_FD,
+      FIRST_TIMEOUT_MILLISECONDS,
+      SERV_PORT,
+      tftpfs_ipv4_loopback,
+      ++block_num,
+      pos_in_file,
+      SMALL_BLOCK_SIZE, /* Number of bytes transferred */
+      get_file_content,
+      true
+    );
+    pos_in_file += SMALL_BLOCK_SIZE;
+  }
+  _Tftp_Add_interaction_send_ack(
+    TFTP_FIRST_FD,
+    block_num,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_recv_data(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    ++block_num,
+    pos_in_file,
+    SMALL_BLOCK_SIZE, /* Number of bytes transferred */
+    get_file_content,
+    true
+  );
+  pos_in_file += SMALL_BLOCK_SIZE;
+  _Tftp_Add_interaction_recv_data(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num + 2, /* Error: One packet from the server has been lost */
+    pos_in_file,
+    SMALL_BLOCK_SIZE, /* Number of bytes transferred */
+    get_file_content,
+    true
+  );
+  _Tftp_Add_interaction_send_ack(
+    TFTP_FIRST_FD,
+    block_num, /* The packet is assumed to be lost/does not reach the server */
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_recv_data(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num + 3, /* Error: One packet from the server has been lost */
+    pos_in_file,
+    SMALL_BLOCK_SIZE, /* Number of bytes transferred */
+    get_file_content,
+    true
+  );
+  block_num = 9; /* The ACK for DATA packet 10 did not reach the server */
+  pos_in_file = block_num * SMALL_BLOCK_SIZE;
+  for ( i = 0; i < SMALL_WINDOW_SIZE; ++i ) {
+    _Tftp_Add_interaction_recv_data(
+      TFTP_FIRST_FD,
+      FIRST_TIMEOUT_MILLISECONDS,
+      SERV_PORT,
+      tftpfs_ipv4_loopback,
+      ++block_num,
+      pos_in_file,
+      SMALL_BLOCK_SIZE, /* Number of bytes transferred */
+      get_file_content,
+      true
+    );
+    pos_in_file += SMALL_BLOCK_SIZE;
+  }
+  _Tftp_Add_interaction_send_ack(
+    TFTP_FIRST_FD,
+    block_num,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_recv_data(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    ++block_num,
+    pos_in_file,
+    0, /* Number of bytes transferred */
+    get_file_content,
+    true
+  );
+  _Tftp_Add_interaction_send_ack(
+    TFTP_FIRST_FD,
+    block_num,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_read = read_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    10, /* Bytes read per call to read() */
+    SIZE_MAX,
+    &ctx->fd0
+  );
+  T_eq_int( bytes_read, pos_in_file );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Read a file using windowsize = 4.  All kinds of packet loss, out of order
+ * packets and duplicated packets occur.
+ * This test uses window size and block size options.  It is a stress test
+ * for all kind of network problems which can appear during a transfer
+ * of a file with window size larger than 1.
+ * Tests:
+ *   * The client uses non-default options (windowsize = 4 and blocksize = 12).
+ *   * The first DATA packet of a window is lost.
+ *   * The last DATA packet of a window is lost.
+ *   * The middle DATA packets of a window is lost.
+ *   * The first two DATA packets of a window are received in reverse order.
+ *   * The middle two DATA packets of a window are received in reverse order.
+ *   * The last two DATA packets of a window are received in reverse order.
+ *   * The a DATA packet is received duplicated.
+ *   * The an old DATA packet is received duplicated.
+ *   * The a very old DATA packet is received duplicated.
+ *   * The normal ACK of a window is not received by the server.
+ *   * The server repeats a whole window (ensure the client sends an ACK
+ *     packet despite of the repetition).
+ *   * An ACK for an out-of-order packet is not received by the server.
+ *   * Windows with errors appear consecutively without
+ *     error free windows in between.
+ *   * After the reception of the first two DATA packets of a window,
+ *     a timeout occurs and the client must send an ACK.
+ *   * File transfer ends exactly with the last packet of a window
+ *     (the second last packet is full and the last one is empty).
+ *   * The test reads a file from the file system in chunks 100 bytes.
+ */
+T_TEST_CASE_FIXTURE( read_file_windowsize_trouble, &fixture_small_opt_size )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int i;
+  int bytes_read;
+  uint16_t block_num = 0;
+  size_t pos_in_file = 0;
+  int timeout = FIRST_TIMEOUT_MILLISECONDS;
+  const char options[] =
+    TFTP_OPTION_WINDOWSIZE"\0"
+    RTEMS_XSTRING( SMALL_WINDOW_SIZE ) "\0"
+    TFTP_OPTION_BLKSIZE "\0"
+    RTEMS_XSTRING( SMALL_BLOCK_SIZE );
+  /*
+   * A positive number is the number of a DATA packet received.
+   * A negative number is the number of an ACK packet send.
+   * A zero indicates a timeout when waiting for a DATA packet.
+   * Each line corresponds to a window.
+   */
+  int16_t pkg_sequence[] = {
+    1, 1, 2, 3, 2, 1, 3, 4, -4, /* Duplicated DATA packets */
+    6, -4, 7, 8, /* DATA packet 5 lost */
+    6, 5, 7, -5, 4, 8, /* DATA packet 5 and 6 received in revers order;
+                          reception of an very old packet: 4 */
+    7, 6, 8, -6, /* DATA packet 6 and 7 received in revers order;
+                    DATA packet 9 not send or lost */
+    6, 7, 8, 9, -9, /* ACK packet 6 was not received by server */
+    10, 11, 12, 0, -12, /* DATA packet 13 lost */
+    13, 16, -13, /* DATA packets 14, 15 lost */
+    12, 13, 14, 15, -15, 16, 17, /* Reception of duplicated old packets 12 and
+                                    13 */
+    16, 17, 18, 19, -19, /* Normal sequence; ACK 19 not receive by server */
+    16, 17, 18, 19, -19, /* Normal sequence repeated;
+                            ACK 19 not receive by server */
+    16, 19, -19, 18, 17, /* Sequence repeated but DATA packet 17 and 18
+                            received after 19 and in revers order */
+    20, -20, 21, 22, 23, /* ACK 20 because the client assumes the server
+                            did not get ACK 19 and restarted with 17 */
+    21, 22, 23, 24, -24, /* Normal sequence */
+    25, 27, -25, 26, 28, -26, /* The middle data packets 26, 27 are exchanged;
+                                 the client assumes DATA 26 being the start
+                                 of the next window and sends an ACK 26
+                                 upon reception of out-of-sequence DATA 28;
+                                 assume ACK 26 not received by server */
+    26, 27, 29, -27, 28, /* The last data packets are exchanged;
+                            ACK 27 not received by server */
+    26, 27, 28, 29, -29, /* Normal sequence repeated from ACK 25 */
+    30, 31, 0, -31, /* The last two data packets are lost (timeout) */
+    32, 33, 34, /* Normal sequence; the last DATA packet (here missing) will
+                   contain no data and ends the transfer at a window
+                   boundary; a final ACK (here missing too) follows */
+  };
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_rrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    SMALL_BLOCK_SIZE,
+    SMALL_WINDOW_SIZE,
+    true
+  );
+  _Tftp_Add_interaction_recv_oack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    options,
+    sizeof( options ),
+    true
+  );
+  _Tftp_Add_interaction_send_ack(
+    TFTP_FIRST_FD,
+    block_num,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  for ( i = 0; i < RTEMS_ARRAY_SIZE( pkg_sequence ); ++i ) {
+    if ( pkg_sequence[i] == 0 ) {
+      block_num = pkg_sequence[i];
+      _Tftp_Add_interaction_recv_nothing(
+        TFTP_FIRST_FD, /* Timeout: No packet received within timeout period */
+        timeout
+      );
+      timeout = TIMEOUT_MILLISECONDS;
+    } else if ( pkg_sequence[i] > 0 ) {
+      block_num = pkg_sequence[i];
+      _Tftp_Add_interaction_recv_data(
+        TFTP_FIRST_FD,
+        timeout,
+        SERV_PORT,
+        tftpfs_ipv4_loopback,
+        block_num,
+        ( block_num - 1 ) * SMALL_BLOCK_SIZE,
+        SMALL_BLOCK_SIZE, /* Number of bytes transferred */
+        get_file_content,
+        pkg_sequence[i] > 0 /* pkg_sequence[i] == 0 means timeout */
+      );
+      timeout = FIRST_TIMEOUT_MILLISECONDS;
+      pos_in_file = block_num * SMALL_BLOCK_SIZE;
+    } else {
+      block_num = -pkg_sequence[i];
+      _Tftp_Add_interaction_send_ack(
+        TFTP_FIRST_FD,
+        block_num,
+        SERV_PORT,
+        tftpfs_ipv4_loopback,
+        true
+      );
+    }
+  }
+  _Tftp_Add_interaction_recv_data(
+    TFTP_FIRST_FD,
+    timeout,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    ++block_num,
+    pos_in_file,
+    0, /* Number of bytes transferred */
+    get_file_content,
+    true
+  );
+  _Tftp_Add_interaction_send_ack(
+    TFTP_FIRST_FD,
+    block_num,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_read = read_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    100, /* Bytes read per call to read() */
+    SIZE_MAX,
+    &ctx->fd0
+  );
+  T_eq_int( bytes_read, pos_in_file );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Write a file to the server using an option to increase block size.
+ * The file is 2 DATA packet and 1 byte long.  No timeouts, packet loss, ...
+ * Tests:
+ *   * The client uses only the blksize option in WRQ and OACK.
+ *   * The client uses a block size which is larger than the default size.
+ *   * The server can change the block size value in the OACK.
+ *   * The option name in the OACK can be upper or lower case.
+ *   * First and second DATA packets are full.
+ *   * The second DATA packet is not full and signals the end of the transfer.
+ *   * The client handles an empty DATA packet correctly as
+ *     end of file indicator.
+ *   * The test writes the file to the file system in chunks of 333 bytes.
+ */
+T_TEST_CASE_FIXTURE( write_simple_file_large_blocks, &fixture_large_blocksize )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_written;
+  size_t pos_in_file = 0;
+  uint16_t block_num = 1;
+  uint16_t block_size = 211;
+  const char options[] = "BLKsiZe" "\0" "211";
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_wrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    LARGE_BLOCK_SIZE,
+    NO_WINDOW_SIZE_OPTION,
+    true
+  );
+  _Tftp_Add_interaction_recv_oack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    options,
+    sizeof( options ),
+    true
+  );
+  _Tftp_Add_interaction_send_data(
+    TFTP_FIRST_FD,
+    block_num,
+    pos_in_file,
+    block_size,
+    get_file_content,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  pos_in_file += block_size;
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_send_data(
+    TFTP_FIRST_FD,
+    block_num,
+    pos_in_file,
+    block_size,
+    get_file_content,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  pos_in_file += block_size;
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_send_data(
+    TFTP_FIRST_FD,
+    block_num,
+    pos_in_file,
+    1, /* Data bytes in this block */
+    get_file_content,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  pos_in_file += 1;
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_written = write_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    pos_in_file, /* Size of file */
+    333, /* Bytes written per call to write() */
+    &ctx->fd0
+  );
+  T_eq_int( bytes_written, pos_in_file );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Write a file to the server using default options.
+ * The file is 23 data packet long.  No timeouts, packet loss, ...
+ * Tests:
+ *   * The client uses the default options
+ *     (windowsize = 8 and blocksize = 1456).
+ *   * The server sends the options in the same order the client
+ *     did send them.
+ *   * The 24th data packet signals the end of file transfer.
+ *   * Client sends an empty data packet as end of file indicator.
+ *   * The client handles files correctly which end exactly at
+ *     a window border.
+ */
+T_TEST_CASE_FIXTURE(
+  write_simple_file_default_options,
+  &fixture_default_options
+)
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int i;
+  int bytes_written;
+  size_t pos_in_file = 0;
+  uint16_t block_num = 1;
+  const char options[] =
+    TFTP_OPTION_BLKSIZE "\0"
+    RTEMS_XSTRING( TFTP_DEFAULT_BLOCK_SIZE ) "\0"
+    TFTP_OPTION_WINDOWSIZE "\0"
+    RTEMS_XSTRING( TFTP_DEFAULT_WINDOW_SIZE );
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_wrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_DEFAULT_BLOCK_SIZE,
+    TFTP_DEFAULT_WINDOW_SIZE,
+    true
+  );
+  _Tftp_Add_interaction_recv_oack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    options,
+    sizeof( options ),
+    true
+  );
+  for ( i = 0; i < 23; ++i ) {
+    _Tftp_Add_interaction_send_data(
+      TFTP_FIRST_FD,
+      block_num++,
+      pos_in_file,
+      TFTP_DEFAULT_BLOCK_SIZE,
+      get_file_content,
+      SERV_PORT,
+      tftpfs_ipv4_loopback,
+      true
+    );
+    pos_in_file += TFTP_DEFAULT_BLOCK_SIZE;
+    if ( i % 8 == 7 ) {
+      _Tftp_Add_interaction_recv_ack(
+        TFTP_FIRST_FD,
+        FIRST_TIMEOUT_MILLISECONDS,
+        SERV_PORT,
+        tftpfs_ipv4_loopback,
+        block_num - 1,
+        true
+      );
+    } else {
+      _Tftp_Add_interaction_recv_nothing(
+        TFTP_FIRST_FD,
+        DO_NOT_WAIT_FOR_ANY_TIMEOUT
+      );
+    }
+  }
+  _Tftp_Add_interaction_send_data(
+    TFTP_FIRST_FD,
+    block_num,
+    pos_in_file,
+    0,
+    get_file_content,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_written = write_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    pos_in_file, /* Size of file */
+    333, /* Bytes written per call to write() */
+    &ctx->fd0
+  );
+  T_eq_int( bytes_written, pos_in_file );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Write a file following exactly the scenario from RFC 7440.
+ * This test uses window size and block size options.  There are lost packets.
+ * Tests:
+ *   * The client uses non-default options
+ *     (windowsize = 4 and blocksize = 12).
+ *   * Test the scenario included in RFC 7440.
+ *   * The server sends the options in the inverse order the client
+ *     did send them.
+ *   * The data of the file ends exactly at a window size border (i.e.
+ *     after the last window the server sends a single empty DATA packet).
+ *   * The test writes a file to the file system in chunks of 10 bytes.
+ */
+T_TEST_CASE_FIXTURE( write_file_rfc7440_scenario, &fixture_small_opt_size )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int i;
+  int bytes_written;
+  size_t pos_in_file = 0;
+  uint16_t block_num = 1;
+  const char options[] =
+    TFTP_OPTION_WINDOWSIZE"\0"
+    RTEMS_XSTRING( SMALL_WINDOW_SIZE ) "\0"
+    TFTP_OPTION_BLKSIZE "\0"
+    RTEMS_XSTRING( SMALL_BLOCK_SIZE );
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_wrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    SMALL_BLOCK_SIZE,
+    SMALL_WINDOW_SIZE,
+    true
+  );
+  _Tftp_Add_interaction_recv_oack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    options,
+    sizeof( options ),
+    true
+  );
+  for ( i = 0; i < 6; ++i ) {
+    _Tftp_Add_interaction_send_data(
+      TFTP_FIRST_FD,
+      block_num++,
+      pos_in_file,
+      SMALL_BLOCK_SIZE,
+      get_file_content,
+      SERV_PORT,
+      tftpfs_ipv4_loopback,
+      true
+    );
+    pos_in_file += SMALL_BLOCK_SIZE;
+    if ( i % 4 == 3 ) {
+      _Tftp_Add_interaction_recv_ack(
+        TFTP_FIRST_FD,
+        FIRST_TIMEOUT_MILLISECONDS,
+        SERV_PORT,
+        tftpfs_ipv4_loopback,
+        block_num - 1,
+        true
+      );
+    } else {
+      _Tftp_Add_interaction_recv_nothing(
+        TFTP_FIRST_FD,
+        DO_NOT_WAIT_FOR_ANY_TIMEOUT
+      );
+    }
+  }
+  _Tftp_Add_interaction_send_data(
+    TFTP_FIRST_FD,
+    block_num,
+    pos_in_file,
+    SMALL_BLOCK_SIZE,
+    get_file_content,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    DO_NOT_WAIT_FOR_ANY_TIMEOUT,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    5,
+    true
+  );
+  block_num = 6;
+  pos_in_file = (block_num - 1) * SMALL_BLOCK_SIZE;
+  for ( i = 0; i < 7; ++i ) {
+    _Tftp_Add_interaction_send_data(
+      TFTP_FIRST_FD,
+      block_num++,
+      pos_in_file,
+      SMALL_BLOCK_SIZE,
+      get_file_content,
+      SERV_PORT,
+      tftpfs_ipv4_loopback,
+      true
+    );
+    pos_in_file += SMALL_BLOCK_SIZE;
+    if ( i % 4 == 3 ) {
+      _Tftp_Add_interaction_recv_ack(
+        TFTP_FIRST_FD,
+        FIRST_TIMEOUT_MILLISECONDS,
+        SERV_PORT,
+        tftpfs_ipv4_loopback,
+        block_num - 1,
+        true
+      );
+    } else {
+      _Tftp_Add_interaction_recv_nothing(
+        TFTP_FIRST_FD,
+        DO_NOT_WAIT_FOR_ANY_TIMEOUT
+      );
+    }
+  }
+  _Tftp_Add_interaction_send_data(
+    TFTP_FIRST_FD,
+    block_num,
+    pos_in_file,
+    SMALL_BLOCK_SIZE,
+    get_file_content,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_recv_nothing(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS
+  );
+  block_num = 10;
+  pos_in_file = (block_num - 1) * SMALL_BLOCK_SIZE;
+  for ( i = 0; i < 4; ++i ) {
+    _Tftp_Add_interaction_send_data(
+      TFTP_FIRST_FD,
+      block_num++,
+      pos_in_file,
+      SMALL_BLOCK_SIZE,
+      get_file_content,
+      SERV_PORT,
+      tftpfs_ipv4_loopback,
+      true
+    );
+    pos_in_file += SMALL_BLOCK_SIZE;
+    if ( i % 4 == 3 ) {
+      _Tftp_Add_interaction_recv_ack(
+        TFTP_FIRST_FD,
+        FIRST_TIMEOUT_MILLISECONDS,
+        SERV_PORT,
+        tftpfs_ipv4_loopback,
+        block_num - 1,
+        true
+      );
+    } else {
+      _Tftp_Add_interaction_recv_nothing(
+        TFTP_FIRST_FD,
+        DO_NOT_WAIT_FOR_ANY_TIMEOUT
+      );
+    }
+  }
+  _Tftp_Add_interaction_send_data(
+    TFTP_FIRST_FD,
+    block_num,
+    pos_in_file,
+    0,
+    get_file_content,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_written = write_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    pos_in_file, /* Size of file */
+    10, /* Bytes written per call to write() */
+    &ctx->fd0
+  );
+  T_eq_int( bytes_written, pos_in_file );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Write a file using windowsize = 4.  All kinds of packet loss, out of
+ * order packets and duplicated ACK packets appear.
+ * This test uses window size and block size options.  It is a stress test
+ * for all kind of network problems which can appear during a transfer
+ * of a file with window size larger than 1.
+ * Tests:
+ *   * The server repeats a whole window (timeout).
+ *   * The client receives a duplicated ACK packet (directly in sequence).
+ *   * The client receives a duplicated ACK (after sending a window).
+ *   * The client receives an ACK with a block number which is not
+ *     the end of the current window.
+ *   * The client receives an very old ACK in the middle of a window.
+ *   * The client receives an very old ACK at the end of a window.
+ *   * The client receives an ACK for a not yet send DATA packet in the
+ *     middle of a window (should be ignored or cause a error).
+ *   * The client receives an ACK for a not yet send DATA packet at the
+ *     end of a window (should be ignored or cause a error).
+ *   * The client receives an ACK after sending a full window.
+ *   * The client receives an ACK before all DATA packets of a
+ *     window have been sent.
+ *   * The client must repeat the first window completely (timeout).
+ *   * The client must repeat the first window partially.
+ *   * The client must repeat the last window completely (timeout).
+ *   * The client must repeat the last window partially.
+ *   * Windows with errors in between appear consecutively, without
+ *     error free windows in between.
+ *   * The test writes a file to the file system in chunks of block size.
+ */
+T_TEST_CASE_FIXTURE( write_file_windowsize_trouble, &fixture_small_opt_size )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int i;
+  int bytes_written;
+  size_t pos_in_file = 0;
+  uint16_t block_num = 1;
+  int timeout = FIRST_TIMEOUT_MILLISECONDS;
+  const char options[] =
+    TFTP_OPTION_BLKSIZE "\0"
+    RTEMS_XSTRING( SMALL_BLOCK_SIZE ) "\0"
+    TFTP_OPTION_WINDOWSIZE"\0"
+    RTEMS_XSTRING( SMALL_WINDOW_SIZE );
+  /*
+   * A positive number is the number of an ACK packet received
+   *   at the end of window.
+   * A 9999 indicates a timeout when waiting for an ACK packet.
+   * A zero indicates no ACK packet is received when checking for it.
+   * A positive number >= 10000 is the number+10000 of an ACK packet received
+   *   while only checking for a packet.
+   * A negative number is the number of a DATA packet send
+   *   at the end of a window.
+   * A negative number <= -10000 is the number-10000 of an *empty* DATA
+   *   packet send.
+   * Each line corresponds to a window.
+   */
+  int16_t pkg_sequence[] = {
+    -1, 0, -2, 0, -3, 0, -4, 9999, /* First window, trigger full repeat */
+    -1, 0, -2, 0, -3, 0, -4, 2, /* ACK at end of window;
+                                   first window must be partially repeated */
+    -3, 0, -4, 0, -5, 0, -6, 6, /* Normal sequence */
+    -7, 0, -8, 0, -9, 0, -10, 6, /* Duplicate ACK */
+    -7, 10006, /* Duplicate ACK; ACK before sending all packets of a window */
+    -7, 0, -8, 10008, /* ACK before sending all packets of a window;
+                         ACK is not at the window end */
+    -9, 10007, 0, -10, 10013, 0, -11, 0, -12, 12, /* Reception of very old ACK;
+                                                    Reception of future ACK (The
+                                                    wrong "future" ACK must be
+                                                    beyond the block size)
+                                                    in the middle of a window */
+    -13, 0, -14, 0, -15, 0, -16, 11, 17, 16, /* Reception of very old ACK;
+                                                Reception of future ACK at the
+                                                end of a window */
+    -17, 0, -18, 0, -10019, 9999, /* Last window timeout, trigger full repeat */
+    -17, 0, -18, 0, -10019, 16, /* Last window, duplicated ACK */
+    -17, 0, -18, 10017, /* Last window, ACK before sending all packets */
+    -18, 0, -10019, 19 /* Last window partially repeated */
+  };
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_wrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    SMALL_BLOCK_SIZE,
+    SMALL_WINDOW_SIZE,
+    true
+  );
+  _Tftp_Add_interaction_recv_oack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    options,
+    sizeof( options ),
+    true
+  );
+  for ( i = 0; i < RTEMS_ARRAY_SIZE( pkg_sequence ); ++i ) {
+    if ( pkg_sequence[i] == 0 ) {
+      _Tftp_Add_interaction_recv_nothing(
+        TFTP_FIRST_FD,
+        DO_NOT_WAIT_FOR_ANY_TIMEOUT
+      );
+      timeout = FIRST_TIMEOUT_MILLISECONDS;
+    } else if ( pkg_sequence[i] == 9999 ) {
+      _Tftp_Add_interaction_recv_nothing(
+        TFTP_FIRST_FD,
+        timeout
+      );
+      timeout = FIRST_TIMEOUT_MILLISECONDS;
+    } else if ( pkg_sequence[i] >= 10000 ) {
+      block_num = pkg_sequence[i] - 10000;
+      _Tftp_Add_interaction_recv_ack(
+        TFTP_FIRST_FD,
+        DO_NOT_WAIT_FOR_ANY_TIMEOUT,
+        SERV_PORT,
+        tftpfs_ipv4_loopback,
+        block_num++,
+        true
+      );
+      timeout = FIRST_TIMEOUT_MILLISECONDS;
+    } else if ( pkg_sequence[i] > 0 ) {
+      block_num = pkg_sequence[i];
+      _Tftp_Add_interaction_recv_ack(
+        TFTP_FIRST_FD,
+        timeout,
+        SERV_PORT,
+        tftpfs_ipv4_loopback,
+        block_num++,
+        true
+      );
+      timeout = FIRST_TIMEOUT_MILLISECONDS;
+    } else if ( pkg_sequence[i] <= -10000 ) {
+      block_num = -pkg_sequence[i] - 10000;
+      pos_in_file = (block_num - 1) * SMALL_BLOCK_SIZE;
+      _Tftp_Add_interaction_send_data(
+        TFTP_FIRST_FD,
+        block_num++,
+        pos_in_file,
+        0, /* Number of bytes transferred */
+        get_file_content,
+        SERV_PORT,
+        tftpfs_ipv4_loopback,
+        true
+      );
+      timeout = FIRST_TIMEOUT_MILLISECONDS;
+    } else {
+      block_num = -pkg_sequence[i];
+      pos_in_file = (block_num - 1) * SMALL_BLOCK_SIZE;
+      _Tftp_Add_interaction_send_data(
+        TFTP_FIRST_FD,
+        block_num++,
+        pos_in_file,
+        SMALL_BLOCK_SIZE,
+        get_file_content,
+        SERV_PORT,
+        tftpfs_ipv4_loopback,
+        true
+      );
+      timeout = FIRST_TIMEOUT_MILLISECONDS;
+      pos_in_file += SMALL_BLOCK_SIZE;
+    }
+  }
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_written = write_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    pos_in_file, /* Size of file */
+    SMALL_BLOCK_SIZE, /* Bytes written per call to write() */
+    &ctx->fd0
+  );
+  T_eq_int( bytes_written, pos_in_file );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Write a file to the server where the server sends an OACK without options.
+ * The file is half a data packet long.  No timeouts, packet loss, ...
+ * Tests:
+ *   * The client processes an OACK without any options in it correctly.
+ *   * The client uses the RFC1350 block size because the server does
+ *     not agree to the options send.
+ *   * The first data packet is half full and signals the end of the transfer.
+ *   * The test reads a file from the file system in chunks of double block size.
+ */
+T_TEST_CASE_FIXTURE( write_tiny_file_OACK_no_options, &fixture_large_blocksize )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_written;
+  size_t pos_in_file = 0;
+  uint16_t block_num = 1;
+  const char options[] = {};
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_wrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    LARGE_BLOCK_SIZE,
+    NO_WINDOW_SIZE_OPTION,
+    true
+  );
+  _Tftp_Add_interaction_recv_oack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    options,
+    sizeof( options ),
+    true
+  );
+  _Tftp_Add_interaction_send_data(
+    TFTP_FIRST_FD,
+    block_num,
+    pos_in_file,
+    TFTP_RFC1350_BLOCK_SIZE / 2,
+    get_file_content,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  pos_in_file += TFTP_RFC1350_BLOCK_SIZE / 2;
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_written = write_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    pos_in_file, /* Size of file */
+    2 * TFTP_RFC1350_BLOCK_SIZE, /* Bytes written per call to write() */
+    &ctx->fd0
+  );
+  T_eq_int( bytes_written, pos_in_file );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Read a file and when the server responses with an ERROR to options
+ * fallback to no options.
+ * The file is one byte long.  No timeouts, packet loss, ...
+ * Tests:
+ *   * The client uses windowsize and blksize option in the first RRQ.
+ *   * Upon reception of an ERROR packet from the server, the client
+ *     re-tries opening the session using an RRQ without options.
+ *   * The server accepts the RRQ without options by sending a DATA packet.
+ *   * The second RRQ is sent to the TFTP server port 69 and not to the
+ *     port from which the first ERROR packet came from.
+ *   * The first data packet contains a single byte and signals the
+ *     end of the transfer.
+ */
+T_TEST_CASE_FIXTURE( read_file_fallback_to_no_options,
+  &fixture_default_options )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_read;
+  uint16_t block_num = 1;
+  size_t pos_in_file = 0;
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_rrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_DEFAULT_BLOCK_SIZE,
+    TFTP_DEFAULT_WINDOW_SIZE,
+    true
+  );
+  _Tftp_Add_interaction_recv_error(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_ERROR_CODE_OPTION_NEGO,
+    "Don't like options",
+    true
+  );
+  _Tftp_Add_interaction_send_rrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    NO_BLOCK_SIZE_OPTION,
+    NO_WINDOW_SIZE_OPTION,
+    true
+  );
+  _Tftp_Add_interaction_recv_data(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num,
+    pos_in_file,
+    1, /* Number of bytes transferred */
+    get_file_content,
+    true
+  );
+  pos_in_file += 1;
+  _Tftp_Add_interaction_send_ack(
+    TFTP_FIRST_FD,
+    block_num++,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_read = read_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    TFTP_DEFAULT_BLOCK_SIZE / 2, /* Bytes read per call to read() */
+    SIZE_MAX,
+    &ctx->fd0
+  );
+  T_eq_int( bytes_read, pos_in_file );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Read a file from the server but the server responds with
+ * an ERROR to all RRQ with and without options.
+ * The file is one byte long.  No timeouts, packet loss, ...
+ * Tests:
+ *   * The client uses windowsize and blksize option in the first RRQ.
+ *   * Upon reception of an ERROR packet from the server, the client
+ *     re-tries opening a session using an RRQ without options.
+ *   * The second RRQ is sent to the TFTP server port 69 and not to the
+ *     port from which the first ERROR packet came from.
+ *   * The server does not accept the RRQ without options
+ *     and responds again with an ERROR packet.
+ *   * The client ends all attempts to create a connection after
+ *     receiving an ERROR packet to an RRQ without options.
+ *   * The client signals the error to the user.
+ */
+T_TEST_CASE_FIXTURE( read_file_useless_fallback_to_no_options,
+  &fixture_default_options )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_read;
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_rrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_DEFAULT_BLOCK_SIZE,
+    TFTP_DEFAULT_WINDOW_SIZE,
+    true
+  );
+  _Tftp_Add_interaction_recv_error(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_ERROR_CODE_ILLEGAL,
+    "Don't like options",
+    true
+  );
+  _Tftp_Add_interaction_send_rrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    NO_BLOCK_SIZE_OPTION,
+    NO_WINDOW_SIZE_OPTION,
+    true
+  );
+  _Tftp_Add_interaction_recv_error(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_ERROR_CODE_ILLEGAL,
+    "Go away",
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_read = read_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    TFTP_DEFAULT_BLOCK_SIZE / 2, /* Bytes read per call to read() */
+    SIZE_MAX,
+    &ctx->fd0
+  );
+  T_eq_int( errno, EINVAL );
+  T_eq_int( bytes_read, 0 );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Write a file to the server and the server responses with an ACK packet.
+ * The file is half a data packet long.  No timeouts, packet loss, ...
+ * Tests:
+ *   * The client uses windowsize and blksize option in the WRQ.
+ *   * The client uses the RFC1350 option values for the data transfer
+ *     because the server responded with an ACK packet.
+ *   * The whole package sequence behaves as if only RFC1350 was used.
+ *   * The first data packet is half filled and signals the end of the
+ *     transfer.
+ */
+T_TEST_CASE_FIXTURE( write_file_ACK_instead_of_OACK, &fixture_default_options )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_written;
+  size_t pos_in_file = 0;
+  uint16_t block_num = 0;
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_wrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_DEFAULT_BLOCK_SIZE,
+    TFTP_DEFAULT_WINDOW_SIZE,
+    true
+  );
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_send_data(
+    TFTP_FIRST_FD,
+    block_num,
+    pos_in_file,
+    TFTP_RFC1350_BLOCK_SIZE / 2,
+    get_file_content,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  pos_in_file += TFTP_RFC1350_BLOCK_SIZE / 2;
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_written = write_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    pos_in_file, /* Size of file */
+    2 * TFTP_RFC1350_BLOCK_SIZE, /* Bytes written per call to write() */
+    &ctx->fd0
+  );
+  T_eq_int( bytes_written, pos_in_file );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Write a file and when the server responses with an ERROR to options
+ * fallback to no options.
+ * The file is half a data packet long.  No timeouts, packet loss, ...
+ * Tests:
+ *   * The client uses windowsize and blksize options in the first WRQ.
+ *   * Upon reception of an ERROR from the server, the client re-tries
+ *     opening a session using a WRQ without options.
+ *   * The second WRQ is sent to the TFTP server port 69 and not to the
+ *     port from which the first ERROR packet came from.
+ *   * The server accepts the WRQ without options by sending an ACK packet.
+ *   * The first data packet is half filled and signals the end of the transfer.
+ */
+T_TEST_CASE_FIXTURE( write_file_fallback_to_no_options,
+  &fixture_default_options )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_written;
+  size_t pos_in_file = 0;
+  uint16_t block_num = 0;
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_wrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_DEFAULT_BLOCK_SIZE,
+    TFTP_DEFAULT_WINDOW_SIZE,
+    true
+  );
+  _Tftp_Add_interaction_recv_error(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_ERROR_CODE_ILLEGAL,
+    "Don't like options",
+    true
+  );
+  _Tftp_Add_interaction_send_wrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    NO_BLOCK_SIZE_OPTION,
+    NO_WINDOW_SIZE_OPTION,
+    true
+  );
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_send_data(
+    TFTP_FIRST_FD,
+    block_num,
+    pos_in_file,
+    TFTP_RFC1350_BLOCK_SIZE / 2,
+    get_file_content,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  pos_in_file += TFTP_RFC1350_BLOCK_SIZE / 2;
+  _Tftp_Add_interaction_recv_ack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    block_num++,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_written = write_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    pos_in_file, /* Size of file */
+    2 * TFTP_RFC1350_BLOCK_SIZE, /* Bytes written per call to write() */
+    &ctx->fd0
+  );
+  T_eq_int( bytes_written, pos_in_file );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * The server sends a malformed OACK: The final 0 byte is missing.
+ */
+T_TEST_CASE_FIXTURE( OACK_without_null, &fixture_default_options )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_read;
+  const char options[] = { 'b', 'l', 'k', 's', 'i', 'z', 'e', '\0', '1', '2' };
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_rrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_DEFAULT_BLOCK_SIZE,
+    TFTP_DEFAULT_WINDOW_SIZE,
+    true
+  );
+  _Tftp_Add_interaction_recv_oack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    options,
+    sizeof( options ),
+    true
+  );
+  _Tftp_Add_interaction_send_error(
+    TFTP_FIRST_FD,
+    TFTP_ERROR_CODE_OPTION_NEGO,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_read = read_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    LARGE_BLOCK_SIZE, /* Bytes read per call to read() */
+    SIZE_MAX,
+    &ctx->fd0
+  );
+  T_eq_int( errno, EPROTO );
+  T_eq_int( bytes_read, 0 );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Server sends a malformed OACK packet: The value of the option is missing.
+ */
+T_TEST_CASE_FIXTURE( OACK_without_option_value, &fixture_default_options )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_read;
+  const char options[] = TFTP_OPTION_BLKSIZE;
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_rrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_DEFAULT_BLOCK_SIZE,
+    TFTP_DEFAULT_WINDOW_SIZE,
+    true
+  );
+  _Tftp_Add_interaction_recv_oack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    options,
+    sizeof( options ),
+    true
+  );
+  _Tftp_Add_interaction_send_error(
+    TFTP_FIRST_FD,
+    TFTP_ERROR_CODE_OPTION_NEGO,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_read = read_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    LARGE_BLOCK_SIZE, /* Bytes read per call to read() */
+    SIZE_MAX,
+    &ctx->fd0
+  );
+  T_eq_int( errno, EPROTO );
+  T_eq_int( bytes_read, 0 );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Server sends a malformed OACK packet: The option is unknown.
+ */
+T_TEST_CASE_FIXTURE( OACK_with_unknown_option, &fixture_default_options )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_read;
+  const char options[] =
+    "shoesize" "\0"
+    RTEMS_XSTRING( TFTP_DEFAULT_BLOCK_SIZE );
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_rrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_DEFAULT_BLOCK_SIZE,
+    TFTP_DEFAULT_WINDOW_SIZE,
+    true
+  );
+  _Tftp_Add_interaction_recv_oack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    options,
+    sizeof( options ),
+    true
+  );
+  _Tftp_Add_interaction_send_error(
+    TFTP_FIRST_FD,
+    TFTP_ERROR_CODE_OPTION_NEGO,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_read = read_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    LARGE_BLOCK_SIZE, /* Bytes read per call to read() */
+    SIZE_MAX,
+    &ctx->fd0
+  );
+  T_eq_int( errno, EPROTO );
+  T_eq_int( bytes_read, 0 );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Server sends a malformed OACK packet: The value of the option is
+ * not a number.
+ */
+T_TEST_CASE_FIXTURE( OACK_malformed_option_value, &fixture_default_options )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_read;
+  const char options[] = TFTP_OPTION_BLKSIZE "\0" "abc";
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_rrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_DEFAULT_BLOCK_SIZE,
+    TFTP_DEFAULT_WINDOW_SIZE,
+    true
+  );
+  _Tftp_Add_interaction_recv_oack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    options,
+    sizeof( options ),
+    true
+  );
+  _Tftp_Add_interaction_send_error(
+    TFTP_FIRST_FD,
+    TFTP_ERROR_CODE_OPTION_NEGO,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_read = read_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    LARGE_BLOCK_SIZE, /* Bytes read per call to read() */
+    SIZE_MAX,
+    &ctx->fd0
+  );
+  T_eq_int( errno, EPROTO );
+  T_eq_int( bytes_read, 0 );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Server sends a malformed OACK packet: The value of the option is empty.
+ */
+T_TEST_CASE_FIXTURE( OACK_with_empty_option_value, &fixture_default_options )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_read;
+  const char options[] = TFTP_OPTION_BLKSIZE "\0";
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_rrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_DEFAULT_BLOCK_SIZE,
+    TFTP_DEFAULT_WINDOW_SIZE,
+    true
+  );
+  _Tftp_Add_interaction_recv_oack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    options,
+    sizeof( options ),
+    true
+  );
+  _Tftp_Add_interaction_send_error(
+    TFTP_FIRST_FD,
+    TFTP_ERROR_CODE_OPTION_NEGO,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_read = read_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    LARGE_BLOCK_SIZE, /* Bytes read per call to read() */
+    SIZE_MAX,
+    &ctx->fd0
+  );
+  T_eq_int( errno, EPROTO );
+  T_eq_int( bytes_read, 0 );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Server sends a malformed OACK packet: The option name is empty.
+ */
+T_TEST_CASE_FIXTURE( OACK_with_empty_option_name, &fixture_default_options )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_read;
+  const char options[] = "\0" RTEMS_XSTRING( TFTP_DEFAULT_BLOCK_SIZE );
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_rrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_DEFAULT_BLOCK_SIZE,
+    TFTP_DEFAULT_WINDOW_SIZE,
+    true
+  );
+  _Tftp_Add_interaction_recv_oack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    options,
+    sizeof( options ),
+    true
+  );
+  _Tftp_Add_interaction_send_error(
+    TFTP_FIRST_FD,
+    TFTP_ERROR_CODE_OPTION_NEGO,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_read = read_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    LARGE_BLOCK_SIZE, /* Bytes read per call to read() */
+    SIZE_MAX,
+    &ctx->fd0
+  );
+  T_eq_int( errno, EPROTO );
+  T_eq_int( bytes_read, 0 );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Server sends a malformed OACK packet: The block size option value
+ * is too small.
+ */
+T_TEST_CASE_FIXTURE( OACK_blocksize_too_small, &fixture_default_options )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_read;
+  const char options[] = TFTP_OPTION_BLKSIZE "\0" "7";
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_rrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_DEFAULT_BLOCK_SIZE,
+    TFTP_DEFAULT_WINDOW_SIZE,
+    true
+  );
+  _Tftp_Add_interaction_recv_oack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    options,
+    sizeof( options ),
+    true
+  );
+  _Tftp_Add_interaction_send_error(
+    TFTP_FIRST_FD,
+    TFTP_ERROR_CODE_OPTION_NEGO,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_read = read_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    LARGE_BLOCK_SIZE, /* Bytes read per call to read() */
+    SIZE_MAX,
+    &ctx->fd0
+  );
+  T_eq_int( errno, EPROTO );
+  T_eq_int( bytes_read, 0 );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Server sends a malformed OACK packet: The block size option value
+ * is too large.
+ */
+T_TEST_CASE_FIXTURE( OACK_blocksize_too_large, &fixture_default_options )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_read;
+  const char options[] = TFTP_OPTION_BLKSIZE "\0" "1457";
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_rrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_DEFAULT_BLOCK_SIZE,
+    TFTP_DEFAULT_WINDOW_SIZE,
+    true
+  );
+  _Tftp_Add_interaction_recv_oack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    options,
+    sizeof( options ),
+    true
+  );
+  _Tftp_Add_interaction_send_error(
+    TFTP_FIRST_FD,
+    TFTP_ERROR_CODE_OPTION_NEGO,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_read = read_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    LARGE_BLOCK_SIZE, /* Bytes read per call to read() */
+    SIZE_MAX,
+    &ctx->fd0
+  );
+  T_eq_int( errno, EPROTO );
+  T_eq_int( bytes_read, 0 );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Server sends a malformed OACK packet: The windowsize option value
+ * is too small.
+ */
+T_TEST_CASE_FIXTURE( OACK_windowsize_too_small, &fixture_default_options )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_read;
+  const char options[] = TFTP_OPTION_WINDOWSIZE "\0" "0";
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_rrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_DEFAULT_BLOCK_SIZE,
+    TFTP_DEFAULT_WINDOW_SIZE,
+    true
+  );
+  _Tftp_Add_interaction_recv_oack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    options,
+    sizeof( options ),
+    true
+  );
+  _Tftp_Add_interaction_send_error(
+    TFTP_FIRST_FD,
+    TFTP_ERROR_CODE_OPTION_NEGO,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_read = read_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    LARGE_BLOCK_SIZE, /* Bytes read per call to read() */
+    SIZE_MAX,
+    &ctx->fd0
+  );
+  T_eq_int( errno, EPROTO );
+  T_eq_int( bytes_read, 0 );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Server sends a malformed OACK packet: The windowsize option is too large.
+ */
+T_TEST_CASE_FIXTURE( OACK_windowsize_too_large, &fixture_default_options )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_read;
+  const char options[] = TFTP_OPTION_WINDOWSIZE "\0" "9";
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_rrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    TFTP_DEFAULT_BLOCK_SIZE,
+    TFTP_DEFAULT_WINDOW_SIZE,
+    true
+  );
+  _Tftp_Add_interaction_recv_oack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    options,
+    sizeof( options ),
+    true
+  );
+  _Tftp_Add_interaction_send_error(
+    TFTP_FIRST_FD,
+    TFTP_ERROR_CODE_OPTION_NEGO,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_read = read_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    LARGE_BLOCK_SIZE, /* Bytes read per call to read() */
+    SIZE_MAX,
+    &ctx->fd0
+  );
+  T_eq_int( errno, EPROTO );
+  T_eq_int( bytes_read, 0 );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
+#if ENABLE_ALL_TESTS
+/*
+ * Server sends a malformed OACK packet: There is a known but surplus option.
+ */
+T_TEST_CASE_FIXTURE( OACK_with_surplus_option, &fixture_large_blocksize )
+{
+  tftp_test_context *ctx = T_fixture_context();
+  int bytes_read;
+  const char options[] =
+    TFTP_OPTION_BLKSIZE "\0"
+    RTEMS_XSTRING( LARGE_BLOCK_SIZE ) "\0"
+    TFTP_OPTION_WINDOWSIZE "\0"
+    "1";
+
+  /* T_set_verbosity( T_VERBOSE ); */
+  _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD );
+#ifdef RTEMS_NETWORKING
+  _Tftp_Add_interaction_bind( TFTP_FIRST_FD, AF_INET, 0 );
+#endif
+  _Tftp_Add_interaction_send_rrq(
+    TFTP_FIRST_FD,
+    tftpfs_file,
+    TFTP_STD_PORT,
+    tftpfs_ipv4_loopback,
+    LARGE_BLOCK_SIZE,
+    NO_WINDOW_SIZE_OPTION,
+    true
+  );
+  _Tftp_Add_interaction_recv_oack(
+    TFTP_FIRST_FD,
+    FIRST_TIMEOUT_MILLISECONDS,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    options,
+    sizeof( options ),
+    true
+  );
+  _Tftp_Add_interaction_send_error(
+    TFTP_FIRST_FD,
+    TFTP_ERROR_CODE_OPTION_NEGO,
+    SERV_PORT,
+    tftpfs_ipv4_loopback,
+    true
+  );
+  _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 );
+
+  bytes_read = read_tftp_file(
+    create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ),
+    LARGE_BLOCK_SIZE, /* Bytes read per call to read() */
+    SIZE_MAX,
+    &ctx->fd0
+  );
+  T_eq_int( errno, EPROTO );
+  T_eq_int( bytes_read, 0 );
+  T_no_more_interactions();
+}
+#endif /* ENABLE_ALL_TESTS */
+
 /*
  * Test suite and configuration
  */
-- 
2.35.3



More information about the devel mailing list