diff --git a/samples/asio_sender_multicast/src/main.cpp b/samples/asio_sender_multicast/src/main.cpp index 131ed72..674b76c 100644 --- a/samples/asio_sender_multicast/src/main.cpp +++ b/samples/asio_sender_multicast/src/main.cpp @@ -33,13 +33,13 @@ int main() asio::io_service io_service; const asio::ip::udp::endpoint endpoint(asio::ip::make_address("239.0.0.1"), 14000); - asio::ip::udp::socket upd_socket(io_service, endpoint.protocol()); + asio::ip::udp::socket udp_socket(io_service, endpoint.protocol()); // set multicast packet TTL { const asio::ip::multicast::hops ttl(2); asio::error_code ec; - upd_socket.set_option(ttl, ec); + udp_socket.set_option(ttl, ec); if (ec) { std::cerr << "ERROR: Setting TTL failed: " << ec.message() << std::endl; @@ -51,7 +51,7 @@ int main() { const asio::ip::multicast::enable_loopback loopback(true); asio::error_code ec; - upd_socket.set_option(loopback, ec); + udp_socket.set_option(loopback, ec); if (ec) { std::cerr << "ERROR: Error setting loopback option: " << ec.message() << std::endl; @@ -65,7 +65,7 @@ int main() std::string buffer_string = "Hello World " + std::to_string(counter); std::cout << "Sending data \"" << buffer_string << "\"" << std::endl; - upd_socket.send_to(asio::buffer(buffer_string), endpoint); + udp_socket.send_to(asio::buffer(buffer_string), endpoint); counter++; std::this_thread::sleep_for(std::chrono::milliseconds(500)); diff --git a/samples/asio_sender_unicast/src/main.cpp b/samples/asio_sender_unicast/src/main.cpp index b89fe26..0a2ecbd 100644 --- a/samples/asio_sender_unicast/src/main.cpp +++ b/samples/asio_sender_unicast/src/main.cpp @@ -33,7 +33,7 @@ int main() asio::io_service io_service; const asio::ip::udp::endpoint endpoint(asio::ip::make_address("127.0.0.1"), 14000); - asio::ip::udp::socket upd_socket(io_service, endpoint.protocol()); + asio::ip::udp::socket udp_socket(io_service, endpoint.protocol()); int counter = 0; for(;;) @@ -41,7 +41,7 @@ int main() std::string buffer_string = "Hello World " + std::to_string(counter); std::cout << "Sending data \"" << buffer_string << "\"" << std::endl; - upd_socket.send_to(asio::buffer(buffer_string), endpoint); + udp_socket.send_to(asio::buffer(buffer_string), endpoint); counter++; std::this_thread::sleep_for(std::chrono::milliseconds(500)); diff --git a/tests/udpcap_test/src/udpcap_test.cpp b/tests/udpcap_test/src/udpcap_test.cpp index fac0e0d..6f83295 100644 --- a/tests/udpcap_test/src/udpcap_test.cpp +++ b/tests/udpcap_test/src/udpcap_test.cpp @@ -78,9 +78,14 @@ TEST(udpcap, RAIIWithSomebodyWaiting) // Check that we didn't receive any bytes ASSERT_EQ(received_bytes, 0); - // TODO: check actual error, which should indicate that the socket is closed ASSERT_TRUE(bool(error)); + // Check the error reason + ASSERT_EQ(error, Udpcap::Error(Udpcap::Error::ErrorCode::SOCKET_CLOSED)); + + // Check that the socket is closed + ASSERT_TRUE(udpcap_socket.isClosed()); + }); // Close the socket @@ -187,10 +192,15 @@ TEST(udpcap, MultipleSmallPackages) if (error) { - // TODO: Check that actual error reason - // Indicates that somebody closed the socket ASSERT_EQ(received_messages.get(), num_packages_to_send); + + // Check the error reason + ASSERT_EQ(error, Udpcap::Error(Udpcap::Error::ErrorCode::SOCKET_CLOSED)); + + // Check that the socket is closed + ASSERT_TRUE(udpcap_socket.isClosed()); + break; } @@ -332,6 +342,13 @@ TEST(udpcap, DelayedPackageReceiveMultiplePackages) { // Indicates that somebody closed the socket ASSERT_EQ(received_messages.get(), num_packages_to_send); + + // Check the error reason + ASSERT_EQ(error, Udpcap::Error(Udpcap::Error::ErrorCode::SOCKET_CLOSED)); + + // Check that the socket is closed + ASSERT_TRUE(udpcap_socket.isClosed()); + break; } @@ -419,7 +436,7 @@ TEST(udpcap, Timeout) ASSERT_EQ(error, Udpcap::Error::TIMEOUT); ASSERT_EQ(received_bytes, 0); - ASSERT_GE(std::chrono::duration_cast(end_time - start_time).count(), 100); + ASSERT_GE(std::chrono::duration_cast(end_time - start_time).count(), 100); // TODO: This sometimes fails. Check why! } // Something already is in the socket, so the call must return earlier @@ -489,16 +506,191 @@ TEST(udpcap, Timeout) udpcap_socket.close(); } -// TODO: Write a test that tests the Source Address and Source Port +// Test receiving without binding the socket (-> error) +TEST(udpcap, ReceiveNotBound) +{ + // Create a udpcap socket + Udpcap::UdpcapSocket udpcap_socket; + ASSERT_TRUE(udpcap_socket.isValid()); + + // Initialize variables for the sender's address and port + Udpcap::HostAddress sender_address; + uint16_t sender_port(0); + + // Allocate buffer with max udp datagram size + std::vector received_datagram; + received_datagram.resize(65536); + + // Initialize error object + Udpcap::Error error = Udpcap::Error::ErrorCode::GENERIC_ERROR; + + // blocking receive + size_t received_bytes = udpcap_socket.receiveDatagram(received_datagram.data(), received_datagram.size(), &sender_address, &sender_port, error); + + // Check if the received datagram is valid and contains "Hello World" + ASSERT_EQ(received_bytes, 0); + ASSERT_TRUE(bool(error)); + ASSERT_EQ(error, Udpcap::Error(Udpcap::Error::ErrorCode::NOT_BOUND)); +} -// TODO: Test the returned errors of the receiveDatagram function +// Test the multicast functionality +TEST(udpcap, MulticastReceive) +{ + atomic_signalable received_messages1(0); + atomic_signalable received_messages2(0); -// TODO: test isclosed function + // Create two udpcap sockets + Udpcap::UdpcapSocket udpcap_socket1; + ASSERT_TRUE(udpcap_socket1.isValid()); -// TODO: rapidly create and destroy sockets to see if the memory is freed correctly https://stackoverflow.com/questions/29174938/googletest-and-memory-leaks + Udpcap::UdpcapSocket udpcap_socket2; + ASSERT_TRUE(udpcap_socket2.isValid()); -// TODO: Test Multicast Receive + udpcap_socket1.setMulticastLoopbackEnabled(true); + udpcap_socket2.setMulticastLoopbackEnabled(true); -// TODO: Test with multiple multicast sockets, that each only receive their own multicast group + // Bind the udpcap sockets to all interfaces + { + bool success = udpcap_socket1.bind(Udpcap::HostAddress::Any(), 14000); + ASSERT_TRUE(success); + } + { + bool success = udpcap_socket2.bind(Udpcap::HostAddress::Any(), 14000); + ASSERT_TRUE(success); + } + + // Join the multicast group 224.0.0.1 on both sockets + { + bool success = udpcap_socket1.joinMulticastGroup(Udpcap::HostAddress("224.0.0.1")); + ASSERT_TRUE(success); + } + { + bool success = udpcap_socket2.joinMulticastGroup(Udpcap::HostAddress("224.0.0.1")); + ASSERT_TRUE(success); + } + + // Join the multicast group 224.0.0.2 on the second socket + { + bool success = udpcap_socket2.joinMulticastGroup(Udpcap::HostAddress("224.0.0.2")); + ASSERT_TRUE(success); + } + + // Create an asio UDP sender socket + asio::io_service io_service; + asio::ip::udp::socket asio_socket(io_service, asio::ip::udp::v4()); + + // open the socket for multicast sending + asio_socket.set_option(asio::ip::multicast::hops(1)); + asio_socket.set_option(asio::ip::multicast::enable_loopback(true)); + + + // Receive datagrams in a separate thread for Socket1 (checks for 224.0.0.1) + std::thread receive_thread1([&udpcap_socket1, &received_messages1]() + { + while (true) + { + // Initialize variables for the sender's address and port + Udpcap::HostAddress sender_address; + uint16_t sender_port(0); + + Udpcap::Error error = Udpcap::Error::ErrorCode::GENERIC_ERROR; + + std::vector received_datagram; + received_datagram.resize(65536); + + size_t bytes_received = udpcap_socket1.receiveDatagram(received_datagram.data(), received_datagram.size(), &sender_address, &sender_port, error); + received_datagram.resize(bytes_received); + + if (error) + { + // Indicates that somebody closed the socket + ASSERT_EQ(received_messages1.get(), 1); + + // Check the error reason + ASSERT_EQ(error, Udpcap::Error(Udpcap::Error::ErrorCode::SOCKET_CLOSED)); + + // Check that the socket is closed + ASSERT_TRUE(udpcap_socket1.isClosed()); + + break; + } + + // Check if the received datagram is valid and contains "224.0.0.1" + ASSERT_EQ(std::string(received_datagram.data(), received_datagram.size()), "224.0.0.1"); + received_messages1++; + } + }); + + // Receive datagrams in a separate thread for Socket2 (checks for 224.0.0.1 or 224.0.0.2) + std::thread receive_thread2([&udpcap_socket2, &received_messages2]() + { + while (true) + { + // Initialize variables for the sender's address and port + Udpcap::HostAddress sender_address; + uint16_t sender_port(0); + + Udpcap::Error error = Udpcap::Error::ErrorCode::GENERIC_ERROR; + + std::vector received_datagram; + received_datagram.resize(65536); + + size_t bytes_received = udpcap_socket2.receiveDatagram(received_datagram.data(), received_datagram.size(), &sender_address, &sender_port, error); + received_datagram.resize(bytes_received); + + if (error) + { + // Indicates that somebody closed the socket + ASSERT_EQ(received_messages2.get(), 2); + + // Check the error reason + ASSERT_EQ(error, Udpcap::Error(Udpcap::Error::ErrorCode::SOCKET_CLOSED)); + + // Check that the socket is closed + ASSERT_TRUE(udpcap_socket2.isClosed()); + + break; + } + + // Check if the received datagram is valid and contains "224.0.0.1" or "224.0.0.2" + ASSERT_TRUE(std::string(received_datagram.data(), received_datagram.size()) == "224.0.0.1" + || std::string(received_datagram.data(), received_datagram.size()) == "224.0.0.2"); + received_messages2++; + } + }); + + // Send the multicast message to 224.0.0.1 + { + const asio::ip::udp::endpoint endpoint(asio::ip::make_address("224.0.0.1"), 14000); + std::string buffer_string = "224.0.0.1"; + asio_socket.send_to(asio::buffer(buffer_string), endpoint); + } + + // Send the multicast message to 224.0.0.2 + { + const asio::ip::udp::endpoint endpoint(asio::ip::make_address("224.0.0.2"), 14000); + std::string buffer_string = "224.0.0.2"; + asio_socket.send_to(asio::buffer(buffer_string), endpoint); + } + + // Wait for received_messages1 to be 1 and received_messages2 to be 2 + received_messages1.wait_for([](int value) { return value >= 1; }, std::chrono::milliseconds(500)); + received_messages2.wait_for([](int value) { return value >= 2; }, std::chrono::milliseconds(500)); + + // Check if the received message counters + ASSERT_EQ(received_messages1.get(), 1); + ASSERT_EQ(received_messages2.get(), 2); + + // Close the sockets + asio_socket.close(); + udpcap_socket1.close(); + udpcap_socket2.close(); + + // Join the threads + receive_thread1.join(); + receive_thread2.join(); +} + +// TODO: Write a test that tests the Source Address and Source Port // TODO: Create many sockets in threads, wait for them and destroy them to see if there are any race conditions that lead to crashes diff --git a/udpcap/src/npcap_helpers.cpp b/udpcap/src/npcap_helpers.cpp index 83d0ad3..83dd8c9 100644 --- a/udpcap/src/npcap_helpers.cpp +++ b/udpcap/src/npcap_helpers.cpp @@ -207,23 +207,22 @@ namespace Udpcap bool TestLoopbackDevice() { - typedef std::unique_ptr pcap_if_t_uniqueptr; - std::array errbuf{}; pcap_if_t* alldevs_rawptr = nullptr; - const pcap_if_t_uniqueptr alldevs(&alldevs_rawptr, [](pcap_if_t** p) { pcap_freealldevs(*p); }); bool loopback_device_found = false; - if (pcap_findalldevs(alldevs.get(), errbuf.data()) == -1) + if (pcap_findalldevs(&alldevs_rawptr, errbuf.data()) == -1) { human_readible_error_ = "Error in pcap_findalldevs: " + std::string(errbuf.data()); fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf.data()); + if (alldevs_rawptr != nullptr) + pcap_freealldevs(alldevs_rawptr); return false; } // Check if the loopback device is accessible - for (pcap_if_t* pcap_dev = *alldevs.get(); pcap_dev != nullptr; pcap_dev = pcap_dev->next) + for (pcap_if_t* pcap_dev = alldevs_rawptr; pcap_dev != nullptr; pcap_dev = pcap_dev->next) { if (IsLoopbackDevice_NoLock(pcap_dev->name)) { @@ -231,6 +230,8 @@ namespace Udpcap } } + pcap_freealldevs(alldevs_rawptr); + // if we didn't find the loopback device, the test has failed if (!loopback_device_found) { diff --git a/udpcap/src/udpcap_socket_private.cpp b/udpcap/src/udpcap_socket_private.cpp index 3f33f24..0995f7f 100644 --- a/udpcap/src/udpcap_socket_private.cpp +++ b/udpcap/src/udpcap_socket_private.cpp @@ -267,222 +267,6 @@ namespace Udpcap return((wait_result >= WAIT_OBJECT_0) && wait_result <= (WAIT_OBJECT_0 + num_handles - 1)); } - - std::vector UdpcapSocketPrivate::receiveDatagram_OLD(HostAddress* source_address, uint16_t* source_port) - { - return receiveDatagram_OLD(INFINITE, source_address, source_port); - } - - std::vector UdpcapSocketPrivate::receiveDatagram_OLD(unsigned long timeout_ms, HostAddress* source_address, uint16_t* source_port) - { - if (!is_valid_) - { - // Invalid socket, cannot bind => fail! - LOG_DEBUG("Receive error: Socket is invalid"); - return {}; - } - - if (!bound_state_) - { - // Not bound => fail! - LOG_DEBUG("Receive error: Socket is not bound"); - return{}; - } - - // Lock the lists of open pcap devices in read-mode. We may use the handles, but not modify the lists themselfes. - const std::shared_lock pcap_devices_lists_lock(pcap_devices_lists_mutex_); - - if (pcap_win32_handles_.empty()) - { - // No open devices => fail! - LOG_DEBUG("Receive error: No open devices"); - return{}; - } - - DWORD num_handles = static_cast(pcap_win32_handles_.size()); - if (num_handles > MAXIMUM_WAIT_OBJECTS) - { - LOG_DEBUG("WARNING: Too many open Adapters. " + std::to_string(num_handles) + " adapters are open, only " + std::to_string(MAXIMUM_WAIT_OBJECTS) + " are supported."); - num_handles = MAXIMUM_WAIT_OBJECTS; - } - - const bool wait_forever = (timeout_ms == INFINITE); - auto wait_until = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms); - - std::vector datagram; - CallbackArgsVector callback_args(&datagram, source_address, source_port, bound_port_, pcpp::LinkLayerType::LINKTYPE_NULL); - - do - { - unsigned long remaining_time_to_wait_ms = 0; - if (wait_forever) - { - remaining_time_to_wait_ms = INFINITE; - } - else - { - auto now = std::chrono::steady_clock::now(); - if (now < wait_until) - { - remaining_time_to_wait_ms = static_cast(std::chrono::duration_cast(wait_until - now).count()); - } - } - - std::cerr << "WaitForMultipleObjects START...\n"; - const DWORD wait_result = WaitForMultipleObjects(num_handles, pcap_win32_handles_.data(), static_cast(false), remaining_time_to_wait_ms); - std::cerr << "WaitForMultipleObjects END...\n"; - - if ((wait_result >= WAIT_OBJECT_0) && wait_result <= (WAIT_OBJECT_0 + num_handles - 1)) - { - const int dev_index = (wait_result - WAIT_OBJECT_0); - - { - // Lock the callback lock. While the callback is running, we cannot close the pcap handle, as that may invalidate the data pointer. - const std::lock_guard pcap_devices_callback_lock(pcap_devices_callback_mutex_); - - if (pcap_devices_closed_) - { - // TODO: Return an error - return {}; - } - - callback_args.link_type_ = static_cast(pcap_datalink(pcap_devices_[dev_index].pcap_handle_)); - callback_args.ip_reassembly_ = pcap_devices_ip_reassembly_[dev_index].get(); - - pcap_dispatch(pcap_devices_[dev_index].pcap_handle_, 100, UdpcapSocketPrivate::PacketHandlerVector, reinterpret_cast(&callback_args)); - } - - if (callback_args.success_) - { - // Only return datagram if we successfully received a packet. Otherwise, we will continue receiving data, if there is time left. - return datagram; - } - } - else if ((wait_result >= WAIT_ABANDONED_0) && wait_result <= (WAIT_ABANDONED_0 + num_handles - 1)) - { - LOG_DEBUG("Receive error: WAIT_ABANDONED"); - } - else if (wait_result == WAIT_TIMEOUT) - { - // LOG_DEBUG("Receive error: WAIT_TIMEOUT"); - } - else if (wait_result == WAIT_FAILED) - { - LOG_DEBUG("Receive error: WAIT_FAILED: " + std::system_category().message(GetLastError())); - // TODO: Check if I can always just return here. This definitively happens when I close the socket, so I MUST return in certain cases. But I don't know if there may be cases when this happens without closing the socket. - return {}; - } - } while (wait_forever || (std::chrono::steady_clock::now() < wait_until)); - - return{}; - } - - size_t UdpcapSocketPrivate::receiveDatagram_OLD(char* data, size_t max_len, HostAddress* source_address, uint16_t* source_port) - { - return receiveDatagram_OLD(data, max_len, INFINITE, source_address, source_port); - } - - size_t UdpcapSocketPrivate::receiveDatagram_OLD(char* data, size_t max_len, unsigned long timeout_ms, HostAddress* source_address, uint16_t* source_port) - { - if (!is_valid_) - { - // Invalid socket, cannot bind => fail! - LOG_DEBUG("Receive error: Socket is invalid"); - return{}; - } - - if (!bound_state_) - { - // Not bound => fail! - LOG_DEBUG("Receive error: Socket is not bound"); - return{}; - } - - // Lock the lists of open pcap devices in read-mode. We may use the handles, but not modify the lists themselfes. - const std::shared_lock pcap_devices_list_lock(pcap_devices_lists_mutex_); - - if (pcap_win32_handles_.empty()) - { - // No open devices => fail! - LOG_DEBUG("Receive error: No open devices"); - return{}; - } - - DWORD num_handles = static_cast(pcap_win32_handles_.size()); - if (num_handles > MAXIMUM_WAIT_OBJECTS) - { - LOG_DEBUG("WARNING: Too many open Adapters. " + std::to_string(num_handles) + " adapters are open, only " + std::to_string(MAXIMUM_WAIT_OBJECTS) + " are supported."); - num_handles = MAXIMUM_WAIT_OBJECTS; - } - - const bool wait_forever = (timeout_ms == INFINITE); - auto wait_until = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms); - - CallbackArgsRawPtr callback_args(data, max_len, source_address, source_port, bound_port_, pcpp::LinkLayerType::LINKTYPE_NULL); - - do - { - unsigned long remaining_time_to_wait_ms = 0; - if (wait_forever) - { - remaining_time_to_wait_ms = INFINITE; - } - else - { - auto now = std::chrono::steady_clock::now(); - if (now < wait_until) - { - remaining_time_to_wait_ms = static_cast(std::chrono::duration_cast(wait_until - now).count()); - } - } - - const DWORD wait_result = WaitForMultipleObjects(num_handles, pcap_win32_handles_.data(), static_cast(false), remaining_time_to_wait_ms); - - if ((wait_result >= WAIT_OBJECT_0) && wait_result <= (WAIT_OBJECT_0 + num_handles - 1)) - { - const int dev_index = (wait_result - WAIT_OBJECT_0); - - { - // Lock the callback lock. While the callback is running, we cannot close the pcap handle, as that may invalidate the data pointer. - const std::lock_guard pcap_devices_callback__lock(pcap_devices_callback_mutex_); - - if (pcap_devices_closed_) - { - // TODO: Return an error - return {}; - } - - callback_args.link_type_ = static_cast(pcap_datalink(pcap_devices_[dev_index].pcap_handle_)); - callback_args.ip_reassembly_ = pcap_devices_ip_reassembly_[dev_index].get(); - - pcap_dispatch(pcap_devices_[dev_index].pcap_handle_, 1, UdpcapSocketPrivate::PacketHandlerRawPtr, reinterpret_cast(&callback_args)); - } - - if (callback_args.success_) - { - // Only return datagram if we successfully received a packet. Otherwise, we will continue receiving data, if there is time left. - return callback_args.bytes_copied_; - } - } - else if ((wait_result >= WAIT_ABANDONED_0) && wait_result <= (WAIT_ABANDONED_0 + num_handles - 1)) - { - LOG_DEBUG("Receive error: WAIT_ABANDONED"); - } - else if (wait_result == WAIT_TIMEOUT) - { - // LOG_DEBUG("Receive error: WAIT_TIMEOUT"); - } - else if (wait_result == WAIT_FAILED) - { - LOG_DEBUG("Receive error: WAIT_FAILED: " + std::system_category().message(GetLastError())); - // TODO: Check if I can always just return here. This definitively happens when I close the socket, so I MUST return in certain cases. But I don't know if there may be cases when this happens without closing the socket. - return {}; - } - } while (wait_forever || (std::chrono::steady_clock::now() < wait_until)); - - return 0; - } - size_t UdpcapSocketPrivate::receiveDatagram(char* data , size_t max_len , unsigned long timeout_ms @@ -509,14 +293,6 @@ namespace Udpcap return 0; } - if (!bound_state_) - { - // Not bound => fail! - LOG_DEBUG("Receive error: Socket is not bound"); - error = Udpcap::Error::NOT_BOUND; - return 0; - } - // Check all devices for data { // Variable to store the result @@ -543,6 +319,15 @@ namespace Udpcap error = Udpcap::Error::SOCKET_CLOSED; return 0; } + + // Check if the socket is bound and return an error + if (!bound_state_) + { + // Not bound => fail! + LOG_DEBUG("Receive error: Socket is not bound"); + error = Udpcap::Error::NOT_BOUND; + return 0; + } // Iterate through all devices and check if they have data. There is // no other API (that I know of) to check whether data is available on @@ -806,16 +591,17 @@ namespace Udpcap // Retrieve device list std::array errbuf{}; - pcap_if_t* alldevs_rawptr = nullptr; - const pcap_if_t_uniqueptr alldevs(&alldevs_rawptr, [](pcap_if_t** p) { pcap_freealldevs(*p); }); + pcap_if_t* alldevs_ptr = nullptr; - if (pcap_findalldevs(alldevs.get(), errbuf.data()) == -1) + if (pcap_findalldevs(&alldevs_ptr, errbuf.data()) == -1) { fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf.data()); + if (alldevs_ptr != nullptr) + pcap_freealldevs(alldevs_ptr); return{}; } - for (pcap_if_t* pcap_dev = *alldevs.get(); pcap_dev != nullptr; pcap_dev = pcap_dev->next) + for (pcap_if_t* pcap_dev = alldevs_ptr; pcap_dev != nullptr; pcap_dev = pcap_dev->next) { // A user may have done something bad like assigning an IPv4 address to // the loopback adapter. We don't want to open it in that case. In a real- @@ -835,12 +621,15 @@ namespace Udpcap if (device_ipv4_addr->sin_addr.s_addr == ip.toInt()) { // The IPv4 address matches! + pcap_freealldevs(alldevs_ptr); return std::make_pair(std::string(pcap_dev->name), std::string(pcap_dev->description)); } } } } + pcap_freealldevs(alldevs_ptr); + // Nothing found => nullptr return{}; } @@ -849,20 +638,24 @@ namespace Udpcap { // Retrieve device list std::array errbuf{}; - pcap_if_t* alldevs_rawptr = nullptr; - const pcap_if_t_uniqueptr alldevs(&alldevs_rawptr, [](pcap_if_t** p) { pcap_freealldevs(*p); }); + pcap_if_t* alldevs_ptr = nullptr; - if (pcap_findalldevs(alldevs.get(), errbuf.data()) == -1) + if (pcap_findalldevs(&alldevs_ptr, errbuf.data()) == -1) { fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf.data()); + if (alldevs_ptr != nullptr) + pcap_freealldevs(alldevs_ptr); return{}; } std::vector> alldev_vector; - for (pcap_if_t* pcap_dev = *alldevs.get(); pcap_dev != nullptr; pcap_dev = pcap_dev->next) + for (pcap_if_t* pcap_dev = alldevs_ptr; pcap_dev != nullptr; pcap_dev = pcap_dev->next) { alldev_vector.emplace_back(std::string(pcap_dev->name), std::string(pcap_dev->description)); } + + pcap_freealldevs(alldevs_ptr); + return alldev_vector; } @@ -1098,67 +891,6 @@ namespace Udpcap kickstart_socket.close(); } - void UdpcapSocketPrivate::PacketHandlerVector(unsigned char* param, const struct pcap_pkthdr* header, const unsigned char* pkt_data) - { - std::cerr << "PacketHandlerVector\n"; - CallbackArgsVector* callback_args = reinterpret_cast(param); - - pcpp::RawPacket rawPacket(pkt_data, header->caplen, header->ts, false, callback_args->link_type_); - const pcpp::Packet packet(&rawPacket, pcpp::UDP); - - const pcpp::IPv4Layer* ip_layer = packet.getLayerOfType(); - const pcpp::UdpLayer* udp_layer = packet.getLayerOfType(); - - if (ip_layer != nullptr) - { - if (ip_layer->isFragment()) - { - // Handle fragmented IP traffic - pcpp::IPReassembly::ReassemblyStatus status(pcpp::IPReassembly::ReassemblyStatus::NON_IP_PACKET); - - // Try to reassemble packet - const pcpp::Packet* reassembled_packet = callback_args->ip_reassembly_->processPacket(&rawPacket, status); - - // If we are done reassembling the packet, we return it to the user - if (reassembled_packet != nullptr) - { - const pcpp::Packet re_parsed_packet(reassembled_packet->getRawPacket(), pcpp::UDP); - - const pcpp::IPv4Layer* reassembled_ip_layer = re_parsed_packet.getLayerOfType(); - const pcpp::UdpLayer* reassembled_udp_layer = re_parsed_packet.getLayerOfType(); - - if ((reassembled_ip_layer != nullptr) && (reassembled_udp_layer != nullptr)) - FillCallbackArgsVector(callback_args, reassembled_ip_layer, reassembled_udp_layer); - - delete reassembled_packet; // We need to manually delete the packet pointer - } - } - else if (udp_layer != nullptr) - { - // Handle normal IP traffic (un-fragmented) - FillCallbackArgsVector(callback_args, ip_layer, udp_layer); - } - } - } - - void UdpcapSocketPrivate::FillCallbackArgsVector(CallbackArgsVector* callback_args, const pcpp::IPv4Layer* ip_layer, const pcpp::UdpLayer* udp_layer) - { - auto dst_port = ntohs(udp_layer->getUdpHeader()->portDst); - - if (dst_port == callback_args->bound_port_) - { - if (callback_args->source_address_ != nullptr) - *callback_args->source_address_ = HostAddress(ip_layer->getSrcIPv4Address().toInt()); - - if (callback_args->source_port_ != nullptr) - *callback_args->source_port_ = ntohs(udp_layer->getUdpHeader()->portSrc); - - callback_args->destination_vector_->reserve(udp_layer->getLayerPayloadSize()); - callback_args->destination_vector_->assign(udp_layer->getLayerPayload(), udp_layer->getLayerPayload() + udp_layer->getLayerPayloadSize()); - callback_args->success_ = true; - } - } - void UdpcapSocketPrivate::PacketHandlerRawPtr(unsigned char* param, const struct pcap_pkthdr* header, const unsigned char* pkt_data) { CallbackArgsRawPtr* callback_args = reinterpret_cast(param); diff --git a/udpcap/src/udpcap_socket_private.h b/udpcap/src/udpcap_socket_private.h index c6d4810..bab61ca 100644 --- a/udpcap/src/udpcap_socket_private.h +++ b/udpcap/src/udpcap_socket_private.h @@ -66,27 +66,6 @@ namespace Udpcap std::string device_name_; }; - struct CallbackArgsVector - { - CallbackArgsVector(std::vector* destination_vector, HostAddress* source_address, uint16_t* source_port, uint16_t bound_port, pcpp::LinkLayerType link_type) - : destination_vector_(destination_vector) - , source_address_ (source_address) - , source_port_ (source_port) - , success_ (false) - , link_type_ (link_type) - , bound_port_ (bound_port) - , ip_reassembly_ (nullptr) - {} - std::vector* const destination_vector_; - HostAddress* const source_address_; - uint16_t* const source_port_; - bool success_; - - pcpp::LinkLayerType link_type_; - const uint16_t bound_port_; - Udpcap::IpReassembly* ip_reassembly_; - }; - struct CallbackArgsRawPtr { CallbackArgsRawPtr(char* destination_buffer, size_t destination_buffer_size, HostAddress* source_address, uint16_t* source_port, uint16_t bound_port, pcpp::LinkLayerType link_type) @@ -142,13 +121,6 @@ namespace Udpcap // TODO: Re-implement or remove. This is currently (2024-02-06) implemented faulty. bool hasPendingDatagrams() const; - std::vector receiveDatagram_OLD(HostAddress* source_address = nullptr, uint16_t* source_port = nullptr); - std::vector receiveDatagram_OLD(unsigned long timeout_ms, HostAddress* source_address = nullptr, uint16_t* source_port = nullptr); - - // TODO: cleanup - size_t receiveDatagram_OLD(char* data, size_t max_len, HostAddress* source_address = nullptr, uint16_t* source_port = nullptr); - size_t receiveDatagram_OLD(char* data, size_t max_len, unsigned long timeout_ms, HostAddress* source_address = nullptr, uint16_t* source_port = nullptr); - size_t receiveDatagram(char* data , size_t max_len , unsigned long timeout_ms @@ -169,9 +141,6 @@ namespace Udpcap //// Internal ////////////////////////////////////////// private: - // RAII for pcap_if_t* - typedef std::unique_ptr pcap_if_t_uniqueptr; - static std::pair getDeviceByIp(const HostAddress& ip); static std::vector> getAllDevices(); @@ -186,9 +155,6 @@ namespace Udpcap void kickstartLoopbackMulticast() const; // Callbacks - static void PacketHandlerVector(unsigned char* param, const struct pcap_pkthdr* header, const unsigned char* pkt_data); - static void FillCallbackArgsVector(CallbackArgsVector* callback_args, const pcpp::IPv4Layer* ip_layer, const pcpp::UdpLayer* udp_layer); - static void PacketHandlerRawPtr(unsigned char* param, const struct pcap_pkthdr* header, const unsigned char* pkt_data); static void FillCallbackArgsRawPtr(CallbackArgsRawPtr* callback_args, const pcpp::IPv4Layer* ip_layer, const pcpp::UdpLayer* udp_layer);