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
13 #define WS_LOG_DOMAIN LOG_DOMAIN_MAIN
24 #include <wsutil/unicode-utils.h>
25 #include <wsutil/win32-utils.h>
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>
37 #include <netinet/tcp.h>
40 #include <wsutil/strtoi.h>
41 #include <wsutil/version_info.h>
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
56 # define SHARKD_UNIX_SUPPORT
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
;
68 err_msg
= ws_init_sockets();
69 if (err_msg
!= NULL
) {
70 ws_warning("ERROR: %s", err_msg
);
72 ws_warning("%s", please_report_bug());
76 if (!strncmp(path
, "unix:", 5))
78 #ifdef SHARKD_UNIX_SUPPORT
79 struct sockaddr_un s_un
;
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
));
108 return INVALID_SOCKET
;
111 fputs("Unix sockets are not available on Windows, use tcp instead.\n", stderr
);
112 return INVALID_SOCKET
;
114 } else if (!strncmp(path
, "tcp:", 4)) {
115 #ifdef SHARKD_TCP_SUPPORT
116 struct sockaddr_in s_in
;
123 port_sep
= strchr(path
, ':');
125 fputs("Missing port number in socket path.\n", stderr
);
126 return INVALID_SOCKET
;
131 if (ws_strtou16(port_sep
+ 1, NULL
, &port
) == false) {
132 fputs("Invalid port number.\n", stderr
);
133 return INVALID_SOCKET
;
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);
141 fd
= socket(AF_INET
, SOCK_STREAM
, 0);
143 if (fd
== INVALID_SOCKET
) {
144 fprintf(stderr
, "Failed to open socket: %s\n",
146 win32strerror(WSAGetLastError())
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
);
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
));
165 return INVALID_SOCKET
;
168 fputs("TCP sockets are not available for security reasons, use Unix sockets instead.\n", stderr
);
169 return INVALID_SOCKET
;
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
));
182 return INVALID_SOCKET
;
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");
211 fprintf(output
, " (Unix domain sockets are unavailable on this platform.)\n");
213 #ifdef SHARKD_TCP_SUPPORT
214 fprintf(output
, " tcp:127.0.0.1:4446 - listen on TCP port 4446\n");
216 fprintf(output
, " (TCP sockets are disabled in this build)\n");
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");
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[]
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'},
284 bool foreground
= false;
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
;
298 signal(SIGCHLD
, SIG_IGN
);
301 if (!strcmp(argv
[1], "-"))
303 mode
= SHARKD_MODE_CLASSIC_CONSOLE
;
307 fd
= socket_init(argv
[1]);
308 if (fd
== INVALID_SOCKET
)
311 mode
= SHARKD_MODE_CLASSIC_DAEMON
;
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.
333 if (ws_optind
> (argc
- 1))
336 opt
= ws_getopt_long(argc
, argv
, optstring
, long_options
, NULL
);
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
344 fprintf(stderr
, "Configuration Profile \"%s\" does not exist\n", ws_optarg
);
350 fd
= socket_init(ws_optarg
);
351 if (fd
== INVALID_SOCKET
)
355 fprintf(stderr
, "Sharkd listening on: %s\n", ws_optarg
);
357 mode
= SHARKD_MODE_GOLD_DAEMON
;
361 show_help_header("Daemon variant of Wireshark");
367 // m is an internal-only option used when the daemon session process is created
368 mode
= SHARKD_MODE_GOLD_CONSOLE
;
371 case 'v': /* Show version and exit */
376 case LONGOPT_FOREGROUND
:
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");
390 if (!foreground
&& (mode
== SHARKD_MODE_CLASSIC_DAEMON
|| mode
== SHARKD_MODE_GOLD_DAEMON
))
392 /* all good - try to daemonize */
396 fprintf(stderr
, "cannot go to background fork() failed: %s\n", g_strerror(errno
));
411 sharkd_loop(int argc _U_
, char* argv
[] _U_
)
413 sharkd_loop(int argc _U_
, char* argv
[])
416 if (mode
== SHARKD_MODE_CLASSIC_CONSOLE
|| mode
== SHARKD_MODE_GOLD_CONSOLE
)
418 return sharkd_session_main(mode
);
428 PROCESS_INFORMATION pi
;
431 char command_line
[2048];
435 fd
= accept(_server_fd
, NULL
, NULL
);
436 if (fd
== INVALID_SOCKET
)
438 fprintf(stderr
, "cannot accept(): %s\n", g_strerror(errno
));
442 /* wireshark is not ready for handling multiple capture files in single process, so fork(), and handle it in separate process */
447 closesocket(_server_fd
);
448 /* redirect stdin, stdout to socket */
453 exit(sharkd_session_main(mode
));
458 fprintf(stderr
, "cannot fork(): %s\n", g_strerror(errno
));
462 memset(&pi
, 0, sizeof(pi
));
463 memset(&si
, 0, 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
);
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
));
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
++)
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
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
);
516 CloseHandle(pi
.hThread
);