1 /*	$NetBSD: filtertest.c,v 1.3 2015/03/31 21:39:43 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that: (1) source code distributions
9  * retain the above copyright notice and this paragraph in its entirety, (2)
10  * distributions including binary code include the above copyright notice and
11  * this paragraph in its entirety in the documentation or other materials
12  * provided with the distribution, and (3) all advertising materials mentioning
13  * features or use of this software display the following acknowledgement:
14  * ``This product includes software developed by the University of California,
15  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
16  * the University nor the names of its contributors may be used to endorse
17  * or promote products derived from this software without specific prior
18  * written permission.
19  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
20  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
21  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22  */
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 <sys/cdefs.h>
31 __RCSID("$NetBSD: filtertest.c,v 1.3 2015/03/31 21:39:43 christos Exp $");
32 
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36 
37 #include <pcap.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <stdarg.h>
42 #include <unistd.h>
43 #include <fcntl.h>
44 #include <errno.h>
45 #include <arpa/inet.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 
49 #ifndef HAVE___ATTRIBUTE__
50 #define __attribute__(x)
51 #endif
52 
53 static char *program_name;
54 
55 /* Forwards */
56 static void usage(void) __attribute__((noreturn));
57 static void error(const char *, ...)
58     __attribute__((noreturn, format (printf, 1, 2)));
59 static void warn(const char *, ...)
60     __attribute__((format (printf, 1, 2)));
61 
62 extern int optind;
63 extern int opterr;
64 extern char *optarg;
65 #ifdef BDEBUG
66 int dflag;
67 #endif
68 
69 /*
70  * On Windows, we need to open the file in binary mode, so that
71  * we get all the bytes specified by the size we get from "fstat()".
72  * On UNIX, that's not necessary.  O_BINARY is defined on Windows;
73  * we define it as 0 if it's not defined, so it does nothing.
74  */
75 #ifndef O_BINARY
76 #define O_BINARY	0
77 #endif
78 
79 static char *
80 read_infile(char *fname)
81 {
82 	register int i, fd, cc;
83 	register char *cp;
84 	struct stat buf;
85 
86 	fd = open(fname, O_RDONLY|O_BINARY);
87 	if (fd < 0)
88 		error("can't open %s: %s", fname, pcap_strerror(errno));
89 
90 	if (fstat(fd, &buf) < 0)
91 		error("can't stat %s: %s", fname, pcap_strerror(errno));
92 
93 	cp = malloc((u_int)buf.st_size + 1);
94 	if (cp == NULL)
95 		error("malloc(%d) for %s: %s", (u_int)buf.st_size + 1,
96 			fname, pcap_strerror(errno));
97 	cc = read(fd, cp, (u_int)buf.st_size);
98 	if (cc < 0)
99 		error("read %s: %s", fname, pcap_strerror(errno));
100 	if (cc != buf.st_size)
101 		error("short read %s (%d != %d)", fname, cc, (int)buf.st_size);
102 
103 	close(fd);
104 	/* replace "# comment" with spaces */
105 	for (i = 0; i < cc; i++) {
106 		if (cp[i] == '#')
107 			while (i < cc && cp[i] != '\n')
108 				cp[i++] = ' ';
109 	}
110 	cp[cc] = '\0';
111 	return (cp);
112 }
113 
114 /* VARARGS */
115 static void
116 error(const char *fmt, ...)
117 {
118 	va_list ap;
119 
120 	(void)fprintf(stderr, "%s: ", program_name);
121 	va_start(ap, fmt);
122 	(void)vfprintf(stderr, fmt, ap);
123 	va_end(ap);
124 	if (*fmt) {
125 		fmt += strlen(fmt);
126 		if (fmt[-1] != '\n')
127 			(void)fputc('\n', stderr);
128 	}
129 	exit(1);
130 	/* NOTREACHED */
131 }
132 
133 /* VARARGS */
134 static void
135 warn(const char *fmt, ...)
136 {
137 	va_list ap;
138 
139 	(void)fprintf(stderr, "%s: WARNING: ", program_name);
140 	va_start(ap, fmt);
141 	(void)vfprintf(stderr, fmt, ap);
142 	va_end(ap);
143 	if (*fmt) {
144 		fmt += strlen(fmt);
145 		if (fmt[-1] != '\n')
146 			(void)fputc('\n', stderr);
147 	}
148 }
149 
150 /*
151  * Copy arg vector into a new buffer, concatenating arguments with spaces.
152  */
153 static char *
154 copy_argv(register char **argv)
155 {
156 	register char **p;
157 	register u_int len = 0;
158 	char *buf;
159 	char *src, *dst;
160 
161 	p = argv;
162 	if (*p == 0)
163 		return 0;
164 
165 	while (*p)
166 		len += strlen(*p++) + 1;
167 
168 	buf = (char *)malloc(len);
169 	if (buf == NULL)
170 		error("copy_argv: malloc");
171 
172 	p = argv;
173 	dst = buf;
174 	while ((src = *p++) != NULL) {
175 		while ((*dst++ = *src++) != '\0')
176 			;
177 		dst[-1] = ' ';
178 	}
179 	dst[-1] = '\0';
180 
181 	return buf;
182 }
183 
184 int
185 main(int argc, char **argv)
186 {
187 	char *cp;
188 	int op;
189 #ifndef BDEBUG
190 	int dflag;
191 #endif
192 	char *infile;
193 	int Oflag;
194 	long snaplen;
195 	char *p;
196 	int dlt;
197 	bpf_u_int32 netmask = PCAP_NETMASK_UNKNOWN;
198 	char *cmdbuf;
199 	pcap_t *pd;
200 	struct bpf_program fcode;
201 
202 #ifdef WIN32
203 	if(wsockinit() != 0) return 1;
204 #endif /* WIN32 */
205 
206 #ifndef BDEBUG
207 	dflag = 1;
208 #else
209 	/* if optimizer debugging is enabled, output DOT graph
210 	 * `dflag=4' is equivalent to -dddd to follow -d/-dd/-ddd
211      * convention in tcpdump command line
212 	 */
213 	dflag = 4;
214 #endif
215 	infile = NULL;
216 	Oflag = 1;
217 	snaplen = 68;
218 
219 	if ((cp = strrchr(argv[0], '/')) != NULL)
220 		program_name = cp + 1;
221 	else
222 		program_name = argv[0];
223 
224 	opterr = 0;
225 	while ((op = getopt(argc, argv, "dF:m:Os:")) != -1) {
226 		switch (op) {
227 
228 		case 'd':
229 			++dflag;
230 			break;
231 
232 		case 'F':
233 			infile = optarg;
234 			break;
235 
236 		case 'O':
237 			Oflag = 0;
238 			break;
239 
240 		case 'm': {
241 			in_addr_t addr;
242 
243 			addr = inet_addr(optarg);
244 			if (addr == INADDR_NONE)
245 				error("invalid netmask %s", optarg);
246 			netmask = addr;
247 			break;
248 		}
249 
250 		case 's': {
251 			char *end;
252 
253 			snaplen = strtol(optarg, &end, 0);
254 			if (optarg == end || *end != '\0'
255 			    || snaplen < 0 || snaplen > 65535)
256 				error("invalid snaplen %s", optarg);
257 			else if (snaplen == 0)
258 				snaplen = 65535;
259 			break;
260 		}
261 
262 		default:
263 			usage();
264 			/* NOTREACHED */
265 		}
266 	}
267 
268 	if (optind >= argc) {
269 		usage();
270 		/* NOTREACHED */
271 	}
272 
273 	dlt = pcap_datalink_name_to_val(argv[optind]);
274 	if (dlt < 0) {
275 		dlt = (int)strtol(argv[optind], &p, 10);
276 		if (p == argv[optind] || *p != '\0')
277 			error("invalid data link type %s", argv[optind]);
278 	}
279 
280 	if (infile)
281 		cmdbuf = read_infile(infile);
282 	else
283 		cmdbuf = copy_argv(&argv[optind+1]);
284 
285 	pd = pcap_open_dead(dlt, snaplen);
286 	if (pd == NULL)
287 		error("Can't open fake pcap_t");
288 
289 	if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0)
290 		error("%s", pcap_geterr(pd));
291 
292 	if (!bpf_validate(fcode.bf_insns, fcode.bf_len))
293 		warn("Filter doesn't pass validation");
294 
295 #ifdef BDEBUG
296 	// replace line feed with space
297 	for (cp = cmdbuf; *cp != '\0'; ++cp) {
298 		if (*cp == '\r' || *cp == '\n') {
299 			*cp = ' ';
300 		}
301 	}
302 	// only show machine code if BDEBUG defined, since dflag > 3
303 	printf("machine codes for filter: %s\n", cmdbuf);
304 #endif
305 
306 	bpf_dump(&fcode, dflag);
307 	pcap_close(pd);
308 	exit(0);
309 }
310 
311 static void
312 usage(void)
313 {
314 	(void)fprintf(stderr, "%s, with %s\n", program_name,
315 	    pcap_lib_version());
316 	(void)fprintf(stderr,
317 	    "Usage: %s [-dO] [ -F file ] [ -m netmask] [ -s snaplen ] dlt [ expression ]\n",
318 	    program_name);
319 	exit(1);
320 }
321