1/*
2 * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that: (1) source code distributions
7 * retain the above copyright notice and this paragraph in its entirety, (2)
8 * distributions including binary code include the above copyright notice and
9 * this paragraph in its entirety in the documentation or other materials
10 * provided with the distribution, and (3) all advertising materials mentioning
11 * features or use of this software display the following acknowledgement:
12 * ``This product includes software developed by the University of California,
13 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14 * the University nor the names of its contributors may be used to endorse
15 * or promote products derived from this software without specific prior
16 * written permission.
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 */
21
22#include "varattrs.h"
23
24#ifndef lint
25static const char copyright[] _U_ =
26    "@(#) Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000\n\
27The Regents of the University of California.  All rights reserved.\n";
28#endif
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <stdarg.h>
34#include <limits.h>
35#ifdef _WIN32
36  #include "getopt.h"
37#else
38  #include <unistd.h>
39#endif
40#include <errno.h>
41#ifndef _WIN32
42  #include <signal.h>
43#endif
44#include <sys/types.h>
45
46#include <pcap.h>
47
48#include "pcap/funcattrs.h"
49
50#ifdef _WIN32
51  #include "portability.h"
52#endif
53
54static char *program_name;
55
56/* Forwards */
57static void PCAP_NORETURN usage(void);
58static void PCAP_NORETURN error(const char *, ...) PCAP_PRINTFLIKE(1, 2);
59static void warning(const char *, ...) PCAP_PRINTFLIKE(1, 2);
60static char *copy_argv(char **);
61
62static pcap_t *pd;
63
64#ifdef _WIN32
65static BOOL WINAPI
66stop_capture(DWORD ctrltype _U_)
67{
68	pcap_breakloop(pd);
69	return TRUE;
70}
71#else
72static void
73stop_capture(int signum _U_)
74{
75	pcap_breakloop(pd);
76}
77#endif
78
79static long
80parse_interface_number(const char *device)
81{
82	const char *p;
83	long devnum;
84	char *end;
85
86	/*
87	 * Search for a colon, terminating any scheme at the beginning
88	 * of the device.
89	 */
90	p = strchr(device, ':');
91	if (p != NULL) {
92		/*
93		 * We found it.  Is it followed by "//"?
94		 */
95		p++;	/* skip the : */
96		if (strncmp(p, "//", 2) == 0) {
97			/*
98			 * Yes.  Search for the next /, at the end of the
99			 * authority part of the URL.
100			 */
101			p += 2;	/* skip the // */
102			p = strchr(p, '/');
103			if (p != NULL) {
104				/*
105				 * OK, past the / is the path.
106				 */
107				device = p + 1;
108			}
109		}
110	}
111	devnum = strtol(device, &end, 10);
112	if (device != end && *end == '\0') {
113		/*
114		 * It's all-numeric, but is it a valid number?
115		 */
116		if (devnum <= 0) {
117			/*
118			 * No, it's not an ordinal.
119			 */
120			error("Invalid adapter index");
121		}
122		return (devnum);
123	} else {
124		/*
125		 * It's not all-numeric; return -1, so our caller
126		 * knows that.
127		 */
128		return (-1);
129	}
130}
131
132static char *
133find_interface_by_number(long devnum)
134{
135	pcap_if_t *dev, *devlist;
136	long i;
137	char ebuf[PCAP_ERRBUF_SIZE];
138	char *device;
139	int status;
140
141	status = pcap_findalldevs(&devlist, ebuf);
142	if (status < 0)
143		error("%s", ebuf);
144	/*
145	 * Look for the devnum-th entry in the list of devices (1-based).
146	 */
147	for (i = 0, dev = devlist; i < devnum-1 && dev != NULL;
148	    i++, dev = dev->next)
149		;
150	if (dev == NULL)
151		error("Invalid adapter index");
152	device = strdup(dev->name);
153	pcap_freealldevs(devlist);
154	return (device);
155}
156
157static pcap_t *
158open_interface(const char *device, int snaplen_set, int snaplen, char *ebuf)
159{
160	pcap_t *pc;
161	int status;
162	char *cp;
163
164	pc = pcap_create(device, ebuf);
165	if (pc == NULL) {
166		/*
167		 * If this failed with "No such device", that means
168		 * the interface doesn't exist; return NULL, so that
169		 * the caller can see whether the device name is
170		 * actually an interface index.
171		 */
172		if (strstr(ebuf, "No such device") != NULL)
173			return (NULL);
174		error("%s", ebuf);
175	}
176	if (snaplen_set) {
177		status = pcap_set_snaplen(pc, snaplen);
178		if (status != 0)
179			error("%s: pcap_set_snaplen failed: %s",
180			    device, pcap_statustostr(status));
181	}
182	status = pcap_set_timeout(pc, 100);
183	if (status != 0)
184		error("%s: pcap_set_timeout failed: %s",
185		    device, pcap_statustostr(status));
186	status = pcap_activate(pc);
187	if (status < 0) {
188		/*
189		 * pcap_activate() failed.
190		 */
191		cp = pcap_geterr(pc);
192		if (status == PCAP_ERROR)
193			error("%s", cp);
194		else if (status == PCAP_ERROR_NO_SUCH_DEVICE) {
195			/*
196			 * Return an error for our caller to handle.
197			 */
198			snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s: %s\n(%s)",
199			    device, pcap_statustostr(status), cp);
200		} else if (status == PCAP_ERROR_PERM_DENIED && *cp != '\0')
201			error("%s: %s\n(%s)", device,
202			    pcap_statustostr(status), cp);
203		else
204			error("%s: %s", device,
205			    pcap_statustostr(status));
206		pcap_close(pc);
207		return (NULL);
208	} else if (status > 0) {
209		/*
210		 * pcap_activate() succeeded, but it's warning us
211		 * of a problem it had.
212		 */
213		cp = pcap_geterr(pc);
214		if (status == PCAP_WARNING)
215			warning("%s", cp);
216		else if (status == PCAP_WARNING_PROMISC_NOTSUP &&
217		         *cp != '\0')
218			warning("%s: %s\n(%s)", device,
219			    pcap_statustostr(status), cp);
220		else
221			warning("%s: %s", device,
222			    pcap_statustostr(status));
223	}
224	return (pc);
225}
226
227#define COMMAND_OPTIONS	"DLi:s:w:y:"
228
229int
230main(int argc, char **argv)
231{
232	int op;
233	char *cp, *cmdbuf = NULL, *device, *end, *savefile = NULL;
234	int snaplen = 0;
235	int snaplen_set = 0;
236	pcap_if_t *devlist;
237	long devnum;
238	int show_interfaces = 0;
239	int show_dlt_types = 0;
240	int ndlts;
241	int *dlts;
242	bpf_u_int32 localnet, netmask;
243	struct bpf_program fcode;
244	char ebuf[PCAP_ERRBUF_SIZE];
245#ifndef _WIN32
246	struct sigaction action;
247#endif
248	int dlt;
249	const char *dlt_name = NULL;
250	int status;
251	pcap_dumper_t *pdd;
252
253	device = NULL;
254	if ((cp = strrchr(argv[0], '/')) != NULL)
255		program_name = cp + 1;
256	else
257		program_name = argv[0];
258
259	opterr = 0;
260	while ((op = getopt(argc, argv, COMMAND_OPTIONS)) != -1) {
261		switch (op) {
262
263		case 'D':
264			show_interfaces = 1;
265			break;
266
267		case 'L':
268			show_dlt_types = 1;
269			break;
270
271		case 'i':
272			device = optarg;
273			break;
274
275		case 's':
276			snaplen = (int)strtol(optarg, &end, 0);
277			if (optarg == end || *end != '\0' || snaplen < 0)
278				error("invalid snaplen %s (must be >= 0)",
279				    optarg);
280			snaplen_set = 1;
281			break;
282
283		case 'w':
284			savefile = optarg;
285			break;
286
287		case 'y':
288			dlt_name = optarg;
289			break;
290
291		default:
292			usage();
293			/* NOTREACHED */
294		}
295	}
296
297	if (show_interfaces) {
298		pcap_if_t *dev;
299		int i;
300
301		if (pcap_findalldevs(&devlist, ebuf) < 0)
302			error("%s", ebuf);
303		for (i = 0, dev = devlist; dev != NULL; i++, dev = dev->next) {
304			printf("%d.%s", i+1, dev->name);
305			if (dev->description != NULL)
306				printf(" (%s)", dev->description);
307			printf("\n");
308		}
309		pcap_freealldevs(devlist);
310		return (0);
311	}
312
313	if (device == NULL) {
314		if (pcap_findalldevs(&devlist, ebuf) == -1)
315			error("%s", ebuf);
316		if (devlist == NULL)
317			error("no interfaces available for capture");
318		device = strdup(devlist->name);
319		pcap_freealldevs(devlist);
320	}
321	if (show_dlt_types) {
322		pd = pcap_create(device, ebuf);
323		if (pd == NULL)
324			error("%s", ebuf);
325		status = pcap_activate(pd);
326		if (status < 0) {
327			/*
328			 * pcap_activate() failed.
329			 */
330			error("%s: %s\n(%s)", device,
331			    pcap_statustostr(status), pcap_geterr(pd));
332		}
333		ndlts = pcap_list_datalinks(pd, &dlts);
334		if (ndlts < 0) {
335			/*
336			 * pcap_list_datalinks() failed.
337			 */
338			error("%s: %s\n(%s)", device,
339			    pcap_statustostr(status), pcap_geterr(pd));
340		}
341		for (int i = 0; i < ndlts; i++) {
342			dlt_name = pcap_datalink_val_to_name(dlts[i]);
343			if (dlt_name == NULL)
344				printf("DLT %d", dlts[i]);
345			else
346				printf("%s", dlt_name);
347			printf("\n");
348		}
349		pcap_free_datalinks(dlts);
350		pcap_close(pd);
351		return 0;
352	}
353
354	if (savefile == NULL)
355		error("no savefile specified");
356
357	*ebuf = '\0';
358
359	pd = open_interface(device, snaplen_set, snaplen, ebuf);
360	if (pd == NULL) {
361		/*
362		 * That failed because the interface couldn't be found.
363		 *
364		 * If we can get a list of interfaces, and the interface name
365		 * is purely numeric, try to use it as a 1-based index
366		 * in the list of interfaces.
367		 */
368		devnum = parse_interface_number(device);
369		if (devnum == -1) {
370			/*
371			 * It's not a number; just report
372			 * the open error and fail.
373			 */
374			error("%s", ebuf);
375		}
376
377		/*
378		 * OK, it's a number; try to find the
379		 * interface with that index, and try
380		 * to open it.
381		 *
382		 * find_interface_by_number() exits if it
383		 * couldn't be found.
384		 */
385		device = find_interface_by_number(devnum);
386		pd = open_interface(device, snaplen_set, snaplen, ebuf);
387		if (pd == NULL)
388			error("%s", ebuf);
389	}
390
391	if (pcap_lookupnet(device, &localnet, &netmask, ebuf) < 0) {
392		localnet = 0;
393		netmask = 0;
394		warning("%s", ebuf);
395	}
396
397	if (dlt_name != NULL) {
398		dlt = pcap_datalink_name_to_val(dlt_name);
399		if (dlt == PCAP_ERROR)
400			error("%s isn't a valid DLT name", dlt_name);
401		if (pcap_set_datalink(pd, dlt) == PCAP_ERROR)
402			error("%s: %s", device, pcap_geterr(pd));
403	}
404
405	/*
406	 * Don't set a filter unless we were given one on the
407	 * command line; if capturing doesn't work, or doesn't
408	 * use the snapshot length, without a filter, that's
409	 * a bug.
410	 */
411	if (optind < argc) {
412		cmdbuf = copy_argv(&argv[optind]);
413
414		if (pcap_compile(pd, &fcode, cmdbuf, 1, netmask) < 0)
415			error("%s", pcap_geterr(pd));
416
417		if (pcap_setfilter(pd, &fcode) < 0)
418			error("%s", pcap_geterr(pd));
419	}
420
421	pdd = pcap_dump_open(pd, savefile);
422	if (pdd == NULL)
423		error("%s", pcap_geterr(pd));
424
425#ifdef _WIN32
426	SetConsoleCtrlHandler(stop_capture, TRUE);
427#else
428	action.sa_handler = stop_capture;
429	sigemptyset(&action.sa_mask);
430	action.sa_flags = 0;
431	if (sigaction(SIGINT, &action, NULL) == -1)
432		error("Can't catch SIGINT: %s\n", strerror(errno));
433#endif
434
435	printf("Listening on %s, link-type ", device);
436	dlt = pcap_datalink(pd);
437	dlt_name = pcap_datalink_val_to_name(dlt);
438	if (dlt_name == NULL)
439		printf("DLT %d", dlt);
440	else
441		printf("%s", dlt_name);
442	printf("\n");
443	for (;;) {
444		status = pcap_dispatch(pd, -1, pcap_dump, (u_char *)pdd);
445		if (status < 0)
446			break;
447		if (status != 0) {
448			printf("%d packets seen\n", status);
449			struct pcap_stat ps;
450			pcap_stats(pd, &ps);
451			printf("%d ps_recv, %d ps_drop, %d ps_ifdrop\n",
452			    ps.ps_recv, ps.ps_drop, ps.ps_ifdrop);
453		}
454	}
455	if (status == -2) {
456		/*
457		 * We got interrupted, so perhaps we didn't
458		 * manage to finish a line we were printing.
459		 * Print an extra newline, just in case.
460		 */
461		putchar('\n');
462		printf("Broken out of loop from SIGINT handler\n");
463	}
464	(void)fflush(stdout);
465	if (status == -1) {
466		/*
467		 * Error.  Report it.
468		 */
469		(void)fprintf(stderr, "%s: pcap_dispatch: %s\n",
470		    program_name, pcap_geterr(pd));
471	}
472	pcap_close(pd);
473	if (cmdbuf != NULL) {
474		pcap_freecode(&fcode);
475		free(cmdbuf);
476	}
477	exit(status == -1 ? 1 : 0);
478}
479
480static void
481usage(void)
482{
483	(void)fprintf(stderr, "Usage: %s -D -L [ -i interface ] [ -s snaplen ] [ -w file ] [ -y dlt ] [expression]\n",
484	    program_name);
485	exit(1);
486}
487
488/* VARARGS */
489static void
490error(const char *fmt, ...)
491{
492	va_list ap;
493
494	(void)fprintf(stderr, "%s: ", program_name);
495	va_start(ap, fmt);
496	(void)vfprintf(stderr, fmt, ap);
497	va_end(ap);
498	if (*fmt) {
499		fmt += strlen(fmt);
500		if (fmt[-1] != '\n')
501			(void)fputc('\n', stderr);
502	}
503	exit(1);
504	/* NOTREACHED */
505}
506
507/* VARARGS */
508static void
509warning(const char *fmt, ...)
510{
511	va_list ap;
512
513	(void)fprintf(stderr, "%s: WARNING: ", program_name);
514	va_start(ap, fmt);
515	(void)vfprintf(stderr, fmt, ap);
516	va_end(ap);
517	if (*fmt) {
518		fmt += strlen(fmt);
519		if (fmt[-1] != '\n')
520			(void)fputc('\n', stderr);
521	}
522}
523
524/*
525 * Copy arg vector into a new buffer, concatenating arguments with spaces.
526 */
527static char *
528copy_argv(register char **argv)
529{
530	register char **p;
531	register size_t len = 0;
532	char *buf;
533	char *src, *dst;
534
535	p = argv;
536	if (*p == 0)
537		return 0;
538
539	while (*p)
540		len += strlen(*p++) + 1;
541
542	buf = (char *)malloc(len);
543	if (buf == NULL)
544		error("copy_argv: malloc");
545
546	p = argv;
547	dst = buf;
548	while ((src = *p++) != NULL) {
549		while ((*dst++ = *src++) != '\0')
550			;
551		dst[-1] = ' ';
552	}
553	dst[-1] = '\0';
554
555	return buf;
556}
557