epan/dissectors/pidl/ C99 drsuapi
[wireshark-sm.git] / sharkd_daemon.c
blobb1c12015092171a3a82069696b47a217dc6475a9
1 /* sharkd_daemon.c
3 * Copyright (C) 2016 Jakub Zawadzki
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
12 #include <config.h>
13 #define WS_LOG_DOMAIN LOG_DOMAIN_MAIN
15 #include <glib.h>
17 #include <stdio.h>
18 #include <errno.h>
19 #include <stddef.h>
20 #include <stdlib.h>
21 #include <signal.h>
23 #ifdef _WIN32
24 #include <wsutil/unicode-utils.h>
25 #include <wsutil/win32-utils.h>
26 #endif
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>
35 #ifndef _WIN32
36 #include <sys/un.h>
37 #include <netinet/tcp.h>
38 #endif
40 #include <wsutil/strtoi.h>
41 #include <wsutil/version_info.h>
43 #include "sharkd.h"
45 #ifdef _WIN32
47 * TCP sockets can work on Linux and other systems, but is disabled by default
48 * because we do not want to encourage insecure setups. Unfiltered access to
49 * sharkd could potentially result in arbitrary command execution.
50 * On Windows, Unix sockets are not supported, so we enable it there.
52 # define SHARKD_TCP_SUPPORT
53 #endif
55 #ifndef _WIN32
56 # define SHARKD_UNIX_SUPPORT
57 #endif
59 static int mode;
60 static socket_handle_t _server_fd = INVALID_SOCKET;
62 static socket_handle_t
63 socket_init(char *path)
65 socket_handle_t fd = INVALID_SOCKET;
66 char *err_msg;
68 err_msg = ws_init_sockets();
69 if (err_msg != NULL) {
70 ws_warning("ERROR: %s", err_msg);
71 g_free(err_msg);
72 ws_warning("%s", please_report_bug());
73 return fd;
76 if (!strncmp(path, "unix:", 5))
78 #ifdef SHARKD_UNIX_SUPPORT
79 struct sockaddr_un s_un;
80 socklen_t s_un_len;
82 path += 5;
84 if (strlen(path) + 1 > sizeof(s_un.sun_path)) {
85 fputs("Socket path too long.\n", stderr);
86 return INVALID_SOCKET;
89 fd = socket(AF_UNIX, SOCK_STREAM, 0);
90 if (fd == INVALID_SOCKET) {
91 fprintf(stderr, "Failed to create socket: %s\n", g_strerror(errno));
92 return INVALID_SOCKET;
95 memset(&s_un, 0, sizeof(s_un));
96 s_un.sun_family = AF_UNIX;
97 (void) g_strlcpy(s_un.sun_path, path, sizeof(s_un.sun_path));
99 s_un_len = (socklen_t)(offsetof(struct sockaddr_un, sun_path) + strlen(s_un.sun_path));
101 if (s_un.sun_path[0] == '@')
102 s_un.sun_path[0] = '\0';
104 if (bind(fd, (struct sockaddr *) &s_un, s_un_len))
106 fprintf(stderr, "Failed to bind socket: %s\n", g_strerror(errno));
107 closesocket(fd);
108 return INVALID_SOCKET;
110 #else
111 fputs("Unix sockets are not available on Windows, use tcp instead.\n", stderr);
112 return INVALID_SOCKET;
113 #endif
114 } else if (!strncmp(path, "tcp:", 4)) {
115 #ifdef SHARKD_TCP_SUPPORT
116 struct sockaddr_in s_in;
117 int one = 1;
118 char *port_sep;
119 uint16_t port;
121 path += 4;
123 port_sep = strchr(path, ':');
124 if (!port_sep) {
125 fputs("Missing port number in socket path.\n", stderr);
126 return INVALID_SOCKET;
129 *port_sep = '\0';
131 if (ws_strtou16(port_sep + 1, NULL, &port) == false) {
132 fputs("Invalid port number.\n", stderr);
133 return INVALID_SOCKET;
136 #ifdef _WIN32
137 /* Need to use WSASocket() to disable overlapped I/O operations,
138 this way on windows SOCKET can be used as HANDLE for stdin/stdout */
139 fd = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0);
140 #else
141 fd = socket(AF_INET, SOCK_STREAM, 0);
142 #endif
143 if (fd == INVALID_SOCKET) {
144 fprintf(stderr, "Failed to open socket: %s\n",
145 #ifdef _WIN32
146 win32strerror(WSAGetLastError())
147 #else
148 g_strerror(errno)
149 #endif
151 return INVALID_SOCKET;
154 s_in.sin_family = AF_INET;
155 ws_inet_pton4(path, (ws_in4_addr *)&(s_in.sin_addr.s_addr));
156 s_in.sin_port = g_htons(port);
157 *port_sep = ':';
159 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &one, sizeof(one));
161 if (bind(fd, (struct sockaddr *) &s_in, sizeof(struct sockaddr_in)))
163 fprintf(stderr, "Failed to bind socket: %s\n", g_strerror(errno));
164 closesocket(fd);
165 return INVALID_SOCKET;
167 #else
168 fputs("TCP sockets are not available for security reasons, use Unix sockets instead.\n", stderr);
169 return INVALID_SOCKET;
170 #endif
172 else
174 fprintf(stderr, "Unsupported socket path '%s', try unix:... for Unix sockets\n", path);
175 return INVALID_SOCKET;
178 if (listen(fd, SOMAXCONN))
180 fprintf(stderr, "Failed to listen on socket: %s\n", g_strerror(errno));
181 closesocket(fd);
182 return INVALID_SOCKET;
185 return fd;
188 static void
189 print_usage(FILE* output)
191 fprintf(output, "\n");
192 fprintf(output, "Usage: sharkd [options]\n");
193 fprintf(output, " or sharkd -\n");
195 fprintf(output, "\n");
196 fprintf(output, "Options:\n");
197 fprintf(output, " -a <socket>, --api <socket>\n");
198 fprintf(output, " listen on this socket instead of the console\n");
199 fprintf(output, " --foreground do not detach from console\n");
200 fprintf(output, " -h, --help show this help information\n");
201 fprintf(output, " -v, --version show version information\n");
202 fprintf(output, " -C <config profile>, --config-profile <config profile>\n");
203 fprintf(output, " start with specified configuration profile\n");
205 fprintf(output, "\n");
206 fprintf(output, "Supported socket types:\n");
207 #ifdef SHARKD_UNIX_SUPPORT
208 fprintf(output, " unix:/tmp/sharkd.sock - listen on Unix domain socket file /tmp/sharkd.sock\n");
209 fprintf(output, " unix:@sharkd - listen on abstract Unix socket 'sharkd' (Linux-only)\n");
210 #else
211 fprintf(output, " (Unix domain sockets are unavailable on this platform.)\n");
212 #endif
213 #ifdef SHARKD_TCP_SUPPORT
214 fprintf(output, " tcp:127.0.0.1:4446 - listen on TCP port 4446\n");
215 #else
216 fprintf(output, " (TCP sockets are disabled in this build)\n");
217 #endif
219 fprintf(output, "\n");
220 fprintf(output, "If no socket option is provided, or if 'sharkd -' is used,\n");
221 fprintf(output, "sharkd will accept commands via console (standard input).\n");
223 fprintf(output, "\n");
224 fprintf(output, "Examples:\n");
225 fprintf(output, " sharkd -\n");
226 fprintf(output, " sharkd -C myprofile\n");
227 #ifdef SHARKD_UNIX_SUPPORT
228 fprintf(output, " sharkd -a unix:/tmp/sharkd.sock -C myprofile\n");
229 #elif defined(SHARKD_TCP_SUPPORT)
230 fprintf(output, " sharkd -a tcp:127.0.0.1:4446 -C myprofile\n");
231 #endif
233 fprintf(output, "\n");
234 fprintf(output, "For security reasons, do not directly expose sharkd to the public Internet.\n");
235 fprintf(output, "Instead, have a separate backend service to interact with sharkd.\n");
236 fprintf(output, "\n");
237 fprintf(output, "For full details, see https://wiki.wireshark.org/Development/sharkd\n");
238 fprintf(output, "\n");
242 sharkd_init(int argc, char **argv)
245 * The leading + ensures that getopt_long() does not permute the argv[]
246 * entries.
248 * We have to make sure that the first getopt_long() preserves the content
249 * of argv[] for the subsequent getopt_long() call.
251 * We use getopt_long() in both cases to ensure that we're using a routine
252 * whose permutation behavior we can control in the same fashion on all
253 * platforms, and so that, if we ever need to process a long argument before
254 * doing further initialization, we can do so.
256 * Glibc and Solaris libc document that a leading + disables permutation
257 * of options, regardless of whether POSIXLY_CORRECT is set or not; *BSD
258 * and macOS don't document it, but do so anyway.
260 * We do *not* use a leading - because the behavior of a leading - is
261 * platform-dependent.
264 #define OPTSTRING "+" "a:hmvC:"
265 #define LONGOPT_FOREGROUND 4000
267 static const char optstring[] = OPTSTRING;
269 static const struct ws_option long_options[] = {
270 {"api", ws_required_argument, NULL, 'a'},
271 {"foreground", ws_no_argument, NULL, LONGOPT_FOREGROUND},
272 {"help", ws_no_argument, NULL, 'h'},
273 {"version", ws_no_argument, NULL, 'v'},
274 {"config-profile", ws_required_argument, NULL, 'C'},
275 {0, 0, 0, 0 }
278 int opt;
280 #ifndef _WIN32
281 pid_t pid;
282 #endif
283 socket_handle_t fd;
284 bool foreground = false;
286 if (argc < 2)
288 print_usage(stderr);
289 return -1;
292 // check for classic command line
293 if (!strcmp(argv[1], "-") || argv[1][0] == 't' || argv[1][0] == 'u')
295 mode = SHARKD_MODE_CLASSIC_CONSOLE;
297 #ifndef _WIN32
298 signal(SIGCHLD, SIG_IGN);
299 #endif
301 if (!strcmp(argv[1], "-"))
303 mode = SHARKD_MODE_CLASSIC_CONSOLE;
305 else
307 fd = socket_init(argv[1]);
308 if (fd == INVALID_SOCKET)
309 return -1;
310 _server_fd = fd;
311 mode = SHARKD_MODE_CLASSIC_DAEMON;
314 else
315 mode = SHARKD_MODE_GOLD_CONSOLE; // assume we are running as gold console
317 if (mode >= SHARKD_MODE_GOLD_CONSOLE)
320 In Daemon Mode, we will come through here twice; once when we start the Daemon and
321 once again after we have forked the session process. The second time through, the
322 session process has already had its stdin and stdout wired up to the TCP or UNIX
323 socket and so in the original version of sharkd the session process is invoked with
324 the command line: sharkd -
326 When not using the classic command line, we want to spawn the session process with
327 the complete command line with all the new options but with the -a option and
328 parameter removed. Invoking a second time with the -a option will cause a loop
329 where we repeatedly spawn a new session process.
332 do {
333 if (ws_optind > (argc - 1))
334 break;
336 opt = ws_getopt_long(argc, argv, optstring, long_options, NULL);
338 switch (opt) {
339 case 'C': /* Configuration Profile */
340 if (profile_exists(ws_optarg, false)) {
341 set_profile_name(ws_optarg); // In Daemon Mode, we may need to do this again in the child process
343 else {
344 fprintf(stderr, "Configuration Profile \"%s\" does not exist\n", ws_optarg);
345 return -1;
347 break;
349 case 'a':
350 fd = socket_init(ws_optarg);
351 if (fd == INVALID_SOCKET)
352 return -1;
353 _server_fd = fd;
355 fprintf(stderr, "Sharkd listening on: %s\n", ws_optarg);
357 mode = SHARKD_MODE_GOLD_DAEMON;
358 break;
360 case 'h':
361 show_help_header("Daemon variant of Wireshark");
362 print_usage(stdout);
363 exit(0);
364 break;
366 case 'm':
367 // m is an internal-only option used when the daemon session process is created
368 mode = SHARKD_MODE_GOLD_CONSOLE;
369 break;
371 case 'v': /* Show version and exit */
372 show_version();
373 exit(0);
374 break;
376 case LONGOPT_FOREGROUND:
377 foreground = true;
378 break;
380 default:
381 if (!ws_optopt)
382 fprintf(stderr, "This option isn't supported: %s\n", argv[ws_optind]);
383 fprintf(stderr, "Use sharkd -h for details of supported options\n");
384 exit(0);
385 break;
387 } while (opt != -1);
390 if (!foreground && (mode == SHARKD_MODE_CLASSIC_DAEMON || mode == SHARKD_MODE_GOLD_DAEMON))
392 /* all good - try to daemonize */
393 #ifndef _WIN32
394 pid = fork();
395 if (pid == -1)
396 fprintf(stderr, "cannot go to background fork() failed: %s\n", g_strerror(errno));
398 if (pid != 0)
400 /* parent */
401 exit(0);
403 #endif
406 return 0;
410 #ifndef _WIN32
411 sharkd_loop(int argc _U_, char* argv[] _U_)
412 #else
413 sharkd_loop(int argc _U_, char* argv[])
414 #endif
416 if (mode == SHARKD_MODE_CLASSIC_CONSOLE || mode == SHARKD_MODE_GOLD_CONSOLE)
418 return sharkd_session_main(mode);
421 while (1)
423 #ifndef _WIN32
424 pid_t pid;
425 #else
426 size_t i_handles;
427 HANDLE handles[2];
428 PROCESS_INFORMATION pi;
429 STARTUPINFO si;
430 char *exename;
431 char command_line[2048];
432 #endif
433 socket_handle_t fd;
435 fd = accept(_server_fd, NULL, NULL);
436 if (fd == INVALID_SOCKET)
438 fprintf(stderr, "cannot accept(): %s\n", g_strerror(errno));
439 continue;
442 /* wireshark is not ready for handling multiple capture files in single process, so fork(), and handle it in separate process */
443 #ifndef _WIN32
444 pid = fork();
445 if (pid == 0)
447 closesocket(_server_fd);
448 /* redirect stdin, stdout to socket */
449 dup2(fd, 0);
450 dup2(fd, 1);
451 close(fd);
453 exit(sharkd_session_main(mode));
456 if (pid == -1)
458 fprintf(stderr, "cannot fork(): %s\n", g_strerror(errno));
461 #else
462 memset(&pi, 0, sizeof(pi));
463 memset(&si, 0, sizeof(si));
465 si.cb = sizeof(si);
466 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
467 si.hStdInput = (HANDLE) fd;
468 si.hStdOutput = (HANDLE) fd;
469 si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
471 i_handles = 0;
472 handles[i_handles++] = (HANDLE)fd;
473 if (si.hStdError != NULL) {
474 handles[i_handles++] = si.hStdError;
477 exename = get_executable_path("sharkd");
479 // we need to pass in all of the command line parameters except the -a parameter
480 // passing in -a at this point would could a loop, each iteration of which would generate a new session process
481 memset(&command_line, 0, sizeof(command_line));
483 if (mode <= SHARKD_MODE_CLASSIC_DAEMON)
485 (void) g_strlcat(command_line, "sharkd.exe -", sizeof(command_line));
487 else
489 // The -m option used here is an internal-only option that notifies the child process that it should
490 // run in Gold Console mode
491 (void) g_strlcat(command_line, "sharkd.exe -m", sizeof(command_line));
493 for (int i = 1; i < argc; i++)
495 if (
496 !g_ascii_strncasecmp(argv[i], "-a", strlen(argv[i]))
497 || !g_ascii_strncasecmp(argv[i], "--api", strlen(argv[i]))
500 i++; // skip the socket details
502 else
504 (void) g_strlcat(command_line, " ", sizeof(command_line));
505 (void) g_strlcat(command_line, argv[i], sizeof(command_line));
510 if (!win32_create_process(exename, command_line, NULL, NULL, i_handles, handles, 0, NULL, NULL, &si, &pi))
512 fprintf(stderr, "win32_create_process(%s) failed\n", exename);
514 else
516 CloseHandle(pi.hThread);
519 g_free(exename);
520 #endif
522 closesocket(fd);
524 return 0;