4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
26 #include "epan/address.h"
27 #include "epan/addr_resolv.h"
28 #include "epan/prefs.h"
29 #include "epan/strutil.h"
31 #include <wsutil/filesystem.h>
36 * Collect command-line arguments as a string consisting of the arguments,
37 * separated by spaces.
40 get_args_as_string(int argc
, char **argv
, int optindex
)
47 * Find out how long the string will be.
50 for (i
= optindex
; i
< argc
; i
++) {
51 len
+= (int) strlen(argv
[i
]);
52 len
++; /* space, or '\0' if this is the last argument */
56 * If no arguments, return empty string
62 * Allocate the buffer for the string.
64 argstring
= (char *)g_malloc(len
);
67 * Now construct the string.
72 (void) g_strlcat(argstring
, argv
[i
], len
);
76 (void) g_strlcat(argstring
, " ", len
);
81 /* Compute the difference between two seconds/microseconds time stamps. */
83 compute_timestamp_diff(int *diffsec
, int *diffusec
,
84 uint32_t sec1
, uint32_t usec1
, uint32_t sec2
, uint32_t usec2
)
87 /* The seconds part of the first time is the same as the seconds
88 part of the second time, so if the microseconds part of the first
89 time is less than the microseconds part of the second time, the
90 first time is before the second time. The microseconds part of
91 the delta should just be the difference between the microseconds
92 part of the first time and the microseconds part of the second
93 time; don't adjust the seconds part of the delta, as it's OK if
94 the microseconds part is negative. */
96 *diffsec
= sec1
- sec2
;
97 *diffusec
= usec1
- usec2
;
98 } else if (sec1
<= sec2
) {
99 /* The seconds part of the first time is less than the seconds part
100 of the second time, so the first time is before the second time.
102 Both the "seconds" and "microseconds" value of the delta
103 should have the same sign, so if the difference between the
104 microseconds values would be *positive*, subtract 1,000,000
105 from it, and add one to the seconds value. */
106 *diffsec
= sec1
- sec2
;
107 if (usec2
>= usec1
) {
108 *diffusec
= usec1
- usec2
;
110 *diffusec
= (usec1
- 1000000) - usec2
;
114 /* Oh, good, we're not caught in a chronosynclastic infindibulum. */
115 *diffsec
= sec1
- sec2
;
116 if (usec2
<= usec1
) {
117 *diffusec
= usec1
- usec2
;
119 *diffusec
= (usec1
+ 1000000) - usec2
;
125 /* Remove any %<interface_name> from an IP address. */
126 static char *sanitize_filter_ip(char *hostname
) {
130 ret
= g_strdup(hostname
);
134 end
= strchr(ret
, '%');
140 /* Try to figure out if we're remotely connected, e.g. via ssh or
141 Terminal Server, and create a capture filter that matches aspects of the
142 connection. We match the following environment variables:
144 SSH_CONNECTION (ssh): <remote IP> <remote port> <local IP> <local port>
145 SSH_CLIENT (ssh): <remote IP> <remote port> <local port>
146 REMOTEHOST (tcsh, others?): <remote name>
147 DISPLAY (x11): [remote name]:<display num>
148 SESSIONNAME (terminal server): <remote name>
151 const char *get_conn_cfilter(void) {
152 static GString
*filter_str
= NULL
;
154 char *lastp
, *lastc
, *p
;
155 char *pprotocol
= NULL
;
156 char *phostname
= NULL
;
160 if (filter_str
== NULL
) {
161 filter_str
= g_string_new("");
163 if ((env
= getenv("SSH_CONNECTION")) != NULL
) {
164 tokens
= g_strsplit(env
, " ", 4);
165 if (g_strv_length(tokens
) == 4) {
166 remip
= sanitize_filter_ip(tokens
[0]);
167 locip
= sanitize_filter_ip(tokens
[2]);
168 g_string_printf(filter_str
, "not (tcp port %s and host %s "
169 "and tcp port %s and host %s)", tokens
[1], remip
,
175 } else if ((env
= getenv("SSH_CLIENT")) != NULL
) {
176 tokens
= g_strsplit(env
, " ", 3);
177 if (g_strv_length(tokens
) == 3) {
178 remip
= sanitize_filter_ip(tokens
[2]);
179 g_string_printf(filter_str
, "not (tcp port %s and host %s "
180 "and tcp port %s)", tokens
[1], tokens
[0], remip
);
184 } else if ((env
= getenv("REMOTEHOST")) != NULL
) {
185 /* FreeBSD 7.0 sets REMOTEHOST to an empty string */
186 if (g_ascii_strcasecmp(env
, "localhost") == 0 ||
187 strcmp(env
, "127.0.0.1") == 0 ||
188 strcmp(env
, "") == 0) {
191 remip
= sanitize_filter_ip(env
);
192 g_string_printf(filter_str
, "not host %s", remip
);
194 } else if ((env
= getenv("DISPLAY")) != NULL
) {
196 * This mirrors what _X11TransConnectDisplay() does.
197 * Note that, on some systems, the hostname can
198 * begin with "/", which means that it's a pathname
199 * of a UNIX domain socket to connect to.
201 * The comments mirror those in _X11TransConnectDisplay(),
204 * Display names may be of the following format:
206 * [protocol./] [hostname] : [:] displaynumber [.screennumber]
208 * A string with exactly two colons separating hostname
209 * from the display indicates a DECnet style name. Colons
210 * in the hostname may occur if an IPv6 numeric address
211 * is used as the hostname. An IPv6 numeric address may
212 * also end in a double colon, so three colons in a row
213 * indicates an IPv6 address ending in :: followed by
214 * :display. To make it easier for people to read, an
215 * IPv6 numeric address hostname may be surrounded by []
216 * in a similar fashion to the IPv6 numeric address URL
217 * syntax defined by IETF RFC 2732.
219 * If no hostname and no protocol is specified, the string
220 * is interpreted as the most efficient local connection
221 * to a server on the same machine. This is usually:
225 * o UNIX domain socket
226 * o TCP to local host.
232 * Step 0, find the protocol. This is delimited by
233 * the optional slash ('/').
235 for (lastp
= p
; *p
!= '\0' && *p
!= ':' && *p
!= '/'; p
++)
238 return ""; /* must have a colon */
240 if (p
!= lastp
&& *p
!= ':') { /* protocol given? */
245 if (p
- lastp
!= 3 || g_ascii_strncasecmp(lastp
, "tcp", 3) != 0)
246 return ""; /* not TCP */
247 p
++; /* skip the '/' */
249 p
= env
; /* reset the pointer in
250 case no protocol was given */
253 * Step 1, find the hostname. This is delimited either by
254 * one colon, or two colons in the case of DECnet (DECnet
255 * Phase V allows a single colon in the hostname). (See
256 * note above regarding IPv6 numeric addresses with
257 * triple colons or [] brackets.)
261 for (; *p
!= '\0'; p
++)
266 return ""; /* must have a colon */
268 if ((lastp
!= lastc
) && (*(lastc
- 1) == ':')
269 && (((lastc
- 1) == lastp
) || (*(lastc
- 2) != ':'))) {
270 /* DECnet display specified */
273 hostlen
= lastc
- lastp
;
276 return ""; /* no hostname supplied */
278 phostname
= (char *)g_malloc(hostlen
+ 1);
279 memcpy(phostname
, lastp
, hostlen
);
280 phostname
[hostlen
] = '\0';
282 if (pprotocol
== NULL
) {
284 * No protocol was explicitly specified, so it
285 * could be a local connection over a transport
288 * Does the host name refer to the local host?
289 * If so, the connection would probably be a
292 * XXX - compare against our host name?
293 * _X11TransConnectDisplay() does.
295 if (g_ascii_strcasecmp(phostname
, "localhost") == 0 ||
296 strcmp(phostname
, "127.0.0.1") == 0) {
302 * A host name of "unix" (case-sensitive) also
303 * causes a local connection.
305 if (strcmp(phostname
, "unix") == 0) {
311 * Does the host name begin with "/"? If so,
312 * it's presumed to be the pathname of a
313 * UNIX domain socket.
315 if (phostname
[0] == '/') {
321 g_string_printf(filter_str
, "not host %s", phostname
);
324 } else if (GetSystemMetrics(SM_REMOTESESSION
)) {
325 /* We have a remote session: https://docs.microsoft.com/en-us/windows/win32/termserv/detecting-the-terminal-services-environment */
326 g_string_printf(filter_str
, "not port 3389");
331 return filter_str
->str
;
334 bool display_is_remote(void)
336 static bool remote_display_checked
;
337 static bool is_remote
;
339 if (!remote_display_checked
) {
340 is_remote
= (strlen(get_conn_cfilter()) > 0);
346 static char *last_open_dir
;
349 get_last_open_dir(void)
351 return last_open_dir
;
355 set_last_open_dir(const char *dirname
)
358 char *new_last_open_dir
;
360 if (dirname
&& dirname
[0]) {
361 len
= strlen(dirname
);
362 if (dirname
[len
-1] == G_DIR_SEPARATOR
) {
363 new_last_open_dir
= g_strconcat(dirname
, (char *)NULL
);
366 new_last_open_dir
= g_strconcat(dirname
,
367 G_DIR_SEPARATOR_S
, (char *)NULL
);
370 new_last_open_dir
= NULL
;
373 g_free(last_open_dir
);
374 last_open_dir
= new_last_open_dir
;
378 get_open_dialog_initial_dir(void)
380 const char *initial_dir
;
382 switch (prefs
.gui_fileopen_style
) {
384 case FO_STYLE_LAST_OPENED
:
385 /* The user has specified that we should start out in the last directory
388 If we have a "last directory in which a file was opened", use that.
390 If not, use the user's personal data file directory. */
391 /* This is now the default behaviour in file_selection_new() */
392 initial_dir
= get_last_open_dir();
393 if (initial_dir
== NULL
)
394 initial_dir
= get_persdatafile_dir();
397 case FO_STYLE_SPECIFIED
:
398 /* The user has specified that we should always start out in a
399 specified directory; if they've specified that directory,
400 start out by showing the files in that dir, otherwise use
401 the user's personal data file directory. */
402 if (prefs
.gui_fileopen_dir
[0] != '\0')
403 initial_dir
= prefs
.gui_fileopen_dir
;
405 initial_dir
= get_persdatafile_dir();
409 initial_dir
= get_current_working_dir();
413 ws_assert_not_reached();