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