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
23 #include <wsutil/unicode-utils.h>
24 #include <wsutil/win32-utils.h>
27 #include <wsutil/filesystem.h>
28 #include <wsutil/socket.h>
29 #include <wsutil/inet_addr.h>
30 #include <wsutil/please_report_bug.h>
32 * If we have getopt_long() in the system library, include <getopt.h>.
33 * Otherwise, we're using our own getopt_long() (either because the
34 * system has getopt() but not getopt_long(), as with some UN*Xes,
35 * or because it doesn't even have getopt(), as with Windows), so
36 * include our getopt_long()'s header.
38 #ifdef HAVE_GETOPT_LONG
41 #include <wsutil/wsgetopt.h>
46 #include <netinet/tcp.h>
49 #include <wsutil/strtoi.h>
50 #include <version_info.h>
55 /* for windows support TCP sockets */
56 # define SHARKD_TCP_SUPPORT
58 /* for other system support only local sockets */
59 # define SHARKD_UNIX_SUPPORT
63 static socket_handle_t _server_fd = INVALID_SOCKET;
65 static socket_handle_t
66 socket_init(char *path)
68 socket_handle_t fd = INVALID_SOCKET;
71 err_msg = ws_init_sockets();
72 if (err_msg != NULL) {
73 g_warning("ERROR: %s", err_msg);
75 g_warning("%s", please_report_bug());
79 #ifdef SHARKD_UNIX_SUPPORT
80 if (!strncmp(path, "unix:", 5))
82 struct sockaddr_un s_un;
87 if (strlen(path) + 1 > sizeof(s_un.sun_path))
88 return INVALID_SOCKET;
90 fd = socket(AF_UNIX, SOCK_STREAM, 0);
91 if (fd == INVALID_SOCKET)
92 return INVALID_SOCKET;
94 memset(&s_un, 0, sizeof(s_un));
95 s_un.sun_family = AF_UNIX;
96 g_strlcpy(s_un.sun_path, path, sizeof(s_un.sun_path));
98 s_un_len = (socklen_t)(offsetof(struct sockaddr_un, sun_path) + strlen(s_un.sun_path));
100 if (s_un.sun_path[0] == '@')
101 s_un.sun_path[0] = '\0';
103 if (bind(fd, (struct sockaddr *) &s_un, s_un_len))
106 return INVALID_SOCKET;
112 #ifdef SHARKD_TCP_SUPPORT
113 if (!strncmp(path, "tcp:", 4))
115 struct sockaddr_in s_in;
122 port_sep = strchr(path, ':');
124 return INVALID_SOCKET;
128 if (ws_strtou16(port_sep + 1, NULL, &port) == FALSE)
129 return INVALID_SOCKET;
132 /* Need to use WSASocket() to disable overlapped I/O operations,
133 this way on windows SOCKET can be used as HANDLE for stdin/stdout */
134 fd = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, 0);
136 fd = socket(AF_INET, SOCK_STREAM, 0);
138 if (fd == INVALID_SOCKET)
139 return INVALID_SOCKET;
141 s_in.sin_family = AF_INET;
142 ws_inet_pton4(path, &(s_in.sin_addr.s_addr));
143 s_in.sin_port = g_htons(port);
146 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &one, sizeof(one));
148 if (bind(fd, (struct sockaddr *) &s_in, sizeof(struct sockaddr_in)))
151 return INVALID_SOCKET;
157 return INVALID_SOCKET;
160 if (listen(fd, SOMAXCONN))
163 return INVALID_SOCKET;
170 print_usage(FILE* output)
172 fprintf(output, "\n");
173 fprintf(output, "Usage: sharkd [<classic_options>|<gold_options>]\n");
175 fprintf(output, "\n");
176 fprintf(output, "Classic (classic_options):\n");
177 fprintf(output, " [-|<socket>]\n");
178 fprintf(output, "\n");
179 fprintf(output, " <socket> examples:\n");
180 #ifdef SHARKD_UNIX_SUPPORT
181 fprintf(output, " - unix:/tmp/sharkd.sock - listen on unix file /tmp/sharkd.sock\n");
183 #ifdef SHARKD_TCP_SUPPORT
184 fprintf(output, " - tcp:127.0.0.1:4446 - listen on TCP port 4446\n");
187 fprintf(output, "\n");
188 fprintf(output, "Gold (gold_options):\n");
189 fprintf(output, " -a <socket>, --api <socket>\n");
190 fprintf(output, " listen on this socket\n");
191 fprintf(output, " -h, --help show this help information\n");
192 fprintf(output, " -v, --version show version information\n");
193 fprintf(output, " -C <config profile>, --config-profile <config profile>\n");
194 fprintf(output, " start with specified configuration profile\n");
196 fprintf(output, "\n");
197 fprintf(output, " Examples:\n");
198 fprintf(output, " sharkd -C myprofile\n");
199 fprintf(output, " sharkd -a tcp:127.0.0.1:4446 -C myprofile\n");
201 fprintf(output, "\n");
202 fprintf(output, "See the sharkd page of the Wireshark wiki for full details.\n");
203 fprintf(output, "\n");
207 sharkd_init(int argc, char **argv)
210 * The leading + ensures that getopt_long() does not permute the argv[]
213 * We have to make sure that the first getopt_long() preserves the content
214 * of argv[] for the subsequent getopt_long() call.
216 * We use getopt_long() in both cases to ensure that we're using a routine
217 * whose permutation behavior we can control in the same fashion on all
218 * platforms, and so that, if we ever need to process a long argument before
219 * doing further initialization, we can do so.
221 * Glibc and Solaris libc document that a leading + disables permutation
222 * of options, regardless of whether POSIXLY_CORRECT is set or not; *BSD
223 * and macOS don't document it, but do so anyway.
225 * We do *not* use a leading - because the behavior of a leading - is
226 * platform-dependent.
229 #define OPTSTRING "+" "a:hmvC:"
231 static const char optstring[] = OPTSTRING;
233 // right now we don't have any long options
234 static const struct option long_options[] = {
235 {"api", required_argument, NULL, 'a'},
236 {"help", no_argument, NULL, 'h'},
237 {"version", no_argument, NULL, 'v'},
238 {"config-profile", required_argument, NULL, 'C'},
255 // check for classic command line
256 if (!strcmp(argv[1], "-") || argv[1][0] == 't' || argv[1][0] == 'u')
258 mode = SHARKD_MODE_CLASSIC_CONSOLE;
261 signal(SIGCHLD, SIG_IGN);
264 if (!strcmp(argv[1], "-"))
266 mode = SHARKD_MODE_CLASSIC_CONSOLE;
270 fd = socket_init(argv[1]);
271 if (fd == INVALID_SOCKET)
274 mode = SHARKD_MODE_CLASSIC_DAEMON;
278 mode = SHARKD_MODE_GOLD_CONSOLE; // assume we are running as gold console
280 if (mode >= SHARKD_MODE_GOLD_CONSOLE)
283 In Daemon Mode, we will come through here twice; once when we start the Daemon and
284 once again after we have forked the session process. The second time through, the
285 session process has already had its stdin and stdout wired up to the TCP or UNIX
286 socket and so in the orignal version of sharkd the session process is invoked with
287 the command line: sharkd -
289 When not using the classic command line, we want to spawn the session process with
290 the complete command line with all the new options but with the -a option and
291 parameter removed. Invoking a second time with the -a option will cause a loop
292 where we repeatedly spawn a new session process.
296 if (optind > (argc - 1))
299 opt = getopt_long(argc, argv, optstring, long_options, NULL);
302 case 'C': /* Configuration Profile */
303 if (profile_exists(optarg, FALSE)) {
304 set_profile_name(optarg); // In Daemon Mode, we may need to do this again in the child process
307 fprintf(stderr, "Configuration Profile \"%s\" does not exist\n", optarg);
313 fd = socket_init(optarg);
314 if (fd == INVALID_SOCKET)
318 fprintf(stderr, "Sharkd listening on: %s\n", optarg);
320 mode = SHARKD_MODE_GOLD_DAEMON;
329 // m is an internal-only option used when the daemon session process is created
330 mode = SHARKD_MODE_GOLD_CONSOLE;
333 case 'v': /* Show version and exit */
340 fprintf(stderr, "This option isn't supported: %s\n", argv[optind]);
341 fprintf(stderr, "Use sharkd -h for details of supported options\n");
348 if (mode == SHARKD_MODE_CLASSIC_DAEMON || mode == SHARKD_MODE_GOLD_DAEMON)
350 /* all good - try to daemonize */
354 fprintf(stderr, "cannot go to background fork() failed: %s\n", g_strerror(errno));
369 sharkd_loop(int argc _U_, char* argv[] _U_)
371 sharkd_loop(int argc _U_, char* argv[])
374 if (mode == SHARKD_MODE_CLASSIC_CONSOLE || mode == SHARKD_MODE_GOLD_CONSOLE)
376 return sharkd_session_main(mode);
384 PROCESS_INFORMATION pi;
387 char command_line[2048];
391 fd = accept(_server_fd, NULL, NULL);
392 if (fd == INVALID_SOCKET)
394 fprintf(stderr, "cannot accept(): %s\n", g_strerror(errno));
398 /* wireshark is not ready for handling multiple capture files in single process, so fork(), and handle it in separate process */
403 closesocket(_server_fd);
404 /* redirect stdin, stdout to socket */
409 exit(sharkd_session_main(mode));
414 fprintf(stderr, "cannot fork(): %s\n", g_strerror(errno));
418 memset(&pi, 0, sizeof(pi));
419 memset(&si, 0, sizeof(si));
422 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
423 si.hStdInput = (HANDLE) fd;
424 si.hStdOutput = (HANDLE) fd;
425 si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
427 exename = g_strdup_printf("%s\\%s", get_progfile_dir(), "sharkd.exe");
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 g_strlcat(command_line, "sharkd.exe -", sizeof(command_line));
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 g_strlcat(command_line, "sharkd.exe -m", sizeof(command_line));
443 for (size_t i = 1; i < argc; i++)
446 !g_ascii_strncasecmp(argv[i], "-a", (guint)strlen(argv[i]))
447 || !g_ascii_strncasecmp(argv[i], "--api", (guint)strlen(argv[i]))
450 i++; // skip the socket details
454 g_strlcat(command_line, " ", sizeof(command_line));
455 g_strlcat(command_line, argv[i], sizeof(command_line));
460 if (!win32_create_process(exename, command_line, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
462 fprintf(stderr, "win32_create_process(%s) failed\n", exename);
466 CloseHandle(pi.hThread);
478 * Editor modelines - https://www.wireshark.org/tools/modelines.html
483 * indent-tabs-mode: t
486 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
487 * :indentSize=8:tabSize=8:noTabs=false: