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