attr_dissector_fn_t
[wireshark-sm.git] / ui / util.c
blobd87033199553666efda0d14c263749383127d3cf
1 /* util.c
2 * Utility routines
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
9 */
11 #include "config.h"
13 #include <glib.h>
15 #include <stdlib.h>
16 #include <string.h>
18 #ifdef HAVE_UNISTD_H
19 #include <unistd.h>
20 #endif
22 #ifdef _WIN32
23 #include <windows.h>
24 #endif
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>
33 #include "ui/util.h"
36 * Collect command-line arguments as a string consisting of the arguments,
37 * separated by spaces.
39 char *
40 get_args_as_string(int argc, char **argv, int optindex)
42 int len;
43 int i;
44 char *argstring;
47 * Find out how long the string will be.
49 len = 0;
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
58 if (len == 0)
59 return g_strdup("");
62 * Allocate the buffer for the string.
64 argstring = (char *)g_malloc(len);
67 * Now construct the string.
69 argstring[0] = '\0';
70 i = optindex;
71 for (;;) {
72 (void) g_strlcat(argstring, argv[i], len);
73 i++;
74 if (i == argc)
75 break;
76 (void) g_strlcat(argstring, " ", len);
78 return argstring;
81 /* Compute the difference between two seconds/microseconds time stamps. */
82 void
83 compute_timestamp_diff(int *diffsec, int *diffusec,
84 uint32_t sec1, uint32_t usec1, uint32_t sec2, uint32_t usec2)
86 if (sec1 == sec2) {
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;
109 } else {
110 *diffusec = (usec1 - 1000000) - usec2;
111 (*diffsec)++;
113 } else {
114 /* Oh, good, we're not caught in a chronosynclastic infindibulum. */
115 *diffsec = sec1 - sec2;
116 if (usec2 <= usec1) {
117 *diffusec = usec1 - usec2;
118 } else {
119 *diffusec = (usec1 + 1000000) - usec2;
120 (*diffsec)--;
125 /* Remove any %<interface_name> from an IP address. */
126 static char *sanitize_filter_ip(char *hostname) {
127 char *end;
128 char *ret;
130 ret = g_strdup(hostname);
131 if (!ret)
132 return NULL;
134 end = strchr(ret, '%');
135 if (end)
136 *end = '\0';
137 return 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;
153 char *env, **tokens;
154 char *lastp, *lastc, *p;
155 char *pprotocol = NULL;
156 char *phostname = NULL;
157 size_t hostlen;
158 char *remip, *locip;
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,
170 tokens[3], locip);
171 g_free(remip);
172 g_free(locip);
174 g_strfreev(tokens);
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);
181 g_free(remip);
183 g_strfreev(tokens);
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) {
189 return "";
191 remip = sanitize_filter_ip(env);
192 g_string_printf(filter_str, "not host %s", remip);
193 g_free(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(),
202 * too. :-)
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:
223 * o shared memory
224 * o local stream
225 * o UNIX domain socket
226 * o TCP to local host.
229 p = env;
232 * Step 0, find the protocol. This is delimited by
233 * the optional slash ('/').
235 for (lastp = p; *p != '\0' && *p != ':' && *p != '/'; p++)
237 if (*p == '\0')
238 return ""; /* must have a colon */
240 if (p != lastp && *p != ':') { /* protocol given? */
241 /* Yes */
242 pprotocol = p;
244 /* Is it TCP? */
245 if (p - lastp != 3 || g_ascii_strncasecmp(lastp, "tcp", 3) != 0)
246 return ""; /* not TCP */
247 p++; /* skip the '/' */
248 } else
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.)
259 lastp = p;
260 lastc = NULL;
261 for (; *p != '\0'; p++)
262 if (*p == ':')
263 lastc = p;
265 if (lastc == NULL)
266 return ""; /* must have a colon */
268 if ((lastp != lastc) && (*(lastc - 1) == ':')
269 && (((lastc - 1) == lastp) || (*(lastc - 2) != ':'))) {
270 /* DECnet display specified */
271 return "";
272 } else
273 hostlen = lastc - lastp;
275 if (hostlen == 0)
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
286 * that we won't see.
288 * Does the host name refer to the local host?
289 * If so, the connection would probably be a
290 * local connection.
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) {
297 g_free(phostname);
298 return "";
302 * A host name of "unix" (case-sensitive) also
303 * causes a local connection.
305 if (strcmp(phostname, "unix") == 0) {
306 g_free(phostname);
307 return "";
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] == '/') {
316 g_free(phostname);
317 return "";
321 g_string_printf(filter_str, "not host %s", phostname);
322 g_free(phostname);
323 #ifdef _WIN32
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");
327 #endif /* _WIN32 */
328 } else {
329 return "";
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);
342 return is_remote;
345 // MUST be UTF-8
346 static char *last_open_dir;
348 const char *
349 get_last_open_dir(void)
351 return last_open_dir;
354 void
355 set_last_open_dir(const char *dirname)
357 size_t len;
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);
365 else {
366 new_last_open_dir = g_strconcat(dirname,
367 G_DIR_SEPARATOR_S, (char *)NULL);
369 } else {
370 new_last_open_dir = NULL;
373 g_free(last_open_dir);
374 last_open_dir = new_last_open_dir;
377 const char *
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
386 we looked in.
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();
395 break;
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;
404 else
405 initial_dir = get_persdatafile_dir();
406 break;
408 case FO_STYLE_CWD:
409 initial_dir = get_current_working_dir();
410 break;
412 default:
413 ws_assert_not_reached();
414 initial_dir = NULL;
415 break;
417 return initial_dir;