[PATCH 6/7] TFTPFS: Add tests
Frank Kuehndel
frank.kuehndel at embedded-brains.de
Fri Jun 3 15:22:45 UTC 2022
From: Frank Kühndel <frank.kuehndel at 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
More information about the devel
mailing list