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
25 static const char copyright[] _U_ =
26     "@(#) Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000\n\
27 The 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 
54 static char *program_name;
55 
56 /* Forwards */
57 static void PCAP_NORETURN usage(void);
58 static void PCAP_NORETURN error(const char *, ...) PCAP_PRINTFLIKE(1, 2);
59 static void warning(const char *, ...) PCAP_PRINTFLIKE(1, 2);
60 static char *copy_argv(char **);
61 
62 static pcap_t *pd;
63 
64 #ifdef _WIN32
65 static BOOL WINAPI
66 stop_capture(DWORD ctrltype _U_)
67 {
68 	pcap_breakloop(pd);
69 	return TRUE;
70 }
71 #else
72 static void
73 stop_capture(int signum _U_)
74 {
75 	pcap_breakloop(pd);
76 }
77 #endif
78 
79 static long
80 parse_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 
132 static char *
133 find_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 
157 static pcap_t *
158 open_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 
229 int
230 main(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 
480 static void
481 usage(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 */
489 static void
490 error(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 */
508 static void
509 warning(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  */
527 static char *
528 copy_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