From: Frank Kühndel <frank.kuehn...@embedded-brains.de> --- testsuites/fstests/tftpfs/init.c | 3197 ++++++++++++++++++++++++++++-- 1 file changed, 3032 insertions(+), 165 deletions(-)
diff --git a/testsuites/fstests/tftpfs/init.c b/testsuites/fstests/tftpfs/init.c index 91308ec75b..a7ef03cf74 100644 --- a/testsuites/fstests/tftpfs/init.c +++ b/testsuites/fstests/tftpfs/init.c @@ -171,6 +171,32 @@ static const T_fixture fixture_default_options = { .initial_context = &tftp_context }; +static void setup_mount_point( void *context ) +{ + int result; + + _Tftp_Reset(); + result = mkdir( tftpfs_mount_point, S_IRWXU | S_IRWXG | S_IRWXO ); + T_assert_eq_int( result, 0 ); +} + +static void teardown_mount_point( void *context ) +{ + int result; + + result = rmdir( tftpfs_mount_point ); + T_assert_eq_int( result, 0 ); + _Tftp_Reset(); +} + +static const T_fixture fixture_mount_point = { + .setup = setup_mount_point, + .stop = NULL, + .teardown = teardown_mount_point, + .scope = NULL, + .initial_context = &tftp_context +}; + /* * Test helper functions */ @@ -618,191 +644,540 @@ static int rdwt_tftp_client_file( } /* - * Test cases for the TFTP client interface - * - * Since the TFTP file system uses the TFTP client interface for all - * file transfers, the function of the TFTP client is almost - * completely tested by the tests for the file system interface. - * The test cases here - for the TFTP client interface - test only - * those aspects not (easily) testable through the file system interface. + * Unit test cases */ -#if ENABLE_ALL_TESTS /* - * Write a file to the server using the TFTP client interface. - * The test uses the default options. - * The file is 2 and a half data packet long. No timeouts, packet loss, ... + * This is a classical unit test for the function tftp_initialize_net_config(). * Tests: - * * The default options (windowsize = 8 and blocksize = 1456) are used. - * * tftp_open() is called with default configuration values. - * * The test writes a file using only the TFTP client (i.e. not using the - * file system) - * * The code supports the use of a server name instead of an IP address. - * * The first window is also the last window. - * * The only ACK packet is the one at the end of window. - * * Between sending data packets, the client checks whether any packets - * are received. - * * The client handles files correctly which end in the middle of a window. + * * tftp_initialize_net_config() sets correct default values as defined + * in the documentation of the data structures tftp_net_config + * and tftp_options. */ -T_TEST_CASE_FIXTURE( client_write_simple_file, &fixture_default_options ) +T_TEST_CASE( tftp_initialize_net_config ) { - tftp_test_context *ctx = T_fixture_context(); tftp_net_config config; - int bytes_written; - uint16_t block_num = 1; - 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 ); + memset( &config, 0, sizeof( config ) ); + tftp_initialize_net_config( &config ); + T_eq_u16( config.retransmissions, 6 ); + T_eq_u16( config.server_port, 69 ); + T_eq_u32( config.timeout, 1000 ); + T_eq_u32( config.first_timeout, 400 ); + T_eq_u16( config.options.block_size, 1456 ); + T_eq_u16( config.options.window_size, 8 ); +} - /* 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, +/* + * This is a classical unit test for the function tftp_initialize_net_config(). + * Tests: + * * tftp_initialize_net_config() does not crash when called with a + * NULL pointer. + */ +T_TEST_CASE( tftp_initialize_net_config_null ) +{ + tftp_initialize_net_config( NULL ); +} + +/* + * This is a classical unit test for the function tftp_open(). + * Tests: + * * tftp_open() returns an error when called with a NULL pointer + * for hostname. + */ +T_TEST_CASE_FIXTURE( tftp_open_null_hostname, &fixture_rfc1350 ) +{ + tftp_test_context *ctx = T_fixture_context(); + int res; + + res = tftp_open( + NULL, /* hostname */ tftpfs_file, - TFTP_STD_PORT, - tftpfs_server0_ipv4, - TFTP_DEFAULT_BLOCK_SIZE, - TFTP_DEFAULT_WINDOW_SIZE, - true - ); - _Tftp_Add_interaction_recv_oack( - TFTP_FIRST_FD, - FIRST_TIMEOUT_MILLISECONDS, - SERV_PORT, - tftpfs_server0_ipv4, - options, - sizeof( options ), - true - ); - _Tftp_Add_interaction_send_data( - TFTP_FIRST_FD, - block_num++, - pos_in_file, - TFTP_DEFAULT_BLOCK_SIZE, - get_file_content, - SERV_PORT, - tftpfs_server0_ipv4, - true - ); - pos_in_file += TFTP_DEFAULT_BLOCK_SIZE; - _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, - TFTP_DEFAULT_BLOCK_SIZE, - get_file_content, - SERV_PORT, - tftpfs_server0_ipv4, - true - ); - pos_in_file += TFTP_DEFAULT_BLOCK_SIZE; - _Tftp_Add_interaction_recv_nothing( - TFTP_FIRST_FD, - DO_NOT_WAIT_FOR_ANY_TIMEOUT + true, /* is_for_reading */ + NULL, /* config */ + &ctx->tftp_handle ); - _Tftp_Add_interaction_send_data( - TFTP_FIRST_FD, - block_num, - pos_in_file, - TFTP_DEFAULT_BLOCK_SIZE / 2, /* Data bytes in this block */ - get_file_content, - SERV_PORT, - tftpfs_server0_ipv4, - true + T_eq_int( res, EINVAL ); + T_null( ctx->tftp_handle ); +} + +/* + * This is a classical unit test for the function tftp_open(). + * Tests: + * * tftp_open() returns an error when called with a NULL pointer + * for filename. + */ +T_TEST_CASE_FIXTURE( tftp_open_null_filename, &fixture_rfc1350 ) +{ + tftp_test_context *ctx = T_fixture_context(); + int res; + + res = tftp_open( + tftpfs_ipv4_loopback, + NULL, /* filename */ + true, /* is_for_reading */ + NULL, /* config */ + &ctx->tftp_handle ); - pos_in_file += TFTP_DEFAULT_BLOCK_SIZE / 2; - _Tftp_Add_interaction_recv_ack( - TFTP_FIRST_FD, - FIRST_TIMEOUT_MILLISECONDS, - SERV_PORT, - tftpfs_server0_ipv4, - block_num++, - true + T_eq_int( res, EINVAL ); + T_null( ctx->tftp_handle ); +} + +/* + * This is a classical unit test for the function tftp_open(). + * Tests: + * * tftp_open() returns an error when called with a NULL pointer + * for tftp_handle. + */ +T_TEST_CASE( tftp_open_null_tftp_handle ) +{ + int res; + + res = tftp_open( + tftpfs_ipv4_loopback, + tftpfs_file, + true, /* is_for_reading */ + NULL, /* config */ + NULL /* tftp_handle */ ); + T_eq_int( res, EINVAL ); +} + +/* + * This is a classical unit test for the function tftp_open(). + * Tests: + * * tftp_open() returns an error when called with value 0 for + * option window_size. + */ +T_TEST_CASE_FIXTURE( tftp_open_illegal_window_size, &fixture_rfc1350 ) +{ + tftp_test_context *ctx = T_fixture_context(); + tftp_net_config config; + int res; + + _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD ); _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 ); tftp_initialize_net_config( &config ); - bytes_written = rdwt_tftp_client_file( - tftpfs_server0_name, + config.options.window_size = 1 - 1; + + res = tftp_open( + tftpfs_ipv4_loopback, tftpfs_file, - false, /* is_for_reading */ - pos_in_file, /* file_size for writing files only */ + true, /* is_for_reading */ &config, &ctx->tftp_handle ); - T_eq_sz( bytes_written, pos_in_file ); - T_eq_int( errno, 0 ); + T_eq_int( res, EINVAL ); + T_null( ctx->tftp_handle ); T_no_more_interactions(); } -#endif /* ENABLE_ALL_TESTS */ /* - * Test cases for the file system interface + * This is a classical unit test for the function tftp_open(). + * Tests: + * * tftp_open() returns an error when called with a too small + * value for option block_size. */ +T_TEST_CASE_FIXTURE( tftp_open_block_size_too_small, &fixture_rfc1350 ) +{ + tftp_test_context *ctx = T_fixture_context(); + tftp_net_config config; + int res; + + _Tftp_Add_interaction_socket( AF_INET, SOCK_DGRAM, 0, TFTP_FIRST_FD ); + _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 ); + + tftp_initialize_net_config( &config ); + config.options.block_size = 8 - 1; + + res = tftp_open( + tftpfs_ipv4_loopback, + tftpfs_file, + true, /* is_for_reading */ + &config, + &ctx->tftp_handle + ); + T_eq_int( res, EINVAL ); + T_null( ctx->tftp_handle ); + T_no_more_interactions(); +} -#if ENABLE_ALL_TESTS /* - * Read a file from the server using only RFC1350. - * The file is two and a half data packet long. No timeouts, packet loss, ... + * This is a classical unit test for the function tftp_open(). * 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 and second data packet are full. - * * The third data packet signals the end of transfer. - * * Read the file from file system in one big chunk of exactly - * the size of the file. + * * tftp_open() returns an error when called with a too large + * value for option block_size. */ -T_TEST_CASE_FIXTURE( read_simple_file, &fixture_rfc1350 ) +T_TEST_CASE_FIXTURE( tftp_open_block_size_too_large, &fixture_rfc1350 ) { tftp_test_context *ctx = T_fixture_context(); - int bytes_read; - uint16_t block_num = 1; - size_t pos_in_file = 0; + tftp_net_config config; + int res; - /* 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_data( - TFTP_FIRST_FD, - FIRST_TIMEOUT_MILLISECONDS, - SERV_PORT, + _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 ); + + tftp_initialize_net_config( &config ); + config.options.block_size = 65464 + 1; + + res = tftp_open( tftpfs_ipv4_loopback, - block_num, - pos_in_file, - TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ - get_file_content, - true + tftpfs_file, + false, /* is_for_reading */ + &config, + &ctx->tftp_handle ); - pos_in_file += TFTP_RFC1350_BLOCK_SIZE; - _Tftp_Add_interaction_send_ack( - TFTP_FIRST_FD, - block_num++, - SERV_PORT, - tftpfs_ipv4_loopback, - true + T_eq_int( res, EINVAL ); + T_null( ctx->tftp_handle ); + T_no_more_interactions(); +} + +/* + * This is a classical unit test for the function tftp_read(). + * Tests: + * * tftp_read() returns an error when called with a NULL pointer + * for tftp_handle. + */ +T_TEST_CASE( tftp_read_null_tftp_handle ) +{ + char data_buffer[10]; + ssize_t res; + + res = tftp_read( + NULL, /* tftp_handle */ + data_buffer, + sizeof( data_buffer) + ); + T_eq_int( res, -EIO ); +} + +/* + * This is a classical unit test for the function tftp_read(). + * Tests: + * * tftp_read() returns an error when called with a NULL pointer + * for buffer. + */ +T_TEST_CASE( tftp_read_null_buffer ) +{ + int tftp_handle; + ssize_t res; + + res = tftp_read( + &tftp_handle, + NULL, /* buffer */ + 8 + ); + T_eq_int( res, -EIO ); +} + +/* + * This is a classical unit test for the function tftp_write(). + * Tests: + * * tftp_write() returns an error when called with a NULL pointer + * for tftp_handle. + */ +T_TEST_CASE( tftp_write_null_tftp_handle ) +{ + char data_buffer[10]; + ssize_t res; + + res = tftp_write( + NULL, /* tftp_handle */ + data_buffer, + sizeof( data_buffer) + ); + T_eq_int( res, -EIO ); +} + +/* + * This is a classical unit test for the function tftp_write(). + * Tests: + * * tftp_write() returns an error when called with a NULL pointer + * for buffer. + */ +T_TEST_CASE( tftp_write_null_buffer ) +{ + int tftp_handle; + ssize_t res; + + res = tftp_write( + &tftp_handle, + NULL, /* buffer */ + 8 + ); + T_eq_int( res, -EIO ); +} + +/* + * This is a classical unit test for the function tftp_close(). + * Tests: + * * tftp_close() returns 0 when called with a NULL pointer. + */ +T_TEST_CASE( tftp_close_null ) +{ + T_eq_int( tftp_close( NULL ), 0 ); +} + +/* + * This is a classical unit test for the function _Tftpfs_Parse_options(). + * Tests: + * * Parsing an empty string has no effects. + */ +T_TEST_CASE( _Tftpfs_Parse_options_empty ) +{ + size_t err_pos; + uint32_t flags = 0; + tftp_net_config config; + + tftp_initialize_net_config( &config ); + err_pos = _Tftpfs_Parse_options( "", &config, &flags ); + T_eq_sz( err_pos, 0 ); + T_eq_u16( config.options.block_size, TFTP_DEFAULT_BLOCK_SIZE ); + T_eq_u16( config.options.window_size, TFTP_DEFAULT_WINDOW_SIZE ); + T_eq_u32( flags, 0 ); +} + +/* + * This is a classical unit test for the function _Tftpfs_Parse_options(). + * Tests: + * * Providing an NULL pointer instead of a string has no effect. + */ +T_TEST_CASE( _Tftpfs_Parse_options_null ) +{ + size_t err_pos; + uint32_t flags = 0; + tftp_net_config config; + + tftp_initialize_net_config( &config ); + err_pos = _Tftpfs_Parse_options( NULL, &config, &flags ); + T_eq_sz( err_pos, 0 ); + T_eq_u16( config.options.block_size, TFTP_DEFAULT_BLOCK_SIZE ); + T_eq_u16( config.options.window_size, TFTP_DEFAULT_WINDOW_SIZE ); + T_eq_u32( flags, 0 ); +} + +/* + * This is a classical unit test for the function _Tftpfs_Parse_options(). + * Tests: + * * Option "verbose" has the desired effect. + */ +T_TEST_CASE( _Tftpfs_Parse_options_verbose ) +{ + size_t err_pos; + uint32_t flags = 0; + tftp_net_config config; + + tftp_initialize_net_config( &config ); + err_pos = _Tftpfs_Parse_options( "verbose", &config, &flags ); + T_eq_sz( err_pos, 0 ); + T_eq_u16( config.options.block_size, TFTP_DEFAULT_BLOCK_SIZE ); + T_eq_u16( config.options.window_size, TFTP_DEFAULT_WINDOW_SIZE ); + T_gt_u32( flags, 0 ); +} + +/* + * This is a classical unit test for the function _Tftpfs_Parse_options(). + * Tests: + * * Option "rfc1350" has the desired effect. + */ +T_TEST_CASE( _Tftpfs_Parse_options_rfc1350 ) +{ + size_t err_pos; + uint32_t flags = 0; + tftp_net_config config; + + tftp_initialize_net_config( &config ); + err_pos = _Tftpfs_Parse_options( "rfc1350", &config, &flags ); + T_eq_sz( err_pos, 0 ); + T_eq_u16( config.options.block_size, TFTP_RFC1350_BLOCK_SIZE ); + T_eq_u16( config.options.window_size, TFTP_RFC1350_WINDOW_SIZE ); + T_eq_u32( flags, 0 ); +} + +/* + * This is a classical unit test for the function _Tftpfs_Parse_options(). + * Tests: + * * Option "blocksize" has the desired effect. + */ +T_TEST_CASE( _Tftpfs_Parse_options_blocksize ) +{ + size_t err_pos; + uint32_t flags = 0; + tftp_net_config config; + + tftp_initialize_net_config( &config ); + err_pos = _Tftpfs_Parse_options( "blocksize=21", &config, &flags ); + T_eq_sz( err_pos, 0 ); + T_eq_u16( config.options.block_size, 21 ); + T_eq_u16( config.options.window_size, TFTP_DEFAULT_WINDOW_SIZE ); + T_eq_u32( flags, 0 ); +} + +/* + * This is a classical unit test for the function _Tftpfs_Parse_options(). + * Tests: + * * Option "windowsize" has the desired effect. + */ +T_TEST_CASE( _Tftpfs_Parse_options_windowsize ) +{ + size_t err_pos; + uint32_t flags = 0; + tftp_net_config config; + + tftp_initialize_net_config( &config ); + err_pos = _Tftpfs_Parse_options( "windowsize=13", &config, &flags ); + T_eq_sz( err_pos, 0 ); + T_eq_u16( config.options.block_size, TFTP_DEFAULT_BLOCK_SIZE ); + T_eq_u16( config.options.window_size, 13 ); + T_eq_u32( flags, 0 ); +} + +/* + * This is a classical unit test for the function _Tftpfs_Parse_options(). + * Tests: + * * Processing of all options in one string works as expected. + */ +T_TEST_CASE( _Tftpfs_Parse_options_all ) +{ + size_t err_pos; + uint32_t flags = 0; + tftp_net_config config; + + tftp_initialize_net_config( &config ); + err_pos = _Tftpfs_Parse_options( "rfc1350,blocksize=1234,windowsize=4567,verbose", &config, &flags ); + T_eq_sz( err_pos, 0 ); + T_eq_u16( config.options.block_size, 1234 ); + T_eq_u16( config.options.window_size, 4567 ); + T_gt_u32( flags, 0 ); +} + +/* + * This is a classical unit test for the function _Tftpfs_Parse_options(). + * Tests: + * * Parser ignores unnecessary commas. + */ +T_TEST_CASE( _Tftpfs_Parse_options_surplus_comma ) +{ + size_t err_pos; + uint32_t flags = 0; + tftp_net_config config; + + tftp_initialize_net_config( &config ); + err_pos = _Tftpfs_Parse_options( ",blocksize=1234,,,,windowsize=4567,,", &config, &flags ); + T_eq_sz( err_pos, 0 ); + T_eq_u16( config.options.block_size, 1234 ); + T_eq_u16( config.options.window_size, 4567 ); + T_eq_u32( flags, 0 ); +} + +/* + * This is a classical unit test for the function _Tftpfs_Parse_options(). + * Tests: + * * Parser detects a bad value. + */ +T_TEST_CASE( _Tftpfs_Parse_options_bad_value ) +{ + size_t err_pos; + uint32_t flags = 0; + tftp_net_config config; + + tftp_initialize_net_config( &config ); + err_pos = _Tftpfs_Parse_options( "blocksize=123.4,windowsize=4567", &config, &flags ); + T_eq_sz( err_pos, 14 ); +} + +/* + * This is a classical unit test for the function _Tftpfs_Parse_options(). + * Tests: + * * Parser detects an illegal option. + */ +T_TEST_CASE( _Tftpfs_Parse_options_illegal_option ) +{ + size_t err_pos; + uint32_t flags = 0; + tftp_net_config config; + + tftp_initialize_net_config( &config ); + err_pos = _Tftpfs_Parse_options( "blocksize=123,illegal", &config, &flags ); + T_eq_sz( err_pos, 15 ); +} + +/* + * This is a classical unit test for the function _Tftpfs_Parse_options(). + * Tests: + * * Parser detects a truncated option. + */ +T_TEST_CASE( _Tftpfs_Parse_options_truncated_option ) +{ + size_t err_pos; + uint32_t flags = 0; + tftp_net_config config; + + tftp_initialize_net_config( &config ); + err_pos = _Tftpfs_Parse_options( "blocksize", &config, &flags ); + T_eq_sz( err_pos, 1 ); +} + +/* + * This is a classical unit test for the function rtems_tftpfs_initialize(). + * Tests: + * * Correct error handling in case mount options cannot be parsed. + */ +T_TEST_CASE_FIXTURE( mount_with_bad_options, &fixture_mount_point ) +{ + int result; + + result = mount( + "", + tftpfs_mount_point, + RTEMS_FILESYSTEM_TYPE_TFTPFS, + RTEMS_FILESYSTEM_READ_WRITE, + "windowsize=4567,blocksize=123bad" + ); + T_assert_le_int( result, -1 ); + T_assert_eq_int( errno, EINVAL ); +} + +/* + * Test cases for the TFTP client interface + * + * Since the TFTP file system uses the TFTP client interface for all + * file transfers, the function of the TFTP client is almost + * completely tested by the tests for the file system interface. + * The test cases here - for the TFTP client interface - test only + * those aspects not (easily) testable through the file system interface. + */ + +#if ENABLE_ALL_TESTS +/* + * Read a file from the server using the TFTP client interface. + * The file is one byte long. No timeouts, packet loss, ... + * Tests: + * * tftp_open() called with NULL for config uses + * default configuration values. + * * Read a file using only the TFTP client (i.e. not using the + * file system) + */ +T_TEST_CASE_FIXTURE( client_open_with_NULL_config, &fixture_rfc1350 ) +{ + 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, @@ -811,11 +1186,97 @@ T_TEST_CASE_FIXTURE( read_simple_file, &fixture_rfc1350 ) tftpfs_ipv4_loopback, block_num, pos_in_file, - TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + 1, /* Number of bytes transferred */ get_file_content, true ); - pos_in_file += TFTP_RFC1350_BLOCK_SIZE; + 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 = rdwt_tftp_client_file( + tftpfs_ipv4_loopback, + tftpfs_file, + true, /* is_for_reading */ + -1, /* file_size for writing files only */ + NULL, /* config */ + &ctx->tftp_handle + ); + T_eq_sz( bytes_read, pos_in_file ); + T_eq_int( errno, 0 ); + T_no_more_interactions(); +} +#endif /* ENABLE_ALL_TESTS */ + +#if ENABLE_ALL_TESTS +/* + * Read a very short file from the server using the TFTP client interface. + * The file is one data packet long. Use none-default configuration values. + * The second and the third DATA packets are lost. This causes + * a termination of the connection because only two retransmissions are + * configured. + * Tests: + * * tftp_open() called with all configuration values having + * none default values. + * * The test writes a file using only the TFTP client (i.e. not using the + * file system API). + * * The client uses the none default configuration values: + * retransmissions, server_port, timeout, first_timeout, + * block_size, window_size. + * * The server sends the options in a different order than the client. + * * The option names in the OACK can be upper or lower case. + * * If windowsize > 1, the client sends ACK only each windowsize packet. + * * If windowsize > 1 and no packet is received in the timeout period, + * the client retransmits the last ACK. + * * The client makes a limited number of retransmissions attempts + * and then terminates the connections with an error. + */ +T_TEST_CASE_FIXTURE( client_open_with_none_default_config, &fixture_rfc1350 ) +{ + tftp_test_context *ctx = T_fixture_context(); + tftp_net_config config; + int bytes_read; + uint16_t block_num = 0; + size_t pos_in_file = 0; + uint16_t retransmissions = 2; + uint16_t server_port = 3456; + uint32_t timeout = 300; + uint32_t first_timeout = 200; + uint16_t block_size = 8; + uint16_t window_size = 2; + const char options[] = + "WINDOWSIZE" "\0" "2\0" + TFTP_OPTION_BLKSIZE "\0" "8"; + + /* 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, + server_port, + tftpfs_ipv4_loopback, + block_size, + window_size, + true + ); + _Tftp_Add_interaction_recv_oack( + TFTP_FIRST_FD, + first_timeout, + SERV_PORT, + tftpfs_ipv4_loopback, + options, + sizeof( options ), + true + ); _Tftp_Add_interaction_send_ack( TFTP_FIRST_FD, block_num++, @@ -825,16 +1286,28 @@ T_TEST_CASE_FIXTURE( read_simple_file, &fixture_rfc1350 ) ); _Tftp_Add_interaction_recv_data( TFTP_FIRST_FD, - FIRST_TIMEOUT_MILLISECONDS, + first_timeout, + SERV_PORT, + tftpfs_ipv4_loopback, + block_num++, + pos_in_file, + block_size, /* Number of bytes transferred */ + get_file_content, + true + ); + pos_in_file += TFTP_RFC1350_BLOCK_SIZE; + _Tftp_Add_interaction_recv_data( + TFTP_FIRST_FD, + first_timeout, SERV_PORT, tftpfs_ipv4_loopback, block_num, pos_in_file, - TFTP_RFC1350_BLOCK_SIZE / 2, /* Number of bytes transferred */ + block_size, /* Number of bytes transferred */ get_file_content, true ); - pos_in_file += TFTP_RFC1350_BLOCK_SIZE / 2; + pos_in_file += TFTP_RFC1350_BLOCK_SIZE; _Tftp_Add_interaction_send_ack( TFTP_FIRST_FD, block_num++, @@ -842,20 +1315,2414 @@ T_TEST_CASE_FIXTURE( read_simple_file, &fixture_rfc1350 ) 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 ), - /* Bytes read per call to read() */ - 2 * TFTP_RFC1350_BLOCK_SIZE + TFTP_RFC1350_BLOCK_SIZE / 2, - SIZE_MAX, - &ctx->fd0 - ); + _Tftp_Add_interaction_recv_nothing( + TFTP_FIRST_FD, + first_timeout /* Timeout: No packet received within timeout period */ + ); + _Tftp_Add_interaction_send_ack( + TFTP_FIRST_FD, + block_num - 1, /* Block number OK: Last block successfully received */ + SERV_PORT, + tftpfs_ipv4_loopback, + true + ); + _Tftp_Add_interaction_recv_nothing( + TFTP_FIRST_FD, + timeout /* Timeout: No packet received within timeout period */ + ); + _Tftp_Add_interaction_send_error( + TFTP_FIRST_FD, + TFTP_ERROR_CODE_NO_USER, + SERV_PORT, + tftpfs_ipv4_loopback, + true + ); + _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 ); + + tftp_initialize_net_config( &config ); + config.retransmissions = retransmissions; + config.server_port = server_port; + config.timeout = timeout; + config.first_timeout = first_timeout; + config.options.block_size = block_size; + config.options.window_size = window_size; + + bytes_read = rdwt_tftp_client_file( + tftpfs_ipv4_loopback, + tftpfs_file, + true, /* is_for_reading */ + -1, /* file_size for writing files only */ + &config, + &ctx->tftp_handle + ); + + /* + * Not a bug but not nice: The client has received data before the connection + * breaks down due to timeout and this data is not provided to the user. + */ + + T_eq_sz( bytes_read, 0 ); + T_eq_int( errno, EIO ); + T_no_more_interactions(); +} +#endif /* ENABLE_ALL_TESTS */ + +#if ENABLE_ALL_TESTS +/* + * Attempt to write to a file open for reading using the TFTP client interface. + * Tests: + * * tftp_open() called with NULL for config uses + * default configuration values. + * * Read a file using only the TFTP client (i.e. not using the + * file system) + * * The attempt to write to a file open for reading is rejected + * with an error. + * * The server receives an error message to indicate that the client + * closes the connection without having transferred data. + */ +T_TEST_CASE_FIXTURE( client_write_to_file_opened_for_reading, &fixture_rfc1350 ) +{ + tftp_test_context *ctx = T_fixture_context(); + int res = 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 + ); + /* Sending an ACK at this place before the ERROR would be OK, too. */ + _Tftp_Add_interaction_send_error( + TFTP_FIRST_FD, + TFTP_ERROR_CODE_NO_USER, + SERV_PORT, + tftpfs_ipv4_loopback, + true + ); + _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 ); + + res = tftp_open( + tftpfs_ipv4_loopback, + tftpfs_file, + true, /* is_for_reading */ + NULL, /* Config */ + &ctx->tftp_handle + ); + T_eq_int( res, 0 ); + T_assert_not_null( ctx->tftp_handle ); + + res = (int) tftp_write( ctx->tftp_handle, &res, 1 ); + T_eq_int( res, -EIO ); + + res = tftp_close( ctx->tftp_handle ); + ctx->tftp_handle = NULL; /* Avoid that the fixture closes it again */ + T_eq_int( res, 0 ); + T_no_more_interactions(); +} +#endif /* ENABLE_ALL_TESTS */ + +#if ENABLE_ALL_TESTS +/* + * Attempt to read from a file open for writing using the TFTP client + * interface. + * Tests: + * * tftp_open() called with NULL for config uses + * default configuration values. + * * Read a file using only the TFTP client (i.e. not using the + * file system) + * * Attempt to read from a file open for writing is rejected with an error. + */ +T_TEST_CASE_FIXTURE( client_read_to_file_opened_for_writing, &fixture_rfc1350 ) +{ + tftp_test_context *ctx = T_fixture_context(); + int res = 0; + uint16_t block_num = 1; + 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_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 + ); + _Tftp_Add_interaction_send_data( + TFTP_FIRST_FD, + block_num, + pos_in_file, + 0, /* Data size */ + 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 ); + + res = tftp_open( + tftpfs_ipv4_loopback, + tftpfs_file, + false, /* is_for_reading */ + NULL, /* config */ + &ctx->tftp_handle + ); + T_eq_int( res, 0 ); + T_assert_not_null( ctx->tftp_handle ); + + res = (int) tftp_read( ctx->tftp_handle, &res, 1 ); + T_eq_int( res, -EIO ); + + res = tftp_close( ctx->tftp_handle ); + ctx->tftp_handle = NULL; /* Avoid that the fixture closes it again */ + T_eq_int( res, 0 ); + T_no_more_interactions(); +} +#endif /* ENABLE_ALL_TESTS */ + +#if ENABLE_ALL_TESTS +/* + * Write a file to the server using the TFTP client interface. + * The test uses the default options. + * The file is 2 and a half data packet long. No timeouts, packet loss, ... + * Tests: + * * The default options (windowsize = 8 and blocksize = 1456) are used. + * * tftp_open() is called with default configuration values. + * * The test writes a file using only the TFTP client (i.e. not using the + * file system) + * * The code supports the use of a server name instead of an IP address. + * * The first window is also the last window. + * * The only ACK packet is the one at the end of window. + * * Between sending data packets, the client checks whether any packets + * are received. + * * The client handles files correctly which end in the middle of a window. + */ +T_TEST_CASE_FIXTURE( client_write_simple_file, &fixture_default_options ) +{ + tftp_test_context *ctx = T_fixture_context(); + tftp_net_config config; + int bytes_written; + uint16_t block_num = 1; + 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_wrq( + TFTP_FIRST_FD, + tftpfs_file, + TFTP_STD_PORT, + tftpfs_server0_ipv4, + TFTP_DEFAULT_BLOCK_SIZE, + TFTP_DEFAULT_WINDOW_SIZE, + true + ); + _Tftp_Add_interaction_recv_oack( + TFTP_FIRST_FD, + FIRST_TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_server0_ipv4, + options, + sizeof( options ), + true + ); + _Tftp_Add_interaction_send_data( + TFTP_FIRST_FD, + block_num++, + pos_in_file, + TFTP_DEFAULT_BLOCK_SIZE, + get_file_content, + SERV_PORT, + tftpfs_server0_ipv4, + true + ); + pos_in_file += TFTP_DEFAULT_BLOCK_SIZE; + _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, + TFTP_DEFAULT_BLOCK_SIZE, + get_file_content, + SERV_PORT, + tftpfs_server0_ipv4, + true + ); + pos_in_file += TFTP_DEFAULT_BLOCK_SIZE; + _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, + TFTP_DEFAULT_BLOCK_SIZE / 2, /* Data bytes in this block */ + get_file_content, + SERV_PORT, + tftpfs_server0_ipv4, + true + ); + pos_in_file += TFTP_DEFAULT_BLOCK_SIZE / 2; + _Tftp_Add_interaction_recv_ack( + TFTP_FIRST_FD, + FIRST_TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_server0_ipv4, + block_num++, + true + ); + _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 ); + + tftp_initialize_net_config( &config ); + bytes_written = rdwt_tftp_client_file( + tftpfs_server0_name, + tftpfs_file, + false, /* is_for_reading */ + pos_in_file, /* file_size for writing files only */ + &config, + &ctx->tftp_handle + ); + T_eq_sz( bytes_written, pos_in_file ); + T_eq_int( errno, 0 ); + T_no_more_interactions(); +} +#endif /* ENABLE_ALL_TESTS */ + +/* + * Test cases for the file system interface + */ + +#if ENABLE_ALL_TESTS +/* + * Read a file from the server using only RFC1350. + * The file is two and a half data packet long. No timeouts, packet loss, ... + * 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 and second data packet are full. + * * The third data packet signals the end of transfer. + * * Read the file from file system in one big chunk of exactly + * the size of the file. + */ +T_TEST_CASE_FIXTURE( read_simple_file, &fixture_rfc1350 ) +{ + 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, + 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, + TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_file_content, + true + ); + pos_in_file += TFTP_RFC1350_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_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_file_content, + true + ); + pos_in_file += TFTP_RFC1350_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_RFC1350_BLOCK_SIZE / 2, /* Number of bytes transferred */ + get_file_content, + true + ); + pos_in_file += TFTP_RFC1350_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 ), + /* Bytes read per call to read() */ + 2 * TFTP_RFC1350_BLOCK_SIZE + TFTP_RFC1350_BLOCK_SIZE / 2, + 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 using only RFC1350. + * The file is one byte long. No timeouts, packet loss, ... + * 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 not full and signals the end of the transfer. + * * The test reads a file from the file system in one-byte chunks. + */ +T_TEST_CASE_FIXTURE( read_tiny_file, &fixture_rfc1350 ) +{ + 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, + 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 ), + 1, /* 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 using only RFC1350. + * The file is one data packet long. No timeouts, packet loss, ... + * 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 data packet is empty and signals the end of the transfer. + * * The client handles an empty data packet correctly as end + * of file indicator. + * * The test reads a file from the file system in chunks of three quarters + * of the block size. + */ +T_TEST_CASE_FIXTURE( read_one_block_file, &fixture_rfc1350 ) +{ + 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, + 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, + TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_file_content, + true + ); + pos_in_file += TFTP_RFC1350_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 ), + /* Bytes read per call to read() */ + TFTP_RFC1350_BLOCK_SIZE / 4 * 3, + 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 using only RFC1350. + * The file is one data packet long. + * The client receives stray packets: + * * A packet from an unknown server (wrong TID) + * * An ACK packet instead of a 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 first data packet is full. + * * The next received packet originates from a wrong TID + * (i.e. wrong connection). + * * Upon reception of a packet with a wrong TID, the client sends + * an ERROR message with code 5 and does not terminate the current + * transfer. + * * The final received packet is an ACK packet instead or the expected + * DATA packet. + * * Upon the reception of an unexpected packet, the client terminates + * the connection by sending an error packet to the server. + */ +T_TEST_CASE_FIXTURE( read_file_stray_packets, &fixture_rfc1350 ) +{ + 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, + 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, + TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_file_content, + true + ); + pos_in_file += TFTP_RFC1350_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 + 1, /* Stray packet with wrong server TID */ + tftpfs_ipv4_loopback, + block_num, + pos_in_file, + TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_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_ack( /* Stray ACK packet */ + TFTP_FIRST_FD, + FIRST_TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_ipv4_loopback, + block_num - 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_read = read_tftp_file( + create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ), + /* Bytes read per call to read() */ + TFTP_RFC1350_BLOCK_SIZE / 4 * 3, + SIZE_MAX, + &ctx->fd0 + ); + T_eq_int( errno, EPROTO ); + /* + * The client received one packet with TFTP_RFC1350_BLOCK_SIZE + * before the error occurred. The test reads in chunks of + * TFTP_RFC1350_BLOCK_SIZE /4 * 3. Thus, after the first chunk + * the client signals the error to the test. + * + * It would be a little improvement if the client would return all + * bytes received before signaling the error. + */ + T_eq_int( bytes_read, TFTP_RFC1350_BLOCK_SIZE / 4 * 3 ); + T_no_more_interactions(); +} +#endif /* ENABLE_ALL_TESTS */ + +#if ENABLE_ALL_TESTS +/* + * Read a file from the server using only RFC1350. + * The file is one data packet long. + * The server sends an error message after the first 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 all packets. + * * The client handles error packets from the server and stops the + * connection by signaling an error to the user on the file system side. + * * The test reads a file from the file system in chunks of three quarters + * of the block size. + */ +T_TEST_CASE_FIXTURE( read_one_block_file_server_error, &fixture_rfc1350 ) +{ + 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, + 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, + TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_file_content, + true + ); + pos_in_file += TFTP_RFC1350_BLOCK_SIZE; + _Tftp_Add_interaction_send_ack( + TFTP_FIRST_FD, + block_num++, + SERV_PORT, + tftpfs_ipv4_loopback, + true + ); + _Tftp_Add_interaction_recv_error( + TFTP_FIRST_FD, + FIRST_TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_ipv4_loopback, + TFTP_ERROR_CODE_NO_ACCESS, + "Cannot read more", + true + ); + _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 ); + + bytes_read = read_tftp_file( + create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ), + /* Bytes read per call to read() */ + TFTP_RFC1350_BLOCK_SIZE / 4 * 3, + SIZE_MAX, + &ctx->fd0 + ); + T_eq_int( bytes_read, TFTP_RFC1350_BLOCK_SIZE / 4 * 3 ); + T_eq_int( errno, EPERM ); + T_no_more_interactions(); +} +#endif /* ENABLE_ALL_TESTS */ + +#if ENABLE_ALL_TESTS +/* + * Read a file from the server using only RFC1350. + * The file is one data packet long. + * The server sends a malformed error packet after the first DATA packet. + * The error message in the packet is not a 0 terminated string + * 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 handles malformed errors from the server and does not crash. + * * The test reads a file from the file system in chunks of three quarters + * of the block size. + */ +T_TEST_CASE_FIXTURE( read_one_block_file_malformed_server_error, &fixture_rfc1350 ) +{ + tftp_test_context *ctx = T_fixture_context(); + int bytes_read; + uint16_t block_num = 1; + size_t pos_in_file = 0; + static const uint8_t packet_malformed_error[] = { + 0x00, 0x05, /* Opcode = TFTP_OPCODE_ERROR */ + 0x00, 0x02, /* Error code = TFTP_ERROR_CODE_NO_ACCESS */ + 'n', 'o', ' ', 'a', 'c', 'c', 'e', 's', 's' /* missing '\0' at the end */ + }; + + /* 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_data( + TFTP_FIRST_FD, + FIRST_TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_ipv4_loopback, + block_num, + pos_in_file, + TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_file_content, + true + ); + pos_in_file += TFTP_RFC1350_BLOCK_SIZE; + _Tftp_Add_interaction_send_ack( + TFTP_FIRST_FD, + block_num++, + SERV_PORT, + tftpfs_ipv4_loopback, + true + ); + _Tftp_Add_interaction_recv_raw( + TFTP_FIRST_FD, + FIRST_TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_ipv4_loopback, + sizeof( packet_malformed_error ), /* Malformed ERROR packet */ + packet_malformed_error, + true + ); + _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 ); + + bytes_read = read_tftp_file( + create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ), + /* Bytes read per call to read() */ + TFTP_RFC1350_BLOCK_SIZE / 4 * 3, + SIZE_MAX, + &ctx->fd0 + ); + T_eq_int( bytes_read, TFTP_RFC1350_BLOCK_SIZE / 4 * 3 ); + T_eq_int( errno, EPERM ); + T_no_more_interactions(); +} +#endif /* ENABLE_ALL_TESTS */ + +#if ENABLE_ALL_TESTS +/* + * Read a file from the server using only RFC1350. + * The reader on the file system side stops after having read half a + * data packet and before having received the whole file and closes the file. + * 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 client handles the closing of the file by the user correctly. + * * The client sends an error to the server after the user stops reading + * the file. + * * The test reads a file from the file system in chunks of block size. + */ +T_TEST_CASE_FIXTURE( read_one_block_close_file, &fixture_rfc1350 ) +{ + 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, + 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, + TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_file_content, + true + ); + /* Sending an ACK at this place before the ERROR would be OK, too. */ + _Tftp_Add_interaction_send_error( + TFTP_FIRST_FD, + TFTP_ERROR_CODE_NO_USER, + 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_RFC1350_BLOCK_SIZE / 4, /* Bytes read per call to read() */ + TFTP_RFC1350_BLOCK_SIZE / 2, /* Max bytes read from this file */ + &ctx->fd0 + ); + T_eq_int( bytes_read, TFTP_RFC1350_BLOCK_SIZE / 2 ); + T_no_more_interactions(); +} +#endif /* ENABLE_ALL_TESTS */ + +#if ENABLE_ALL_TESTS +/* + * Read a file from the server using only RFC1350. + * The reader on the file system side just open()s and then immediately closes + * the file without ever reading a single byte. + * 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 client handles the closing of the file by the user correctly. + * * The client sends an error to the server when the user closes the file. + */ +T_TEST_CASE_FIXTURE( read_close_file_immediately, &fixture_rfc1350 ) +{ + 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, + 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, + TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_file_content, + true + ); + /* Sending an ACK at this place before the ERROR would be OK, too. */ + _Tftp_Add_interaction_send_error( + TFTP_FIRST_FD, + TFTP_ERROR_CODE_NO_USER, + 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_RFC1350_BLOCK_SIZE / 4, /* Bytes read per call to read() */ + 0, /* Max bytes read from this file */ + &ctx->fd0 + ); + T_eq_int( bytes_read, 0 ); + T_no_more_interactions(); +} +#endif /* ENABLE_ALL_TESTS */ + +#if ENABLE_ALL_TESTS +/* + * Read an empty file from the server using only RFC1350. + * No timeouts, packet loss, ... + * Tests: + * * The code supports requests without options (RFC1350 only). + * * The code supports the use of a server name instead of an IP address. + * * 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 all packets. + * * The first data packet has length 0. + * * The client can read empty files from the server. + * * The test reads a file from the file system in one big chunk which + * is larger than the file. + */ +T_TEST_CASE_FIXTURE( read_empty_file, &fixture_rfc1350 ) +{ + 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_server0_ipv4, + NO_BLOCK_SIZE_OPTION, + NO_WINDOW_SIZE_OPTION, + true + ); + _Tftp_Add_interaction_recv_data( + TFTP_FIRST_FD, + FIRST_TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_server0_ipv4, + 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_server0_ipv4, + true + ); + _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 ); + + bytes_read = read_tftp_file( + create_tftpfs_path( tftpfs_server0_name, tftpfs_file ), + /* Bytes read per call to read() */ + TFTP_RFC1350_BLOCK_SIZE, + SIZE_MAX, + &ctx->fd0 + ); + T_assert_eq_int( bytes_read, pos_in_file ); + T_no_more_interactions(); +} +#endif /* ENABLE_ALL_TESTS */ + +#if ENABLE_ALL_TESTS +/* + * Read an empty file from the server using only RFC1350. + * The first two RRQ packets are lost. + * Tests: + * * The code supports requests without options (RFC1350 only). + * * The code supports the use of a server name instead of an IP address. + * * The first packet is sent to standard port 69 of server. + * * The client uses a short time out for first packets. + * * The client uses a longer time out for repeated packets. + * * The client repeats lost RRQs packets. + * * The client does not repeat the ACK packet for the last DATA packet + * which signals the end of transfer. + * * The first data packet is empty and signals the end of the transfer. + * * It is possible to read a file with 0 bytes content from + * the file system. + */ +T_TEST_CASE_FIXTURE( read_empty_file_looing_rrq, &fixture_rfc1350 ) +{ + 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_server0_ipv4, + NO_BLOCK_SIZE_OPTION, + NO_WINDOW_SIZE_OPTION, + true + ); + _Tftp_Add_interaction_recv_nothing( + TFTP_FIRST_FD, /* Timeout: No packet received within timeout period */ + FIRST_TIMEOUT_MILLISECONDS + ); + _Tftp_Add_interaction_send_rrq( + TFTP_FIRST_FD, + tftpfs_file, + TFTP_STD_PORT, + tftpfs_server0_ipv4, + NO_BLOCK_SIZE_OPTION, + NO_WINDOW_SIZE_OPTION, + true + ); + _Tftp_Add_interaction_recv_nothing( + TFTP_FIRST_FD, /* Timeout: No packet received within timeout period */ + TIMEOUT_MILLISECONDS + ); + _Tftp_Add_interaction_send_rrq( + TFTP_FIRST_FD, + tftpfs_file, + TFTP_STD_PORT, + tftpfs_server0_ipv4, + NO_BLOCK_SIZE_OPTION, + NO_WINDOW_SIZE_OPTION, + true + ); + _Tftp_Add_interaction_recv_data( + TFTP_FIRST_FD, + TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_server0_ipv4, + 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_server0_ipv4, + true + ); + _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 ); + + bytes_read = read_tftp_file( + create_tftpfs_path( tftpfs_server0_name, tftpfs_file ), + /* Bytes read per call to read() */ + TFTP_RFC1350_BLOCK_SIZE, + SIZE_MAX, + &ctx->fd0 + ); + T_assert_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 using only RFC1350. + * The file is one and a half data packet long. + * Two data packet are lost (timeout) and the client must repeat the ACK. + * 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. + * * The client uses a short time out for first packets. + * * The client uses a longer time out for repeated packets. + * * The client repeats the ACK packets which are supposed + * to be lost. + * * The client does not repeat the ACK packet for the last DATA packet + * which signals the end of transfer. + * * The test reads a file in chunks of 17 bytes from file system. + */ +T_TEST_CASE_FIXTURE( read_small_file_lost_packets, &fixture_rfc1350 ) +{ + 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, + 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, + TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_file_content, + true + ); + pos_in_file += TFTP_RFC1350_BLOCK_SIZE; + _Tftp_Add_interaction_send_ack( + TFTP_FIRST_FD, + block_num, + SERV_PORT, + 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_ack( + TFTP_FIRST_FD, + block_num, + SERV_PORT, + tftpfs_ipv4_loopback, + true + ); + _Tftp_Add_interaction_recv_nothing( + TFTP_FIRST_FD, /* Timeout: No packet received within timeout period */ + TIMEOUT_MILLISECONDS + ); + _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_MILLISECONDS, + SERV_PORT, + tftpfs_ipv4_loopback, + block_num, + pos_in_file, + TFTP_RFC1350_BLOCK_SIZE / 2, /* Number of bytes transferred */ + get_file_content, + true + ); + pos_in_file += TFTP_RFC1350_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 ), + 17, /* 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 using only RFC1350. + * The file is exactly one data packet long. + * The client receives a malformed DATA packet (wrong op code). + * 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 all packets. + * * The first data packet is full. + * * The client terminates the connection with an error message upon + * reception of a malformed packet. + */ +T_TEST_CASE_FIXTURE( read_small_file_malformed_packet_1, &fixture_rfc1350 ) +{ + tftp_test_context *ctx = T_fixture_context(); + int bytes_read; + uint16_t block_num = 1; + 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_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, + TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_file_content, + true + ); + pos_in_file += TFTP_RFC1350_BLOCK_SIZE; + _Tftp_Add_interaction_send_ack( + TFTP_FIRST_FD, + block_num, + 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_1 ), /* Malformed DATA 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_read = read_tftp_file( + create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ), + TFTP_RFC1350_BLOCK_SIZE / 4, /* Bytes read per call to read() */ + SIZE_MAX, + &ctx->fd0 + ); + T_eq_int( bytes_read, pos_in_file ); + T_eq_int( errno, EPROTO ); + T_no_more_interactions(); +} +#endif /* ENABLE_ALL_TESTS */ + +#if ENABLE_ALL_TESTS +/* + * Read a file from the server using only RFC1350. + * The file is exactly one data packet long. + * The client receives a malformed DATA packet (wrong op code). + * 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 data packet is malformed. + * * The client terminates the connection with an error message upon + * reception of a malformed packet. + */ +T_TEST_CASE_FIXTURE( read_small_file_malformed_packet_2, &fixture_rfc1350 ) +{ + tftp_test_context *ctx = T_fixture_context(); + int bytes_read; + uint16_t block_num = 1; + size_t pos_in_file = 0; + static const uint8_t packet_illegal_opcode_2[] = { 0x03, 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_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, + TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_file_content, + true + ); + pos_in_file += TFTP_RFC1350_BLOCK_SIZE; + _Tftp_Add_interaction_send_ack( + TFTP_FIRST_FD, + block_num, + 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 DATA 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_read = read_tftp_file( + create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ), + TFTP_RFC1350_BLOCK_SIZE / 4, /* Bytes read per call to read() */ + SIZE_MAX, + &ctx->fd0 + ); + T_eq_int( bytes_read, pos_in_file ); + T_eq_int( errno, EPROTO ); + T_no_more_interactions(); +} +#endif /* ENABLE_ALL_TESTS */ + +#if ENABLE_ALL_TESTS +/* + * Read a file from the server using only RFC1350. + * The file is exactly one data packet long. + * The client receives 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 DATA packet received after the RRQ packet is malformed. + * It is too short with only one byte length. + * * The client sends an error and terminates the file transfer upon + * reception of a malformed packet. + */ +T_TEST_CASE_FIXTURE( read_file_malformed_ack_1, &fixture_rfc1350 ) +{ + tftp_test_context *ctx = T_fixture_context(); + int bytes_read; + size_t pos_in_file = 0; + static const uint8_t packet_too_short_1[] = { 0x03 }; + + /* 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_raw( + TFTP_FIRST_FD, + FIRST_TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_ipv4_loopback, + sizeof( packet_too_short_1 ), /* Malformed DATA 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_read = read_tftp_file( + create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ), + TFTP_RFC1350_BLOCK_SIZE / 4, /* Bytes read per call to read() */ + SIZE_MAX, + &ctx->fd0 + ); + T_eq_int( bytes_read, pos_in_file ); + T_eq_int( errno, EPROTO ); + T_no_more_interactions(); +} +#endif /* ENABLE_ALL_TESTS */ + +#if ENABLE_ALL_TESTS +/* + * Read a file from the server using only RFC1350. + * The file is exactly one data packet long. + * The client receives 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 DATA packet received after the RRQ packet is malformed. + * It is too short with only two bytes length. + * * The client sends an error and terminates the file transfer upon + * reception of a malformed packet. + */ +T_TEST_CASE_FIXTURE( read_file_malformed_ack_2, &fixture_rfc1350 ) +{ + tftp_test_context *ctx = T_fixture_context(); + int bytes_read; + size_t pos_in_file = 0; + static const uint8_t packet_too_short_2[] = { 0x00, 0x03 }; + + /* 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_raw( + TFTP_FIRST_FD, + FIRST_TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_ipv4_loopback, + sizeof( packet_too_short_2 ), /* Malformed DATA 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_read = read_tftp_file( + create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ), + TFTP_RFC1350_BLOCK_SIZE / 4, /* Bytes read per call to read() */ + SIZE_MAX, + &ctx->fd0 + ); + T_eq_int( bytes_read, pos_in_file ); + T_eq_int( errno, EPROTO ); + T_no_more_interactions(); +} +#endif /* ENABLE_ALL_TESTS */ + +#if ENABLE_ALL_TESTS +/* + * Read a file from the server using only RFC1350. + * The file is exactly one data packet long. + * The client receives 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 DATA packet received after the RRQ packet is malformed. + * It is too short with only three bytes length. + * * The client sends an error and terminates the file transfer upon + * reception of a malformed packet. + */ +T_TEST_CASE_FIXTURE( read_file_malformed_ack_3, &fixture_rfc1350 ) +{ + tftp_test_context *ctx = T_fixture_context(); + int bytes_read; + size_t pos_in_file = 0; + static const uint8_t packet_too_short_3[] = { 0x00, 0x03, 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_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_raw( + TFTP_FIRST_FD, + FIRST_TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_ipv4_loopback, + sizeof( packet_too_short_3 ), /* Malformed DATA 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_read = read_tftp_file( + create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ), + TFTP_RFC1350_BLOCK_SIZE / 4, /* Bytes read per call to read() */ + SIZE_MAX, + &ctx->fd0 + ); + T_eq_int( bytes_read, pos_in_file ); + T_eq_int( errno, EPROTO ); + T_no_more_interactions(); +} +#endif /* ENABLE_ALL_TESTS */ + +#if ENABLE_ALL_TESTS +/* + * Read a file from the server using only RFC1350. + * The file is exactly one data packet long. + * The client receives a data packet with block number 0. + * 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 received after the RRQ packet is malformed. + * It has block number 0. + * * The client sends an error and terminates the file transfer upon + * reception of a malformed packet. + */ +T_TEST_CASE_FIXTURE( read_file_block_number_0, &fixture_rfc1350 ) +{ + tftp_test_context *ctx = T_fixture_context(); + int bytes_read; + 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, + 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, + 0, /* Wrong block number 0 */ + pos_in_file, + TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_file_content, + 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 ), + TFTP_RFC1350_BLOCK_SIZE / 4, /* Bytes read per call to read() */ + SIZE_MAX, + &ctx->fd0 + ); + T_eq_int( bytes_read, pos_in_file ); + T_eq_int( errno, EPROTO ); + T_no_more_interactions(); +} +#endif /* ENABLE_ALL_TESTS */ + +#if ENABLE_ALL_TESTS +/* + * Read a file from the server using only RFC1350. + * The file is exactly one data packet long. + * The client receives 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 DATA packet received after the RRQ packet is malformed. + * The packet contains an illegal op code. + * * The client sends an error and terminates the file transfer upon + * reception of a malformed packet. + */ +T_TEST_CASE_FIXTURE( read_file_illegal_opcode_1, &fixture_rfc1350 ) +{ + tftp_test_context *ctx = T_fixture_context(); + int bytes_read; + 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_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_raw( + TFTP_FIRST_FD, + FIRST_TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_ipv4_loopback, + sizeof( packet_illegal_opcode_1 ), /* Malformed DATA 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_read = read_tftp_file( + create_tftpfs_path( tftpfs_ipv4_loopback, tftpfs_file ), + TFTP_RFC1350_BLOCK_SIZE / 4, /* Bytes read per call to read() */ + SIZE_MAX, + &ctx->fd0 + ); + T_eq_int( bytes_read, pos_in_file ); + T_eq_int( errno, EPROTO ); + T_no_more_interactions(); +} +#endif /* ENABLE_ALL_TESTS */ + +#if ENABLE_ALL_TESTS +/* + * Read a file from the server using only RFC1350. + * The file is exactly two data packet long. + * The client receives DATA 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. + * * The second RRQ is sent to the TFTP server port 69 and not to the + * port from which the first packet with the wrong block number came from. + * * The client uses a short time out for all packets. + * * The client uses a longer time out for repeated packets. + * * The client handles DATA packets with the wrong block numbers + * appropriately. + * * The third data packet is empty and signals the end of the transfer. + * * Old data packets are ignored (i.e. do not cause a retransmission). + * * Duplicates of the last data packet cause a retransmission of the + * last ACK. + * * The first data packet with a block number larger than the expected one, + * cause a retransmission of ACK or RRQ. (They can appear together + * with the windowsize option.) + * * The test reads a file from the file system in one big chunk with is larger + * than the files size. + */ +T_TEST_CASE_FIXTURE( read_two_block_file_wrong_block_numbers, &fixture_rfc1350 ) +{ + 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, + 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 + 1, /* Wrong block number */ + pos_in_file, + TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_file_content, + 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 + 1, /* Wrong block number */ + pos_in_file, + TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_file_content, + true + ); + _Tftp_Add_interaction_recv_data( + TFTP_FIRST_FD, + FIRST_TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_ipv4_loopback, + block_num, + pos_in_file, + TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_file_content, + true + ); + pos_in_file += TFTP_RFC1350_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 - 1, /* Wrong block number / duplicates last packet */ + pos_in_file, + TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_file_content, + true + ); + _Tftp_Add_interaction_send_ack( + TFTP_FIRST_FD, + block_num - 1, /* Client assumes last ACK got lost and retransmits it. */ + SERV_PORT, + tftpfs_ipv4_loopback, + true + ); + _Tftp_Add_interaction_recv_data( + TFTP_FIRST_FD, + FIRST_TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_ipv4_loopback, + block_num + 1, /* Wrong block number */ + pos_in_file, + TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_file_content, + true + ); + _Tftp_Add_interaction_send_ack( + TFTP_FIRST_FD, + block_num - 1, /* Client assumes last ACK got lost and retransmits it. */ + SERV_PORT, + tftpfs_ipv4_loopback, + true + ); + _Tftp_Add_interaction_recv_data( + TFTP_FIRST_FD, + FIRST_TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_ipv4_loopback, + block_num + 1, /* Wrong block number */ + pos_in_file, + TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_file_content, + true + ); + _Tftp_Add_interaction_recv_data( + TFTP_FIRST_FD, + FIRST_TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_ipv4_loopback, + block_num, + pos_in_file, + TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_file_content, + true + ); + pos_in_file += TFTP_RFC1350_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 - 2, /* Wrong block number */ + pos_in_file, + TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_file_content, + true + ); + _Tftp_Add_interaction_recv_data( + TFTP_FIRST_FD, + FIRST_TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_ipv4_loopback, + block_num + 1, /* Wrong block number */ + pos_in_file, + TFTP_RFC1350_BLOCK_SIZE, /* Number of bytes transferred */ + get_file_content, + true + ); + _Tftp_Add_interaction_send_ack( + TFTP_FIRST_FD, + block_num - 1, /* Client assumes last ACK got lost and retransmits it. */ + 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 ), + /* Bytes read per call to read() */ + 3 * TFTP_RFC1350_BLOCK_SIZE, + SIZE_MAX, + &ctx->fd0 + ); T_eq_int( bytes_read, pos_in_file ); T_no_more_interactions(); } #endif /* ENABLE_ALL_TESTS */ +#if ENABLE_ALL_TESTS +/* + * Attempt to read a file from the server using filename without ':'. + * Tests: + * * The TFTP FS rejects malformed file names (i.e. it does not crash). + */ +T_TEST_CASE_FIXTURE( read_malformed_filename, &fixture_rfc1350 ) +{ + tftp_test_context *ctx = T_fixture_context(); + int bytes_read; + char buffer[100]; + int len; + + len = snprintf( + buffer, + sizeof( buffer ), + "%s/%s", + tftpfs_mount_point, + tftpfs_server0_name + ); + + T_quiet_gt_int( len, 0 ); + T_quiet_lt_int( len, (int) sizeof( buffer ) ); + + bytes_read = read_tftp_file( + buffer, + /* Bytes read per call to read() */ + TFTP_RFC1350_BLOCK_SIZE, + SIZE_MAX, + &ctx->fd0 + ); + T_assert_eq_int( bytes_read, 0 ); + T_assert_eq_int( errno, EINVAL ); +} +#endif /* ENABLE_ALL_TESTS */ + +#if ENABLE_ALL_TESTS +/* + * Attempt to read a file from a none exiting server address. + * Tests: + * * TFTP FS returns an error if the server name cannot be resolved. + */ +T_TEST_CASE_FIXTURE( read_from_unknown_ip_address, &fixture_rfc1350 ) +{ + tftp_test_context *ctx = T_fixture_context(); + int bytes_read; + + bytes_read = read_tftp_file( + create_tftpfs_path( "not-existing-server-address", tftpfs_file ), + /* Bytes read per call to read() */ + TFTP_RFC1350_BLOCK_SIZE, + SIZE_MAX, + &ctx->fd0 + ); + T_assert_eq_int( bytes_read, 0 ); + T_assert_eq_int( errno, ENOENT ); +} +#endif /* ENABLE_ALL_TESTS */ + +#if ENABLE_ALL_TESTS +/* + * Attempt to read a file which the server does not know + * No timeouts, packet loss, ... + * Tests: + * * The code supports requests without options (RFC1350 only). + * * The code supports the use of a server name instead of an IP address. + * * The client handles an ERROR packet received upon sending an RRQ + * correctly. + * * TFTP FS returns an error upon the reception of the ERROR packet. + */ +T_TEST_CASE_FIXTURE( read_not_existing_file, &fixture_rfc1350 ) +{ + 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_server0_ipv4, + NO_BLOCK_SIZE_OPTION, + NO_WINDOW_SIZE_OPTION, + true + ); + _Tftp_Add_interaction_recv_error( + TFTP_FIRST_FD, + FIRST_TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_server0_ipv4, + TFTP_ERROR_CODE_NOT_FOUND, + "No such file", + true + ); + _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 ); + + bytes_read = read_tftp_file( + create_tftpfs_path( tftpfs_server0_name, tftpfs_file ), + /* Bytes read per call to read() */ + TFTP_RFC1350_BLOCK_SIZE, + SIZE_MAX, + &ctx->fd0 + ); + T_assert_eq_int( bytes_read, 0 ); + T_assert_eq_int( errno, ENOENT ); + T_no_more_interactions(); +} +#endif /* ENABLE_ALL_TESTS */ + +#if ENABLE_ALL_TESTS +/* + * Write an empty file to the server using only RFC1350. + * The first two WRQ as well as the first two DATA packets are lost. + * Tests: + * * The code supports requests without options (RFC1350 only). + * * The code supports the use of a server name instead of an IP address. + * * The all WRQ are 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 first packets. + * * The client uses a longer time out for repeated packets. + * * The client repeats the WRQs and DATA packets which are supposed + * to be lost. + * * When a timeout occurs, the client repeats the last and empty packet. + * * The first data packet is empty and signals the end of the transfer. + * * The test writes a file with 0 bytes content to the file system. + */ +T_TEST_CASE_FIXTURE( write_empty_file_packet_losts, &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_server0_ipv4, + NO_BLOCK_SIZE_OPTION, + NO_WINDOW_SIZE_OPTION, + true + ); + _Tftp_Add_interaction_recv_nothing( + TFTP_FIRST_FD, /* Timeout: No packet received within timeout period */ + FIRST_TIMEOUT_MILLISECONDS + ); + _Tftp_Add_interaction_send_wrq( + TFTP_FIRST_FD, + tftpfs_file, + TFTP_STD_PORT, + tftpfs_server0_ipv4, + NO_BLOCK_SIZE_OPTION, + NO_WINDOW_SIZE_OPTION, + true + ); + _Tftp_Add_interaction_recv_nothing( + TFTP_FIRST_FD, /* Timeout: No packet received within timeout period */ + TIMEOUT_MILLISECONDS + ); + _Tftp_Add_interaction_send_wrq( + TFTP_FIRST_FD, + tftpfs_file, + TFTP_STD_PORT, + tftpfs_server0_ipv4, + NO_BLOCK_SIZE_OPTION, + NO_WINDOW_SIZE_OPTION, + true + ); + _Tftp_Add_interaction_recv_ack( + TFTP_FIRST_FD, + TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_server0_ipv4, + block_num++, + true + ); + _Tftp_Add_interaction_send_data( + TFTP_FIRST_FD, + block_num, + pos_in_file, + 0, /* Number of bytes transferred */ + get_file_content, + SERV_PORT, + tftpfs_server0_ipv4, + true + ); + _Tftp_Add_interaction_recv_nothing( + TFTP_FIRST_FD, /* Timeout: No packet received within timeout period */ + FIRST_TIMEOUT_MILLISECONDS + ); + _Tftp_Add_interaction_send_data( + TFTP_FIRST_FD, + block_num, + pos_in_file, + 0, /* Number of bytes transferred */ + get_file_content, + SERV_PORT, + tftpfs_server0_ipv4, + true + ); + _Tftp_Add_interaction_recv_nothing( + TFTP_FIRST_FD, /* Timeout: No packet received within timeout period */ + TIMEOUT_MILLISECONDS + ); + _Tftp_Add_interaction_send_data( + TFTP_FIRST_FD, + block_num, + pos_in_file, + 0, /* Number of bytes transferred */ + get_file_content, + SERV_PORT, + tftpfs_server0_ipv4, + true + ); + _Tftp_Add_interaction_recv_ack( + TFTP_FIRST_FD, + TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_server0_ipv4, + block_num++, + true + ); + _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 ); + + bytes_written = write_tftp_file( + create_tftpfs_path( tftpfs_server0_name, tftpfs_file ), + 0, /* Size of file */ + 4, /* 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 very short file to the server using only RFC1350. + * The file is one and half data packets long. + * The first two DATA packets and one ACK packet are lost. + * Tests: + * * The code supports requests without options (RFC1350 only). + * * The code supports the use of a server name instead of an IP address. + * * 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 first packets. + * * The client uses a longer time out for repeated packets. + * * The client repeats sending DATA packets which are supposed + * to be lost. + * * The client also repeats the last DATA packet when it is supposed + * to be lost. + * * The client repeats sending DATA packets when an ACK packet is repeated + * (presumably the DATA packet has been lost). + * * The test writes a file to the file system in chunks of a quarter of the block size. + */ +T_TEST_CASE_FIXTURE( write_tiny_file_packet_losts, &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_server0_ipv4, + NO_BLOCK_SIZE_OPTION, + NO_WINDOW_SIZE_OPTION, + true + ); + _Tftp_Add_interaction_recv_ack( + TFTP_FIRST_FD, + FIRST_TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_server0_ipv4, + 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_server0_ipv4, + true + ); + _Tftp_Add_interaction_recv_nothing( + TFTP_FIRST_FD, /* Timeout: No packet received within timeout period */ + FIRST_TIMEOUT_MILLISECONDS + ); + _Tftp_Add_interaction_send_data( + TFTP_FIRST_FD, + block_num, + pos_in_file, + TFTP_RFC1350_BLOCK_SIZE, + get_file_content, + SERV_PORT, + tftpfs_server0_ipv4, + true + ); + _Tftp_Add_interaction_recv_nothing( + TFTP_FIRST_FD, /* Timeout: No packet received within timeout period */ + TIMEOUT_MILLISECONDS + ); + _Tftp_Add_interaction_send_data( + TFTP_FIRST_FD, + block_num, + pos_in_file, + TFTP_RFC1350_BLOCK_SIZE, + get_file_content, + SERV_PORT, + tftpfs_server0_ipv4, + true + ); + _Tftp_Add_interaction_recv_ack( + TFTP_FIRST_FD, + TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_server0_ipv4, + block_num++, + true + ); + pos_in_file += TFTP_RFC1350_BLOCK_SIZE; + _Tftp_Add_interaction_send_data( + TFTP_FIRST_FD, + block_num, + pos_in_file, + TFTP_RFC1350_BLOCK_SIZE / 2, + get_file_content, + SERV_PORT, + tftpfs_server0_ipv4, + true + ); + _Tftp_Add_interaction_recv_ack( + TFTP_FIRST_FD, + FIRST_TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_server0_ipv4, + block_num - 1, /* Repeated ACK packet received */ + 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_server0_ipv4, + true + ); + pos_in_file += TFTP_RFC1350_BLOCK_SIZE / 2; + _Tftp_Add_interaction_recv_ack( + TFTP_FIRST_FD, + FIRST_TIMEOUT_MILLISECONDS, + SERV_PORT, + tftpfs_server0_ipv4, + block_num++, + true + ); + _Tftp_Add_interaction_close( TFTP_FIRST_FD, 0 ); + + bytes_written = write_tftp_file( + create_tftpfs_path( tftpfs_server0_name, tftpfs_file ), + TFTP_RFC1350_BLOCK_SIZE / 2 * 3, /* Size of file */ + TFTP_RFC1350_BLOCK_SIZE / 4, /* 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. -- 2.35.3 _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel