Move make-manuf to the tools directory.
[obnox/wireshark/wip.git] / util.c
diff --git a/util.c b/util.c
index 53321293f1c59a4edb2f37a14ed223d826464391..500330a8fc04e3f2c2909b830a1dcbd81ea6b41e 100644 (file)
--- a/util.c
+++ b/util.c
@@ -1,23 +1,22 @@
 /* util.c
  * Utility routines
  *
- * $Id: util.c,v 1.16 1999/07/09 04:18:36 gram Exp $
+ * $Id$
  *
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@zing.org>
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
  * Copyright 1998 Gerald Combs
  *
- * 
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
  * of the License, or (at your option) any later version.
- * 
+ *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- * 
+ *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #include <glib.h>
 
-#include <gtk/gtk.h>
-
-#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
 #include <stdio.h>
-
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
+#include <errno.h>
 
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
 
-#ifdef NEED_SNPRINTF_H
-# ifdef HAVE_STDARG_H
-#  include <stdarg.h>
-# else
-#  include <varargs.h>
-# endif
-# include "snprintf.h"
-#endif
+#include <epan/address.h>
+#include <epan/addr_resolv.h>
+#include <epan/strutil.h>
 
 #include "util.h"
 
-#include "image/icon-excl.xpm"
-#include "image/icon-ethereal.xpm"
+/*
+ * Collect command-line arguments as a string consisting of the arguments,
+ * separated by spaces.
+ */
+char *
+get_args_as_string(int argc, char **argv, int optindex)
+{
+       int len;
+       int i;
+       char *argstring;
 
-static void simple_dialog_cancel_cb(GtkWidget *, gpointer);
+       /*
+        * Find out how long the string will be.
+        */
+       len = 0;
+       for (i = optindex; i < argc; i++) {
+               len += (int) strlen(argv[i]);
+               len++;  /* space, or '\0' if this is the last argument */
+       }
 
-const gchar *bm_key = "button mask";
+       /*
+        * Allocate the buffer for the string.
+        */
+       argstring = (char *)g_malloc(len);
 
-/* Simple dialog function - Displays a dialog box with the supplied message
- * text.
- * 
- * Args:
- * type       : One of ESD_TYPE_*.
- * btn_mask   : The address of a gint.  The value passed in determines if
- *              the 'Cancel' button is displayed.  The button pressed by the 
- *              user is passed back.
- * msg_format : Sprintf-style format of the text displayed in the dialog.
- * ...        : Argument list for msg_format
- *
- */
-#define ESD_MAX_MSG_LEN 2048
+       /*
+        * Now construct the string.
+        */
+       argstring[0] = '\0';
+       i = optindex;
+       for (;;) {
+               g_strlcat(argstring, argv[i], len);
+               i++;
+               if (i == argc)
+                       break;
+               g_strlcat(argstring, " ", len);
+       }
+       return argstring;
+}
+
+/* Compute the difference between two seconds/microseconds time stamps. */
 void
