]> The Tcpdump Group git mirrors - tcpdump/blobdiff - tcpdump.c
provide multiline output for PIM Joins/Grafts/Graft-Acks
[tcpdump] / tcpdump.c
index a8b2fcae18a1a793359fb5806469972a0788aece..4dc2fe0aa39e4569d758982eb0de21518f71a555 100644 (file)
--- a/tcpdump.c
+++ b/tcpdump.c
@@ -30,7 +30,7 @@ static const char copyright[] _U_ =
     "@(#) Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000\n\
 The Regents of the University of California.  All rights reserved.\n";
 static const char rcsid[] _U_ =
-    "@(#) $Header: /tcpdump/master/tcpdump/tcpdump.c,v 1.219 2003-11-18 08:53:19 guy Exp $ (LBL)";
+    "@(#) $Header: /tcpdump/master/tcpdump/tcpdump.c,v 1.235 2004-03-17 19:40:42 guy Exp $ (LBL)";
 #endif
 
 /*
@@ -56,11 +56,17 @@ extern int SIZE_BUF;
 #define uint UINT
 #endif /* WIN32 */
 
+#ifdef HAVE_SMI_H
+#include <smi.h>
+#endif
+
 #include <pcap.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <pwd.h>
+#include <grp.h>
 
 #include "interface.h"
 #include "addrtoname.h"
@@ -72,11 +78,11 @@ extern int SIZE_BUF;
 int dflag;                     /* print filter code */
 int eflag;                     /* print ethernet header */
 int fflag;                     /* don't translate "foreign" IP address */
-int Lflag;                     /* list available data link types and exit */
+static int Lflag;              /* list available data link types and exit */
 int nflag;                     /* leave addresses as numbers */
 int Nflag;                     /* remove domains from printed host names */
-int Oflag = 1;                 /* run filter code optimizer */
-int pflag;                     /* don't go promiscuous */
+static int Oflag = 1;          /* run filter code optimizer */
+static int pflag;              /* don't go promiscuous */
 int qflag;                     /* quick (shorter) output */
 int Rflag = 1;                 /* print sequence # field in AH/ESP*/
 int sflag = 0;                 /* use the libsmi to translate OIDs */
@@ -87,9 +93,20 @@ int uflag = 0;                       /* Print undecoded NFS handles */
 int vflag;                     /* verbose */
 int xflag;                     /* print packet in hex */
 int Xflag;                     /* print packet in ascii as well as hex */
-off_t Cflag = 0;                /* rotate dump files after this many bytes */
+static off_t Cflag = 0;                /* rotate dump files after this many bytes */
 int Aflag = 0;                  /* print packet only in ascii observing LF, CR, TAB, SPACE */
-int dlt = -1;          /* if != -1, ask libpcap for the DLT it names */
+static int dlt = -1;           /* if != -1, ask libpcap for the DLT it names */
+static int Cflag_count = 0;    /* Keep track of which file number we're writing */
+static int Wflag = 0;          /* recycle output files after this number of files */
+static int WflagChars = 0;
+
+/*
+ * Define the maximum number of files for the -C flag, and how many
+ * characters can be added to a filename for the -C flag (which
+ * should be enough to handle MAX_CFLAG - 1).
+ */
+#define MAX_CFLAG      1000000
+#define MAX_CFLAG_CHARS        6
 
 const char *dlt_name = NULL;
 
@@ -112,11 +129,20 @@ static void show_dlts_and_exit(pcap_t *pd) __attribute__((noreturn));
 static void print_packet(u_char *, const struct pcap_pkthdr *, const u_char *);
 static void dump_packet_and_trunc(u_char *, const struct pcap_pkthdr *, const u_char *);
 static void dump_packet(u_char *, const struct pcap_pkthdr *, const u_char *);
+static void droproot(const char *, const char *);
 
 #ifdef SIGINFO
 RETSIGTYPE requestinfo(int);
 #endif
 
+#if defined(USE_WIN32_MM_TIMER)
+  #include <MMsystem.h>
+  static UINT timer_id;
+  static void CALLBACK verbose_stats_dump(UINT, UINT, DWORD_PTR, DWORD_PTR, DWORD_PTR);
+#elif defined(HAVE_ALARM)
+  static void verbose_stats_dump(int sig);
+#endif
+
 static void info(int);
 static u_int packets_captured;
 
