We always HAVE_CONFIG_H so don't bother checking whether we have it or not.
[metze/wireshark/wip.git] / ui / util.c
1 /* util.c
2  * Utility routines
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24
25 #include "config.h"
26
27 #include <glib.h>
28
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stdio.h>
32 #include <errno.h>
33
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37
38 #ifdef HAVE_WINDOWS_H
39 #include <windows.h>
40 #endif
41
42 #include <epan/address.h>
43 #include <epan/addr_resolv.h>
44 #include <epan/strutil.h>
45
46 #include "ui/util.h"
47
48 /*
49  * Collect command-line arguments as a string consisting of the arguments,
50  * separated by spaces.
51  */
52 char *
53 get_args_as_string(int argc, char **argv, int optindex)
54 {
55         int len;
56         int i;
57         char *argstring;
58
59         /*
60          * Find out how long the string will be.
61          */
62         len = 0;
63         for (i = optindex; i < argc; i++) {
64                 len += (int) strlen(argv[i]);
65                 len++;  /* space, or '\0' if this is the last argument */
66         }
67
68         /*
69          * Allocate the buffer for the string.
70          */
71         argstring = (char *)g_malloc(len);
72
73         /*
74          * Now construct the string.
75          */
76         argstring[0] = '\0';
77         i = optindex;
78         for (;;) {
79                 g_strlcat(argstring, argv[i], len);
80                 i++;
81                 if (i == argc)
82                         break;
83                 g_strlcat(argstring, " ", len);
84         }
85         return argstring;
86 }
87
88 /* Compute the difference between two seconds/microseconds time stamps. */
89 void
90 compute_timestamp_diff(gint *diffsec, gint *diffusec,
91         guint32 sec1, guint32 usec1, guint32 sec2, guint32 usec2)
92 {
93   if (sec1 == sec2) {
94     /* The seconds part of the first time is the same as the seconds
95        part of the second time, so if the microseconds part of the first
96        time is less than the microseconds part of the second time, the
97        first time is before the second time.  The microseconds part of
98        the delta should just be the difference between the microseconds
99        part of the first time and the microseconds part of the second
100        time; don't adjust the seconds part of the delta, as it's OK if
101        the microseconds part is negative. */
102
103     *diffsec = sec1 - sec2;
104     *diffusec = usec1 - usec2;
105   } else if (sec1 <= sec2) {
106     /* The seconds part of the first time is less than the seconds part
107        of the second time, so the first time is before the second time.
108
109        Both the "seconds" and "microseconds" value of the delta
110        should have the same sign, so if the difference between the
111        microseconds values would be *positive*, subtract 1,000,000
112        from it, and add one to the seconds value. */
113     *diffsec = sec1 - sec2;
114     if (usec2 >= usec1) {
115       *diffusec = usec1 - usec2;
116     } else {
117       *diffusec = (usec1 - 1000000) - usec2;
118       (*diffsec)++;
119     }
120   } else {
121     /* Oh, good, we're not caught in a chronosynclastic infindibulum. */
122     *diffsec = sec1 - sec2;
123     if (usec2 <= usec1) {
124       *diffusec = usec1 - usec2;
125     } else {
126       *diffusec = (usec1 + 1000000) - usec2;
127       (*diffsec)--;
128     }
129   }
130 }
131
132 /* Remove any %<interface_name> from an IP address. */
133 static char *sanitize_filter_ip(char *hostname) {
134     gchar *end;
135     gchar *ret;
136
137     ret = g_strdup(hostname);
138     if (!ret)
139         return NULL;
140
141     end = strchr(ret, '%');
142     if (end)
143         *end = '\0';
144     return ret;
145 }
146
147 /* Try to figure out if we're remotely connected, e.g. via ssh or
148    Terminal Server, and create a capture filter that matches aspects of the
149    connection.  We match the following environment variables:
150
151    SSH_CONNECTION (ssh): <remote IP> <remote port> <local IP> <local port>
152    SSH_CLIENT (ssh): <remote IP> <remote port> <local port>
153    REMOTEHOST (tcsh, others?): <remote name>
154    DISPLAY (x11): [remote name]:<display num>
155    SESSIONNAME (terminal server): <remote name>
156  */
157
158 const gchar *get_conn_cfilter(void) {
159         static GString *filter_str = NULL;
160         gchar *env, **tokens;
161         char *lastp, *lastc, *p;
162         char *pprotocol = NULL;
163         char *phostname = NULL;
164         size_t hostlen;
165         char *remip, *locip;
166
167         if (filter_str == NULL) {
168                 filter_str = g_string_new("");
169         }
170         if ((env = getenv("SSH_CONNECTION")) != NULL) {
171                 tokens = g_strsplit(env, " ", 4);
172                 if (tokens[3]) {
173                         remip = sanitize_filter_ip(tokens[0]);
174                         locip = sanitize_filter_ip(tokens[2]);
175                         g_string_printf(filter_str, "not (tcp port %s and %s host %s "
176                                                          "and tcp port %s and %s host %s)", tokens[1], host_ip_af(remip), remip,
177                                 tokens[3], host_ip_af(locip), locip);
178                         g_free(remip);
179                         g_free(locip);
180                         return filter_str->str;
181                 }
182         } else if ((env = getenv("SSH_CLIENT")) != NULL) {
183                 tokens = g_strsplit(env, " ", 3);
184                 remip = sanitize_filter_ip(tokens[2]);
185                 g_string_printf(filter_str, "not (tcp port %s and %s host %s "
186                         "and tcp port %s)", tokens[1], host_ip_af(remip), tokens[0], remip);
187                 g_free(remip);
188                 return filter_str->str;
189         } else if ((env = getenv("REMOTEHOST")) != NULL) {
190                 /* FreeBSD 7.0 sets REMOTEHOST to an empty string */
191                 if (g_ascii_strcasecmp(env, "localhost") == 0 ||
192                     strcmp(env, "127.0.0.1") == 0 ||
193                     strcmp(env, "") == 0) {
194                         return "";
195                 }
196                 remip = sanitize_filter_ip(env);
197                 g_string_printf(filter_str, "not %s host %s", host_ip_af(remip), remip);
198                 g_free(remip);
199                 return filter_str->str;
200         } else if ((env = getenv("DISPLAY")) != NULL) {
201                 /*
202                  * This mirrors what _X11TransConnectDisplay() does.
203                  * Note that, on some systems, the hostname can
204                  * begin with "/", which means that it's a pathname
205                  * of a UNIX domain socket to connect to.
206                  *
207                  * The comments mirror those in _X11TransConnectDisplay(),
208                  * too. :-)
209                  *
210                  * Display names may be of the following format:
211                  *
212                  *    [protoco./] [hostname] : [:] displaynumber [.screennumber]
213                  *
214                  * A string with exactly two colons separating hostname
215                  * from the display indicates a DECnet style name.  Colons
216                  * in the hostname may occur if an IPv6 numeric address
217                  * is used as the hostname.  An IPv6 numeric address may
218                  * also end in a double colon, so three colons in a row
219                  * indicates an IPv6 address ending in :: followed by
220                  * :display.  To make it easier for people to read, an
221                  * IPv6 numeric address hostname may be surrounded by []
222                  * in a similar fashion to the IPv6 numeric address URL
223                  * syntax defined by IETF RFC 2732.
224                  *
225                  * If no hostname and no protocol is specified, the string
226                  * is interpreted as the most efficient local connection
227                  * to a server on the same machine.  This is usually:
228                  *
229                  *    o shared memory
230                  *    o local stream
231                  *    o UNIX domain socket
232                  *    o TCP to local host.
233                  */
234
235                 p = env;
236
237                 /*
238                  * Step 0, find the protocol.  This is delimited by
239                  * the optional slash ('/').
240                  */
241                 for (lastp = p; *p != '\0' && *p != ':' && *p != '/'; p++)
242                         ;
243                 if (*p == '\0')
244                         return "";      /* must have a colon */
245
246                 if (p != lastp && *p != ':') {  /* protocol given? */
247                         /* Yes */
248                         pprotocol = p;
249
250                         /* Is it TCP? */
251                         if (p - lastp != 3 || g_ascii_strncasecmp(lastp, "tcp", 3) != 0)
252                                 return "";      /* not TCP */
253                         p++;                    /* skip the '/' */
254                 } else
255                         p = env;                /* reset the pointer in
256                                                    case no protocol was given */
257
258                 /*
259                  * Step 1, find the hostname.  This is delimited either by
260                  * one colon, or two colons in the case of DECnet (DECnet
261                  * Phase V allows a single colon in the hostname).  (See
262                  * note above regarding IPv6 numeric addresses with
263                  * triple colons or [] brackets.)
264                  */
265                 lastp = p;
266                 lastc = NULL;
267                 for (; *p != '\0'; p++)
268                         if (*p == ':')
269                                 lastc = p;
270
271                 if (lastc == NULL)
272                         return "";              /* must have a colon */
273
274                 if ((lastp != lastc) && (*(lastc - 1) == ':')
275                     && (((lastc - 1) == lastp) || (*(lastc - 2) != ':'))) {
276                         /* DECnet display specified */
277                         return "";
278                 } else
279                         hostlen = lastc - lastp;
280
281                 if (hostlen == 0)
282                         return "";      /* no hostname supplied */
283
284                 phostname = (char *)g_malloc(hostlen + 1);
285                 memcpy(phostname, lastp, hostlen);
286                 phostname[hostlen] = '\0';
287
288                 if (pprotocol == NULL) {
289                         /*
290                          * No protocol was explicitly specified, so it
291                          * could be a local connection over a transport
292                          * that we won't see.
293                          *
294                          * Does the host name refer to the local host?
295                          * If so, the connection would probably be a
296                          * local connection.
297                          *
298                          * XXX - compare against our host name?
299                          * _X11TransConnectDisplay() does.
300                          */
301                         if (g_ascii_strcasecmp(phostname, "localhost") == 0 ||
302                             strcmp(phostname, "127.0.0.1") == 0) {
303                                 g_free(phostname);
304                                 return "";
305                         }
306
307                         /*
308                          * A host name of "unix" (case-sensitive) also
309                          * causes a local connection.
310                          */
311                         if (strcmp(phostname, "unix") == 0) {
312                                 g_free(phostname);
313                                 return "";
314                         }
315
316                         /*
317                          * Does the host name begin with "/"?  If so,
318                          * it's presumed to be the pathname of a
319                          * UNIX domain socket.
320                          */
321                         if (phostname[0] == '/') {
322                                 g_free(phostname);
323                                 return "";
324                         }
325                 }
326
327                 g_string_printf(filter_str, "not %s host %s",
328                         host_ip_af(phostname), phostname);
329                 g_free(phostname);
330                 return filter_str->str;
331 #ifdef _WIN32
332         } else if (GetSystemMetrics(SM_REMOTESESSION)) {
333                 /* We have a remote session: http://msdn.microsoft.com/en-us/library/aa380798%28VS.85%29.aspx */
334                 g_string_printf(filter_str, "not tcp port 3389");
335                 return filter_str->str;
336 #endif /* _WIN32 */
337         }
338         return "";
339 }