From e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 22:34:10 +0200 Subject: Adding upstream version 4.2.2. Signed-off-by: Daniel Baumann --- extcap/udpdump.c | 490 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 490 insertions(+) create mode 100644 extcap/udpdump.c (limited to 'extcap/udpdump.c') diff --git a/extcap/udpdump.c b/extcap/udpdump.c new file mode 100644 index 0000000..7f4602a --- /dev/null +++ b/extcap/udpdump.c @@ -0,0 +1,490 @@ +/* udpdump.c + * udpdump is an extcap tool used to get packets exported from a source (like a network device or a GSMTAP producer) that + * are dumped to a pcap file + * + * Copyright 2016, Dario Lombardo + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" +#define WS_LOG_DOMAIN "udpdump" + +#include + +#include +#include +#include + +#ifdef HAVE_SYS_TIME_H + #include +#endif + +#ifdef HAVE_NETINET_IN_H + #include +#endif + +#include +#include + +#ifdef HAVE_UNISTD_H + #include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PCAP_SNAPLEN 0xffff + +#define UDPDUMP_DEFAULT_PORT 5555 + +#define UDPDUMP_EXTCAP_INTERFACE "udpdump" +#define UDPDUMP_VERSION_MAJOR "0" +#define UDPDUMP_VERSION_MINOR "1" +#define UDPDUMP_VERSION_RELEASE "0" + +#define PKT_BUF_SIZE 65535 + +#define UDPDUMP_EXPORT_HEADER_LEN 40 + +enum { + EXTCAP_BASE_OPTIONS_ENUM, + OPT_HELP, + OPT_VERSION, + OPT_PORT, + OPT_PAYLOAD +}; + +static struct ws_option longopts[] = { + EXTCAP_BASE_OPTIONS, + /* Generic application options */ + { "help", ws_no_argument, NULL, OPT_HELP}, + { "version", ws_no_argument, NULL, OPT_VERSION}, + /* Interfaces options */ + { "port", ws_required_argument, NULL, OPT_PORT}, + { "payload", ws_required_argument, NULL, OPT_PAYLOAD}, + { 0, 0, 0, 0 } +}; + +static int list_config(char *interface) +{ + unsigned inc = 0; + + if (!interface) { + ws_warning("No interface specified."); + return EXIT_FAILURE; + } + + printf("arg {number=%u}{call=--port}{display=Listen port}" + "{type=unsigned}{range=1,65535}{default=%u}{tooltip=The port the receiver listens on}\n", + inc++, UDPDUMP_DEFAULT_PORT); + printf("arg {number=%u}{call=--payload}{display=Payload type}" + "{type=string}{default=data}{tooltip=The type used to describe the payload in the exported pdu format}\n", + inc++); + + extcap_config_debug(&inc); + + return EXIT_SUCCESS; +} + +static int setup_listener(const uint16_t port, socket_handle_t* sock) +{ + int optval; + struct sockaddr_in serveraddr; +#ifndef _WIN32 + struct timeval timeout = { 1, 0 }; +#endif + + *sock = socket(AF_INET, SOCK_DGRAM, 0); + + if (*sock == INVALID_SOCKET) { + ws_warning("Error opening socket: %s", strerror(errno)); + return EXIT_FAILURE; + } + + optval = 1; + if (setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, (socklen_t)sizeof(int)) < 0) { + ws_warning("Can't set socket option SO_REUSEADDR: %s", strerror(errno)); + goto cleanup_setup_listener; + } + +#ifndef _WIN32 + if (setsockopt (*sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, (socklen_t)sizeof(timeout)) < 0) { + ws_warning("Can't set socket option SO_RCVTIMEO: %s", strerror(errno)); + goto cleanup_setup_listener; + } +#endif + + memset(&serveraddr, 0x0, sizeof(serveraddr)); + serveraddr.sin_family = AF_INET; + serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); + serveraddr.sin_port = htons(port); + + if (bind(*sock, (struct sockaddr *)&serveraddr, (socklen_t)sizeof(serveraddr)) < 0) { + ws_warning("Error on binding: %s", strerror(errno)); + goto cleanup_setup_listener; + } + + return EXIT_SUCCESS; + +cleanup_setup_listener: + closesocket(*sock); + return EXIT_FAILURE; + +} + +static int setup_dumpfile(const char* fifo, FILE** fp) +{ + uint64_t bytes_written = 0; + int err; + + if (!g_strcmp0(fifo, "-")) { + *fp = stdout; + return EXIT_SUCCESS; + } + + *fp = fopen(fifo, "wb"); + if (!(*fp)) { + ws_warning("Error creating output file: %s", g_strerror(errno)); + return EXIT_FAILURE; + } + + if (!libpcap_write_file_header(*fp, 252, PCAP_SNAPLEN, false, &bytes_written, &err)) { + ws_warning("Can't write pcap file header: %s", g_strerror(err)); + return EXIT_FAILURE; + } + + fflush(*fp); + + return EXIT_SUCCESS; +} + +static void add_proto_name(uint8_t* mbuf, unsigned* offset, const char* proto_name) +{ + size_t proto_str_len = strlen(proto_name); + uint16_t proto_name_len = (uint16_t)((proto_str_len + 3) & 0xfffffffc); + + phton16(mbuf + *offset, EXP_PDU_TAG_DISSECTOR_NAME); + *offset += 2; + phton16(mbuf + *offset, proto_name_len); + *offset += 2; + + memcpy(mbuf + *offset, proto_name, proto_str_len); + *offset += proto_name_len; +} + +static void add_ip_source_address(uint8_t* mbuf, unsigned* offset, uint32_t source_address) +{ + phton16(mbuf + *offset, EXP_PDU_TAG_IPV4_SRC); + *offset += 2; + phton16(mbuf + *offset, 4); + *offset += 2; + memcpy(mbuf + *offset, &source_address, 4); + *offset += 4; +} + +static void add_ip_dest_address(uint8_t* mbuf, unsigned* offset, uint32_t dest_address) +{ + phton16(mbuf + *offset, EXP_PDU_TAG_IPV4_DST); + *offset += 2; + phton16(mbuf + *offset, 4); + *offset += 2; + memcpy(mbuf + *offset, &dest_address, 4); + *offset += 4; +} + +static void add_udp_source_port(uint8_t* mbuf, unsigned* offset, uint16_t src_port) +{ + uint32_t port = htonl(src_port); + + phton16(mbuf + *offset, EXP_PDU_TAG_SRC_PORT); + *offset += 2; + phton16(mbuf + *offset, 4); + *offset += 2; + memcpy(mbuf + *offset, &port, 4); + *offset += 4; +} + +static void add_udp_dst_port(uint8_t* mbuf, unsigned* offset, uint16_t dst_port) +{ + uint32_t port = htonl(dst_port); + + phton16(mbuf + *offset, EXP_PDU_TAG_DST_PORT); + *offset += 2; + phton16(mbuf + *offset, 4); + *offset += 2; + memcpy(mbuf + *offset, &port, 4); + *offset += 4; +} + +static void add_end_options(uint8_t* mbuf, unsigned* offset) +{ + memset(mbuf + *offset, 0x0, 4); + *offset += 4; +} + +static int dump_packet(const char* proto_name, const uint16_t listenport, const char* buf, + const ssize_t buflen, const struct sockaddr_in clientaddr, FILE* fp) +{ + uint8_t* mbuf; + unsigned offset = 0; + int64_t curtime = g_get_real_time(); + uint64_t bytes_written = 0; + int err; + int ret = EXIT_SUCCESS; + + /* The space we need is the standard header + variable lengths */ + mbuf = (uint8_t*)g_malloc0(UDPDUMP_EXPORT_HEADER_LEN + ((strlen(proto_name) + 3) & 0xfffffffc) + buflen); + + add_proto_name(mbuf, &offset, proto_name); + add_ip_source_address(mbuf, &offset, clientaddr.sin_addr.s_addr); + add_ip_dest_address(mbuf, &offset, WS_IN4_LOOPBACK); + add_udp_source_port(mbuf, &offset, clientaddr.sin_port); + add_udp_dst_port(mbuf, &offset, listenport); + add_end_options(mbuf, &offset); + + memcpy(mbuf + offset, buf, buflen); + offset += (unsigned)buflen; + + if (!libpcap_write_packet(fp, + (uint32_t)(curtime / G_USEC_PER_SEC), (uint32_t)(curtime % G_USEC_PER_SEC), + offset, offset, mbuf, &bytes_written, &err)) { + ws_warning("Can't write packet: %s", g_strerror(err)); + ret = EXIT_FAILURE; + } + + fflush(fp); + + g_free(mbuf); + return ret; +} + +static void run_listener(const char* fifo, const uint16_t port, const char* proto_name) +{ + struct sockaddr_in clientaddr; + socklen_t clientlen = sizeof(clientaddr); + socket_handle_t sock; + char* buf; + ssize_t buflen; + FILE* fp = NULL; + + if (setup_dumpfile(fifo, &fp) == EXIT_FAILURE) { + if (fp) + fclose(fp); + return; + } + + if (setup_listener(port, &sock) == EXIT_FAILURE) + return; + + ws_debug("Listener running on port %u", port); + + buf = (char*)g_malloc(PKT_BUF_SIZE); + while(!extcap_end_application) { + memset(buf, 0x0, PKT_BUF_SIZE); + + buflen = recvfrom(sock, buf, PKT_BUF_SIZE, 0, (struct sockaddr *)&clientaddr, &clientlen); + if (buflen < 0) { + switch(errno) { + case EAGAIN: + case EINTR: + break; + default: +#ifdef _WIN32 + { + wchar_t *errmsg = NULL; + int err = WSAGetLastError(); + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&errmsg, 0, NULL); + ws_warning("Error in recvfrom: %S (err=%d)", errmsg, err); + LocalFree(errmsg); + } +#else + ws_warning("Error in recvfrom: %s (errno=%d)", strerror(errno), errno); +#endif + extcap_end_application = true; + break; + } + } else { + if (dump_packet(proto_name, port, buf, buflen, clientaddr, fp) == EXIT_FAILURE) + extcap_end_application = true; + } + } + + fclose(fp); + closesocket(sock); + g_free(buf); +} + +int main(int argc, char *argv[]) +{ + char* err_msg; + int option_idx = 0; + int result; + uint16_t port = 0; + int ret = EXIT_FAILURE; + extcap_parameters* extcap_conf = g_new0(extcap_parameters, 1); + char* help_url; + char* help_header = NULL; + char* payload = NULL; + char* port_msg = NULL; + + /* Initialize log handler early so we can have proper logging during startup. */ + extcap_log_init("udpdump"); + + /* + * Get credential information for later use. + */ + init_process_policies(); + + /* + * Attempt to get the pathname of the directory containing the + * executable file. + */ + err_msg = configuration_init(argv[0], NULL); + if (err_msg != NULL) { + ws_warning("Can't get pathname of directory containing the extcap program: %s.", + err_msg); + g_free(err_msg); + } + + help_url = data_file_url("udpdump.html"); + extcap_base_set_util_info(extcap_conf, argv[0], UDPDUMP_VERSION_MAJOR, UDPDUMP_VERSION_MINOR, UDPDUMP_VERSION_RELEASE, + help_url); + g_free(help_url); + extcap_base_register_interface(extcap_conf, UDPDUMP_EXTCAP_INTERFACE, "UDP Listener remote capture", 252, "Exported PDUs"); + + help_header = ws_strdup_printf( + " %s --extcap-interfaces\n" + " %s --extcap-interface=%s --extcap-dlts\n" + " %s --extcap-interface=%s --extcap-config\n" + " %s --extcap-interface=%s --port 5555 --fifo myfifo --capture", + argv[0], argv[0], UDPDUMP_EXTCAP_INTERFACE, argv[0], UDPDUMP_EXTCAP_INTERFACE, argv[0], UDPDUMP_EXTCAP_INTERFACE); + extcap_help_add_header(extcap_conf, help_header); + g_free(help_header); + extcap_help_add_option(extcap_conf, "--help", "print this help"); + extcap_help_add_option(extcap_conf, "--version", "print the version"); + port_msg = ws_strdup_printf("the port to listens on. Default: %u", UDPDUMP_DEFAULT_PORT); + extcap_help_add_option(extcap_conf, "--port ", port_msg); + g_free(port_msg); + + ws_opterr = 0; + ws_optind = 0; + + if (argc == 1) { + extcap_help_print(extcap_conf); + goto end; + } + + while ((result = ws_getopt_long(argc, argv, ":", longopts, &option_idx)) != -1) { + switch (result) { + + case OPT_HELP: + extcap_help_print(extcap_conf); + ret = EXIT_SUCCESS; + goto end; + + case OPT_VERSION: + extcap_version_print(extcap_conf); + goto end; + + case OPT_PORT: + if (!ws_strtou16(ws_optarg, NULL, &port)) { + ws_warning("Invalid port: %s", ws_optarg); + goto end; + } + break; + + case OPT_PAYLOAD: + g_free(payload); + payload = g_strdup(ws_optarg); + break; + + case ':': + /* missing option argument */ + ws_warning("Option '%s' requires an argument", argv[ws_optind - 1]); + break; + + default: + if (!extcap_base_parse_options(extcap_conf, result - EXTCAP_OPT_LIST_INTERFACES, ws_optarg)) { + ws_warning("Invalid option: %s", argv[ws_optind - 1]); + goto end; + } + } + } + + extcap_cmdline_debug(argv, argc); + + if (ws_optind != argc) { + ws_warning("Unexpected extra option: %s", argv[ws_optind]); + goto end; + } + + if (extcap_base_handle_interface(extcap_conf)) { + ret = EXIT_SUCCESS; + goto end; + } + + if (!extcap_base_register_graceful_shutdown_cb(extcap_conf, NULL)) { + ret = EXIT_SUCCESS; + goto end; + } + + if (extcap_conf->show_config) { + ret = list_config(extcap_conf->interface); + goto end; + } + + if (!payload) + payload = g_strdup("data"); + + err_msg = ws_init_sockets(); + if (err_msg != NULL) { + ws_warning("Error: %s", err_msg); + g_free(err_msg); + ws_warning("%s", please_report_bug()); + goto end; + } + + if (port == 0) + port = UDPDUMP_DEFAULT_PORT; + + if (extcap_conf->capture) + run_listener(extcap_conf->fifo, port, payload); + +end: + /* clean up stuff */ + extcap_base_cleanup(&extcap_conf); + g_free(payload); + return ret; +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */ -- cgit v1.2.3