Revert "LATER... ei_kerberos_kdc_session_key ..."
[wireshark-sm.git] / sharkd_daemon.c
blob07bd108223f2512a802512548abb49038a1ac9cc
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
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
53 static int mode;
54 static socket_handle_t _server_fd = INVALID_SOCKET;
56 static socket_handle_t
57 socket_init(char *path)
59 socket_handle_t fd = INVALID_SOCKET;
60 char *err_msg;
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;
70 #ifdef SHARKD_UNIX_SUPPORT
71 if (!strncmp(path, "unix:", 5))
73 struct sockaddr_un s_un;
74 socklen_t s_un_len;
76 path += 5;
78 if (strlen(path) + 1 > sizeof(s_un.sun_path))
79 return INVALID_SOCKET;
81 fd = socket(AF_UNIX, SOCK_STREAM, 0);
82 if (fd == INVALID_SOCKET)
83 return INVALID_SOCKET;
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));
89 s_un_len = (socklen_t)(offsetof(struct sockaddr_un, sun_path) + strlen(s_un.sun_path));
91 if (s_un.sun_path[0] == '@')
92 s_un.sun_path[0] = '\0';
94 if (bind(fd, (struct sockaddr *) &s_un, s_un_len))
96 closesocket(fd);
97 return INVALID_SOCKET;
100 else
101 #endif
103 #ifdef SHARKD_TCP_SUPPORT
104 if (!strncmp(path, "tcp:", 4))
106 struct sockaddr_in s_in;
107 int one = 1;
108 char *port_sep;
109 uint16_t port;
111 path += 4;
113 port_sep = strchr(path, ':');
114 if (!port_sep)
115 return INVALID_SOCKET;
117 *port_sep = '\0';
119 if (ws_strtou16(port_sep + 1, NULL, &port) == false)
120 return INVALID_SOCKET;
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;
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 = ':';
137 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &one, sizeof(one));
139 if (bind(fd, (struct sockaddr *) &s_in, sizeof(struct sockaddr_in)))
141 closesocket(fd);
142 return INVALID_SOCKET;
145 else
146 #endif
148 return INVALID_SOCKET;
151 if (listen(fd, SOMAXCONN))
153 closesocket(fd);
154 return INVALID_SOCKET;
157 return fd;
160 static void
161 print_usage(FILE* output)
163 fprintf(output, "\n");
164 fprintf(output, "Usage: sharkd [<classic_options>|<gold_options>]\n");
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
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");
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");
192 fprintf(output, "\n");
193 fprintf(output, "See the sharkd page of the Wireshark wiki for full details.\n");
194 fprintf(output, "\n");
198 sharkd_init(int argc, char **argv)
201 * The leading + ensures that getopt_long() does not permute the argv[]
202 * entries.
204 * We have to make sure that the first getopt_long() preserves the content
205 * of argv[] for the subsequent getopt_long() call.
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.
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.
216 * We do *not* use a leading - because the behavior of a leading - is
217 * platform-dependent.
220 #define OPTSTRING "+" "a:hmvC:"
222 static const char optstring[] = OPTSTRING;
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 }
233 int opt;
235 #ifndef _WIN32
236 pid_t pid;
237 #endif
238 socket_handle_t fd;
240 if (argc < 2)
242 print_usage(stderr);
243 return -1;
246 // check for classic command line
247 if (!strcmp(argv[1], "-") || argv[1][0] == 't' || argv[1][0] == 'u')
249 mode = SHARKD_MODE_CLASSIC_CONSOLE;
251 #ifndef _WIN32
252 signal(SIGCHLD, SIG_IGN);
253 #endif
255 if (!strcmp(argv[1], "-"))
257 mode = SHARKD_MODE_CLASSIC_CONSOLE;
259 else
261 fd = socket_init(argv[1]);
262 if (fd == INVALID_SOCKET)
263 return -1;
264 _server_fd = fd;
265 mode = SHARKD_MODE_CLASSIC_DAEMON;
268 else
269 mode = SHARKD_MODE_GOLD_CONSOLE; // assume we are running as gold console
271 if (mode >= SHARKD_MODE_GOLD_CONSOLE)
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 -
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.
286 do {
287 if (ws_optind > (argc - 1))
288 break;
290 opt = ws_getopt_long(argc, argv, optstring, long_options, NULL);
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
297 else {
298 fprintf(stderr, "Configuration Profile \"%s\" does not exist\n", ws_optarg);
299 return -1;
301 break;
303 case 'a':
304 fd = socket_init(ws_optarg);
305 if (fd == INVALID_SOCKET)
306 return -1;
307 _server_fd = fd;
309 fprintf(stderr, "Sharkd listening on: %s\n", ws_optarg);
311 mode = SHARKD_MODE_GOLD_DAEMON;
312 break;
314 case 'h':
315 show_help_header("Daemon variant of Wireshark");
316 print_usage(stderr);
317 exit(0);
318 break;
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;
325 case 'v': /* Show version and exit */
326 show_version();
327 exit(0);
328 break;
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;
337 } while (opt != -1);
340 if (mode == SHARKD_MODE_CLASSIC_DAEMON || mode == SHARKD_MODE_GOLD_DAEMON)
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));
348 if (pid != 0)
350 /* parent */
351 exit(0);
353 #endif
356 return 0;
360 #ifndef _WIN32
361 sharkd_loop(int argc _U_, char* argv[] _U_)
362 #else
363 sharkd_loop(int argc _U_, char* argv[])
364 #endif
366 if (mode == SHARKD_MODE_CLASSIC_CONSOLE || mode == SHARKD_MODE_GOLD_CONSOLE)
368 return sharkd_session_main(mode);
371 while (1)
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;
385 fd = accept(_server_fd, NULL, NULL);
386 if (fd == INVALID_SOCKET)
388 fprintf(stderr, "cannot accept(): %s\n", g_strerror(errno));
389 continue;
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)
397 closesocket(_server_fd);
398 /* redirect stdin, stdout to socket */
399 dup2(fd, 0);
400 dup2(fd, 1);
401 close(fd);
403 exit(sharkd_session_main(mode));
406 if (pid == -1)
408 fprintf(stderr, "cannot fork(): %s\n", g_strerror(errno));
411 #else
412 memset(&pi, 0, sizeof(pi));
413 memset(&si, 0, sizeof(si));
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);
421 i_handles = 0;
422 handles[i_handles++] = (HANDLE)fd;
423 if (si.hStdError != NULL) {
424 handles[i_handles++] = si.hStdError;
427 exename = get_executable_path("sharkd");
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));
433 if (mode <= SHARKD_MODE_CLASSIC_DAEMON)
435 (void) g_strlcat(command_line, "sharkd.exe -", sizeof(command_line));
437 else
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));
443 for (int i = 1; i < argc; i++)
445 if (
446 !g_ascii_strncasecmp(argv[i], "-a", strlen(argv[i]))
447 || !g_ascii_strncasecmp(argv[i], "--api", strlen(argv[i]))
450 i++; // skip the socket details
452 else
454 (void) g_strlcat(command_line, " ", sizeof(command_line));
455 (void) g_strlcat(command_line, argv[i], sizeof(command_line));
460 if (!win32_create_process(exename, command_line, NULL, NULL, i_handles, handles, 0, NULL, NULL, &si, &pi))
462 fprintf(stderr, "win32_create_process(%s) failed\n", exename);
464 else
466 CloseHandle(pi.hThread);
469 g_free(exename);
470 #endif
472 closesocket(fd);
474 return 0;