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>
46 /* for windows support TCP sockets */
47 # define SHARKD_TCP_SUPPORT
49 /* for other system support only local sockets */
50 # define SHARKD_UNIX_SUPPORT
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
;
62 err_msg
= ws_init_sockets();
63 if (err_msg
!= NULL
) {
64 ws_warning("ERROR: %s", err_msg
);
66 ws_warning("%s", please_report_bug());
70 #ifdef SHARKD_UNIX_SUPPORT
71 if (!strncmp(path
, "unix:", 5))
73 struct sockaddr_un s_un
;
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
))
97 return INVALID_SOCKET
;
103 #ifdef SHARKD_TCP_SUPPORT
104 if (!strncmp(path
, "tcp:", 4))
106 struct sockaddr_in s_in
;
113 port_sep
= strchr(path
, ':');
115 return INVALID_SOCKET
;
119 if (ws_strtou16(port_sep
+ 1, NULL
, &port
) == false)
120 return INVALID_SOCKET
;
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);
127 fd
= socket(AF_INET
, SOCK_STREAM
, 0);
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
);
137 setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, (void *) &one
, sizeof(one
));
139 if (bind(fd
, (struct sockaddr
*) &s_in
, sizeof(struct sockaddr_in
)))
142 return INVALID_SOCKET
;
148 return INVALID_SOCKET
;
151 if (listen(fd
, SOMAXCONN
))
154 return INVALID_SOCKET
;
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");
174 #ifdef SHARKD_TCP_SUPPORT
175 fprintf(output
, " - tcp:127.0.0.1:4446 - listen on TCP port 4446\n");
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[]
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'},
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
;
252 signal(SIGCHLD
, SIG_IGN
);
255 if (!strcmp(argv
[1], "-"))
257 mode
= SHARKD_MODE_CLASSIC_CONSOLE
;
261 fd
= socket_init(argv
[1]);
262 if (fd
== INVALID_SOCKET
)
265 mode
= SHARKD_MODE_CLASSIC_DAEMON
;
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.
287 if (ws_optind
> (argc
- 1))
290 opt
= ws_getopt_long(argc
, argv
, optstring
, long_options
, NULL
);
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
298 fprintf(stderr
, "Configuration Profile \"%s\" does not exist\n", ws_optarg
);
304 fd
= socket_init(ws_optarg
);
305 if (fd
== INVALID_SOCKET
)
309 fprintf(stderr
, "Sharkd listening on: %s\n", ws_optarg
);
311 mode
= SHARKD_MODE_GOLD_DAEMON
;
315 show_help_header("Daemon variant of Wireshark");
321 // m is an internal-only option used when the daemon session process is created
322 mode
= SHARKD_MODE_GOLD_CONSOLE
;
325 case 'v': /* Show version and exit */
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");
340 if (mode
== SHARKD_MODE_CLASSIC_DAEMON
|| mode
== SHARKD_MODE_GOLD_DAEMON
)
342 /* all good - try to daemonize */
346 fprintf(stderr
, "cannot go to background fork() failed: %s\n", g_strerror(errno
));
361 sharkd_loop(int argc _U_
, char* argv
[] _U_
)
363 sharkd_loop(int argc _U_
, char* argv
[])
366 if (mode
== SHARKD_MODE_CLASSIC_CONSOLE
|| mode
== SHARKD_MODE_GOLD_CONSOLE
)
368 return sharkd_session_main(mode
);
378 PROCESS_INFORMATION pi
;
381 char command_line
[2048];
385 fd
= accept(_server_fd
, NULL
, NULL
);
386 if (fd
== INVALID_SOCKET
)
388 fprintf(stderr
, "cannot accept(): %s\n", g_strerror(errno
));
392 /* wireshark is not ready for handling multiple capture files in single process, so fork(), and handle it in separate process */
397 closesocket(_server_fd
);
398 /* redirect stdin, stdout to socket */
403 exit(sharkd_session_main(mode
));
408 fprintf(stderr
, "cannot fork(): %s\n", g_strerror(errno
));
412 memset(&pi
, 0, sizeof(pi
));
413 memset(&si
, 0, 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
);
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
));
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
++)
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
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
);
466 CloseHandle(pi
.hThread
);