@@ -205,6 +231,12 @@ static struct printer printers[] = {
 #endif
 #ifdef DLT_ENC
        { enc_if_print,         DLT_ENC },
+#endif
+#ifdef DLT_SYMANTEC_FIREWALL
+       { symantec_if_print,    DLT_SYMANTEC_FIREWALL },
+#endif
+#ifdef DLT_APPLE_IP_OVER_IEEE1394
+       { ap1394_if_print,      DLT_APPLE_IP_OVER_IEEE1394 },
 #endif
        { NULL,                 0 },
 };
@@ -256,7 +288,8 @@ show_dlts_and_exit(pcap_t *pd)
        while (--n_dlts >= 0) {
                dlt_name = pcap_datalink_val_to_name(dlts[n_dlts]);
                if (dlt_name != NULL) {
-                       (void) fprintf(stderr, "  %s", dlt_name);
+                       (void) fprintf(stderr, "  %s (%s)", dlt_name,
+                           pcap_datalink_val_to_description(dlts[n_dlts]));
 
                        /*
                         * OK, does tcpdump handle that type?
@@ -297,12 +330,68 @@ show_dlts_and_exit(pcap_t *pd)
 #define U_FLAG
 #endif
 
+/* Drop root privileges and chroot if necessary */
+static void
+droproot(const char *username, const char *chroot_dir)
+{
+       struct passwd *pw = NULL;
+
+       if (chroot_dir && !username) {
+               fprintf(stderr, "Chroot without dropping root is insecure\n");
+               exit(1);
+       }
+       
+       pw = getpwnam(username);
+       if (pw) {
+               if (chroot_dir) {
+                       if (chroot(chroot_dir) != 0 || chdir ("/") != 0) {
+                               fprintf(stderr, "Couldn't chroot/chdir to '%.64s'\n", chroot_dir);
+                               exit(1);
+                       }
+               }
+               if (initgroups(pw->pw_name, pw->pw_gid) != 0 || setgid(pw->pw_gid) != 0 ||
+                                setuid(pw->pw_uid) != 0) {
+                       fprintf(stderr, "Couldn't change to '%.32s' uid=%d gid=%d\n", username, 
+                                                       pw->pw_uid, pw->pw_gid);
+                       exit(1);
+               }
+       }
+       else {
+               fprintf(stderr, "Couldn't find user '%.32s'\n", username);
+               exit(1);
+       }
+}
+
+static int
+getWflagChars(int x)
+{
+       int c = 0;
+
+       x -= 1;
+       while (x > 0) {
+               c += 1;
+               x /= 10;
+       }
+
+       return c;
+}
+
+
+static void
+MakeFilename(char *buffer, char *orig_name, int cnt, int max_chars)
+{
+       if (cnt == 0 && max_chars == 0)
+               strcpy(buffer, orig_name);
+       else
+               sprintf(buffer, "%s%0*d", orig_name, max_chars, cnt);
+}
+
 int
 main(int argc, char **argv)
 {
        register int cnt, op, i;
        bpf_u_int32 localnet, netmask;
-       register char *cp, *infile, *cmdbuf, *device, *RFileName, *WFileName;
+       register char *cp, *infile, *cmdbuf, *device, *RFileName, *WFileName, *WFileNameAlt;
        pcap_handler callback;
        int type;
        struct bpf_program fcode;
@@ -313,6 +402,8 @@ main(int argc, char **argv)
        struct dump_info dumpinfo;
        u_char *pcap_userdata;
        char ebuf[PCAP_ERRBUF_SIZE];
+       char *username = NULL;
+       char *chroot_dir = NULL;
 #ifdef HAVE_PCAP_FINDALLDEVS
        pcap_if_t *devpointer;
        int devnum;
@@ -342,7 +433,7 @@ main(int argc, char **argv)
 
        opterr = 0;
        while (
-           (op = getopt(argc, argv, "aA" B_FLAG "c:C:d" D_FLAG "eE:fF:i:lLm:nNOpqr:Rs:StT:u" U_FLAG "vw:xXy:Y")) != -1)
+           (op = getopt(argc, argv, "aA" B_FLAG "c:C:d" D_FLAG "eE:fF:i:lLm:nNOpqr:Rs:StT:u" U_FLAG "vw:W:xXy:YZ:")) != -1)
                switch (op) {
 
                case 'a':
@@ -493,6 +584,7 @@ main(int argc, char **argv)
                                      program_name, optarg);
                        (void)fprintf(stderr, "(no libsmi support)\n");
 #endif
+                       break;
 
                case 'O':
                        Oflag = 0;
@@ -575,6 +667,13 @@ main(int argc, char **argv)
                        WFileName = optarg;
                        break;
 
+               case 'W':
+                       Wflag = atoi(optarg);
+                       if (Wflag < 0) 
+                               error("invalid number of output files %s", optarg);
+                       WflagChars = getWflagChars(Wflag);
+                       break;
+
                case 'x':
                        ++xflag;
                        break;
@@ -605,6 +704,16 @@ main(int argc, char **argv)
                        }
                        break;
 #endif
+               case 'Z':
+                       if (optarg) {
+                               username = strdup(optarg);
+                       }
+                       else {
+                               usage();
+                               /* NOTREACHED */
+                       }
+                       break;
+
                default:
                        usage();
                        /* NOTREACHED */
@@ -613,6 +722,24 @@ main(int argc, char **argv)
        if (tflag > 0)
                thiszone = gmt2local(0);
 
+#ifdef WITH_CHROOT
+       /* if run as root, prepare for chrooting */
+       if (getuid() == 0 || geteuid() == 0) {
+               /* future extensibility for cmd-line arguments */
+               if (!chroot_dir)
+                       chroot_dir = WITH_CHROOT;
+       }
+#endif
+
+#ifdef WITH_USER
+       /* if run as root, prepare for dropping root privileges */
+       if (getuid() == 0 || geteuid() == 0) {
+               /* Run with '-Z root' to restore old behaviour */ 
+               if (!username)
+                       username = WITH_USER;
+       }
+#endif
+
        if (RFileName != NULL) {
                int dlt;
                const char *dlt_name;
@@ -627,17 +754,23 @@ main(int argc, char **argv)
                 * people's trace files (especially if we're set-UID
                 * root).
                 */
-               setuid(getuid());
+               if (setgid(getgid()) != 0 || setuid(getuid()) != 0 )
+                       fprintf(stderr, "Warning: setgid/setuid failed !\n");
 #endif /* WIN32 */
                pd = pcap_open_offline(RFileName, ebuf);
                if (pd == NULL)
                        error("%s", ebuf);
                dlt = pcap_datalink(pd);
                dlt_name = pcap_datalink_val_to_name(dlt);
-               if (dlt_name == NULL)
-                       dlt_name = "???";
-                printf("reading from file %s, link-type %u (%s)\n",
-                      RFileName, dlt, dlt_name);
+               if (dlt_name == NULL) {
+                       fprintf(stderr, "reading from file %s, link-type %u\n",
+                           RFileName, dlt);
+               } else {
+                       fprintf(stderr,
+                           "reading from file %s, link-type %s (%s)\n",
+                           RFileName, dlt_name,
+                           pcap_datalink_val_to_description(dlt));
+               }
                localnet = 0;
                netmask = 0;
                if (fflag != 0)
@@ -669,6 +802,13 @@ main(int argc, char **argv)
                        error("%s", ebuf);
                else if (*ebuf)
                        warning("%s", ebuf);
+               /*
+                * Let user own process after socket has been opened.
+                */
+#ifndef WIN32
+               if (setgid(getgid()) != 0 || setuid(getuid()) != 0)
+                       fprintf(stderr, "Warning: setgid/setuid failed !\n");
+#endif /* WIN32 */
 #ifdef WIN32
                if(UserBufferSize != 1000000)
                        if(pcap_setbuff(pd, UserBufferSize)==-1){
@@ -706,12 +846,6 @@ main(int argc, char **argv)
                        netmask = 0;
                        warning("%s", ebuf);
                }
-               /*
-                * Let user own process after socket has been opened.
-                */
-#ifndef WIN32
-               setuid(getuid());
-#endif /* WIN32 */
        }
        if (infile)
                cmdbuf = read_infile(infile);
@@ -741,7 +875,13 @@ main(int argc, char **argv)
        if (pcap_setfilter(pd, &fcode) < 0)
                error("%s", pcap_geterr(pd));
        if (WFileName) {
-               pcap_dumper_t *p = pcap_dump_open(pd, WFileName);
+               pcap_dumper_t *p;
+
+               WFileNameAlt = (char *)malloc(strlen(WFileName) + MAX_CFLAG_CHARS + 1);
+               if (WFileNameAlt == NULL)
+                       error("malloc of WFileNameAlt");
+               MakeFilename(WFileNameAlt, WFileName, 0, WflagChars);
+               p = pcap_dump_open(pd, WFileNameAlt);
                if (p == NULL)
                        error("%s", pcap_geterr(pd));
                if (Cflag != 0) {
@@ -767,9 +907,36 @@ main(int argc, char **argv)
                callback = print_packet;
                pcap_userdata = (u_char *)&printinfo;
        }
+#ifndef WIN32
+       /*
+        * We cannot do this earlier, because we want to be able to open
+        * the file (if done) for writing before giving up permissions.
+        */
+       if (getuid() == 0 || geteuid() == 0) {
+               if (username || chroot_dir)
+                       droproot(username, chroot_dir);
+       }
+#endif /* WIN32 */
 #ifdef SIGINFO
        (void)setsignal(SIGINFO, requestinfo);
 #endif
+
+       if (vflag > 0 && WFileName) {
+               /*
+                * When capturing to a file, "-v" means tcpdump should,
+                * every 10 secodns, "v"erbosely report the number of
+                * packets captured.
+                */
+#ifdef USE_WIN32_MM_TIMER
+               /* call verbose_stats_dump() each 1000 +/-100msec */
+               timer_id = timeSetEvent(1000, 100, verbose_stats_dump, 0, TIME_PERIODIC);
+               setvbuf(stderr, NULL, _IONBF, 0);
+#elif defined(HAVE_ALARM)
+               (void)setsignal(SIGALRM, verbose_stats_dump);
+               alarm(1);
+#endif
+       }
+
 #ifndef WIN32
        if (RFileName == NULL) {
                int dlt;
@@ -783,10 +950,14 @@ main(int argc, char **argv)
                        (void)fprintf(stderr, "%s: ", program_name);
                dlt = pcap_datalink(pd);
                dlt_name = pcap_datalink_val_to_name(dlt);
-               if (dlt_name == NULL)
-                       dlt_name = "???";
-               (void)fprintf(stderr, "listening on %s, link-type %u (%s), capture size %u bytes\n",
-                   device, dlt, dlt_name, snaplen);
+               if (dlt_name == NULL) {
+                       (void)fprintf(stderr, "listening on %s, link-type %u, capture size %u bytes\n",
+                           device, dlt, snaplen);
+               } else {
+                       (void)fprintf(stderr, "listening on %s, link-type %s (%s), capture size %u bytes\n",
+                           device, dlt_name,
+                           pcap_datalink_val_to_description(dlt), snaplen);
+               }
                (void)fflush(stderr);
        }
 #endif /* WIN32 */
@@ -828,6 +999,14 @@ main(int argc, char **argv)
 static RETSIGTYPE
 cleanup(int signo _U_)
 {
+#ifdef USE_WIN32_MM_TIMER
+       if (timer_id)
+               timeKillEvent(timer_id);
+       timer_id = 0;
+#elif defined(HAVE_ALARM)
+       alarm(0);
+#endif
+
 #ifdef HAVE_PCAP_BREAKLOOP
        /*
         * We have "pcap_breakloop()"; use it, so that we do as little
@@ -884,38 +1063,10 @@ info(register int verbose)
        infoprint = 0;
 }
 
-static void
-reverse(char *s)
-{
-       int i, j, c;
-
-       for (i = 0, j = strlen(s) - 1; i < j; i++, j--) {
-               c = s[i];
-               s[i] = s[j];
-               s[j] = c;
-       }
-}
-
-
-static void
-swebitoa(unsigned int n, char *s)
-{
-       unsigned int i;
-
-       i = 0;
-       do {
-               s[i++] = n % 10 + '0';
-       } while ((n /= 10) > 0);
-
-       s[i] = '\0';
-       reverse(s);
-}
-
 static void
 dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
 {
        struct dump_info *dump_info;
-       static uint cnt = 2;
        char *name;
 
        ++packets_captured;
@@ -930,13 +1081,22 @@ dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *s
         * file could put it over Cflag.
         */
        if (ftell((FILE *)dump_info->p) > Cflag) {
-               name = (char *) malloc(strlen(dump_info->WFileName) + 4);
+               /*
+                * Close the current file and open a new one.
+                */
+               pcap_dump_close(dump_info->p);
+               Cflag_count++;
+               if (Wflag > 0) {
+                       if (Cflag_count >= Wflag)
+                               Cflag_count = 0;
+               } else {
+                       if (Cflag_count >= MAX_CFLAG)
+                               error("too many output files");
+               }
+               name = (char *)malloc(strlen(dump_info->WFileName) + MAX_CFLAG_CHARS + 1);
                if (name == NULL)
                        error("dump_packet_and_trunc: malloc");
-               strcpy(name, dump_info->WFileName);
-               swebitoa(cnt, name + strlen(dump_info->WFileName));
-               cnt++;
-               pcap_dump_close(dump_info->p);
+               MakeFilename(name, dump_info->WFileName, Cflag_count, WflagChars);
                dump_info->p = pcap_dump_open(dump_info->pd, name);
                free(name);
                if (dump_info->p == NULL)
@@ -1001,7 +1161,7 @@ print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
                        /*
                         * Include the link-layer header.
                         */
-                       default_print_unaligned(sp, h->caplen);
+                       default_print(sp, h->caplen);
                } else {
                        /*
                         * Don't include the link-layer header - and if
@@ -1009,7 +1169,7 @@ print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
                         * print nothing.
                         */
                        if (h->caplen > hdrlen)
-                               default_print_unaligned(sp + hdrlen,
+                               default_print(sp + hdrlen,
                                    h->caplen - hdrlen);
                }
        }
@@ -1021,32 +1181,6 @@ print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
                info(0);
 }
 
-/* Like default_print() but data need not be aligned */
-void
-default_print_unaligned(register const u_char *cp, register u_int length)
-{
-       register u_int i, s;
-       register int nshorts;
-
-       if (Xflag) {
-               ascii_print(cp, length);
-               return;
-       }
-       nshorts = (u_int) length / sizeof(u_short);
-       i = 0;
-       while (--nshorts >= 0) {
-               if ((i++ % 8) == 0)
-                       (void)printf("\n\t\t\t");
-               s = *cp++;
-               (void)printf(" %02x%02x", s, *cp++);
-       }
-       if (length & 1) {
-               if ((i % 8) == 0)
-                       (void)printf("\n\t\t\t");
-               (void)printf(" %02x", *cp);
-       }
-}
-
 #ifdef WIN32
        /*
         * XXX - there should really be libpcap calls to get the version
@@ -1067,7 +1201,9 @@ default_print_unaligned(register const u_char *cp, register u_int length)
         * "Wpcap_version" information on Windows.
         */
        char WDversion[]="current-cvs.tcpdump.org";
+#if !defined(HAVE_GENERATED_VERSION)
        char version[]="current-cvs.tcpdump.org";
+#endif
        char pcap_version[]="current-cvs.tcpdump.org";
        char Wpcap_version[]="3.0 alpha";
 #endif
@@ -1078,7 +1214,7 @@ default_print_unaligned(register const u_char *cp, register u_int length)
 void
 default_print(register const u_char *bp, register u_int length)
 {
-       default_print_unaligned(bp, length);
+    ascii_print("\n\t", bp, length); /* pass on lf and identation string */
 }
 
 #ifdef SIGINFO
@@ -1091,6 +1227,29 @@ RETSIGTYPE requestinfo(int signo _U_)
 }
 #endif
 
