decrypt
[metze/wireshark/wip.git] / extcap / udpdump.c
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
4  *
5  * Copyright 2016, Dario Lombardo <lomato@gmail.com>
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * SPDX-License-Identifier: GPL-2.0-or-later
12  */
13
14 #include "config.h"
15
16 #include <extcap/extcap-base.h>
17
18 #include <glib.h>
19 #include <glib/gprintf.h>
20 #include <stdlib.h>
21
22 #ifdef HAVE_SYS_TIME_H
23         #include <sys/time.h>
24 #endif
25
26 #ifdef HAVE_SYS_SOCKET_H
27         #include <sys/socket.h>
28 #endif
29
30 #ifdef HAVE_NETINET_IN_H
31         #include <netinet/in.h>
32 #endif
33
34 #include <string.h>
35 #include <errno.h>
36
37 #ifdef HAVE_UNISTD_H
38         #include <unistd.h>
39 #endif
40
41 #ifdef HAVE_ARPA_INET_H
42         #include <arpa/inet.h>
43 #endif
44
45 #include <writecap/pcapio.h>
46 #include <wiretap/wtap.h>
47 #include <wsutil/strtoi.h>
48 #include <wsutil/inet_addr.h>
49 #include <wsutil/filesystem.h>
50
51 #define PCAP_SNAPLEN 0xffff
52
53 #define UDPDUMP_DEFAULT_PORT 5555
54
55 #define UDPDUMP_EXTCAP_INTERFACE "udpdump"
56 #define UDPDUMP_VERSION_MAJOR "0"
57 #define UDPDUMP_VERSION_MINOR "1"
58 #define UDPDUMP_VERSION_RELEASE "0"
59
60 #define PKT_BUF_SIZE 65535
61
62 #define UDPDUMP_EXPORT_HEADER_LEN 40
63
64 /* Tags (from exported_pdu.h) */
65 #define EXP_PDU_TAG_PROTO_NAME  12
66 #define EXP_PDU_TAG_IPV4_SRC    20
67 #define EXP_PDU_TAG_IPV4_DST    21
68 #define EXP_PDU_TAG_SRC_PORT    25
69 #define EXP_PDU_TAG_DST_PORT    26
70
71 static gboolean run_loop = TRUE;
72
73 enum {
74         EXTCAP_BASE_OPTIONS_ENUM,
75         OPT_HELP,
76         OPT_VERSION,
77         OPT_PORT,
78         OPT_PAYLOAD
79 };
80
81 static struct option longopts[] = {
82         EXTCAP_BASE_OPTIONS,
83         /* Generic application options */
84         { "help", no_argument, NULL, OPT_HELP},
85         { "version", no_argument, NULL, OPT_VERSION},
86         /* Interfaces options */
87         { "port", required_argument, NULL, OPT_PORT},
88         { "payload", required_argument, NULL, OPT_PAYLOAD},
89     { 0, 0, 0, 0 }
90 };
91
92 static int list_config(char *interface)
93 {
94         unsigned inc = 0;
95
96         if (!interface) {
97                 g_warning("No interface specified.");
98                 return EXIT_FAILURE;
99         }
100
101         printf("arg {number=%u}{call=--port}{display=Listen port}"
102                 "{type=unsigned}{range=1,65535}{default=%u}{tooltip=The port the receiver listens on}\n",
103                 inc++, UDPDUMP_DEFAULT_PORT);
104         printf("arg {number=%u}{call=--payload}{display=Payload type}"
105                 "{type=string}{default=data}{tooltip=The type used to describe the payload in the exported pdu format}\n",
106                 inc++);
107
108         extcap_config_debug(&inc);
109
110         return EXIT_SUCCESS;
111 }
112
113 static int setup_listener(const guint16 port, socket_handle_t* sock)
114 {
115         int optval;
116         struct sockaddr_in serveraddr;
117 #ifndef _WIN32
118         struct timeval timeout = { 1, 0 };
119 #endif
120
121         *sock = socket(AF_INET, SOCK_DGRAM, 0);
122
123         if (*sock == INVALID_SOCKET) {
124                 g_warning("Error opening socket: %s", strerror(errno));
125                 return EXIT_FAILURE;
126         }
127
128         optval = 1;
129         if (setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, (socklen_t)sizeof(int)) < 0) {
130                 g_warning("Can't set socket option SO_REUSEADDR: %s", strerror(errno));
131                 goto cleanup_setup_listener;
132         }
133
134 #ifndef _WIN32
135         if (setsockopt (*sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, (socklen_t)sizeof(timeout)) < 0) {
136                 g_warning("Can't set socket option SO_RCVTIMEO: %s", strerror(errno));
137                 goto cleanup_setup_listener;
138         }
139 #endif
140
141         memset(&serveraddr, 0x0, sizeof(serveraddr));
142         serveraddr.sin_family = AF_INET;
143         serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
144         serveraddr.sin_port = htons(port);
145
146         if (bind(*sock, (struct sockaddr *)&serveraddr, (socklen_t)sizeof(serveraddr)) < 0) {
147                 g_warning("Error on binding: %s", strerror(errno));
148                 goto cleanup_setup_listener;
149         }
150
151         return EXIT_SUCCESS;
152
153 cleanup_setup_listener:
154         closesocket(*sock);
155         return EXIT_FAILURE;
156
157 }
158
159 static void exit_from_loop(int signo _U_)
160 {
161         g_warning("Exiting from main loop");
162         run_loop = FALSE;
163 }
164
165 static int setup_dumpfile(const char* fifo, FILE** fp)
166 {
167         guint64 bytes_written = 0;
168         int err;
169
170         if (!g_strcmp0(fifo, "-")) {
171                 *fp = stdout;
172                 return EXIT_SUCCESS;
173         }
174
175         *fp = fopen(fifo, "wb");
176         if (!(*fp)) {
177                 g_warning("Error creating output file: %s", g_strerror(errno));
178                 return EXIT_FAILURE;
179         }
180
181         if (!libpcap_write_file_header(*fp, 252, PCAP_SNAPLEN, FALSE, &bytes_written, &err)) {
182                 g_warning("Can't write pcap file header: %s", g_strerror(err));
183                 return EXIT_FAILURE;
184         }
185
186         return EXIT_SUCCESS;
187 }
188
189 static void add_proto_name(char* mbuf, guint* offset, const char* proto_name)
190 {
191         size_t proto_str_len = strlen(proto_name);
192         guint16 proto_name_len = (guint16)((proto_str_len + 3) & 0xfffffffc);
193
194         mbuf[*offset] = 0;
195         mbuf[*offset+1] = EXP_PDU_TAG_PROTO_NAME;
196         *offset += 2;
197         mbuf[*offset] = proto_name_len >> 8;
198         mbuf[*offset+1] = proto_name_len & 0xff;
199         *offset += 2;
200
201         memcpy(mbuf + *offset, proto_name, proto_str_len);
202         *offset += proto_name_len;
203 }
204
205 static void add_ip_source_address(char* mbuf, guint* offset, uint32_t source_address)
206 {
207         mbuf[*offset] = 0x00;
208         mbuf[*offset+1] = EXP_PDU_TAG_IPV4_SRC;
209         mbuf[*offset+2] = 0;
210         mbuf[*offset+3] = 4;
211         *offset += 4;
212         memcpy(mbuf + *offset, &source_address, 4);
213         *offset += 4;
214 }
215
216 static void add_ip_dest_address(char* mbuf, guint* offset, uint32_t dest_address)
217 {
218         mbuf[*offset] = 0;
219         mbuf[*offset+1] = EXP_PDU_TAG_IPV4_DST;
220         mbuf[*offset+2] = 0;
221         mbuf[*offset+3] = 4;
222         *offset += 4;
223         memcpy(mbuf + *offset, &dest_address, 4);
224         *offset += 4;
225 }
226
227 static void add_udp_source_port(char* mbuf, guint* offset, uint16_t src_port)
228 {
229         uint32_t port = htonl(src_port);
230
231         mbuf[*offset] = 0;
232         mbuf[*offset+1] = EXP_PDU_TAG_SRC_PORT;
233         mbuf[*offset+2] = 0;
234         mbuf[*offset+3] = 4;
235         *offset += 4;
236         memcpy(mbuf + *offset, &port, 4);
237         *offset += 4;
238 }
239
240 static void add_udp_dst_port(char* mbuf, guint* offset, uint16_t dst_port)
241 {
242         uint32_t port = htonl(dst_port);
243
244         mbuf[*offset] = 0;
245         mbuf[*offset+1] = EXP_PDU_TAG_DST_PORT;
246         mbuf[*offset+2] = 0;
247         mbuf[*offset+3] = 4;
248         *offset += 4;
249         memcpy(mbuf + *offset, &port, 4);
250         *offset += 4;
251 }
252
253 static void add_end_options(char* mbuf, guint* offset)
254 {
255         memset(mbuf + *offset, 0x0, 4);
256         *offset += 4;
257 }
258
259 static int dump_packet(const char* proto_name, const guint16 listenport, const char* buf,
260                 const ssize_t buflen, const struct sockaddr_in clientaddr, FILE* fp)
261 {
262         char* mbuf;
263         guint offset = 0;
264         time_t curtime = time(NULL);
265         guint64 bytes_written = 0;
266         int err;
267         int ret = EXIT_SUCCESS;
268
269         /* The space we need is the standard header + variable lengths */
270         mbuf = (char*)g_malloc0(UDPDUMP_EXPORT_HEADER_LEN + ((strlen(proto_name) + 3) & 0xfffffffc) + buflen);
271
272         add_proto_name(mbuf, &offset, proto_name);
273         add_ip_source_address(mbuf, &offset, clientaddr.sin_addr.s_addr);
274         add_ip_dest_address(mbuf, &offset, WS_IN4_LOOPBACK);
275         add_udp_source_port(mbuf, &offset, clientaddr.sin_port);
276         add_udp_dst_port(mbuf, &offset, listenport);
277         add_end_options(mbuf, &offset);
278
279         memcpy(mbuf + offset, buf, buflen);
280         offset += (guint)buflen;
281
282         if (!libpcap_write_packet(fp, curtime, (guint32)(curtime / 1000), offset, offset, mbuf, &bytes_written, &err)) {
283                 g_warning("Can't write packet: %s", g_strerror(err));
284                 ret = EXIT_FAILURE;
285         }
286
287         fflush(fp);
288
289         g_free(mbuf);
290         return ret;
291 }
292
293 static void run_listener(const char* fifo, const guint16 port, const char* proto_name)
294 {
295         struct sockaddr_in clientaddr;
296         int clientlen = sizeof(clientaddr);
297         socket_handle_t sock;
298         char* buf;
299         ssize_t buflen;
300         FILE* fp = NULL;
301
302         if (signal(SIGINT, exit_from_loop) == SIG_ERR) {
303                 g_warning("Can't set signal handler");
304                 return;
305         }
306
307         if (setup_dumpfile(fifo, &fp) == EXIT_FAILURE) {
308                 if (fp)
309                         fclose(fp);
310                 return;
311         }
312
313         if (setup_listener(port, &sock) == EXIT_FAILURE)
314                 return;
315
316         g_debug("Listener running on port %u", port);
317
318         buf = (char*)g_malloc(PKT_BUF_SIZE);
319         while(run_loop == TRUE) {
320                 memset(buf, 0x0, PKT_BUF_SIZE);
321
322                 buflen = recvfrom(sock, buf, PKT_BUF_SIZE, 0, (struct sockaddr *)&clientaddr, &clientlen);
323                 if (buflen < 0) {
324                         switch(errno) {
325                                 case EAGAIN:
326                                 case EINTR:
327                                         break;
328                                 default:
329 #ifdef _WIN32
330                                         {
331                                                 wchar_t *errmsg = NULL;
332                                                 int err = WSAGetLastError();
333                                                 FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
334                                                         NULL, err,
335                                                         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
336                                                         (LPWSTR)&errmsg, 0, NULL);
337                                                 g_warning("Error in recvfrom: %S (err=%d)", errmsg, err);
338                                                 LocalFree(errmsg);
339                                         }
340 #else
341                                         g_warning("Error in recvfrom: %s (errno=%d)", strerror(errno), errno);
342 #endif
343                                         run_loop = FALSE;
344                                         break;
345                         }
346                 } else {
347                         if (dump_packet(proto_name, port, buf, buflen, clientaddr, fp) == EXIT_FAILURE)
348                                 run_loop = FALSE;
349                 }
350         }
351
352         fclose(fp);
353         closesocket(sock);
354         g_free(buf);
355 }
356
357 int real_main(int argc, char *argv[])
358 {
359         int option_idx = 0;
360         int result;
361         guint16 port = 0;
362         int ret = EXIT_FAILURE;
363         extcap_parameters* extcap_conf = g_new0(extcap_parameters, 1);
364         char* help_url;
365         char* help_header = NULL;
366         char* payload = NULL;
367         char* port_msg = NULL;
368 #ifdef _WIN32
369         WSADATA wsaData;
370 #endif  /* _WIN32 */
371
372         help_url = data_file_url("udpdump.html");
373         extcap_base_set_util_info(extcap_conf, argv[0], UDPDUMP_VERSION_MAJOR, UDPDUMP_VERSION_MINOR, UDPDUMP_VERSION_RELEASE,
374                 help_url);
375         g_free(help_url);
376         extcap_base_register_interface(extcap_conf, UDPDUMP_EXTCAP_INTERFACE, "UDP Listener remote capture", 252, "Exported PDUs");
377
378         help_header = g_strdup_printf(
379                 " %s --extcap-interfaces\n"
380                 " %s --extcap-interface=%s --extcap-dlts\n"
381                 " %s --extcap-interface=%s --extcap-config\n"
382                 " %s --extcap-interface=%s --port 5555 --fifo myfifo --capture",
383                 argv[0], argv[0], UDPDUMP_EXTCAP_INTERFACE, argv[0], UDPDUMP_EXTCAP_INTERFACE, argv[0], UDPDUMP_EXTCAP_INTERFACE);
384         extcap_help_add_header(extcap_conf, help_header);
385         g_free(help_header);
386         extcap_help_add_option(extcap_conf, "--help", "print this help");
387         extcap_help_add_option(extcap_conf, "--version", "print the version");
388         port_msg = g_strdup_printf("the port to listens on. Default: %u", UDPDUMP_DEFAULT_PORT);
389         extcap_help_add_option(extcap_conf, "--port <port>", port_msg);
390         g_free(port_msg);
391
392         opterr = 0;
393         optind = 0;
394
395         if (argc == 1) {
396                 extcap_help_print(extcap_conf);
397                 goto end;
398         }
399
400         while ((result = getopt_long(argc, argv, ":", longopts, &option_idx)) != -1) {
401                 switch (result) {
402
403                 case OPT_HELP:
404                         extcap_help_print(extcap_conf);
405                         ret = EXIT_SUCCESS;
406                         goto end;
407
408                 case OPT_VERSION:
409                         printf("%s\n", extcap_conf->version);
410                         goto end;
411
412                 case OPT_PORT:
413                         if (!ws_strtou16(optarg, NULL, &port)) {
414                                 g_warning("Invalid port: %s", optarg);
415                                 goto end;
416                         }
417                         break;
418
419                 case OPT_PAYLOAD:
420                         g_free(payload);
421                         payload = g_strdup(optarg);
422                         break;
423
424                 case ':':
425                         /* missing option argument */
426                         g_warning("Option '%s' requires an argument", argv[optind - 1]);
427                         break;
428
429                 default:
430                         if (!extcap_base_parse_options(extcap_conf, result - EXTCAP_OPT_LIST_INTERFACES, optarg)) {
431                                 g_warning("Invalid option: %s", argv[optind - 1]);
432                                 goto end;
433                         }
434                 }
435         }
436
437         extcap_cmdline_debug(argv, argc);
438
439         if (optind != argc) {
440                 g_warning("Unexpected extra option: %s", argv[optind]);
441                 goto end;
442         }
443
444         if (extcap_base_handle_interface(extcap_conf)) {
445                 ret = EXIT_SUCCESS;
446                 goto end;
447         }
448
449         if (extcap_conf->show_config) {
450                 ret = list_config(extcap_conf->interface);
451                 goto end;
452         }
453
454         if (!payload)
455                 payload = g_strdup("data");
456
457 #ifdef _WIN32
458         result = WSAStartup(MAKEWORD(1,1), &wsaData);
459         if (result != 0) {
460                 g_warning("Error: WSAStartup failed with error: %d", result);
461                 goto end;
462         }
463 #endif  /* _WIN32 */
464
465         if (port == 0)
466                 port = UDPDUMP_DEFAULT_PORT;
467
468         if (extcap_conf->capture)
469                 run_listener(extcap_conf->fifo, port, payload);
470
471 end:
472         /* clean up stuff */
473         extcap_base_cleanup(&extcap_conf);
474         g_free(payload);
475         return ret;
476 }
477
478 #ifdef _WIN32
479 int
480 wmain(int argc, wchar_t *wc_argv[])
481 {
482         char **argv;
483
484         argv = arg_list_utf_16to8(argc, wc_argv);
485         return real_main(argc, argv);
486 }
487 #else
488 int
489 main(int argc, char *argv[])
490 {
491         return real_main(argc, argv);
492 }
493 #endif
494
495 /*
496  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
497  *
498  * Local variables:
499  * c-basic-offset: 8
500  * tab-width: 8
501  * indent-tabs-mode: t
502  * End:
503  *
504  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
505  * :indentSize=8:tabSize=8:noTabs=false:
506  */