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