From: Frank Kühndel <frank.kuehn...@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 _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel