From Xiao Xiangquan:
[obnox/wireshark/wip.git] / tap-follow.c
1 /* tap-follow.c
2  *
3  * Copyright 2011, QA Cafe <info@qacafe.com>
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 /* This module provides udp and tcp follow stream capabilities to tshark.
27  * It is only used by tshark and not wireshark.
28  */
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37
38 #include <ctype.h>
39 #include <stdio.h>
40
41 #include <glib.h>
42 #include <epan/addr_resolv.h>
43 #include <epan/epan_dissect.h>
44 #include <epan/follow.h>
45 #include <epan/stat_cmd_args.h>
46 #include <epan/tap.h>
47 #include <epan/tvbuff-int.h>
48
49 #include "wsutil/file_util.h"
50 #include "tempfile.h"
51
52 WS_VAR_IMPORT FILE *data_out_file;
53
54 typedef enum
55 {
56   type_TCP,
57   type_UDP
58 } type_e;
59
60 typedef enum
61 {
62   mode_HEX,
63   mode_ASCII,
64   mode_RAW
65 } mode_e;
66
67 typedef struct
68 {
69   type_e        type;
70   mode_e        mode;
71
72   /* filter */
73   guint32       index;
74   address       addr[2];
75   int           port[2];
76   guint8        addrBuf[2][16];
77
78   /* range */
79   guint32       chunkMin;
80   guint32       chunkMax;
81
82   /* stream chunk file */
83   FILE *        filep;
84   gchar *       filenamep;
85 } follow_t;
86
87 #define STR_FOLLOW      "follow,"
88 #define STR_FOLLOW_TCP  STR_FOLLOW "tcp"
89 #define STR_FOLLOW_UDP  STR_FOLLOW "udp"
90
91 #define STR_HEX         ",hex"
92 #define STR_ASCII       ",ascii"
93 #define STR_RAW         ",raw"
94
95 static void
96 followExit(
97   const char *  strp
98   )
99 {
100   fprintf(stderr, "tshark: follow - %s\n", strp);
101   exit(1);
102 }
103
104 static const char *
105 followStrType(
106   const follow_t *      fp
107   )
108 {
109   switch (fp->type)
110   {
111   case type_TCP:        return "tcp";
112   case type_UDP:        return "udp";
113   }
114
115   g_assert_not_reached();
116
117   return "<unknown-type>";
118 }
119
120 static const char *
121 followStrMode(
122   const follow_t *      fp
123   )
124 {
125   switch (fp->mode)
126   {
127   case mode_HEX:        return "hex";
128   case mode_ASCII:      return "ascii";
129   case mode_RAW:        return "raw";
130   }
131
132   g_assert_not_reached();
133
134   return "<unknown-mode>";
135 }
136
137 static const char *
138 followStrFilter(
139   const follow_t *      fp
140   )
141 {
142   static char   filter[512];
143   int           len     = 0;
144   const gchar * verp;
145   gchar         ip0[MAX_IP6_STR_LEN];
146   gchar         ip1[MAX_IP6_STR_LEN];
147
148   if (fp->index != G_MAXUINT32)
149   {
150     switch (fp->type)
151     {
152     case type_TCP:
153       len = g_snprintf(filter, sizeof filter,
154                      "tcp.stream eq %d", fp->index);
155       break;
156     case type_UDP:
157       break;
158     }
159   }
160   else
161   {
162     verp = fp->addr[0].type == AT_IPv6 ? "v6" : "";
163     address_to_str_buf(&fp->addr[0], ip0, sizeof ip0);
164     address_to_str_buf(&fp->addr[1], ip1, sizeof ip1);
165
166     switch (fp->type)
167     {
168     case type_TCP:
169       len = g_snprintf(filter, sizeof filter,
170                      "((ip%s.src eq %s and tcp.srcport eq %d) and "
171                      "(ip%s.dst eq %s and tcp.dstport eq %d))"
172                      " or "
173                      "((ip%s.src eq %s and tcp.srcport eq %d) and "
174                      "(ip%s.dst eq %s and tcp.dstport eq %d))",
175                      verp, ip0, fp->port[0],
176                      verp, ip1, fp->port[1],
177                      verp, ip1, fp->port[1],
178                      verp, ip0, fp->port[0]);
179       break;
180     case type_UDP:
181       len = g_snprintf(filter, sizeof filter,
182                      "((ip%s.src eq %s and udp.srcport eq %d) and "
183                      "(ip%s.dst eq %s and udp.dstport eq %d))"
184                      " or "
185                      "((ip%s.src eq %s and udp.srcport eq %d) and "
186                      "(ip%s.dst eq %s and udp.dstport eq %d))",
187                      verp, ip0, fp->port[0],
188                      verp, ip1, fp->port[1],
189                      verp, ip1, fp->port[1],
190                      verp, ip0, fp->port[0]);
191       break;
192     }
193   }
194
195   if (len == 0)
196   {
197     followExit("Don't know how to create filter.");
198   }
199
200   if (len == sizeof filter)
201   {
202     followExit("Filter buffer overflow.");
203   }
204
205   return filter;
206 }
207
208 static void
209 followFileClose(
210   follow_t *    fp
211   )
212 {
213   if (fp->filep != NULL)
214   {
215     fclose(fp->filep);
216     fp->filep = NULL;
217     if (fp->type == type_TCP)
218     {
219       data_out_file = NULL;
220     }
221   }
222
223   if (fp->filenamep != NULL)
224   {
225     ws_unlink(fp->filenamep);
226     g_free(fp->filenamep);
227     fp->filenamep = NULL;
228   }
229 }
230
231 static void
232 followFileOpen(
233   follow_t *    fp
234   )
235 {
236   int           fd;
237   char *        tempfilep;
238
239   if (fp->type == type_TCP && data_out_file != NULL)
240   {
241     followExit("Only one TCP stream can be followed at a time.");
242   }
243
244   followFileClose(fp);
245
246   fd = create_tempfile(&tempfilep, "follow");
247   if (fd == -1)
248   {
249     followExit("Error creating temp file.");
250   }
251
252   fp->filenamep = g_strdup(tempfilep);
253   if (fp->filenamep == NULL)
254   {
255     ws_close(fd);
256     ws_unlink(tempfilep);
257     followExit("Error duping temp file name.");
258   }
259
260   fp->filep = fdopen(fd, "w+b");
261   if (fp->filep == NULL)
262   {
263     ws_close(fd);
264     ws_unlink(fp->filenamep);
265     g_free(fp->filenamep);
266     fp->filenamep = NULL;
267     followExit("Error opening temp file stream.");
268   }
269
270   if (fp->type == type_TCP)
271   {
272     data_out_file = fp->filep;
273   }
274 }
275
276 static follow_t *
277 followAlloc(
278   type_e        type
279   )
280 {
281   follow_t *    fp;
282
283   fp = g_malloc0(sizeof *fp);
284
285   fp->type = type;
286   SET_ADDRESS(&fp->addr[0], AT_NONE, 0, fp->addrBuf[0]);
287   SET_ADDRESS(&fp->addr[1], AT_NONE, 0, fp->addrBuf[1]);
288
289   return fp;
290 }
291
292 static void
293 followFree(
294   follow_t *    fp
295   )
296 {
297   followFileClose(fp);
298   g_free(fp);
299 }
300
301 static int
302 followPacket(
303   void *                contextp,
304   packet_info *         pip,
305   epan_dissect_t *      edp _U_,
306   const void *          datap
307   )
308 {
309   follow_t *            fp      = contextp;
310   const tvbuff_t *      tvbp    = datap;
311   tcp_stream_chunk      sc;
312   size_t                size;
313
314   if (tvbp->length > 0)
315   {
316     memcpy(sc.src_addr, pip->net_src.data, pip->net_src.len);
317     sc.src_port = pip->srcport;
318     sc.dlen     = tvbp->length;
319
320     size = fwrite(&sc, 1, sizeof sc, fp->filep);
321     if (sizeof sc != size)
322     {
323       followExit("Error writing stream chunk header.");
324     }
325
326     size = fwrite(tvbp->real_data, 1, sc.dlen, fp->filep);
327     if (sc.dlen != size)
328     {
329       followExit("Error writing stream chunk data.");
330     }
331   }
332
333   return 0;
334 }
335
336 #define BYTES_PER_LINE  16
337 #define OFFSET_START    0
338 #define OFFSET_LEN      8
339 #define OFFSET_SPACE    2
340 #define HEX_START       (OFFSET_START + OFFSET_LEN + OFFSET_SPACE)
341 #define HEX_LEN         (BYTES_PER_LINE * 3)    /* extra space at column 8 */
342 #define HEX_SPACE       2
343 #define ASCII_START     (HEX_START + HEX_LEN + HEX_SPACE)
344 #define ASCII_LEN       (BYTES_PER_LINE + 1)    /* extra space at column 8 */
345 #define ASCII_SPACE     0
346 #define LINE_LEN        (ASCII_START + ASCII_LEN + ASCII_SPACE)
347
348 static const char       bin2hex[] = {'0', '1', '2', '3', '4', '5', '6', '7',
349                                      '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
350 static void
351 followPrintHex(
352   const char *  prefixp,
353   guint32       offset,
354   void *        datap,
355   int           len
356   )
357 {
358   int           ii;
359   int           jj;
360   int           kk;
361   guint8        val;
362   char          line[LINE_LEN + 1];
363
364   for (ii = 0, jj = 0, kk = 0; ii < len; )
365   {
366     if ((ii % BYTES_PER_LINE) == 0)
367     {
368       /* new line */
369       g_snprintf(line, LINE_LEN + 1, "%0*X", OFFSET_LEN, offset);
370       memset(line + HEX_START - OFFSET_SPACE, ' ',
371              HEX_LEN + OFFSET_SPACE + HEX_SPACE);
372
373       /* offset of hex */
374       jj = HEX_START;
375
376       /* offset of ascii */
377       kk = ASCII_START;
378     }
379
380     val = ((guint8 *)datap)[ii];
381
382     line[jj++] = bin2hex[val >> 4];
383     line[jj++] = bin2hex[val & 0xf];
384     jj++;
385
386     line[kk++] = val >= ' ' && val < 0x7f ? val : '.';
387
388     /* extra space at column 8 */
389     if (++ii % BYTES_PER_LINE == BYTES_PER_LINE/2)
390     {
391       line[jj++] = ' ';
392       line[kk++] = ' ';
393     }
394
395     if ((ii % BYTES_PER_LINE) == 0 || ii == len)
396     {
397       /* end of line or buffer */
398       if (line[kk - 1] == ' ')
399       {
400         kk--;
401       }
402       line[kk] = 0;
403       printf("%s%s\n", prefixp, line);
404       offset += BYTES_PER_LINE;
405     }
406   }
407 }
408
409 static void
410 followDraw(
411   void *        contextp
412   )
413 {
414   static const char     seperator[] =
415     "===================================================================\n";
416
417   follow_t *            fp      = contextp;
418   tcp_stream_chunk      sc;
419   int                   node;
420   const address *       addr[2];
421   int                   port[2];
422   gchar                 buf[MAX_IP6_STR_LEN];
423   guint32               ii;
424   guint32               jj;
425   guint32               len;
426   guint32               chunk;
427   guint32               offset[2];
428   guint8                bin[4096];
429   char                  data[(sizeof bin * 2) + 2];
430
431   g_assert(sizeof bin % BYTES_PER_LINE == 0);
432
433   if (fp->type == type_TCP)
434   {
435     follow_stats_t      stats;
436     address_type        type;
437     int                 len;
438
439     follow_stats(&stats);
440
441     if (stats.is_ipv6)
442     {
443       type = AT_IPv6;
444       len  = 16;
445     }
446     else
447     {
448       type = AT_IPv4;
449       len  = 4;
450     }
451
452     for (node = 0; node < 2; node++)
453     {
454       memcpy(fp->addrBuf[node], stats.ip_address[node], len);
455       SET_ADDRESS(&fp->addr[node], type, len, fp->addrBuf[node]);
456       fp->port[node] = stats.port[node];
457     }
458   }
459
460   /* find first stream chunk */
461   rewind(fp->filep);
462   for (chunk = 0;;)
463   {
464     len = (guint32)fread(&sc, 1, sizeof sc, fp->filep);
465     if (len != sizeof sc)
466     {
467       /* no data */
468       sc.dlen = 0;
469       memcpy(sc.src_addr, fp->addr[0].data, fp->addr[0].len) ;
470       sc.src_port = fp->port[0];
471       break;
472     }
473     if (sc.dlen > 0)
474     {
475       chunk++;
476       break;
477     }
478   }
479
480   /* node 0 is source of first chunk with data */
481   if (memcmp(sc.src_addr, fp->addr[0].data, fp->addr[0].len) == 0 &&
482       sc.src_port == fp->port[0])
483   {
484     addr[0] = &fp->addr[0];
485     port[0] = fp->port[0];
486     addr[1] = &fp->addr[1];
487     port[1] = fp->port[1];
488   }
489   else
490   {
491     addr[0] = &fp->addr[1];
492     port[0] = fp->port[1];
493     addr[1] = &fp->addr[0];
494     port[1] = fp->port[0];
495   }
496
497   printf("\n%s", seperator);
498   printf("Follow: %s,%s\n", followStrType(fp), followStrMode(fp));
499   printf("Filter: %s\n", followStrFilter(fp));
500
501   for (node = 0; node < 2; node++)
502   {
503     address_to_str_buf(addr[node], buf, sizeof buf);
504     if (addr[node]->type == AT_IPv6)
505     {
506       printf("Node %u: [%s]:%d\n", node, buf, port[node]);
507     }
508     else
509     {
510       printf("Node %u: %s:%d\n", node, buf, port[node]);
511     }
512   }
513
514   offset[0] = offset[1] = 0;
515
516   while (chunk <= fp->chunkMax)
517   {
518     node = (memcmp(addr[0]->data, sc.src_addr, addr[0]->len) == 0 &&
519             port[0] == sc.src_port) ? 0 : 1;
520
521     if (chunk < fp->chunkMin)
522     {
523       while (sc.dlen > 0)
524       {
525         len = sc.dlen < sizeof bin ? sc.dlen : sizeof bin;
526         sc.dlen -= len;
527         if (fread(bin, 1, len, fp->filep) != len)
528         {
529           followExit("Error reading stream chunk data.");
530         }
531         offset[node] += len;
532       }
533     }
534     else
535     {
536       switch (fp->mode)
537       {
538       case mode_HEX:
539         break;
540
541       case mode_ASCII:
542         printf("%s%d\n", node ? "\t" : "", sc.dlen);
543         break;
544
545       case mode_RAW:
546         if (node)
547         {
548           putchar('\t');
549         }
550         break;
551       }
552
553       while (sc.dlen > 0)
554       {
555         len = sc.dlen < sizeof bin ? sc.dlen : sizeof bin;
556         sc.dlen -= len;
557         if (fread(bin, 1, len, fp->filep) != len)
558         {
559           followExit("Error reading stream chunk data.");
560         }
561
562         switch (fp->mode)
563         {
564         case mode_HEX:
565           followPrintHex(node ? "\t" : "", offset[node], bin, len);
566           break;
567
568         case mode_ASCII:
569           for (ii = 0; ii < len; ii++)
570           {
571             switch (bin[ii])
572             {
573             case '\r':
574             case '\n':
575               data[ii] = bin[ii];
576             break;
577             default:
578               data[ii] = isprint(bin[ii]) ? bin[ii] : '.';
579               break;
580             }
581           }
582           if (sc.dlen == 0)
583           {
584             data[ii++] = '\n';
585           }
586           data[ii] = 0;
587           printf("%s", data);
588           break;
589
590         case mode_RAW:
591           for (ii = 0, jj = 0; ii < len; ii++)
592           {
593             data[jj++] = bin2hex[bin[ii] >> 4];
594             data[jj++] = bin2hex[bin[ii] & 0xf];
595           }
596           if (sc.dlen == 0)
597           {
598             data[jj++] = '\n';
599           }
600           data[jj] = 0;
601           printf("%s", data);
602         }
603
604         offset[node] += len;
605       }
606     }
607
608     for (;;)
609     {
610       len = (guint32)fread(&sc, 1, sizeof sc, fp->filep);
611       if (len != sizeof sc)
612       {
613         /* no more data */
614         sc.dlen = 0;
615         chunk = G_MAXUINT32;
616         goto done;
617       }
618       if (sc.dlen > 0)
619       {
620         chunk++;
621         break;
622       }
623     }
624   }
625
626 done:
627
628   printf("%s", seperator);
629
630   followFileClose(fp);
631 }
632
633 static gboolean
634 followArgStrncmp(
635   const char ** optargp,
636   const char *  strp
637   )
638 {
639   int           len     = (guint32)strlen(strp);
640
641   if (strncmp(*optargp, strp, len) == 0)
642   {
643     *optargp += len;
644     return TRUE;
645   }
646   return FALSE;
647 }
648
649 static void
650 followArgMode(
651   const char ** optargp,
652   follow_t *    fp
653   )
654 {
655   if (followArgStrncmp(optargp, STR_HEX))
656   {
657     fp->mode = mode_HEX;
658   }
659   else if (followArgStrncmp(optargp, STR_ASCII))
660   {
661     fp->mode = mode_ASCII;
662   }
663   else if (followArgStrncmp(optargp, STR_RAW))
664   {
665     fp->mode = mode_RAW;
666   }
667   else
668   {
669     followExit("Invalid display mode.");
670   }
671 }
672
673 static void
674 followArgFilter(
675   const char ** optargp,
676   follow_t *    fp
677   )
678 {
679 #define _STRING(s)      # s
680 #define STRING(s)       _STRING(s)
681
682 #define ADDR_CHARS      80
683 #define ADDR_LEN        (ADDR_CHARS + 1)
684 #define ADDRv6_FMT      ",[%" STRING(ADDR_CHARS) "[^]]]:%d%n"
685 #define ADDRv4_FMT      ",%" STRING(ADDR_CHARS) "[^:]:%d%n"
686
687   int           len;
688   unsigned int  ii;
689   char          addr[ADDR_LEN];
690
691   if (sscanf(*optargp, ",%u%n", &fp->index, &len) == 1 &&
692       ((*optargp)[len] == 0 || (*optargp)[len] == ','))
693   {
694     *optargp += len;
695   }
696   else
697   {
698     for (ii = 0; ii < sizeof fp->addr/sizeof *fp->addr; ii++)
699     {
700       if ((sscanf(*optargp, ADDRv6_FMT, addr, &fp->port[ii], &len) != 2 &&
701            sscanf(*optargp, ADDRv4_FMT, addr, &fp->port[ii], &len) != 2) ||
702           fp->port[ii] <= 0 || fp->port[ii] > G_MAXUINT16)
703       {
704         followExit("Invalid address:port pair.");
705       }
706
707       if (strcmp("ip6", host_ip_af(addr)) == 0)
708       {
709         if (!get_host_ipaddr6(addr, (struct e_in6_addr *)fp->addrBuf[ii]))
710         {
711           followExit("Can't get IPv6 address");
712         }
713         SET_ADDRESS(&fp->addr[ii], AT_IPv6, 16, fp->addrBuf[ii]);
714       }
715       else
716       {
717         if (!get_host_ipaddr(addr, (guint32 *)fp->addrBuf[ii]))
718         {
719           followExit("Can't get IPv4 address");
720         }
721         SET_ADDRESS(&fp->addr[ii], AT_IPv4, 4, fp->addrBuf[ii]);
722       }
723
724       *optargp += len;
725     }
726
727     if (fp->addr[0].type != fp->addr[1].type)
728     {
729       followExit("Mismatched IP address types.");
730     }
731     fp->index = G_MAXUINT32;
732   }
733 }
734
735 static void
736 followArgRange(
737   const char ** optargp,
738   follow_t *    fp
739   )
740 {
741   int           len;
742
743   if (**optargp == 0)
744   {
745     fp->chunkMin = 1;
746     fp->chunkMax = G_MAXUINT32;
747   }
748   else
749   {
750     if (sscanf(*optargp, ",%u-%u%n",  &fp->chunkMin, &fp->chunkMax, &len) == 2)
751     {
752       *optargp += len;
753     }
754     else if (sscanf(*optargp, ",%u%n", &fp->chunkMin, &len) == 1)
755     {
756       fp->chunkMax = fp->chunkMin;
757       *optargp += len;
758     }
759     else
760     {
761       followExit("Invalid range.");
762     }
763
764     if (fp->chunkMin < 1 || fp->chunkMin > fp->chunkMax)
765     {
766       followExit("Invalid range value.");
767     }
768   }
769 }
770
771 static void
772 followArgDone(
773   const char * optarg
774   )
775 {
776   if (*optarg != 0)
777   {
778     followExit("Invalid parameter.");
779   }
780 }
781
782 static void
783 followTcp(
784   const char *  optarg,
785   void *        userdata _U_
786   )
787 {
788   follow_t *    fp;
789   GString *     errp;
790
791   optarg += strlen(STR_FOLLOW_TCP);
792
793   fp = followAlloc(type_TCP);
794
795   followArgMode(&optarg, fp);
796   followArgFilter(&optarg, fp);
797   followArgRange(&optarg, fp);
798   followArgDone(optarg);
799
800   reset_tcp_reassembly();
801   if (fp->index != G_MAXUINT32)
802   {
803     if (!follow_tcp_index(fp->index))
804     {
805       followExit("Can't follow tcp index.");
806     }
807   }
808   else
809   {
810     if (!follow_tcp_addr(&fp->addr[0], fp->port[0],
811                          &fp->addr[1], fp->port[1]))
812     {
813       followExit("Can't follow tcp address/port pairs.");
814     }
815   }
816
817   followFileOpen(fp);
818
819   errp = register_tap_listener("frame", fp, NULL, 0,
820                                NULL, NULL, followDraw);
821   if (errp != NULL)
822   {
823     followFree(fp);
824     g_string_free(errp, TRUE);
825     followExit("Error registering tcp tap listner.");
826   }
827 }
828
829 static void
830 followUdp(
831   const char *  optarg,
832   void *        userdata _U_
833   )
834 {
835   follow_t *    fp;
836   GString *     errp;
837
838   optarg += strlen(STR_FOLLOW_UDP);
839
840   fp = followAlloc(type_UDP);
841
842   followArgMode(&optarg, fp);
843   followArgFilter(&optarg, fp);
844   followArgRange(&optarg, fp);
845   followArgDone(optarg);
846
847   if (fp->index != G_MAXUINT32)
848   {
849     followExit("UDP does not support index filters.");
850   }
851
852   followFileOpen(fp);
853
854   errp = register_tap_listener("udp_follow", fp, followStrFilter(fp), 0,
855                                NULL, followPacket, followDraw);
856   if (errp != NULL)
857   {
858     followFree(fp);
859     g_string_free(errp, TRUE);
860     followExit("Error registering udp tap listner.");
861   }
862 }
863
864 void
865 register_tap_listener_follow(void)
866 {
867   register_stat_cmd_arg(STR_FOLLOW_TCP, followTcp, NULL);
868   register_stat_cmd_arg(STR_FOLLOW_UDP, followUdp, NULL);
869 }