Protect pcap_fopen calls
[freeradius.git] / src / main / radsniff.c
index 72214cf..935d2ce 100644 (file)
@@ -39,7 +39,11 @@ static VALUE_PAIR *filter_vps = NULL;
 #define DEBUG if (fr_debug_flag) printf
 
 static int minimal = 0;
+static int do_sort = 0;
+struct timeval start_pcap = {0, 0};
 static rbtree_t *filter_tree = NULL;
+static pcap_dumper_t *pcap_dumper = NULL;
+
 typedef int (*rbcmp)(const void *, const void *);
 
 static int filter_packet(RADIUS_PACKET *packet)
@@ -121,6 +125,63 @@ static int filter_packet(RADIUS_PACKET *packet)
        return 1;
 }
 
+/*
+ *     Bubble goodness
+ */
+static void sort(RADIUS_PACKET *packet)
+{
+       int i, j, size;
+       VALUE_PAIR *vp, *tmp;
+       VALUE_PAIR *array[1024]; /* way more than necessary */
+
+       size = 0;
+       for (vp = packet->vps; vp != NULL; vp = vp->next) {
+               array[size++] = vp;
+       }
+
+       if (size == 0) return;
+
+       for (i = 0; i < size - 1; i++)  {
+               for (j = 0; j < size - 1 - i; j++) {
+                       if (array[j + 1]->attribute < array[j]->attribute)  {
+                               tmp = array[j];         
+                               array[j] = array[j + 1];
+                               array[j + 1] = tmp;
+                       }
+               }
+       }
+
+       /*
+        *      And put them back again.
+        */
+       vp = packet->vps = array[0];
+       for (i = 1; i < size; i++) {
+               vp->next = array[i];
+               vp = array[i];
+       }
+       vp->next = NULL;
+}
+
+#define USEC 1000000
+static void tv_sub(const struct timeval *end, const struct timeval *start,
+                  struct timeval *elapsed)
+{
+       elapsed->tv_sec = end->tv_sec - start->tv_sec;
+       if (elapsed->tv_sec > 0) {
+               elapsed->tv_sec--;
+               elapsed->tv_usec = USEC;
+       } else {
+               elapsed->tv_usec = 0;
+       }
+       elapsed->tv_usec += end->tv_usec;
+       elapsed->tv_usec -= start->tv_usec;
+       
+       if (elapsed->tv_usec >= USEC) {
+               elapsed->tv_usec -= USEC;
+               elapsed->tv_sec++;
+       }
+}
+
 static void got_packet(uint8_t *args, const struct pcap_pkthdr *header, const uint8_t *data)
 {
        /* Just a counter of how many packets we've had */
@@ -136,14 +197,22 @@ static void got_packet(uint8_t *args, const struct pcap_pkthdr *header, const ui
        int size_udp = sizeof(struct udp_header);
        /* For FreeRADIUS */
        RADIUS_PACKET *packet;
+       struct timeval elapsed;
 
        args = args;            /* -Wunused */
 
        /* Define our packet's attributes */
-       ethernet = (const struct ethernet_header*)(data);
-       ip = (const struct ip_header*)(data + size_ethernet);
-       udp = (const struct udp_header*)(data + size_ethernet + size_ip);
-       payload = (const uint8_t *)(data + size_ethernet + size_ip + size_udp);
+
+       if ((data[0] == 2) && (data[1] == 0) &&
+           (data[2] == 0) && (data[3] == 0)) {
+               ip = (const struct ip_header*) (data + 4);
+
+       } else {
+               ethernet = (const struct ethernet_header*)(data);
+               ip = (const struct ip_header*)(data + size_ethernet);
+       }
+       udp = (const struct udp_header*)(((const uint8_t *) ip) + size_ip);
+       payload = (const uint8_t *)(((const uint8_t *) udp) + size_udp);
 
        packet = malloc(sizeof(*packet));
        if (!packet) {
@@ -160,14 +229,14 @@ static void got_packet(uint8_t *args, const struct pcap_pkthdr *header, const ui
        packet->dst_port = ntohs(udp->udp_dport);
 
        packet->data = payload;
-       packet->data_len = header->len - size_ethernet - size_ip - size_udp;
+       packet->data_len = header->len - (payload - data);
 
        if (!rad_packet_ok(packet, 0)) {
-               fr_perror("Packet");
+               printf("Packet: %s\n", fr_strerror());
                
-               fprintf(stderr, "\tFrom:    %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
-               fprintf(stderr, "\tTo:      %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
-               fprintf(stderr, "\tType:    %s\n", fr_packet_codes[packet->code]);
+               printf("\tFrom:    %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
+               printf("\tTo:      %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
+               printf("\tType:    %s\n", fr_packet_codes[packet->code]);
 
                free(packet);
                return;
@@ -187,21 +256,38 @@ static void got_packet(uint8_t *args, const struct pcap_pkthdr *header, const ui
                DEBUG("Packet number %d doesn't match\n", count++);
                return;
        }
+
+       if (pcap_dumper) {
+               pcap_dump((void *) pcap_dumper, header, data);
+               goto check_filter;
+       }
+
        printf("%s Id %d\t", fr_packet_codes[packet->code], packet->id);
 
        /* Print the RADIUS packet */
        printf("%s:%d -> ", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
        printf("%s:%d", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
        if (fr_debug_flag) printf("\t(%d packets)", count++);
-       printf("\t%08x", header->ts.tv_sec);
-       printf("\n");
+
+       if (!start_pcap.tv_sec) {
+               start_pcap = header->ts;
+       }
+
+       tv_sub(&header->ts, &start_pcap, &elapsed);
+
+       printf("\t+%u.%03u", (unsigned int) elapsed.tv_sec,
+              (unsigned int) elapsed.tv_usec / 1000);
+       if (!minimal) printf("\n");
        if (!minimal && packet->vps) {
+               if (do_sort) sort(packet);
+
                vp_printlist(stdout, packet->vps);
                pairfree(&packet->vps);
        }
        printf("\n");
        fflush(stdout);
 
+ check_filter:
        /*
         *      If we're doing filtering, Access-Requests are cached
         *      in the filter tree.
@@ -220,14 +306,18 @@ static void NEVER_RETURNS usage(int status)
        fprintf(output, "options:\n");
        fprintf(output, "\t-c count\tNumber of packets to capture.\n");
        fprintf(output, "\t-d directory\tDirectory where the dictionaries are found\n");
-       fprintf(output, "\t-f filter\tPCAP filter. (default is udp port 1812 or 1813 or 1814)\n");
+       fprintf(output, "\t-F\t\tFilter PCAP file from stdin to stdout.\n");
+       fprintf(output, "\t\t\tOutput file will contain RADIUS packets.\n");
+       fprintf(output, "\t-f filter\tPCAP filter. (default is udp port 1812 or 1813)\n");
        fprintf(output, "\t-h\t\tPrint this help message.\n");
        fprintf(output, "\t-i interface\tInterface to capture.\n");
        fprintf(output, "\t-I filename\tRead packets from filename.\n");
        fprintf(output, "\t-m\t\tPrint packet headers only, not contents.\n");
-       fprintf(output, "\t-p port\tList for packets on port.\n");
+       fprintf(output, "\t-p port\t\tListen for packets on port.\n");
        fprintf(output, "\t-r filter\tRADIUS attribute filter.\n");
        fprintf(output, "\t-s secret\tRADIUS secret.\n");
+       fprintf(output, "\t-S\t\tSort attributes in the packet.\n");
+       fprintf(output, "\t\t\tUsed to compare server results.\n");
        fprintf(output, "\t-x\t\tPrint out debugging information.\n");
        exit(status);
 }
@@ -244,17 +334,19 @@ int main(int argc, char *argv[])
        char *pcap_filter = NULL;
        char *radius_filter = NULL;
        char *filename = NULL;
+       char *dump_file = NULL;
        int packet_count = -1;          /* how many packets to sniff */
        int opt;
        FR_TOKEN parsecode;
        const char *radius_dir = RADIUS_DIR;
        int port = 1812;
+       int filter_stdin = 0;
 
        /* Default device */
        dev = pcap_lookupdev(errbuf);
 
        /* Get options */
-       while ((opt = getopt(argc, argv, "c:d:f:hi:I:mp:r:s:xX")) != EOF) {
+       while ((opt = getopt(argc, argv, "c:d:Ff:hi:I:mp:r:s:Sw:xX")) != EOF) {
                switch (opt)
                {
                case 'c':
@@ -267,6 +359,9 @@ int main(int argc, char *argv[])
                case 'd':
                        radius_dir = optarg;
                        break;
+               case 'F':
+                       filter_stdin = 1;
+                       break;
                case 'f':
                        pcap_filter = optarg;
                        break;
@@ -291,6 +386,12 @@ int main(int argc, char *argv[])
                case 's':
                        radius_secret = optarg;
                        break;
+               case 'S':
+                       do_sort = 1;
+                       break;
+               case 'w':
+                       dump_file = optarg;
+                       break;
                case 'x':
                case 'X':       /* for backwards compatibility */
                        fr_debug_flag++;
@@ -300,16 +401,33 @@ int main(int argc, char *argv[])
                }
        }
 
+       /*
+        *      Cross-check command-line arguments.
+        */
+       if (filter_stdin && (filename || dump_file)) usage(1);
+
+#ifndef HAVE_PCAP_FOPEN_OFFLINE
+       if (filter_stdin) {
+               fr_perror("-F is unsupported on this platform.");
+               return 1;
+       }
+#endif
+
        if (!pcap_filter) {
                pcap_filter = buffer;
-               snprintf(buffer, sizeof(buffer), "udp port %d or %d or %d",
-                        port, port + 1, port + 2);
+               snprintf(buffer, sizeof(buffer), "udp port %d or %d",
+                        port, port + 1);
+       }
+       
+       /*
+        *      There are many times where we don't need the dictionaries.
+        */
+       if (fr_debug_flag || radius_filter) {
+               if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
+                       fr_perror("radsniff");
+                       return 1;
+               }
        }
-
-        if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
-                fr_perror("radsniff");
-                return 1;
-        }
 
        if (radius_filter) {
                parsecode = userparse(radius_filter, &filter_vps);
@@ -351,24 +469,52 @@ int main(int argc, char *argv[])
        /* Open the device so we can spy */
        if (filename) {
                descr = pcap_open_offline(filename, errbuf);
+
+#ifdef HAVE_PCAP_FOPEN_OFFLINE
+       } else if (filter_stdin) {
+               descr = pcap_fopen_offline(stdin, errbuf);
+#endif
+
+       } else if (!dev) {
+               fprintf(stderr, "radsniff: No filename or device was specified.\n");
+               exit(1);
+
        } else {
-               descr = pcap_open_live(dev, SNAPLEN, 1, 0, errbuf);
+               descr = pcap_open_live(dev, 65536, 1, 0, errbuf);
        }
        if (descr == NULL)
        {
-               printf("radsniff: pcap_open_live failed (%s)\n", errbuf);
+               fprintf(stderr, "radsniff: pcap_open_live failed (%s)\n", errbuf);
                exit(1);
        }
 
+       if (dump_file) {
+               pcap_dumper = pcap_dump_open(descr, dump_file);
+               if (!pcap_dumper) {
+                       fprintf(stderr, "radsniff: Failed opening output file (%s)\n", pcap_geterr(descr));
+                       exit(1);
+               }
+
+#ifdef HAVE_PCAP_DUMP_FOPEN
+       } else if (filter_stdin) {
+               pcap_dumper = pcap_dump_fopen(descr, stdout);
+               if (!pcap_dumper) {
+                       fprintf(stderr, "radsniff: Failed opening stdout: %s\n", pcap_geterr(descr));
+                       exit(1);
+               }
+
+#endif
+       }
+
        /* Apply the rules */
        if( pcap_compile(descr, &fp, pcap_filter, 0, netp) == -1)
        {
-               printf("radsniff: pcap_compile failed\n");
+               fprintf(stderr, "radsniff: pcap_compile failed\n");
                exit(1);
        }
        if (pcap_setfilter(descr, &fp) == -1)
        {
-               printf("radsniff: pcap_setfilter failed\n");
+               fprintf(stderr, "radsniff: pcap_setfilter failed\n");
                exit(1);
        }