epan/dissectors/pidl/witness/witness.cnf nicer + regen
[metze/wireshark/wip.git] / sharkd_daemon.c
1 /* sharkd_daemon.c
2  *
3  * Copyright (C) 2016 Jakub Zawadzki
4  *
5  * Wireshark - Network traffic analyzer
6  * By Gerald Combs <gerald@wireshark.org>
7  * Copyright 1998 Gerald Combs
8  *
9  * SPDX-License-Identifier: GPL-2.0-or-later
10  */
11
12 #include <config.h>
13 #define WS_LOG_DOMAIN LOG_DOMAIN_MAIN
14
15 #include <glib.h>
16
17 #include <stdio.h>
18 #include <errno.h>
19 #include <stddef.h>
20 #include <stdlib.h>
21 #include <signal.h>
22
23 #ifdef _WIN32
24 #include <wsutil/unicode-utils.h>
25 #include <wsutil/win32-utils.h>
26 #endif
27
28 #include <wsutil/filesystem.h>
29 #include <wsutil/socket.h>
30 #include <wsutil/inet_addr.h>
31 #include <wsutil/please_report_bug.h>
32 #include <wsutil/wslog.h>
33 #include <wsutil/ws_getopt.h>
34
35 #ifndef _WIN32
36 #include <sys/un.h>
37 #include <netinet/tcp.h>
38 #endif
39
40 #include <wsutil/strtoi.h>
41 #include <wsutil/version_info.h>
42
43 #include "sharkd.h"
44
45 #ifdef _WIN32
46 /* for windows support TCP sockets */
47 # define SHARKD_TCP_SUPPORT
48 #else
49 /* for other system support only local sockets */
50 # define SHARKD_UNIX_SUPPORT
51 #endif
52
53 static int mode = 0;
54 static socket_handle_t _server_fd = INVALID_SOCKET;
55
56 static socket_handle_t
57 socket_init(char *path)
58 {
59     socket_handle_t fd = INVALID_SOCKET;
60     char *err_msg;
61
62     err_msg = ws_init_sockets();
63     if (err_msg != NULL) {
64         ws_warning("ERROR: %s", err_msg);
65         g_free(err_msg);
66         ws_warning("%s", please_report_bug());
67         return fd;
68     }
69
70 #ifdef SHARKD_UNIX_SUPPORT
71     if (!strncmp(path, "unix:", 5))
72     {
73         struct sockaddr_un s_un;
74         socklen_t s_un_len;
75
76         path += 5;
77
78         if (strlen(path) + 1 > sizeof(s_un.sun_path))
79             return INVALID_SOCKET;
80
81         fd = socket(AF_UNIX, SOCK_STREAM, 0);
82         if (fd == INVALID_SOCKET)
83             return INVALID_SOCKET;
84
85         memset(&s_un, 0, sizeof(s_un));
86         s_un.sun_family = AF_UNIX;
87         (void) g_strlcpy(s_un.sun_path, path, sizeof(s_un.sun_path));
88
89         s_un_len = (socklen_t)(offsetof(struct sockaddr_un, sun_path) + strlen(s_un.sun_path));
90
91         if (s_un.sun_path[0] == '@')
92             s_un.sun_path[0] = '\0';
93
94         if (bind(fd, (struct sockaddr *) &s_un, s_un_len))
95         {
96             closesocket(fd);
97             return INVALID_SOCKET;
98         }
99     }
100     else
101 #endif
102
103 #ifdef SHARKD_TCP_SUPPORT
104     if (!strncmp(path, "tcp:", 4))
105     {
106         struct sockaddr_in s_in;
107         int one = 1;
108         char *port_sep;
109         guint16 port;
110
111         path += 4;
112
113         port_sep = strchr(path, ':');
114         if (!port_sep)
115             return INVALID_SOCKET;
116
117         *port_sep = '\0';
118
119         if (ws_strtou16(port_sep + 1, NULL, &port) == FALSE)
120             return INVALID_SOCKET;
121
122 #ifdef _WIN32
123         /* Need to use WSASocket() to disable overlapped I/O operations,
124             this way on windows SOCKET can be used as HANDLE for stdin/stdout */
125         fd = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0);
126 #else
127         fd = socket(AF_INET, SOCK_STREAM, 0);
128 #endif
129         if (fd == INVALID_SOCKET)
130             return INVALID_SOCKET;
131
132         s_in.sin_family = AF_INET;
133         ws_inet_pton4(path, (ws_in4_addr *)&(s_in.sin_addr.s_addr));
134         s_in.sin_port = g_htons(port);
135         *port_sep = ':';
136
137         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &one, sizeof(one));
138
139         if (bind(fd, (struct sockaddr *) &s_in, sizeof(struct sockaddr_in)))
140         {
141             closesocket(fd);
142             return INVALID_SOCKET;
143         }
144     }
145     else
146 #endif
147     {
148         return INVALID_SOCKET;
149     }
150
151     if (listen(fd, SOMAXCONN))
152     {
153         closesocket(fd);
154         return INVALID_SOCKET;
155     }
156
157     return fd;
158 }
159
160 static void
161 print_usage(FILE* output)
162 {
163     fprintf(output, "\n");
164     fprintf(output, "Usage: sharkd [<classic_options>|<gold_options>]\n");
165
166     fprintf(output, "\n");
167     fprintf(output, "Classic (classic_options):\n");
168     fprintf(output, "  [-|<socket>]\n");
169     fprintf(output, "\n");
170     fprintf(output, "  <socket> examples:\n");
171 #ifdef SHARKD_UNIX_SUPPORT
172     fprintf(output, "  - unix:/tmp/sharkd.sock - listen on unix file /tmp/sharkd.sock\n");
173 #endif
174 #ifdef SHARKD_TCP_SUPPORT
175     fprintf(output, "  - tcp:127.0.0.1:4446 - listen on TCP port 4446\n");
176 #endif
177
178     fprintf(output, "\n");
179     fprintf(output, "Gold (gold_options):\n");
180     fprintf(output, "  -a <socket>, --api <socket>\n");
181     fprintf(output, "                           listen on this socket\n");
182     fprintf(output, "  -h, --help               show this help information\n");
183     fprintf(output, "  -v, --version            show version information\n");
184     fprintf(output, "  -C <config profile>, --config-profile <config profile>\n");
185     fprintf(output, "                           start with specified configuration profile\n");
186
187     fprintf(output, "\n");
188     fprintf(output, "  Examples:\n");
189     fprintf(output, "    sharkd -C myprofile\n");
190     fprintf(output, "    sharkd -a tcp:127.0.0.1:4446 -C myprofile\n");
191
192     fprintf(output, "\n");
193     fprintf(output, "See the sharkd page of the Wireshark wiki for full details.\n");
194     fprintf(output, "\n");
195 }
196
197 int
198 sharkd_init(int argc, char **argv)
199 {
200     /*
201      * The leading + ensures that getopt_long() does not permute the argv[]
202      * entries.
203      *
204      * We have to make sure that the first getopt_long() preserves the content
205      * of argv[] for the subsequent getopt_long() call.
206      *
207      * We use getopt_long() in both cases to ensure that we're using a routine
208      * whose permutation behavior we can control in the same fashion on all
209      * platforms, and so that, if we ever need to process a long argument before
210      * doing further initialization, we can do so.
211      *
212      * Glibc and Solaris libc document that a leading + disables permutation
213      * of options, regardless of whether POSIXLY_CORRECT is set or not; *BSD
214      * and macOS don't document it, but do so anyway.
215      *
216      * We do *not* use a leading - because the behavior of a leading - is
217      * platform-dependent.
218      */
219
220 #define OPTSTRING "+" "a:hmvC:"
221
222     static const char    optstring[] = OPTSTRING;
223
224     // right now we don't have any long options
225     static const struct ws_option long_options[] = {
226         {"api", ws_required_argument, NULL, 'a'},
227         {"help", ws_no_argument, NULL, 'h'},
228         {"version", ws_no_argument, NULL, 'v'},
229         {"config-profile", ws_required_argument, NULL, 'C'},
230         {0, 0, 0, 0 }
231     };
232
233     int opt;
234
235 #ifndef _WIN32
236     pid_t pid;
237 #endif
238     socket_handle_t fd;
239
240     if (argc < 2)
241     {
242         print_usage(stderr);
243         return -1;
244     }
245
246     // check for classic command line
247     if (!strcmp(argv[1], "-") || argv[1][0] == 't' || argv[1][0] == 'u')
248     {
249         mode = SHARKD_MODE_CLASSIC_CONSOLE;
250
251 #ifndef _WIN32
252         signal(SIGCHLD, SIG_IGN);
253 #endif
254
255         if (!strcmp(argv[1], "-"))
256         {
257             mode = SHARKD_MODE_CLASSIC_CONSOLE;
258         }
259         else
260         {
261             fd = socket_init(argv[1]);
262             if (fd == INVALID_SOCKET)
263                 return -1;
264             _server_fd = fd;
265             mode = SHARKD_MODE_CLASSIC_DAEMON;
266         }
267     }
268     else
269         mode = SHARKD_MODE_GOLD_CONSOLE;  // assume we are running as gold console
270
271     if (mode >= SHARKD_MODE_GOLD_CONSOLE)
272     {
273         /*
274            In Daemon Mode, we will come through here twice; once when we start the Daemon and
275            once again after we have forked the session process.  The second time through, the
276            session process has already had its stdin and stdout wired up to the TCP or UNIX
277            socket and so in the original version of sharkd the session process is invoked with
278            the command line: sharkd -
279
280            When not using the classic command line, we want to spawn the session process with
281            the complete command line with all the new options but with the -a option and
282            parameter removed.  Invoking a second time with the -a option will cause a loop
283            where we repeatedly spawn a new session process.
284            */
285
286         do {
287             if (ws_optind > (argc - 1))
288                 break;
289
290             opt = ws_getopt_long(argc, argv, optstring, long_options, NULL);
291
292             switch (opt) {
293                 case 'C':        /* Configuration Profile */
294                     if (profile_exists(ws_optarg, FALSE)) {
295                         set_profile_name(ws_optarg);  // In Daemon Mode, we may need to do this again in the child process
296                     }
297                     else {
298                         fprintf(stderr, "Configuration Profile \"%s\" does not exist\n", ws_optarg);
299                         return -1;
300                     }
301                     break;
302
303                 case 'a':
304                     fd = socket_init(ws_optarg);
305                     if (fd == INVALID_SOCKET)
306                         return -1;
307                     _server_fd = fd;
308
309                     fprintf(stderr, "Sharkd listening on: %s\n", ws_optarg);
310
311                     mode = SHARKD_MODE_GOLD_DAEMON;
312                     break;
313
314                 case 'h':
315                     show_help_header("Daemon variant of Wireshark");
316                     print_usage(stderr);
317                     exit(0);
318                     break;
319
320                 case 'm':
321                     // m is an internal-only option used when the daemon session process is created
322                     mode = SHARKD_MODE_GOLD_CONSOLE;
323                     break;
324
325                 case 'v':         /* Show version and exit */
326                     show_version();
327                     exit(0);
328                     break;
329
330                 default:
331                     if (!ws_optopt)
332                         fprintf(stderr, "This option isn't supported: %s\n", argv[ws_optind]);
333                     fprintf(stderr, "Use sharkd -h for details of supported options\n");
334                     exit(0);
335                     break;
336             }
337         } while (opt != -1);
338     }
339
340     if (mode == SHARKD_MODE_CLASSIC_DAEMON || mode == SHARKD_MODE_GOLD_DAEMON)
341     {
342         /* all good - try to daemonize */
343 #ifndef _WIN32
344         pid = fork();
345         if (pid == -1)
346             fprintf(stderr, "cannot go to background fork() failed: %s\n", g_strerror(errno));
347
348         if (pid != 0)
349         {
350             /* parent */
351             exit(0);
352         }
353 #endif
354     }
355
356     return 0;
357 }
358
359 int
360 #ifndef _WIN32
361 sharkd_loop(int argc _U_, char* argv[] _U_)
362 #else
363 sharkd_loop(int argc _U_, char* argv[])
364 #endif
365 {
366     if (mode == SHARKD_MODE_CLASSIC_CONSOLE || mode == SHARKD_MODE_GOLD_CONSOLE)
367     {
368         return sharkd_session_main(mode);
369     }
370
371     while (1)
372     {
373 #ifndef _WIN32
374         pid_t pid;
375 #else
376         size_t i_handles;
377         HANDLE handles[2];
378         PROCESS_INFORMATION pi;
379         STARTUPINFO si;
380         char *exename;
381         char command_line[2048];
382 #endif
383         socket_handle_t fd;
384
385         fd = accept(_server_fd, NULL, NULL);
386         if (fd == INVALID_SOCKET)
387         {
388             fprintf(stderr, "cannot accept(): %s\n", g_strerror(errno));
389             continue;
390         }
391
392         /* wireshark is not ready for handling multiple capture files in single process, so fork(), and handle it in separate process */
393 #ifndef _WIN32
394         pid = fork();
395         if (pid == 0)
396         {
397             closesocket(_server_fd);
398             /* redirect stdin, stdout to socket */
399             dup2(fd, 0);
400             dup2(fd, 1);
401             close(fd);
402
403             exit(sharkd_session_main(mode));
404         }
405
406         if (pid == -1)
407         {
408             fprintf(stderr, "cannot fork(): %s\n", g_strerror(errno));
409         }
410
411 #else
412         memset(&pi, 0, sizeof(pi));
413         memset(&si, 0, sizeof(si));
414
415         si.cb = sizeof(si);
416         si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
417         si.hStdInput = (HANDLE) fd;
418         si.hStdOutput = (HANDLE) fd;
419         si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
420
421         i_handles = 0;
422         handles[i_handles++] = (HANDLE)fd;
423         if (si.hStdError != NULL) {
424             handles[i_handles++] = si.hStdError;
425         }
426
427         exename = get_executable_path("sharkd");
428
429         // we need to pass in all of the command line parameters except the -a parameter
430         // passing in -a at this point would could a loop, each iteration of which would generate a new session process
431         memset(&command_line, 0, sizeof(command_line));
432
433         if (mode <= SHARKD_MODE_CLASSIC_DAEMON)
434         {
435             (void) g_strlcat(command_line, "sharkd.exe -", sizeof(command_line));
436         }
437         else
438         {
439             // The -m option used here is an internal-only option that notifies the child process that it should
440             // run in Gold Console mode
441             (void) g_strlcat(command_line, "sharkd.exe -m", sizeof(command_line));
442
443             for (int i = 1; i < argc; i++)
444             {
445                 if (
446                         !g_ascii_strncasecmp(argv[i], "-a", (guint)strlen(argv[i]))
447                         || !g_ascii_strncasecmp(argv[i], "--api", (guint)strlen(argv[i]))
448                    )
449                 {
450                     i++;  // skip the socket details
451                 }
452                 else
453                 {
454                     (void) g_strlcat(command_line, " ", sizeof(command_line));
455                     (void) g_strlcat(command_line, argv[i], sizeof(command_line));
456                 }
457             }
458         }
459
460         if (!win32_create_process(exename, command_line, NULL, NULL, i_handles, handles, 0, NULL, NULL, &si, &pi))
461         {
462             fprintf(stderr, "win32_create_process(%s) failed\n", exename);
463         }
464         else
465         {
466             CloseHandle(pi.hThread);
467         }
468
469         g_free(exename);
470 #endif
471
472         closesocket(fd);
473     }
474     return 0;
475 }