+/*
+ * Called once each second in verbose mode while dumping to file
+ */
+#ifdef USE_WIN32_MM_TIMER
+void CALLBACK verbose_stats_dump (UINT timer_id _U_, UINT msg _U_, DWORD_PTR arg _U_,
+                                  DWORD_PTR dw1 _U_, DWORD_PTR dw2 _U_)
+{
+       struct pcap_stat stat;
+
+       if (infodelay == 0 && pcap_stats(pd, &stat) >= 0)
+               fprintf(stderr, "Got %u\r", packets_captured);
+}
+#elif defined(HAVE_ALARM)
+static void verbose_stats_dump(int sig _U_)
+{
+       struct pcap_stat stat;
+
+       if (infodelay == 0 && pcap_stats(pd, &stat) >= 0)
+               fprintf(stderr, "Got %u\r", packets_captured);
+       alarm(1);
+}
+#endif
+
 static void
 usage(void)
 {
@@ -1120,7 +1279,9 @@ usage(void)
        (void)fprintf(stderr,
 "\t\t[ -E algo:secret ] [ -F file ] [ -i interface ] [ -r file ]\n");
        (void)fprintf(stderr,
-"\t\t[ -s snaplen ] [ -T type ] [ -w file ] [ -y datalinktype ]\n");
+"\t\t[ -s snaplen ] [ -T type ] [ -w file ] [ -W filecount ]\n");
+       (void)fprintf(stderr,
+"\t\t[ -y datalinktype ] [ -Z user ]\n");
        (void)fprintf(stderr,
 "\t\t[ expression ]\n");
        exit(1);