Revert "TODO tools/pidl/lib/Parse/Pidl/Wireshark/NDR.pm \@VALUEREF\@"
[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 <ui/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 orignal 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                     print_usage(stderr);
316                     exit(0);
317                     break;
318
319                 case 'm':
320                     // m is an internal-only option used when the daemon session process is created
321                     mode = SHARKD_MODE_GOLD_CONSOLE;
322                     break;
323
324                 case 'v':         /* Show version and exit */
325                     show_version();
326                     exit(0);
327                     break;
328
329                 default:
330                     if (!ws_optopt)
331                         fprintf(stderr, "This option isn't supported: %s\n", argv[ws_optind]);
332                     fprintf(stderr, "Use sharkd -h for details of supported options\n");
333                     exit(0);
334                     break;
335             }
336         } while (opt != -1);
337     }
338
339     if (mode == SHARKD_MODE_CLASSIC_DAEMON || mode == SHARKD_MODE_GOLD_DAEMON)
340     {
341         /* all good - try to daemonize */
342 #ifndef _WIN32
343         pid = fork();
344         if (pid == -1)
345             fprintf(stderr, "cannot go to background fork() failed: %s\n", g_strerror(errno));
346
347         if (pid != 0)
348         {
349             /* parent */
350             exit(0);
351         }
352 #endif
353     }
354
355     return 0;
356 }
357
358 int
359 #ifndef _WIN32
360 sharkd_loop(int argc _U_, char* argv[] _U_)
361 #else
362 sharkd_loop(int argc _U_, char* argv[])
363 #endif
364 {
365     if (mode == SHARKD_MODE_CLASSIC_CONSOLE || mode == SHARKD_MODE_GOLD_CONSOLE)
366     {
367         return sharkd_session_main(mode);
368     }
369
370     while (1)
371     {
372 #ifndef _WIN32
373         pid_t pid;
374 #else
375         size_t i_handles;
376         HANDLE handles[2];
377         PROCESS_INFORMATION pi;
378         STARTUPINFO si;
379         char *exename;
380         char command_line[2048];
381 #endif
382         socket_handle_t fd;
383
384         fd = accept(_server_fd, NULL, NULL);
385         if (fd == INVALID_SOCKET)
386         {
387             fprintf(stderr, "cannot accept(): %s\n", g_strerror(errno));
388             continue;
389         }
390
391         /* wireshark is not ready for handling multiple capture files in single process, so fork(), and handle it in separate process */
392 #ifndef _WIN32
393         pid = fork();
394         if (pid == 0)
395         {
396             closesocket(_server_fd);
397             /* redirect stdin, stdout to socket */
398             dup2(fd, 0);
399             dup2(fd, 1);
400             close(fd);
401
402             exit(sharkd_session_main(mode));
403         }
404
405         if (pid == -1)
406         {
407             fprintf(stderr, "cannot fork(): %s\n", g_strerror(errno));
408         }
409
410 #else
411         memset(&pi, 0, sizeof(pi));
412         memset(&si, 0, sizeof(si));
413
414         si.cb = sizeof(si);
415         si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
416         si.hStdInput = (HANDLE) fd;
417         si.hStdOutput = (HANDLE) fd;
418         si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
419
420         i_handles = 0;
421         handles[i_handles++] = (HANDLE)fd;
422         if (si.hStdError != NULL) {
423             handles[i_handles++] = si.hStdError;
424         }
425
426         exename = get_executable_path("sharkd");
427
428         // we need to pass in all of the command line parameters except the -a parameter
429         // passing in -a at this point would could a loop, each iteration of which would generate a new session process
430         memset(&command_line, 0, sizeof(command_line));
431
432         if (mode <= SHARKD_MODE_CLASSIC_DAEMON)
433         {
434             (void) g_strlcat(command_line, "sharkd.exe -", sizeof(command_line));
435         }
436         else
437         {
438             // The -m option used here is an internal-only option that notifies the child process that it should
439             // run in Gold Console mode
440             (void) g_strlcat(command_line, "sharkd.exe -m", sizeof(command_line));
441
442             for (int i = 1; i < argc; i++)
443             {
444                 if (
445                         !g_ascii_strncasecmp(argv[i], "-a", (guint)strlen(argv[i]))
446                         || !g_ascii_strncasecmp(argv[i], "--api", (guint)strlen(argv[i]))
447                    )
448                 {
449                     i++;  // skip the socket details
450                 }
451                 else
452                 {
453                     (void) g_strlcat(command_line, " ", sizeof(command_line));
454                     (void) g_strlcat(command_line, argv[i], sizeof(command_line));
455                 }
456             }
457         }
458
459         if (!win32_create_process(exename, command_line, NULL, NULL, i_handles, handles, 0, NULL, NULL, &si, &pi))
460         {
461             fprintf(stderr, "win32_create_process(%s) failed\n", exename);
462         }
463         else
464         {
465             CloseHandle(pi.hThread);
466         }
467
468         g_free(exename);
469 #endif
470
471         closesocket(fd);
472     }
473     return 0;
474 }