From d938d0ca4d89792156ddf5290b201585b8759aac Mon Sep 17 00:00:00 2001 From: lohhiiccc <96543753+lohhiiccc@users.noreply.github.com> Date: Mon, 26 Jan 2026 20:14:11 +0100 Subject: [PATCH] test: add send module unit tests --- tests/send/test_prepare_destination.c | 62 ++++++++++++ tests/send/test_send_echo.c | 98 ++++++++++++++++++ tests/send/test_send_packet.c | 92 +++++++++++++++++ tests/send/test_send_raw.c | 140 ++++++++++++++++++++++++++ tests/send/test_set_socket_ttl.c | 64 ++++++++++++ 5 files changed, 456 insertions(+) create mode 100644 tests/send/test_prepare_destination.c create mode 100644 tests/send/test_send_echo.c create mode 100644 tests/send/test_send_packet.c create mode 100644 tests/send/test_send_raw.c create mode 100644 tests/send/test_set_socket_ttl.c diff --git a/tests/send/test_prepare_destination.c b/tests/send/test_prepare_destination.c new file mode 100644 index 0000000..41d57c4 --- /dev/null +++ b/tests/send/test_prepare_destination.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include "internal/icmp_send.h" + +/* Test 1: Basic destination preparation */ +Test(send_prepare_destination, basic) +{ + struct sockaddr_in addr; + struct in_addr dest; + dest.s_addr = inet_addr("192.168.1.1"); + + send_prepare_destination(&addr, dest); + + cr_assert_eq(addr.sin_family, AF_INET, "Family should be AF_INET"); + cr_assert_eq(addr.sin_addr.s_addr, dest.s_addr, + "Address should match destination"); +} + +/* Test 2: Verify sin_port is 0 for ICMP */ +Test(send_prepare_destination, port_is_zero) +{ + struct sockaddr_in addr; + struct in_addr dest; + dest.s_addr = inet_addr("127.0.0.1"); + + send_prepare_destination(&addr, dest); + + cr_assert_eq(addr.sin_port, 0, "Port should be 0 for ICMP"); +} + +/* Test 3: Verify struct is zeroed */ +Test(send_prepare_destination, struct_zeroed) +{ + struct sockaddr_in addr; + memset(&addr, 0xFF, sizeof(addr)); /* Fill with garbage */ + + struct in_addr dest; + dest.s_addr = inet_addr("10.0.0.1"); + + send_prepare_destination(&addr, dest); + + /* Check sin_zero is zeroed */ + for (size_t i = 0; i < sizeof(addr.sin_zero); i++) + { + cr_assert_eq(addr.sin_zero[i], 0, + "sin_zero[%zu] should be 0", i); + } +} + +/* Test 4: Localhost address */ +Test(send_prepare_destination, localhost) +{ + struct sockaddr_in addr; + struct in_addr dest; + dest.s_addr = inet_addr("127.0.0.1"); + + send_prepare_destination(&addr, dest); + + cr_assert_eq(addr.sin_addr.s_addr, inet_addr("127.0.0.1"), + "Should handle localhost"); +} diff --git a/tests/send/test_send_echo.c b/tests/send/test_send_echo.c new file mode 100644 index 0000000..46935cc --- /dev/null +++ b/tests/send/test_send_echo.c @@ -0,0 +1,98 @@ +#include +#include +#include "icmp.h" +#include "internal/icmp_internal.h" +#include "test_helpers.h" + +/* Test 1: Send echo request successfully */ +Test(send_echo, simple_echo) +{ + if (0 == has_net_raw_capability()) + { + cr_skip("Test requires CAP_NET_RAW or root privileges"); + } + + icmp_handle_t *h = icmp_create(); + cr_assert_not_null(h); + + struct in_addr dest; + dest.s_addr = inet_addr("127.0.0.1"); + + int ret = icmp_send_echo(h, dest, 0x1234, 1, 64); + + cr_assert_eq(ret, 0, "Expected success (0)"); + cr_assert_str_eq(icmp_strerror(h), "No error"); + + icmp_destroy(h); +} + +/* Test 2: NULL handle returns -1 */ +Test(send_echo, null_handle) +{ + struct in_addr dest; + dest.s_addr = inet_addr("127.0.0.1"); + + int ret = icmp_send_echo(NULL, dest, 1, 1, 64); + + cr_assert_eq(ret, -1, "Expected -1 for NULL handle"); +} + +/* Test 3: Invalid FD returns -1 */ +Test(send_echo, invalid_fd) +{ + struct icmp_handle h = {0}; + h.fd = -1; + + struct in_addr dest; + dest.s_addr = inet_addr("127.0.0.1"); + + int ret = icmp_send_echo((icmp_handle_t *)&h, dest, 1, 1, 64); + + cr_assert_eq(ret, -1, "Expected -1 for invalid fd"); +} + +/* Test 4: Different TTL values */ +Test(send_echo, different_ttl) +{ + if (0 == has_net_raw_capability()) + { + cr_skip("Test requires CAP_NET_RAW or root privileges"); + } + + icmp_handle_t *h = icmp_create(); + cr_assert_not_null(h); + + struct in_addr dest; + dest.s_addr = inet_addr("127.0.0.1"); + + int ret1 = icmp_send_echo(h, dest, 1, 1, 1); + cr_assert_eq(ret1, 0, "Expected success with TTL=1"); + + int ret2 = icmp_send_echo(h, dest, 1, 2, 255); + cr_assert_eq(ret2, 0, "Expected success with TTL=255"); + + icmp_destroy(h); +} + +/* Test 5: Different id and seq values */ +Test(send_echo, different_id_seq) +{ + if (0 == has_net_raw_capability()) + { + cr_skip("Test requires CAP_NET_RAW or root privileges"); + } + + icmp_handle_t *h = icmp_create(); + cr_assert_not_null(h); + + struct in_addr dest; + dest.s_addr = inet_addr("127.0.0.1"); + + int ret1 = icmp_send_echo(h, dest, 0xABCD, 42, 64); + cr_assert_eq(ret1, 0, "Expected success with id=0xABCD, seq=42"); + + int ret2 = icmp_send_echo(h, dest, 0, 0, 64); + cr_assert_eq(ret2, 0, "Expected success with id=0, seq=0"); + + icmp_destroy(h); +} diff --git a/tests/send/test_send_packet.c b/tests/send/test_send_packet.c new file mode 100644 index 0000000..fdb3526 --- /dev/null +++ b/tests/send/test_send_packet.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include "icmp.h" +#include "internal/icmp_internal.h" +#include "internal/icmp_send.h" +#include "test_helpers.h" + +/* Test 1: Send simple packet successfully */ +Test(send_packet, success) +{ + if (0 == has_net_raw_capability()) + { + cr_skip("Test requires CAP_NET_RAW or root privileges"); + } + + icmp_handle_t *h = icmp_create(); + cr_assert_not_null(h); + + struct icmp_handle *handle = (struct icmp_handle *)h; + + /* Simple ICMP packet (type=8, code=0) */ + uint8_t packet[] = { + 0x08, 0x00, 0x00, 0x00, /* type, code, checksum */ + 0x00, 0x00, 0x00, 0x00 /* id, seq */ + }; + + struct sockaddr_in dest; + memset(&dest, 0, sizeof(dest)); + dest.sin_family = AF_INET; + dest.sin_addr.s_addr = inet_addr("127.0.0.1"); + + int ret = send_packet(handle, packet, sizeof(packet), &dest); + + cr_assert_eq(ret, 0, "Expected success (0)"); + + icmp_destroy(h); +} + +/* Test 2: Invalid FD returns -1 */ +Test(send_packet, invalid_fd) +{ + struct icmp_handle h = {0}; + h.fd = -1; + h.last_error = ICMP_OK; + h.error_msg[0] = '\0'; + + uint8_t packet[8] = {0}; + + struct sockaddr_in dest; + memset(&dest, 0, sizeof(dest)); + dest.sin_family = AF_INET; + dest.sin_addr.s_addr = inet_addr("127.0.0.1"); + + int ret = send_packet(&h, packet, sizeof(packet), &dest); + + cr_assert_eq(ret, -1, "Expected -1 for invalid FD"); + cr_assert_eq(h.last_error, ICMP_ERR_SEND, + "Error code should be ICMP_ERR_SEND"); +} + +/* Test 3: Send with payload */ +Test(send_packet, with_payload) +{ + if (0 == has_net_raw_capability()) + { + cr_skip("Test requires CAP_NET_RAW or root privileges"); + } + + icmp_handle_t *h = icmp_create(); + cr_assert_not_null(h); + + struct icmp_handle *handle = (struct icmp_handle *)h; + + /* ICMP packet with payload */ + uint8_t packet[16] = { + 0x08, 0x00, 0x00, 0x00, /* type, code, checksum */ + 0x00, 0x00, 0x00, 0x00, /* id, seq */ + 'H', 'E', 'L', 'L', 'O', 0, 0, 0 /* payload */ + }; + + struct sockaddr_in dest; + memset(&dest, 0, sizeof(dest)); + dest.sin_family = AF_INET; + dest.sin_addr.s_addr = inet_addr("127.0.0.1"); + + int ret = send_packet(handle, packet, sizeof(packet), &dest); + + cr_assert_eq(ret, 0, "Expected success with payload"); + + icmp_destroy(h); +} diff --git a/tests/send/test_send_raw.c b/tests/send/test_send_raw.c new file mode 100644 index 0000000..424526c --- /dev/null +++ b/tests/send/test_send_raw.c @@ -0,0 +1,140 @@ +#include +#include +#include +#include "icmp.h" +#include "internal/icmp_internal.h" +#include "icmp_types.h" +#include "test_helpers.h" + +/* Test 1: Send simple ICMP packet (type 8, no payload) */ +Test(send_raw, simple_packet) +{ + if (0 == has_net_raw_capability()) + { + cr_skip("Test requires CAP_NET_RAW or root privileges"); + } + + icmp_handle_t *h = icmp_create(); + cr_assert_not_null(h); + + struct in_addr dest; + dest.s_addr = inet_addr("127.0.0.1"); + + int ret = icmp_send_raw(h, ICMP_TYPE_ECHO_REQUEST, 0, NULL, 0, dest, 64); + + cr_assert_eq(ret, 0, "Expected success (0)"); + cr_assert_str_eq(icmp_strerror(h), "No error"); + + icmp_destroy(h); +} + +/* Test 2: Send packet with payload */ +Test(send_raw, with_payload) +{ + if (0 == has_net_raw_capability()) + { + cr_skip("Test requires CAP_NET_RAW or root privileges"); + } + + icmp_handle_t *h = icmp_create(); + cr_assert_not_null(h); + + struct in_addr dest; + dest.s_addr = inet_addr("127.0.0.1"); + + const char payload[] = "HELLO"; + int ret = icmp_send_raw(h, ICMP_TYPE_ECHO_REQUEST, 0, + payload, 5, dest, 64); + + cr_assert_eq(ret, 0, "Expected success (0)"); + cr_assert_str_eq(icmp_strerror(h), "No error"); + + icmp_destroy(h); +} + +/* Test 3: NULL handle returns -1 */ +Test(send_raw, null_handle) +{ + struct in_addr dest; + dest.s_addr = inet_addr("127.0.0.1"); + + int ret = icmp_send_raw(NULL, ICMP_TYPE_ECHO_REQUEST, 0, + NULL, 0, dest, 64); + + cr_assert_eq(ret, -1, "Expected -1 for NULL handle"); +} + +/* Test 4: NULL payload with len > 0 returns -1 */ +Test(send_raw, null_payload_with_length) +{ + if (0 == has_net_raw_capability()) + { + cr_skip("Test requires CAP_NET_RAW or root privileges"); + } + + icmp_handle_t *h = icmp_create(); + cr_assert_not_null(h); + + struct in_addr dest; + dest.s_addr = inet_addr("127.0.0.1"); + + int ret = icmp_send_raw(h, ICMP_TYPE_ECHO_REQUEST, 0, + NULL, 10, dest, 64); + + cr_assert_eq(ret, -1, "Expected -1 for NULL payload with len > 0"); + + const char *err = icmp_strerror(h); + cr_assert(NULL != strstr(err, "Payload") || NULL != strstr(err, "NULL"), + "Error message should mention payload or NULL"); + + icmp_destroy(h); +} + +/* Test 5: Payload too large returns -1 */ +Test(send_raw, payload_too_large) +{ + if (0 == has_net_raw_capability()) + { + cr_skip("Test requires CAP_NET_RAW or root privileges"); + } + + icmp_handle_t *h = icmp_create(); + cr_assert_not_null(h); + + struct in_addr dest; + dest.s_addr = inet_addr("127.0.0.1"); + + uint8_t large_payload[2000]; + memset(large_payload, 0xAA, sizeof(large_payload)); + + int ret = icmp_send_raw(h, ICMP_TYPE_ECHO_REQUEST, 0, + large_payload, sizeof(large_payload), dest, 64); + + cr_assert_eq(ret, -1, "Expected -1 for payload too large"); + + const char *err = icmp_strerror(h); + cr_assert(NULL != strstr(err, "large") || NULL != strstr(err, "too"), + "Error message should mention 'large' or 'too'"); + + icmp_destroy(h); +} + +/* Test 6: Invalid FD returns -1 */ +Test(send_raw, invalid_fd) +{ + struct icmp_handle h = {0}; + h.fd = -1; + h.last_error = ICMP_OK; + h.error_msg[0] = '\0'; + + struct in_addr dest; + dest.s_addr = inet_addr("127.0.0.1"); + + int ret = icmp_send_raw((icmp_handle_t *)&h, ICMP_TYPE_ECHO_REQUEST, 0, + NULL, 0, dest, 64); + + cr_assert_eq(ret, -1, "Expected -1 for invalid FD"); + cr_assert(NULL != strstr(h.error_msg, "Invalid") || + NULL != strstr(h.error_msg, "socket"), + "Error message should mention 'Invalid' or 'socket'"); +} diff --git a/tests/send/test_set_socket_ttl.c b/tests/send/test_set_socket_ttl.c new file mode 100644 index 0000000..9f7d410 --- /dev/null +++ b/tests/send/test_set_socket_ttl.c @@ -0,0 +1,64 @@ +#include +#include "icmp.h" +#include "internal/icmp_internal.h" +#include "internal/icmp_send.h" +#include "test_helpers.h" + +/* Test 1: Set TTL successfully */ +Test(send_set_socket_ttl, success) +{ + if (0 == has_net_raw_capability()) + { + cr_skip("Test requires CAP_NET_RAW or root privileges"); + } + + icmp_handle_t *h = icmp_create(); + cr_assert_not_null(h); + + struct icmp_handle *handle = (struct icmp_handle *)h; + + int ret = send_set_socket_ttl(handle, 64); + + cr_assert_eq(ret, 0, "Expected success (0)"); + + icmp_destroy(h); +} + +/* Test 2: Invalid FD returns -1 */ +Test(send_set_socket_ttl, invalid_fd) +{ + struct icmp_handle h = {0}; + h.fd = -1; + h.last_error = ICMP_OK; + h.error_msg[0] = '\0'; + + int ret = send_set_socket_ttl(&h, 64); + + cr_assert_eq(ret, -1, "Expected -1 for invalid FD"); + cr_assert_eq(h.last_error, ICMP_ERR_SOCKET, + "Error code should be ICMP_ERR_SOCKET"); +} + +/* Test 3: Different TTL values */ +Test(send_set_socket_ttl, different_values) +{ + if (0 == has_net_raw_capability()) + { + cr_skip("Test requires CAP_NET_RAW or root privileges"); + } + + icmp_handle_t *h = icmp_create(); + cr_assert_not_null(h); + + struct icmp_handle *handle = (struct icmp_handle *)h; + + /* TTL=1 */ + int ret = send_set_socket_ttl(handle, 1); + cr_assert_eq(ret, 0, "Expected success with TTL=1"); + + /* TTL=255 */ + ret = send_set_socket_ttl(handle, 255); + cr_assert_eq(ret, 0, "Expected success with TTL=255"); + + icmp_destroy(h); +}