First.
[yaz-ziffy.git] / src / ziffy.c
1 /*
2  * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
3  * ziffy.c - a promiscuous Z39.50 APDU sniffer for Ethernet
4  *
5  * Copyright (c) 1998-2001 R. Carbone <rocco@ntop.org>
6  * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21  */
22
23
24 /*
25  * Operating System include files
26  */
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <signal.h>
32
33 #include <time.h>
34 #if HAVE_SYS_TIME_H
35 #include <sys/time.h>
36 #endif
37 #include <sys/utsname.h>
38
39 #include <yaz/options.h>
40
41 #include "pcap.h"               /* Packet Capture Library */
42
43 #include "apdu.h"
44
45
46 /*
47  * external
48  */
49 z3950apdu * pduhook (const struct pcap_pkthdr * h, const u_char * p);
50
51
52 #if defined(HAVE_XASN1)
53 void please_finsiel_help_me (z3950apdu * hook);
54 #endif /* HAVE_XASN1 */
55
56 #if defined(HAVE_YAZ)
57 void please_yaz_help_me (z3950apdu * hook);
58 #endif /* HAVE_YAZ */
59
60 #if defined(HAVE_SNACC)
61 void please_snacc_help_me (z3950apdu * hook);
62 #endif /* HAVE_SNACC */
63
64
65 /*
66  * global variables
67  */
68 time_t now;                           /* current time */
69 time_t start_time;                    /* time the program was started */
70 time_t firstapdu_time;                /* time the first APDU was received */
71 time_t laststapdu_time;               /* time the last APDU was received */
72
73 unsigned long int z3950_apduno = 0;   /* # of z3950 apdus so far received */
74 u_char * z3950   = NULL;              /* pointer to the last apdu received */
75 int z3950_size   = 0;                 /* and its size */
76
77 /*
78  * I currently tested the program at home in a null networked environment
79  * and on ethernet 10M lan. the following variable keeps the data-link
80  * encapsulation type. more info in net/bpf.h
81  */
82 int dlt = -1;
83
84 int aflag     = 0; /* attempt to convert numeric network addresses to FQDN */
85
86 int ethflag   = 0;
87 int ipflag    = 0;
88 int tcpflag   = 0;
89 int z3950flag = 0;
90
91
92 /*
93  * Length of saved portion of packet
94  */
95 #define DEFAULT_SNAPLEN 65536   /* This should be enough... */
96 static int snaplen = DEFAULT_SNAPLEN;
97
98 #define DEFAULT_MAXAPDUS -1     /* that means indefinite */
99 static int maxapdus = DEFAULT_MAXAPDUS;
100
101 /*
102  * A handler for pcap, it needs to be global because there is no other way to
103  * pass it to the signal handler, the same can be said about the file descriptor
104  * for SOCK_PACKET.
105  */
106 pcap_t * ph = NULL;
107
108
109 /*
110  * package info
111  */
112 static char __copyright__   [] = "Copyright (c) 1998-2001";
113 static char __author__      [] = "R. Carbone <rocco@ntop.org>";
114 static char __version__     [] = "Version 0.0.3";
115 static char __released__    [] = "June 2001";
116
117
118 char ebuf [PCAP_ERRBUF_SIZE] = {0};
119 struct pcap_stat pcapstats = {0};
120
121 /*
122  * signal handler
123  */
124 void on_signal (int signo)
125 {
126   /*
127    * time for statistics
128    */
129   if (pcap_stats (ph, & pcapstats) != 0)
130     {
131       printf ("Cannot get the statistics due to %s\n", ebuf),
132       exit (-1);
133     }
134   else
135     {
136       printf ("\n\n");
137
138       printf ("%u packets received by decoder\n", pcapstats . ps_recv);
139       printf ("%u packets dropped by kernel\n", pcapstats . ps_drop);
140     }
141
142   fflush (stdout);
143
144   /*
145    * bye bye !
146    */
147   pcap_close (ph);
148
149   exit (0);
150 }
151
152
153
154 /*
155  * You are welcome!
156  */
157 void welcome (char * progname)
158 {
159   time_t now = ((time_t) time ((time_t *) 0));
160   char * nowstring = ctime (& now);
161   struct utsname machine;
162
163   nowstring [24] = '\0';
164   uname (& machine);
165
166   printf ("This is %s %s of %s\n", progname, __version__, __released__);
167   printf ("%s %s\n", __copyright__, __author__);
168   printf ("Started at %s on %s\n\n", nowstring, machine . nodename);
169   printf ("\n");
170   fflush (stdout);
171   fflush (stderr);
172 }
173
174
175 /*
176  * Wrong. Please try again accordingly to ....
177  */
178 void usage (char * progname)
179 {
180   welcome (progname);
181
182   printf ("Usage: %s [--help] [--version]\n\n", progname);
183   printf ("Options:\n");
184   printf ("        h, --help             display this help and exit\n");
185   printf ("        v, --version          output version information and exit\n");
186
187   printf ("         , --                 print filter code\n");
188   printf ("         , --                 print ethernet header\n");
189   printf ("         , --                 try to resolve ip addresses\n");
190   printf ("         , --                 remove domains from printed host names\n");
191   printf ("         , --                 don't translate _foreign_ IP address\n");
192   printf ("         , --                 print packet arrival time\n");
193
194   printf ("        s, --snaplen          \n");
195   printf ("        N, --non-promiscuous  capture APDUs addressed to the host machine\n");
196   printf ("        C, --maxcount         capture maxcount APDUs and then terminate\n");
197
198   printf ("        D, --dropped-packets  display number of packets dropped during capture\n");
199   fflush (stdout);
200 }
201
202
203 /*
204  * This is really the `main' function of the sniffer.
205  *
206  * Parse the incoming APDU, and when possible show all pertinent data.
207  *
208  * 'h' is the pointer to the packet header (independent from interfaces)
209  * 'p' is the pointer to the packet data
210  * 'caplen' is the number of bytes actually captured
211  * 'length' is the length of the packet off the wire
212  */
213 void parse_pdu (u_char * user_data,
214                 const struct pcap_pkthdr * h,
215                 const u_char * p)
216 {
217   z3950apdu * hook;
218   int done = 0;
219
220   if (! (hook = pduhook (h, p)))
221     return;
222
223   /*
224    * update the descriptor of the apdu
225    */
226   hook -> t = & h -> ts;
227   hook -> calling = srchost ();
228   hook -> srcport = srcport ();
229   hook -> called  = dsthost ();
230   hook -> dstport = dstport ();
231
232 #if defined(HAVE_XASN1)
233   if (! done)
234     please_finsiel_help_me (hook);
235   done = 1;
236 #endif /* HAVE_XASN1 */
237
238 #if defined(HAVE_YAZ)
239   if (! done)
240     please_yaz_help_me (hook);
241   done = 1;
242 #endif /* HAVE_YAZ */
243
244 #if defined(HAVE_SNACC)
245   if (! done)
246     please_snacc_help_me (hook);
247   done = 1;
248 #endif /* HAVE_SNACC */
249 }
250
251
252 /*
253  * Oh no! yet another main here
254  */
255 int main (int argc, char * argv [])
256 {
257   int option;
258   const char * optstr = "hvac:ef:i:lnprs:twxz";
259   char *optarg;
260
261   char * progname;
262
263   char * interface = NULL;
264   char * filename = NULL;
265
266   char * filter = NULL;
267   struct bpf_program program = {0};
268   bpf_u_int32 network = {0};
269   bpf_u_int32 netmask = {0};
270
271
272   /*
273    * notice the program name
274    */
275   progname = strrchr (argv [0], '/');
276   if (! progname || ! * progname)
277     progname = * argv;
278   else
279     progname ++;
280
281   /*
282    * Parse command-line options
283    */
284   while ((option = options(optstr, argv, argc, &optarg)) != -2)
285     {
286       switch (option)
287         {
288         default:
289           usage (progname);
290           return (-1);
291
292         case '?':
293           printf ("%s: unrecognized option %c\n", progname, optopt);
294           usage (progname);
295           return (-1);
296
297         case ':':
298           printf ("%s: missing parameter %c\n", progname, optopt);
299           usage (progname);
300           return (-1);
301
302         case 'h':
303           usage (progname);
304           return (0);
305
306         case 'a':
307           aflag = 1;
308           break;
309
310         case 'c':
311           maxapdus = atoi (optarg);
312           if (maxapdus <= 0)
313             printf ("malformed max apdus counter %s", optarg), maxapdus = DEFAULT_MAXAPDUS;
314           break;
315
316         case 'e':
317           ethflag = 1;
318           break;
319
320         case 'f':
321           filename = strdup (optarg);
322           break;
323
324         case 'i':
325           interface = strdup (optarg);
326           break;
327
328         case 'l':
329           break;
330
331         case 'n':
332           break;
333
334         case 'p':
335           break;
336
337         case 'r':
338           break;
339
340         case 's':
341           snaplen = atoi (optarg);
342           if (snaplen <= 0)
343             printf ("malformed snaplen %s", optarg), snaplen = DEFAULT_SNAPLEN;
344           break;
345
346         case 't':
347           tcpflag = 1;
348           break;
349
350         case 'w':
351           break;
352
353         case 'x':
354           ipflag = 1;
355           break;
356
357         case 'z':
358           z3950flag = 1;
359           break;
360         }
361     }
362
363   /*
364    * You are welcome
365    */
366   welcome (progname);
367
368
369   /*
370    * build a string from all remaining arguments
371    */
372   filter = NULL;
373   {
374     int roomsize = 0;
375     while (optind < argc)
376       {
377         roomsize += (strlen (argv [optind]) + 1 + 1);
378         if (filter)
379           {
380             strcat (filter, " ");
381             filter = (char *) realloc (filter, roomsize);
382             strcat (filter, argv [optind ++]);
383           }
384         else
385           {
386             filter = (char *) malloc (roomsize);
387             strcpy (filter, argv [optind ++]);
388           }
389       }
390   }
391
392
393   /*
394    * find a suitable interface, if i don't have one
395    */
396   if (! filename && ! interface && ! (interface = pcap_lookupdev (ebuf)))
397     {
398       printf ("No suitable interfaces found, please specify one with -i\n");
399       exit (-1);
400     }
401
402
403   if ((getuid () && geteuid ()) || setuid (0))
404     {
405       printf ("Sorry, you must be root in order to run this program.\n");
406       exit (-1);
407     }
408
409   /*
410    * time to initialize the libpcap
411    */
412   ph = filename ? pcap_open_offline (filename, ebuf) :
413     pcap_open_live (interface, snaplen, 1, 1000, ebuf);
414
415   if (! ph)
416     printf ("Cannot initialize the libpcap package due to %s\n", ebuf),
417       exit (-1);
418
419   /*
420    * get the interface network number and its mask
421    * (unless we are reading data from a file)
422    */
423   if (! filename && pcap_lookupnet (interface, & network, & netmask, ebuf) < 0)
424     printf ("Cannot lookup for the network due to %s\n", ebuf),
425       exit (-1);
426
427   /*
428    * determine the type of the underlying network and the data-link encapsulation method
429    * (unless we are reading data from a file)
430    */
431   dlt = pcap_datalink (ph);
432
433   if (! filename && dlt != DLT_NULL && dlt != DLT_IEEE802 && dlt != DLT_EN10MB)
434     printf ("Unsupported data-link encapsulation %d\n", dlt),
435       exit (-1);
436
437   /*
438    * compile an optional filter into a BPF program
439    */
440   if (filter && pcap_compile (ph, & program, filter, 1, netmask) == -1)
441     printf ("Cannot compile the filter %s\n", filter),
442       exit (-1);
443
444   /*
445    * apply the filter to the handler
446    */
447   if (filter && pcap_setfilter (ph, & program) == -1)
448     printf ("Cannot set the filter %s\n", filter),
449       exit (-1);
450
451   /*
452    * announce to the world
453    */
454   printf ("%s %s: listening on %s\n", progname, __version__, interface);
455   fflush (stdout);
456
457   /*
458    * Setup signal handlers
459    */
460   signal (SIGTERM, on_signal);
461   signal (SIGINT, on_signal);
462
463
464   /*
465    * Go for fun! and handle any packet received
466    */
467   if (pcap_loop (ph, -1, parse_pdu, NULL) == -1)
468     printf ("%s: error while capturing packets due to %s\n", progname, pcap_geterr (ph)),
469       exit (-1);
470
471   pcap_close (ph);
472
473
474   return (0);
475 }