summaryrefslogtreecommitdiffstats
path: root/testsuites/fstests/tftpfs/tftpfs_interactions.c
diff options
context:
space:
mode:
Diffstat (limited to 'testsuites/fstests/tftpfs/tftpfs_interactions.c')
-rw-r--r--testsuites/fstests/tftpfs/tftpfs_interactions.c984
1 files changed, 984 insertions, 0 deletions
diff --git a/testsuites/fstests/tftpfs/tftpfs_interactions.c b/testsuites/fstests/tftpfs/tftpfs_interactions.c
new file mode 100644
index 0000000000..9ab026c5da
--- /dev/null
+++ b/testsuites/fstests/tftpfs/tftpfs_interactions.c
@@ -0,0 +1,984 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
+/**
+ * @file
+ *
+ * @ingroup RTEMSTestSuiteTestsTFTPFS
+ *
+ * @brief This source file contains the implementation of network interaction
+ * functions related to the UDP network fake for tftpfs testing.
+ *
+ * The UDP Network Fake requires *interactions* between TFTP client and test
+ * (which emulates a TFTP server). The idea is that each test defines a
+ * sequence of interactions. In a successful test run all interactions must
+ * be carried out one-by-one till the *last* interaction is reached.
+ *
+ * Interactions appear when the TFTP client calls functions like
+ * sendto(), recvfrom(), or socket(). Here functions are defined
+ * which
+ *
+ * * handle such interactions and
+ * * permit the tests to easily defined the sequence of interactions.
+ */
+
+/*
+ * Copyright (C) 2022 embedded brains GmbH & Co. KG
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h> /* sprintf() */
+#include <inttypes.h> /* printf() macros like PRId8 */
+#include <arpa/inet.h> /* ntohs() */
+#include <rtems/test.h>
+
+
+#include "tftpfs_interactions.h"
+#include "tftpfs_udp_network_fake.h"
+
+/*
+ * Interaction: socket()
+ */
+
+typedef struct interaction_data_socket {
+ int domain;
+ int type;
+ int protocol;
+ int result;
+} interaction_data_socket;
+
+static bool interact_socket( Tftp_Action *act, void *data )
+{
+ interaction_data_socket *d = data;
+ T_eq_int( act->data.socket.domain, d->domain );
+ T_eq_int( act->data.socket.type, d->type );
+ T_eq_int( act->data.socket.protocol, d->protocol );
+ if (
+ act->data.socket.domain != d->domain ||
+ act->data.socket.type != d->type ||
+ act->data.socket.protocol != d->protocol
+ ) {
+ return false;
+ }
+
+ act->data.socket.result = d->result;
+ return true;
+}
+
+void _Tftp_Add_interaction_socket(
+ int domain,
+ int type,
+ int protocol,
+ int result
+)
+{
+ interaction_data_socket *d;
+
+ d = _Tftp_Append_interaction(
+ TFTP_IA_KIND_SOCKET,
+ interact_socket,
+ sizeof( interaction_data_socket )
+ );
+
+ d->domain = domain;
+ d->type = type;
+ d->protocol = protocol;
+ d->result = result;
+}
+
+/*
+ * Interaction: close()
+ */
+
+typedef struct interaction_data_close {
+ int fd;
+ int result;
+} interaction_data_close;
+
+static bool interact_close( Tftp_Action *act, void *data )
+{
+ interaction_data_close *d = data;
+ T_eq_int( act->data.close.fd, d->fd );
+ if ( act->data.close.fd != d->fd ) {
+ return false;
+ }
+
+ act->data.close.result = d->result;
+ return true;
+}
+
+void _Tftp_Add_interaction_close( int fd, int result )
+{
+ interaction_data_close *d;
+
+ d = _Tftp_Append_interaction(
+ TFTP_IA_KIND_CLOSE,
+ interact_close,
+ sizeof( interaction_data_close )
+ );
+
+ d->fd = fd;
+ d->result = result;
+}
+
+/*
+ * Interaction: bind()
+ */
+
+typedef struct interaction_data_bind {
+ int fd;
+ int family;
+ int result;
+} interaction_data_bind;
+
+static bool interact_bind( Tftp_Action *act, void *data )
+{
+ interaction_data_bind *d = data;
+ T_eq_int( act->data.bind.fd, d->fd );
+ T_eq_int( act->data.bind.family, d->family );
+ if (
+ act->data.bind.fd != d->fd ||
+ act->data.bind.family != d->family
+ ) {
+ return false;
+ }
+
+ act->data.bind.result = d->result;
+ return true;
+}
+
+void _Tftp_Add_interaction_bind( int fd, int family, int result )
+{
+ interaction_data_bind *d;
+
+ d = _Tftp_Append_interaction(
+ TFTP_IA_KIND_BIND,
+ interact_bind,
+ sizeof( interaction_data_bind )
+ );
+
+ d->fd = fd;
+ d->family = family;
+ d->result = result;
+}
+
+/*
+ * Interaction: sendto()
+ */
+
+#define TFTP_MAX_FILENAME_STRLEN 12
+
+typedef struct interaction_data_sendto {
+ int fd;
+ uint16_t dest_port;
+ char dest_addr_str[TFTP_MAX_IP_ADDR_STRLEN];
+ bool result;
+ union {
+ struct {
+ uint16_t opcode;
+ char filename[TFTP_MAX_FILENAME_STRLEN];
+ uint16_t block_size;
+ uint16_t window_size;
+ } rwrq;
+ struct {
+ uint16_t block_num;
+ } ack;
+ struct {
+ uint16_t block_num;
+ size_t start;
+ size_t len;
+ uint8_t (*get_data)( size_t pos );
+ } data;
+ struct {
+ uint16_t error_code;
+ } error;
+ } content;
+} interaction_data_sendto;
+
+static bool check_filename_and_mode(
+ const char **buf,
+ size_t *max_len,
+ const char *filename
+)
+{
+ const char *str;
+ size_t len;
+
+ /* Make sure there is a 0 byte in the end before comparing strings */
+ if ( *max_len <= 0 ) { return false; };
+ T_quiet_eq_u8( *( *buf + *max_len - 1 ), '\0' );
+
+ str = *buf;
+ len = strlen( *buf ) + 1;
+ *buf += len;
+ *max_len -= len;
+ if ( strcmp( str, filename ) != 0 ) {
+ T_true( false, "Filename '%s' does not match '%s'", str, filename );
+ return false;
+ }
+ if ( *max_len <= 0 ) {
+ T_true( false, "Mode string missing." );
+ return false;
+ }
+
+ str = *buf;
+ len = strlen( *buf ) + 1;
+ *buf += len;
+ *max_len -= len;
+ if ( strcasecmp( str, TFTP_MODE_OCTET ) != 0 ) {
+ T_true( false, "Mode '%s' does not match '%s'", str, TFTP_MODE_OCTET );
+ return false;
+ }
+
+ return true;
+}
+
+static bool check_for_option(
+ const char **buf,
+ size_t *max_len,
+ const char *option_name,
+ const char *option_value
+)
+{
+ const char *str;
+ size_t len;
+
+ /* Make sure there is a 0 byte in the end before comparing strings */
+ if ( *max_len <= 0 ) { return false; };
+ T_quiet_eq_u8( *( *buf + *max_len - 1 ), '\0' );
+
+ str = *buf;
+ len = strlen( *buf ) + 1;
+ if ( strcasecmp( str, option_name ) != 0 ) {
+ return false;
+ }
+ *buf += len;
+ *max_len -= len;
+
+ if ( *max_len <= 0 ) {
+ T_true( false, "Option value for '%s' missing.", option_name );
+ return false;
+ }
+
+ str = *buf;
+ len = strlen( *buf ) + 1;
+ *buf += len;
+ *max_len -= len;
+ if ( strcmp( str, option_value ) != 0 ) {
+ T_true(
+ false,
+ "Option '%s': Actual value '%s' does not match '%s'",
+ option_name,
+ str,
+ option_value
+ );
+ return false;
+ }
+
+ return true;
+}
+
+static bool interact_sendto_common( Tftp_Action *act, void *data )
+{
+ interaction_data_sendto *d = data;
+ const Tftp_Packet *pkt = act->data.sendto.buf;
+
+ T_eq_int( act->data.sendto.fd, d->fd );
+ T_eq_int( act->data.sendto.flags, 0 );
+ T_eq_u16( act->data.sendto.dest_port, d->dest_port );
+ T_eq_str( act->data.sendto.dest_addr_str, d->dest_addr_str );
+ T_gt_sz( act->data.sendto.len, sizeof( pkt->opcode ) );
+ if (
+ act->data.sendto.fd != d->fd ||
+ act->data.sendto.flags != 0 ||
+ act->data.sendto.dest_port != d->dest_port ||
+ strcmp( act->data.sendto.dest_addr_str, d->dest_addr_str ) != 0 ||
+ act->data.sendto.len <= sizeof( pkt->opcode )
+ ) {
+ return false;
+ }
+
+ act->data.sendto.result = d->result ? act->data.sendto.len : -1;
+ return true;
+}
+
+static bool interact_sendto_rwrq( Tftp_Action *act, void *data )
+{
+ interaction_data_sendto *d = data;
+ const Tftp_Packet *pkt = act->data.sendto.buf;
+ size_t len = act->data.sendto.len - sizeof( pkt->opcode );
+ const char *buf = pkt->content.rrq.opts;
+ const char *tmp;
+ char block_size_buffer[6];
+ char window_size_buffer[6];
+
+ if ( !interact_sendto_common( act, data ) ) {
+ return false;
+ }
+
+ T_eq_u16( ntohs( pkt->opcode ), d->content.rwrq.opcode );
+ if ( ntohs( pkt->opcode ) != d->content.rwrq.opcode ) { return false; }
+ if ( !check_filename_and_mode( &buf, &len, d->content.rwrq.filename ) ) {
+ return false;
+ }
+
+ snprintf(
+ block_size_buffer,
+ sizeof( block_size_buffer ),
+ "%"PRIu16,
+ d->content.rwrq.block_size
+ );
+ snprintf(
+ window_size_buffer,
+ sizeof( window_size_buffer ),
+ "%"PRIu16,
+ d->content.rwrq.window_size
+ );
+
+ for ( tmp = buf; len > 0; ) {
+ if ( d->content.rwrq.block_size != NO_BLOCK_SIZE_OPTION &&
+ check_for_option(
+ &buf,
+ &len,
+ TFTP_OPTION_BLKSIZE,
+ block_size_buffer )) {
+ d->content.rwrq.block_size = NO_BLOCK_SIZE_OPTION;
+ } else if ( d->content.rwrq.window_size != NO_WINDOW_SIZE_OPTION &&
+ check_for_option(
+ &buf,
+ &len,
+ TFTP_OPTION_WINDOWSIZE,
+ window_size_buffer )) {
+ d->content.rwrq.window_size = NO_WINDOW_SIZE_OPTION;
+ } else {
+ T_true( false, "Error at option '%s'", tmp );
+ return false;
+ }
+ }
+
+ T_eq_sz( len, 0 ); /* Check that all data till the end has been read */
+
+ return true;
+}
+
+static bool interact_sendto_ack( Tftp_Action *act, void *data )
+{
+ interaction_data_sendto *d = data;
+ const Tftp_Packet *pkt = act->data.sendto.buf;
+ size_t pkt_size = sizeof( pkt->opcode ) + sizeof( pkt->content.ack );
+
+ if ( !interact_sendto_common( act, data ) ) {
+ return false;
+ }
+
+ T_eq_u16( ntohs( pkt->opcode ), TFTP_OPCODE_ACK );
+ T_eq_sz( act->data.sendto.len, pkt_size );
+ if ( ntohs( pkt->opcode ) != TFTP_OPCODE_ACK ||
+ act->data.sendto.len != pkt_size
+ ) {
+ return false;
+ }
+
+ T_eq_u16( ntohs( pkt->content.ack.block_num ), d->content.ack.block_num );
+ if ( ntohs( pkt->content.ack.block_num ) != d->content.ack.block_num ) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool interact_sendto_data( Tftp_Action *act, void *data )
+{
+ interaction_data_sendto *d = data;
+ const Tftp_Packet *pkt = act->data.sendto.buf;
+ size_t pkt_size = sizeof( pkt->opcode ) +
+ sizeof( pkt->content.data.block_num ) + d->content.data.len;
+ size_t i;
+
+ if ( !interact_sendto_common( act, data ) ) {
+ return false;
+ }
+
+ T_eq_u16( ntohs( pkt->opcode ), TFTP_OPCODE_DATA );
+ T_eq_sz( act->data.sendto.len, pkt_size );
+ if ( ntohs( pkt->opcode ) != TFTP_OPCODE_DATA ||
+ act->data.sendto.len != pkt_size
+ ) {
+ return false;
+ }
+
+ T_eq_u16( ntohs( pkt->content.ack.block_num ), d->content.ack.block_num );
+ if ( ntohs( pkt->content.ack.block_num ) != d->content.ack.block_num ) {
+ return false;
+ }
+
+ for ( i = 0; i < d->content.data.len; ++i ) {
+ if ( pkt->content.data.bytes[i] !=
+ d->content.data.get_data( d->content.data.start + i ) ) {
+ T_true(
+ false,
+ "Expected byte %02"PRIx8" but TFTP client sent %02"PRIx8
+ " at position %zu",
+ d->content.data.get_data( d->content.data.start + i ),
+ pkt->content.data.bytes[i],
+ d->content.data.start + i
+ );
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool interact_sendto_error( Tftp_Action *act, void *data )
+{
+ interaction_data_sendto *d = data;
+ const Tftp_Packet *pkt = act->data.sendto.buf;
+ size_t pkt_size = sizeof( pkt->opcode ) + sizeof( pkt->content.error );
+
+ if ( !interact_sendto_common( act, data ) ) {
+ return false;
+ }
+
+ T_eq_u16( ntohs( pkt->opcode ), TFTP_OPCODE_ERROR );
+ T_gt_sz( act->data.sendto.len, pkt_size );
+ if ( ntohs( pkt->opcode ) != TFTP_OPCODE_ERROR ||
+ act->data.sendto.len <= pkt_size
+ ) {
+ return false;
+ }
+
+ T_eq_u16(
+ ntohs( pkt->content.error.error_code ),
+ d->content.error.error_code
+ );
+ if ( ntohs( pkt->content.error.error_code ) != d->content.error.error_code ) {
+ return false;
+ }
+
+ return true;
+}
+
+static void add_interaction_send_rwrq(
+ int fd,
+ const char *filename,
+ uint16_t dest_port,
+ const char *dest_addr_str,
+ uint16_t block_size,
+ uint16_t window_size,
+ bool result,
+ uint16_t opcode
+)
+{
+ interaction_data_sendto *d;
+
+ d = _Tftp_Append_interaction(
+ TFTP_IA_KIND_SENDTO,
+ interact_sendto_rwrq,
+ sizeof( interaction_data_sendto )
+ );
+
+ T_assert_lt_sz( strlen( filename ), TFTP_MAX_FILENAME_STRLEN );
+ T_assert_lt_sz( strlen( dest_addr_str ), TFTP_MAX_IP_ADDR_STRLEN );
+ strcpy( d->content.rwrq.filename, filename );
+ strcpy( d->dest_addr_str, dest_addr_str );
+ d->fd = fd;
+ d->dest_port = dest_port;
+ d->content.rwrq.opcode = opcode;
+ d->content.rwrq.block_size = block_size;
+ d->content.rwrq.window_size = window_size;
+ d->result = result;
+}
+
+void _Tftp_Add_interaction_send_rrq(
+ int fd,
+ const char *filename,
+ uint16_t dest_port,
+ const char *dest_addr_str,
+ uint16_t block_size,
+ uint16_t window_size,
+ bool result
+)
+{
+ add_interaction_send_rwrq(
+ fd,
+ filename,
+ dest_port,
+ dest_addr_str,
+ block_size,
+ window_size,
+ result,
+ TFTP_OPCODE_RRQ
+ );
+}
+
+void _Tftp_Add_interaction_send_wrq(
+ int fd,
+ const char *filename,
+ uint16_t dest_port,
+ const char *dest_addr_str,
+ uint16_t block_size,
+ uint16_t window_size,
+ bool result
+)
+{
+ add_interaction_send_rwrq(
+ fd,
+ filename,
+ dest_port,
+ dest_addr_str,
+ block_size,
+ window_size,
+ result,
+ TFTP_OPCODE_WRQ
+ );
+}
+
+void _Tftp_Add_interaction_send_ack(
+ int fd,
+ uint16_t block_num,
+ uint16_t dest_port,
+ const char *dest_addr_str,
+ bool result
+)
+{
+ interaction_data_sendto *d;
+
+ d = _Tftp_Append_interaction(
+ TFTP_IA_KIND_SENDTO,
+ interact_sendto_ack,
+ sizeof( interaction_data_sendto )
+ );
+
+ T_assert_lt_sz( strlen( dest_addr_str ), TFTP_MAX_IP_ADDR_STRLEN );
+ strcpy( d->dest_addr_str, dest_addr_str );
+ d->fd = fd;
+ d->dest_port = dest_port;
+ d->result = result;
+ d->content.ack.block_num = block_num;
+}
+
+void _Tftp_Add_interaction_send_data(
+ int fd,
+ uint16_t block_num,
+ size_t start,
+ size_t len,
+ uint8_t (*get_data)( size_t pos ),
+ uint16_t dest_port,
+ const char *dest_addr_str,
+ bool result
+)
+{
+ interaction_data_sendto *d;
+
+ d = _Tftp_Append_interaction(
+ TFTP_IA_KIND_SENDTO,
+ interact_sendto_data,
+ sizeof( interaction_data_sendto )
+ );
+
+ T_assert_lt_sz( strlen( dest_addr_str ), TFTP_MAX_IP_ADDR_STRLEN );
+ strcpy( d->dest_addr_str, dest_addr_str );
+ d->fd = fd;
+ d->dest_port = dest_port;
+ d->result = result;
+ d->content.data.block_num = block_num;
+ d->content.data.start = start;
+ d->content.data.len = len;
+ d->content.data.get_data = get_data;
+}
+
+void _Tftp_Add_interaction_send_error(
+ int fd,
+ uint16_t error_code,
+ uint16_t dest_port,
+ const char *dest_addr_str,
+ bool result
+)
+{
+ interaction_data_sendto *d;
+
+ d = _Tftp_Append_interaction(
+ TFTP_IA_KIND_SENDTO,
+ interact_sendto_error,
+ sizeof( interaction_data_sendto )
+ );
+
+ T_assert_lt_sz( strlen( dest_addr_str ), TFTP_MAX_IP_ADDR_STRLEN );
+ strcpy( d->dest_addr_str, dest_addr_str );
+ d->fd = fd;
+ d->dest_port = dest_port;
+ d->result = result;
+ d->content.error.error_code = error_code;
+}
+
+/*
+ * Interaction: recvfrom()
+ */
+
+typedef struct interaction_data_recvfrom {
+ int fd;
+ uint32_t timeout_ms;
+ uint16_t src_port;
+ char src_addr_str[TFTP_MAX_IP_ADDR_STRLEN];
+ bool result;
+ union {
+ struct {
+ uint16_t block_num;
+ } ack;
+ struct {
+ size_t options_size;
+ char options[TFTP_MAX_OPTIONS_SIZE];
+ } oack;
+ struct {
+ uint16_t block_num;
+ size_t start;
+ size_t len;
+ uint8_t (*get_data)( size_t pos );
+ } data;
+ struct {
+ uint16_t error_code;
+ char err_msg[TFTP_MAX_ERROR_STRLEN];
+ } error;
+ struct {
+ size_t len;
+ uint8_t bytes[0];
+ } raw;
+ } content;
+} interaction_data_recvfrom;
+
+static bool interact_recvfrom_common(
+ Tftp_Action *act,
+ void *data,
+ size_t actual_size
+)
+{
+ interaction_data_recvfrom *d = data;
+
+ T_eq_int( act->data.recvfrom.fd, d->fd );
+ T_quiet_ge_sz( act->data.recvfrom.len, actual_size );
+ if (
+ act->data.recvfrom.fd != d->fd ||
+ act->data.recvfrom.len < actual_size
+ ) {
+ return false;
+ }
+ if ( d->timeout_ms == DO_NOT_WAIT_FOR_ANY_TIMEOUT ) {
+ T_ne_int( act->data.recvfrom.flags, 0 );
+ if ( act->data.recvfrom.flags == 0 ) {
+ return false;
+ }
+ } else {
+ T_eq_int( act->data.recvfrom.flags, 0 );
+ T_eq_u32( act->data.recvfrom.timeout_ms, d->timeout_ms );
+ if (
+ act->data.recvfrom.flags != 0 ||
+ act->data.recvfrom.timeout_ms != d->timeout_ms
+ ) {
+ return false;
+ }
+ }
+
+ strncpy(
+ act->data.recvfrom.src_addr_str,
+ d->src_addr_str,
+ sizeof( act->data.recvfrom.src_addr_str )
+ );
+ act->data.recvfrom.src_addr_str[
+ sizeof( act->data.recvfrom.src_addr_str ) - 1] = '\0';
+ act->data.recvfrom.src_port = d->src_port;
+ act->data.recvfrom.result = d->result ? actual_size : -1;
+ return true;
+}
+
+static bool interact_recvfrom_data( Tftp_Action *act, void *data )
+{
+ interaction_data_recvfrom *d = data;
+ Tftp_Packet *pkt = act->data.recvfrom.buf;
+ size_t actual_size;
+ size_t i;
+
+ actual_size = sizeof( pkt->opcode ) +
+ sizeof( pkt->content.data.block_num ) + d->content.data.len;
+ if ( !interact_recvfrom_common( act, data, actual_size ) ) {
+ return false;
+ }
+
+ pkt->opcode = htons( TFTP_OPCODE_DATA );
+ pkt->content.data.block_num = htons( d->content.data.block_num );
+ for ( i = 0; i < d->content.data.len; ++i ) {
+ pkt->content.data.bytes[i] =
+ d->content.data.get_data( d->content.data.start + i );
+ }
+
+ return true;
+}
+
+static bool interact_recvfrom_ack( Tftp_Action *act, void *data )
+{
+ interaction_data_recvfrom *d = data;
+ Tftp_Packet *pkt = act->data.recvfrom.buf;
+ size_t actual_size;
+
+ actual_size = sizeof( pkt->opcode ) + sizeof( pkt->content.ack.block_num );
+ if ( !interact_recvfrom_common( act, data, actual_size ) ) {
+ return false;
+ }
+
+ pkt->opcode = htons( TFTP_OPCODE_ACK );
+ pkt->content.ack.block_num = htons( d->content.ack.block_num );
+
+ return true;
+}
+
+static bool interact_recvfrom_oack( Tftp_Action *act, void *data )
+{
+ interaction_data_recvfrom *d = data;
+ Tftp_Packet *pkt = act->data.recvfrom.buf;
+ size_t actual_size;
+
+ actual_size = sizeof( pkt->opcode ) + d->content.oack.options_size;
+ if ( !interact_recvfrom_common( act, data, actual_size ) ) {
+ return false;
+ }
+
+ pkt->opcode = htons( TFTP_OPCODE_OACK );
+ memcpy(
+ pkt->content.oack.opts,
+ d->content.oack.options,
+ d->content.oack.options_size
+ );
+
+ return true;
+}
+
+static bool interact_recvfrom_error( Tftp_Action *act, void *data )
+{
+ interaction_data_recvfrom *d = data;
+ Tftp_Packet *pkt = act->data.recvfrom.buf;
+ size_t actual_size;
+
+ actual_size = sizeof( pkt->opcode ) +
+ sizeof( pkt->content.error.error_code ) +
+ strlen( d->content.error.err_msg ) + 1;
+ if ( !interact_recvfrom_common( act, data, actual_size ) ) {
+ return false;
+ }
+
+ pkt->opcode = htons( TFTP_OPCODE_ERROR );
+ pkt->content.error.error_code = htons( d->content.error.error_code );
+ strncpy(
+ pkt->content.error.err_msg,
+ d->content.error.err_msg,
+ act->data.recvfrom.len - sizeof( pkt->opcode ) -
+ sizeof( pkt->content.error.error_code ) - 1
+ );
+
+ return true;
+}
+
+static bool interact_recvfrom_raw( Tftp_Action *act, void *data )
+{
+ interaction_data_recvfrom *d = data;
+ uint8_t *pkt = act->data.recvfrom.buf;
+ size_t actual_size = d->content.raw.len;
+
+ if ( !interact_recvfrom_common( act, data, actual_size ) ) {
+ return false;
+ }
+
+ memcpy( pkt, d->content.raw.bytes, actual_size );
+
+ return true;
+}
+
+void _Tftp_Add_interaction_recv_data(
+ int fd,
+ uint32_t timeout_ms,
+ uint16_t src_port,
+ const char *src_addr_str,
+ uint16_t block_num,
+ size_t start,
+ size_t len,
+ uint8_t (*get_data)( size_t pos ),
+ bool result
+)
+{
+ interaction_data_recvfrom *d;
+
+ d = _Tftp_Append_interaction(
+ TFTP_IA_KIND_RECVFROM,
+ interact_recvfrom_data,
+ sizeof( interaction_data_recvfrom )
+ );
+
+ T_assert_lt_sz( strlen( src_addr_str ), sizeof( d->src_addr_str ) - 1 );
+ strcpy( d->src_addr_str, src_addr_str );
+ d->fd = fd;
+ d->timeout_ms = timeout_ms;
+ d->src_port = src_port;
+ d->content.data.block_num = block_num;
+ d->content.data.start = start;
+ d->content.data.len = len;
+ d->content.data.get_data = get_data;
+ d->result = result;
+}
+
+void _Tftp_Add_interaction_recv_ack(
+ int fd,
+ uint32_t timeout_ms,
+ uint16_t src_port,
+ const char *src_addr_str,
+ uint16_t block_num,
+ bool result
+)
+{
+ interaction_data_recvfrom *d;
+
+ d = _Tftp_Append_interaction(
+ TFTP_IA_KIND_RECVFROM,
+ interact_recvfrom_ack,
+ sizeof( interaction_data_recvfrom )
+ );
+
+ T_assert_lt_sz( strlen( src_addr_str ), sizeof( d->src_addr_str ) - 1 );
+ strcpy( d->src_addr_str, src_addr_str );
+ d->fd = fd;
+ d->timeout_ms = timeout_ms;
+ d->src_port = src_port;
+ d->content.ack.block_num = block_num;
+ d->result = result;
+}
+
+void _Tftp_Add_interaction_recv_oack(
+ int fd,
+ uint32_t timeout_ms,
+ uint16_t src_port,
+ const char *src_addr_str,
+ const char *options,
+ size_t options_size,
+ bool result
+)
+{
+ interaction_data_recvfrom *d;
+
+ d = _Tftp_Append_interaction(
+ TFTP_IA_KIND_RECVFROM,
+ interact_recvfrom_oack,
+ sizeof( interaction_data_recvfrom )
+ );
+
+ T_assert_lt_sz( strlen( src_addr_str ), sizeof( d->src_addr_str ) - 1 );
+ strcpy( d->src_addr_str, src_addr_str );
+ T_assert_lt_sz( options_size, sizeof( d->content.oack.options ) );
+ memcpy( d->content.oack.options, options, options_size );
+ d->fd = fd;
+ d->timeout_ms = timeout_ms;
+ d->src_port = src_port;
+ d->content.oack.options_size = options_size;
+ d->result = result;
+}
+
+void _Tftp_Add_interaction_recv_error(
+ int fd,
+ uint32_t timeout_ms,
+ uint16_t src_port,
+ const char *src_addr_str,
+ uint16_t error_code,
+ const char *err_msg,
+ bool result
+)
+{
+ interaction_data_recvfrom *d;
+
+ d = _Tftp_Append_interaction(
+ TFTP_IA_KIND_RECVFROM,
+ interact_recvfrom_error,
+ sizeof( interaction_data_recvfrom )
+ );
+
+ T_assert_lt_sz( strlen( src_addr_str ), sizeof( d->src_addr_str ) - 1 );
+ strcpy( d->src_addr_str, src_addr_str );
+ T_assert_lt_sz( strlen( src_addr_str ), sizeof( d->src_addr_str ) - 1 );
+ strcpy( d->content.error.err_msg, err_msg );
+ d->fd = fd;
+ d->timeout_ms = timeout_ms;
+ d->src_port = src_port;
+ d->content.error.error_code = error_code;
+ d->result = result;
+}
+
+void _Tftp_Add_interaction_recv_raw(
+ int fd,
+ uint32_t timeout_ms,
+ uint16_t src_port,
+ const char *src_addr_str,
+ size_t len,
+ const uint8_t *bytes,
+ bool result
+)
+{
+ interaction_data_recvfrom *d;
+
+ d = _Tftp_Append_interaction(
+ TFTP_IA_KIND_RECVFROM,
+ interact_recvfrom_raw,
+ sizeof( interaction_data_recvfrom ) + len
+ );
+
+ T_assert_lt_sz( strlen( src_addr_str ), sizeof( d->src_addr_str ) - 1 );
+ strcpy( d->src_addr_str, src_addr_str );
+ memcpy( d->content.raw.bytes, bytes, len );
+ d->fd = fd;
+ d->timeout_ms = timeout_ms;
+ d->src_port = src_port;
+ d->content.raw.len = len;
+ d->result = result;
+}
+
+void _Tftp_Add_interaction_recv_nothing(
+ int fd,
+ uint32_t timeout_ms
+)
+{
+ static const char *dummy_ip = "0.0.0.0";
+ interaction_data_recvfrom *d;
+
+ d = _Tftp_Append_interaction(
+ TFTP_IA_KIND_RECVFROM,
+ interact_recvfrom_ack,
+ sizeof( interaction_data_recvfrom )
+ );
+
+
+ T_assert_lt_sz( strlen( dummy_ip ), sizeof( d->src_addr_str ) - 1 );
+ strcpy( d->src_addr_str, dummy_ip );
+ d->fd = fd;
+ d->timeout_ms = timeout_ms;
+ d->src_port = 0;
+ d->content.ack.block_num = 0;
+ d->result = false;
+}