-simple_dialog(gint type, gint *btn_mask, gchar *msg_format, ...) {
-  GtkWidget   *win, *main_vb, *top_hb, *type_pm, *msg_label,
-              *bbox, *ok_btn, *cancel_btn;
-  GdkPixmap   *pixmap;
-  GdkBitmap   *mask;
-  GtkStyle    *style;
-  GdkColormap *cmap;
-  va_list      ap;
-  gchar        message[ESD_MAX_MSG_LEN];
-  gchar      **icon;
-
-  /* Main window */
-  win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
-  gtk_container_border_width(GTK_CONTAINER(win), 7);
-
-  switch (type) {
-  case ESD_TYPE_WARN :
-    gtk_window_set_title(GTK_WINDOW(win), "Ethereal: Warning");
-    icon = icon_excl_xpm;
-    break;
-  case ESD_TYPE_CRIT :
-    gtk_window_set_title(GTK_WINDOW(win), "Ethereal: Critical");
-    icon = icon_excl_xpm;
-    break;
-  case ESD_TYPE_INFO :
-  default :
-    icon = icon_ethereal_xpm;
-    gtk_window_set_title(GTK_WINDOW(win), "Ethereal: Information");
-    break;
-  }
+compute_timestamp_diff(gint *diffsec, gint *diffusec,
+       guint32 sec1, guint32 usec1, guint32 sec2, guint32 usec2)
+{
+  if (sec1 == sec2) {
+    /* The seconds part of the first time is the same as the seconds
+       part of the second time, so if the microseconds part of the first
+       time is less than the microseconds part of the second time, the
+       first time is before the second time.  The microseconds part of
+       the delta should just be the difference between the microseconds
+       part of the first time and the microseconds part of the second
+       time; don't adjust the seconds part of the delta, as it's OK if
+       the microseconds part is negative. */
 
-  gtk_object_set_data(GTK_OBJECT(win), bm_key, btn_mask);
-
-  /* Container for our rows */
-  main_vb = gtk_vbox_new(FALSE, 5);
-  gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
-  gtk_container_add(GTK_CONTAINER(win), main_vb);
-  gtk_widget_show(main_vb);
-
-  /* Top row: Icon and message text */
-  top_hb = gtk_hbox_new(FALSE, 10);
-  gtk_container_add(GTK_CONTAINER(main_vb), top_hb);
-  gtk_widget_show(top_hb);
-  
-  style = gtk_widget_get_style(win);
-  cmap  = gdk_colormap_get_system();
-  pixmap = gdk_pixmap_colormap_create_from_xpm_d(NULL, cmap,  &mask,
-    &style->bg[GTK_STATE_NORMAL], icon);
-  type_pm = gtk_pixmap_new(pixmap, mask);
-  gtk_misc_set_alignment (GTK_MISC (type_pm), 0.5, 0.0);
-  gtk_container_add(GTK_CONTAINER(top_hb), type_pm);
-  gtk_widget_show(type_pm);
-
-  /* Load our vararg list into the message string */
-  va_start(ap, msg_format);
-  vsnprintf(message, ESD_MAX_MSG_LEN, msg_format, ap);
-
-  msg_label = gtk_label_new(message);
-  gtk_label_set_justify(GTK_LABEL(msg_label), GTK_JUSTIFY_FILL);
-  gtk_container_add(GTK_CONTAINER(top_hb), msg_label);
-  gtk_widget_show(msg_label);
-  
-  /* Button row */
-  bbox = gtk_hbutton_box_new();
-  gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
-  gtk_container_add(GTK_CONTAINER(main_vb), bbox);
-  gtk_widget_show(bbox);
-
-  ok_btn = gtk_button_new_with_label ("OK");
-  gtk_signal_connect_object(GTK_OBJECT(ok_btn), "clicked",
-    GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT (win)); 
-  gtk_container_add(GTK_CONTAINER(bbox), ok_btn);
-  GTK_WIDGET_SET_FLAGS(ok_btn, GTK_CAN_DEFAULT);
-  gtk_widget_grab_default(ok_btn);
-  gtk_widget_show(ok_btn);
-
-  if (btn_mask && *btn_mask == ESD_BTN_CANCEL) {
-    cancel_btn = gtk_button_new_with_label("Cancel");
-    gtk_signal_connect(GTK_OBJECT(cancel_btn), "clicked",
-      GTK_SIGNAL_FUNC(simple_dialog_cancel_cb), (gpointer) win);
-    gtk_container_add(GTK_CONTAINER(bbox), cancel_btn);
-    GTK_WIDGET_SET_FLAGS(cancel_btn, GTK_CAN_DEFAULT);
-    gtk_widget_show(cancel_btn);
+    *diffsec = sec1 - sec2;
+    *diffusec = usec1 - usec2;
+  } else if (sec1 <= sec2) {
+    /* The seconds part of the first time is less than the seconds part
+       of the second time, so the first time is before the second time.
+
+       Both the "seconds" and "microseconds" value of the delta
+       should have the same sign, so if the difference between the
+       microseconds values would be *positive*, subtract 1,000,000
+       from it, and add one to the seconds value. */
+    *diffsec = sec1 - sec2;
+    if (usec2 >= usec1) {
+      *diffusec = usec1 - usec2;
+    } else {
+      *diffusec = (usec1 - 1000000) - usec2;
+      (*diffsec)++;
+    }
+  } else {
+    /* Oh, good, we're not caught in a chronosynclastic infindibulum. */
+    *diffsec = sec1 - sec2;
+    if (usec2 <= usec1) {
+      *diffusec = usec1 - usec2;
+    } else {
+      *diffusec = (usec1 + 1000000) - usec2;
+      (*diffsec)--;
+    }
   }
+}
 
-  if (btn_mask)
-    *btn_mask = ESD_BTN_OK;
+/* Remove any %<interface_name> from an IP address. */
+char *sanitize_filter_ip(char *hostname) {
+    gchar *end;
+    gchar *ret;
 
-  gtk_widget_show(win);
+    ret = g_strdup(hostname);
+    if (!ret)
+        return NULL;
+
+    end = strchr(ret, '%');
+    if (end)
+        *end = '\0';
+    return ret;
 }
 
-static void
-simple_dialog_cancel_cb(GtkWidget *w, gpointer win) {
-  gint *btn_mask = (gint *) gtk_object_get_data(win, bm_key);
-  
-  if (btn_mask)
-    *btn_mask = ESD_BTN_CANCEL;
-  gtk_widget_destroy(GTK_WIDGET(win));
+/* Try to figure out if we're remotely connected, e.g. via ssh or
+   Terminal Server, and create a capture filter that matches aspects of the
+   connection.  We match the following environment variables:
+
+   SSH_CONNECTION (ssh): <remote IP> <remote port> <local IP> <local port>
+   SSH_CLIENT (ssh): <remote IP> <remote port> <local port>
+   REMOTEHOST (tcsh, others?): <remote name>
+   DISPLAY (x11): [remote name]:<display num>
+   SESSIONNAME (terminal server): <remote name>
+ */
+
+const gchar *get_conn_cfilter(void) {
+       static GString *filter_str = NULL;
+       gchar *env, **tokens;
+       char *lastp, *lastc, *p;
+       char *pprotocol = NULL;
+       char *phostname = NULL;
+       size_t hostlen;
+       char *remip, *locip;
+
+       if (filter_str == NULL) {
+               filter_str = g_string_new("");
+       }
+       if ((env = getenv("SSH_CONNECTION")) != NULL) {
+               tokens = g_strsplit(env, " ", 4);
+               if (tokens[3]) {
+                       remip = sanitize_filter_ip(tokens[0]);
+                       locip = sanitize_filter_ip(tokens[2]);
+                       g_string_printf(filter_str, "not (tcp port %s and %s host %s "
+                                                        "and tcp port %s and %s host %s)", tokens[1], host_ip_af(remip), remip,
+                               tokens[3], host_ip_af(locip), locip);
+                       g_free(remip);
+                       g_free(locip);
+                       return filter_str->str;
+               }
+       } else if ((env = getenv("SSH_CLIENT")) != NULL) {
+               tokens = g_strsplit(env, " ", 3);
+               remip = sanitize_filter_ip(tokens[2]);
+               g_string_printf(filter_str, "not (tcp port %s and %s host %s "
+                       "and tcp port %s)", tokens[1], host_ip_af(remip), tokens[0], remip);
+               g_free(remip);
+               return filter_str->str;
+       } else if ((env = getenv("REMOTEHOST")) != NULL) {
+               /* FreeBSD 7.0 sets REMOTEHOST to an empty string */
+               if (g_ascii_strcasecmp(env, "localhost") == 0 ||
+                   strcmp(env, "127.0.0.1") == 0 ||
+                   strcmp(env, "") == 0) {
+                       return "";
+               }
+               remip = sanitize_filter_ip(env);
+               g_string_printf(filter_str, "not %s host %s", host_ip_af(remip), remip);
+               g_free(remip);
+               return filter_str->str;
+       } else if ((env = getenv("DISPLAY")) != NULL) {
+               /*
+                * This mirrors what _X11TransConnectDisplay() does.
+                * Note that, on some systems, the hostname can
+                * begin with "/", which means that it's a pathname
+                * of a UNIX domain socket to connect to.
+                *
+                * The comments mirror those in _X11TransConnectDisplay(),
+                * too. :-)
+                *
+                * Display names may be of the following format:
+                *
+                *    [protoco./] [hostname] : [:] displaynumber [.screennumber]
+                *
+                * A string with exactly two colons separating hostname
+                * from the display indicates a DECnet style name.  Colons
+                * in the hostname may occur if an IPv6 numeric address
+                * is used as the hostname.  An IPv6 numeric address may
+                * also end in a double colon, so three colons in a row
+                * indicates an IPv6 address ending in :: followed by
+                * :display.  To make it easier for people to read, an
+                * IPv6 numeric address hostname may be surrounded by []
+                * in a similar fashion to the IPv6 numeric address URL
+                * syntax defined by IETF RFC 2732.
+                *
+                * If no hostname and no protocol is specified, the string
+                * is interpreted as the most efficient local connection
+                * to a server on the same machine.  This is usually:
+                *
+                *    o shared memory
+                *    o local stream
+                *    o UNIX domain socket
+                *    o TCP to local host.
+                */
+
+               p = env;
+
+               /*
+                * Step 0, find the protocol.  This is delimited by
+                * the optional slash ('/').
+                */
+               for (lastp = p; *p != '\0' && *p != ':' && *p != '/'; p++)
+                       ;
+               if (*p == '\0')
+                       return "";      /* must have a colon */
+
+               if (p != lastp && *p != ':') {  /* protocol given? */
+                       /* Yes */
+                       pprotocol = p;
+
+                       /* Is it TCP? */
+                       if (p - lastp != 3 || g_ascii_strncasecmp(lastp, "tcp", 3) != 0)
+                               return "";      /* not TCP */
+                       p++;                    /* skip the '/' */
+               } else
+                       p = env;                /* reset the pointer in
+                                                  case no protocol was given */
+
+               /*
+                * Step 1, find the hostname.  This is delimited either by
+                * one colon, or two colons in the case of DECnet (DECnet
+                * Phase V allows a single colon in the hostname).  (See
+                * note above regarding IPv6 numeric addresses with
+                * triple colons or [] brackets.)
+                */
+               lastp = p;
+               lastc = NULL;
+               for (; *p != '\0'; p++)
+                       if (*p == ':')
+                               lastc = p;
+
+               if (lastc == NULL)
+                       return "";              /* must have a colon */
+
+               if ((lastp != lastc) && (*(lastc - 1) == ':')
+                   && (((lastc - 1) == lastp) || (*(lastc - 2) != ':'))) {
+                       /* DECnet display specified */
+                       return "";
+               } else
+                       hostlen = lastc - lastp;
+
+               if (hostlen == 0)
+                       return "";      /* no hostname supplied */
+
+               phostname = (char *)g_malloc(hostlen + 1);
+               memcpy(phostname, lastp, hostlen);
+               phostname[hostlen] = '\0';
+
+               if (pprotocol == NULL) {
+                       /*
+                        * No protocol was explicitly specified, so it
+                        * could be a local connection over a transport
+                        * that we won't see.
+                        *
+                        * Does the host name refer to the local host?
+                        * If so, the connection would probably be a
+                        * local connection.
+                        *
+                        * XXX - compare against our host name?
+                        * _X11TransConnectDisplay() does.
+                        */
+                       if (g_ascii_strcasecmp(phostname, "localhost") == 0 ||
+                           strcmp(phostname, "127.0.0.1") == 0) {
+                               g_free(phostname);
+                               return "";
+                       }
+
+                       /*
+                        * A host name of "unix" (case-sensitive) also
+                        * causes a local connection.
+                        */
+                       if (strcmp(phostname, "unix") == 0) {
+                               g_free(phostname);
+                               return "";
+                       }
+
+                       /*
+                        * Does the host name begin with "/"?  If so,
+                        * it's presumed to be the pathname of a
+                        * UNIX domain socket.
+                        */
+                       if (phostname[0] == '/') {
+                               g_free(phostname);
+                               return "";
+                       }
+               }
+
+               g_string_printf(filter_str, "not %s host %s",
+                       host_ip_af(phostname), phostname);
+               g_free(phostname);
+               return filter_str->str;
+       } else if ((env = getenv("SESSIONNAME")) != NULL) {
+               /* Apparently the KB article at
+                * http://technet2.microsoft.com/WindowsServer/en/library/6caf87bf-3d70-4801-9485-87e9ec3df0171033.mspx?mfr=true
+                * is incorrect.  There are _plenty_ of cases where CLIENTNAME
+                * and SESSIONNAME are set outside of a Terminal Terver session.
+                * It looks like Terminal Server sets SESSIONNAME to RDP-TCP#<number>
+                * for "real" sessions.
+                *
+                * XXX - There's a better way to do this described at
+                * http://www.microsoft.com/technet/archive/termsrv/maintain/featusability/tsrvapi.mspx?mfr=true
+                */
+               if (g_ascii_strncasecmp(env, "rdp", 3) == 0) {
+                       g_string_printf(filter_str, "not tcp port 3389");
+                       return filter_str->str;
+               }
+       }
+       return "";
 }