ldap: assume GSS-SPNEGO as default
[wireshark-sm.git] / extcap / udpdump.c
blob333eaa292bd95ac713983eab53a1253b409c5e62
1 /* udpdump.c
2 * udpdump is an extcap tool used to get packets exported from a source (like a network device or a GSMTAP producer) that
3 * are dumped to a pcap file
5 * Copyright 2016, Dario Lombardo <lomato@gmail.com>
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * SPDX-License-Identifier: GPL-2.0-or-later
14 #include "config.h"
15 #define WS_LOG_DOMAIN "udpdump"
17 #include <extcap/extcap-base.h>
19 #include <glib.h>
20 #include <glib/gprintf.h>
21 #include <stdlib.h>
23 #ifdef HAVE_SYS_TIME_H
24 #include <sys/time.h>
25 #endif
27 #ifdef HAVE_NETINET_IN_H
28 #include <netinet/in.h>
29 #endif
31 #include <string.h>
32 #include <errno.h>
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
38 #include <writecap/pcapio.h>
39 #include <wiretap/wtap.h>
40 #include <wsutil/strtoi.h>
41 #include <wsutil/inet_addr.h>
42 #include <wsutil/filesystem.h>
43 #include <wsutil/privileges.h>
44 #include <wsutil/socket.h>
45 #include <wsutil/please_report_bug.h>
46 #include <wsutil/wslog.h>
47 #include <wsutil/pint.h>
48 #include <wsutil/exported_pdu_tlvs.h>
50 #include <cli_main.h>
52 #define PCAP_SNAPLEN 0xffff
54 #define UDPDUMP_DEFAULT_PORT 5555
56 #define UDPDUMP_EXTCAP_INTERFACE "udpdump"
57 #define UDPDUMP_VERSION_MAJOR "0"
58 #define UDPDUMP_VERSION_MINOR "1"
59 #define UDPDUMP_VERSION_RELEASE "0"
61 #define PKT_BUF_SIZE 65535
63 #define UDPDUMP_EXPORT_HEADER_LEN 40
65 enum {
66 EXTCAP_BASE_OPTIONS_ENUM,
67 OPT_HELP,
68 OPT_VERSION,
69 OPT_PORT,
70 OPT_PAYLOAD
73 static struct ws_option longopts[] = {
74 EXTCAP_BASE_OPTIONS,
75 /* Generic application options */
76 { "help", ws_no_argument, NULL, OPT_HELP},
77 { "version", ws_no_argument, NULL, OPT_VERSION},
78 /* Interfaces options */
79 { "port", ws_required_argument, NULL, OPT_PORT},
80 { "payload", ws_required_argument, NULL, OPT_PAYLOAD},
81 { 0, 0, 0, 0 }
84 static int list_config(char *interface)
86 unsigned inc = 0;
88 if (!interface) {
89 ws_warning("No interface specified.");
90 return EXIT_FAILURE;
93 printf("arg {number=%u}{call=--port}{display=Listen port}"
94 "{type=unsigned}{range=1,65535}{default=%u}{tooltip=The port the receiver listens on}\n",
95 inc++, UDPDUMP_DEFAULT_PORT);
96 printf("arg {number=%u}{call=--payload}{display=Payload type}"
97 "{type=string}{default=data}{tooltip=The type used to describe the payload in the exported pdu format}\n",
98 inc++);
100 extcap_config_debug(&inc);
102 return EXIT_SUCCESS;
105 static int setup_listener(const uint16_t port, socket_handle_t* sock)
107 int optval;
108 struct sockaddr_in serveraddr;
109 #ifndef _WIN32
110 struct timeval timeout = { 1, 0 };
111 #endif
113 *sock = socket(AF_INET, SOCK_DGRAM, 0);
115 if (*sock == INVALID_SOCKET) {
116 ws_warning("Error opening socket: %s", strerror(errno));
117 return EXIT_FAILURE;
120 optval = 1;
121 if (setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, (socklen_t)sizeof(int)) < 0) {
122 ws_warning("Can't set socket option SO_REUSEADDR: %s", strerror(errno));
123 goto cleanup_setup_listener;
126 #ifndef _WIN32
127 if (setsockopt (*sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, (socklen_t)sizeof(timeout)) < 0) {
128 ws_warning("Can't set socket option SO_RCVTIMEO: %s", strerror(errno));
129 goto cleanup_setup_listener;
131 #endif
133 memset(&serveraddr, 0x0, sizeof(serveraddr));
134 serveraddr.sin_family = AF_INET;
135 serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
136 serveraddr.sin_port = htons(port);
138 if (bind(*sock, (struct sockaddr *)&serveraddr, (socklen_t)sizeof(serveraddr)) < 0) {
139 ws_warning("Error on binding: %s", strerror(errno));
140 goto cleanup_setup_listener;
143 return EXIT_SUCCESS;
145 cleanup_setup_listener:
146 closesocket(*sock);
147 return EXIT_FAILURE;
151 static int setup_dumpfile(const char* fifo, FILE** fp)
153 uint64_t bytes_written = 0;
154 int err;
156 if (!g_strcmp0(fifo, "-")) {
157 *fp = stdout;
158 return EXIT_SUCCESS;
161 *fp = fopen(fifo, "wb");
162 if (!(*fp)) {
163 ws_warning("Error creating output file: %s", g_strerror(errno));
164 return EXIT_FAILURE;
167 if (!libpcap_write_file_header(*fp, 252, PCAP_SNAPLEN, false, &bytes_written, &err)) {
168 ws_warning("Can't write pcap file header: %s", g_strerror(err));
169 return EXIT_FAILURE;
172 fflush(*fp);
174 return EXIT_SUCCESS;
177 static void add_proto_name(uint8_t* mbuf, unsigned* offset, const char* proto_name)
179 size_t proto_str_len = strlen(proto_name);
180 uint16_t proto_name_len = (uint16_t)((proto_str_len + 3) & 0xfffffffc);
182 phton16(mbuf + *offset, EXP_PDU_TAG_DISSECTOR_NAME);
183 *offset += 2;
184 phton16(mbuf + *offset, proto_name_len);
185 *offset += 2;
187 memcpy(mbuf + *offset, proto_name, proto_str_len);
188 *offset += proto_name_len;
191 static void add_ip_source_address(uint8_t* mbuf, unsigned* offset, uint32_t source_address)
193 phton16(mbuf + *offset, EXP_PDU_TAG_IPV4_SRC);
194 *offset += 2;
195 phton16(mbuf + *offset, 4);
196 *offset += 2;
197 memcpy(mbuf + *offset, &source_address, 4);
198 *offset += 4;
201 static void add_ip_dest_address(uint8_t* mbuf, unsigned* offset, uint32_t dest_address)
203 phton16(mbuf + *offset, EXP_PDU_TAG_IPV4_DST);
204 *offset += 2;
205 phton16(mbuf + *offset, 4);
206 *offset += 2;
207 memcpy(mbuf + *offset, &dest_address, 4);
208 *offset += 4;
211 static void add_udp_source_port(uint8_t* mbuf, unsigned* offset, uint16_t src_port)
213 uint32_t port = htonl(src_port);
215 phton16(mbuf + *offset, EXP_PDU_TAG_SRC_PORT);
216 *offset += 2;
217 phton16(mbuf + *offset, 4);
218 *offset += 2;
219 memcpy(mbuf + *offset, &port, 4);
220 *offset += 4;
223 static void add_udp_dst_port(uint8_t* mbuf, unsigned* offset, uint16_t dst_port)
225 uint32_t port = htonl(dst_port);
227 phton16(mbuf + *offset, EXP_PDU_TAG_DST_PORT);
228 *offset += 2;
229 phton16(mbuf + *offset, 4);
230 *offset += 2;
231 memcpy(mbuf + *offset, &port, 4);
232 *offset += 4;
235 static void add_end_options(uint8_t* mbuf, unsigned* offset)
237 memset(mbuf + *offset, 0x0, 4);
238 *offset += 4;
241 static int dump_packet(const char* proto_name, const uint16_t listenport, const char* buf,
242 const ssize_t buflen, const struct sockaddr_in clientaddr, FILE* fp)
244 uint8_t* mbuf;
245 unsigned offset = 0;
246 int64_t curtime = g_get_real_time();
247 uint64_t bytes_written = 0;
248 int err;
249 int ret = EXIT_SUCCESS;
251 /* The space we need is the standard header + variable lengths */
252 mbuf = (uint8_t*)g_malloc0(UDPDUMP_EXPORT_HEADER_LEN + ((strlen(proto_name) + 3) & 0xfffffffc) + buflen);
254 add_proto_name(mbuf, &offset, proto_name);
255 add_ip_source_address(mbuf, &offset, clientaddr.sin_addr.s_addr);
256 add_ip_dest_address(mbuf, &offset, g_htonl(INADDR_LOOPBACK));
257 add_udp_source_port(mbuf, &offset, clientaddr.sin_port);
258 add_udp_dst_port(mbuf, &offset, listenport);
259 add_end_options(mbuf, &offset);
261 memcpy(mbuf + offset, buf, buflen);
262 offset += (unsigned)buflen;
264 if (!libpcap_write_packet(fp,
265 (uint32_t)(curtime / G_USEC_PER_SEC), (uint32_t)(curtime % G_USEC_PER_SEC),
266 offset, offset, mbuf, &bytes_written, &err)) {
267 ws_warning("Can't write packet: %s", g_strerror(err));
268 ret = EXIT_FAILURE;
271 fflush(fp);
273 g_free(mbuf);
274 return ret;
277 static void run_listener(const char* fifo, const uint16_t port, const char* proto_name)
279 struct sockaddr_in clientaddr;
280 socklen_t clientlen = sizeof(clientaddr);
281 socket_handle_t sock;
282 char* buf;
283 ssize_t buflen;
284 FILE* fp = NULL;
286 if (setup_dumpfile(fifo, &fp) == EXIT_FAILURE) {
287 if (fp)
288 fclose(fp);
289 return;
292 if (setup_listener(port, &sock) == EXIT_FAILURE)
293 return;
295 ws_debug("Listener running on port %u", port);
297 buf = (char*)g_malloc(PKT_BUF_SIZE);
298 while(!extcap_end_application) {
299 memset(buf, 0x0, PKT_BUF_SIZE);
301 buflen = recvfrom(sock, buf, PKT_BUF_SIZE, 0, (struct sockaddr *)&clientaddr, &clientlen);
302 if (buflen < 0) {
303 switch(errno) {
304 case EAGAIN:
305 case EINTR:
306 break;
307 default:
308 #ifdef _WIN32
310 wchar_t *errmsg = NULL;
311 int err = WSAGetLastError();
312 FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
313 NULL, err,
314 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
315 (LPWSTR)&errmsg, 0, NULL);
316 ws_warning("Error in recvfrom: %S (err=%d)", errmsg, err);
317 LocalFree(errmsg);
319 #else
320 ws_warning("Error in recvfrom: %s (errno=%d)", strerror(errno), errno);
321 #endif
322 extcap_end_application = true;
323 break;
325 } else {
326 if (dump_packet(proto_name, port, buf, buflen, clientaddr, fp) == EXIT_FAILURE)
327 extcap_end_application = true;
331 fclose(fp);
332 closesocket(sock);
333 g_free(buf);
336 int main(int argc, char *argv[])
338 char* err_msg;
339 int option_idx = 0;
340 int result;
341 uint16_t port = 0;
342 int ret = EXIT_FAILURE;
343 extcap_parameters* extcap_conf = g_new0(extcap_parameters, 1);
344 char* help_url;
345 char* help_header = NULL;
346 char* payload = NULL;
347 char* port_msg = NULL;
349 /* Initialize log handler early so we can have proper logging during startup. */
350 extcap_log_init("udpdump");
353 * Get credential information for later use.
355 init_process_policies();
358 * Attempt to get the pathname of the directory containing the
359 * executable file.
361 err_msg = configuration_init(argv[0], NULL);
362 if (err_msg != NULL) {
363 ws_warning("Can't get pathname of directory containing the extcap program: %s.",
364 err_msg);
365 g_free(err_msg);
368 help_url = data_file_url("udpdump.html");
369 extcap_base_set_util_info(extcap_conf, argv[0], UDPDUMP_VERSION_MAJOR, UDPDUMP_VERSION_MINOR, UDPDUMP_VERSION_RELEASE,
370 help_url);
371 g_free(help_url);
372 extcap_base_register_interface(extcap_conf, UDPDUMP_EXTCAP_INTERFACE, "UDP Listener remote capture", 252, "Exported PDUs");
374 help_header = ws_strdup_printf(
375 " %s --extcap-interfaces\n"
376 " %s --extcap-interface=%s --extcap-dlts\n"
377 " %s --extcap-interface=%s --extcap-config\n"
378 " %s --extcap-interface=%s --port 5555 --fifo myfifo --capture",
379 argv[0], argv[0], UDPDUMP_EXTCAP_INTERFACE, argv[0], UDPDUMP_EXTCAP_INTERFACE, argv[0], UDPDUMP_EXTCAP_INTERFACE);
380 extcap_help_add_header(extcap_conf, help_header);
381 g_free(help_header);
382 extcap_help_add_option(extcap_conf, "--help", "print this help");
383 extcap_help_add_option(extcap_conf, "--version", "print the version");
384 port_msg = ws_strdup_printf("the port to listens on. Default: %u", UDPDUMP_DEFAULT_PORT);
385 extcap_help_add_option(extcap_conf, "--port <port>", port_msg);
386 g_free(port_msg);
388 ws_opterr = 0;
389 ws_optind = 0;
391 if (argc == 1) {
392 extcap_help_print(extcap_conf);
393 goto end;
396 while ((result = ws_getopt_long(argc, argv, ":", longopts, &option_idx)) != -1) {
397 switch (result) {
399 case OPT_HELP:
400 extcap_help_print(extcap_conf);
401 ret = EXIT_SUCCESS;
402 goto end;
404 case OPT_VERSION:
405 extcap_version_print(extcap_conf);
406 goto end;
408 case OPT_PORT:
409 if (!ws_strtou16(ws_optarg, NULL, &port)) {
410 ws_warning("Invalid port: %s", ws_optarg);
411 goto end;
413 break;
415 case OPT_PAYLOAD:
416 g_free(payload);
417 payload = g_strdup(ws_optarg);
418 break;
420 case ':':
421 /* missing option argument */
422 ws_warning("Option '%s' requires an argument", argv[ws_optind - 1]);
423 break;
425 default:
426 if (!extcap_base_parse_options(extcap_conf, result - EXTCAP_OPT_LIST_INTERFACES, ws_optarg)) {
427 ws_warning("Invalid option: %s", argv[ws_optind - 1]);
428 goto end;
433 extcap_cmdline_debug(argv, argc);
435 if (ws_optind != argc) {
436 ws_warning("Unexpected extra option: %s", argv[ws_optind]);
437 goto end;
440 if (extcap_base_handle_interface(extcap_conf)) {
441 ret = EXIT_SUCCESS;
442 goto end;
445 if (!extcap_base_register_graceful_shutdown_cb(extcap_conf, NULL)) {
446 ret = EXIT_SUCCESS;
447 goto end;
450 if (extcap_conf->show_config) {
451 ret = list_config(extcap_conf->interface);
452 goto end;
455 if (!payload)
456 payload = g_strdup("data");
458 err_msg = ws_init_sockets();
459 if (err_msg != NULL) {
460 ws_warning("Error: %s", err_msg);
461 g_free(err_msg);
462 ws_warning("%s", please_report_bug());
463 goto end;
466 if (port == 0)
467 port = UDPDUMP_DEFAULT_PORT;
469 if (extcap_conf->capture)
470 run_listener(extcap_conf->fifo, port, payload);
472 end:
473 /* clean up stuff */
474 extcap_base_cleanup(&extcap_conf);
475 g_free(payload);
476 return ret;
480 * Editor modelines - https://www.wireshark.org/tools/modelines.html
482 * Local variables:
483 * c-basic-offset: 8
484 * tab-width: 8
485 * indent-tabs-mode: t
486 * End:
488 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
489 * :indentSize=8:tabSize=8:noTabs=false: