1 : /* parses input from "tcpdump -dd" outputs raw filter,
2 : * Ie. sock filter compiler. Why can't tcpdump do this :( */
3 : #include "ex_utils.h"
4 : #include <limits.h>
5 :
6 : #define CONF_USE_MMAP_DEF FALSE
7 :
8 : /* not in glibc... */
9 : struct sock_filter
10 : {
11 : uint16_t code; /* Actual filter code */
12 : uint8_t jt; /* Jump true */
13 : uint8_t jf; /* Jump false */
14 : uint32_t k; /* Generic multiuse field */
15 : };
16 :
17 : /* not needed here ... see ex_httpd etc. */
18 : struct sock_fprog
19 : {
20 : unsigned short len; /* Number of filter blocks */
21 : struct sock_filter *filter;
22 : };
23 :
24 : /* is the cstr a prefix of the vstr */
25 : #define VPREFIX(vstr, p, l, cstr) \
26 : (((l) >= strlen(cstr)) && vstr_cmp_bod_cstr_eq(vstr, p, l, cstr))
27 :
28 :
29 : #define SOCK_FILTER_OP(x, y, j, k) else if (VPREFIX(s2, pos, len, x)) \
30 : do { \
31 : *num_len = strlen(x); \
32 : *js = (j); \
33 : *ks = (k); \
34 : return (y); \
35 : } while (FALSE)
36 :
37 : /* add ops with X */
38 : #define SOCK_FILTER_OP_PX(x, y, j) \
39 : SOCK_FILTER_OP(x "x", y | 0x8, j, FALSE); \
40 : SOCK_FILTER_OP(x, y, j, TRUE)
41 :
42 : /* human version... */
43 : static unsigned int ex_sock_filter_parse_op(Vstr_base *s2,
44 : size_t pos, size_t len,
45 : size_t *num_len, int *js, int *ks)
46 16 : {
47 16 : size_t tmp = 0;
48 :
49 16 : tmp = vstr_spn_cstr_chrs_fwd(s2, pos, len, " ");
50 16 : len -= tmp; pos += tmp;
51 :
52 : if (0){ } /* FIXME: not all ops */
53 16 : SOCK_FILTER_OP("lda1", 0x30, FALSE, TRUE);
54 16 : SOCK_FILTER_OP("lda2", 0x28, FALSE, TRUE);
55 16 : SOCK_FILTER_OP("lda4", 0x20, FALSE, TRUE);
56 :
57 16 : SOCK_FILTER_OP("ldlx", 0x81, FALSE, FALSE);
58 16 : SOCK_FILTER_OP("ldl", 0x80, FALSE, FALSE);
59 :
60 8 : SOCK_FILTER_OP("ldmx", 0x61, FALSE, TRUE);
61 8 : SOCK_FILTER_OP("ldm", 0x60, FALSE, TRUE);
62 8 : SOCK_FILTER_OP("stmx", 0x03, FALSE, TRUE);
63 8 : SOCK_FILTER_OP("stm", 0x02, FALSE, TRUE);
64 :
65 8 : SOCK_FILTER_OP_PX("add", 0x04, FALSE);
66 8 : SOCK_FILTER_OP_PX("sub", 0x14, FALSE);
67 8 : SOCK_FILTER_OP_PX("mul", 0x24, FALSE);
68 8 : SOCK_FILTER_OP_PX("div", 0x34, FALSE);
69 8 : SOCK_FILTER_OP_PX("or", 0x44, FALSE);
70 8 : SOCK_FILTER_OP_PX("and", 0x54, FALSE);
71 8 : SOCK_FILTER_OP_PX("lsh", 0x64, FALSE);
72 8 : SOCK_FILTER_OP_PX("rsh", 0x74, FALSE);
73 8 : SOCK_FILTER_OP_PX("neg", 0x84, FALSE);
74 :
75 8 : SOCK_FILTER_OP_PX("ja", 0x05, TRUE);
76 8 : SOCK_FILTER_OP_PX("jeq", 0x15, TRUE);
77 8 : SOCK_FILTER_OP_PX("jgt", 0x25, TRUE);
78 8 : SOCK_FILTER_OP_PX("jge", 0x35, TRUE);
79 8 : SOCK_FILTER_OP_PX("jset", 0x45, TRUE);
80 :
81 8 : SOCK_FILTER_OP("tax", 0x07, FALSE, FALSE);
82 8 : SOCK_FILTER_OP("txa", 0x87, FALSE, FALSE);
83 :
84 8 : SOCK_FILTER_OP("reta", 0x16, FALSE, FALSE);
85 0 : SOCK_FILTER_OP("ret", 0x06, FALSE, TRUE);
86 :
87 0 : return (0);
88 : }
89 :
90 : static int ex_sock_filter_process(Vstr_base *s1, Vstr_base *s2)
91 8 : {
92 8 : size_t srch = 0;
93 8 : int ret = FALSE;
94 : struct sock_filter filter[1];
95 : static unsigned int filter_count = 0;
96 : static unsigned int line_count = 0;
97 :
98 : /* we don't want to create more data, if we are over our limit */
99 8 : if (s1->len > EX_MAX_W_DATA_INCORE)
100 0 : return (FALSE);
101 :
102 44 : while ((srch = vstr_srch_chr_fwd(s2, 1, s2->len, '\n')))
103 : {
104 32 : size_t pos = 1;
105 32 : size_t len = srch;
106 32 : size_t num_len = 0;
107 32 : int js = TRUE;
108 32 : int ks = TRUE;
109 : unsigned int num_flags = (VSTR_FLAG_PARSE_NUM_SEP |
110 : VSTR_FLAG_PARSE_NUM_OVERFLOW |
111 : VSTR_FLAG_PARSE_NUM_SPACE |
112 32 : VSTR_FLAG_PARSE_NUM_NO_NEGATIVE);
113 32 : int special_ld_cmd = FALSE;
114 :
115 32 : ++line_count;
116 :
117 32 : if (VPREFIX(s2, pos, len, "#"))
118 : { /* comments */
119 16 : vstr_del(s2, 1, srch);
120 16 : ret = TRUE;
121 16 : continue;
122 : }
123 16 : filter->code = filter->jt = filter->jf = filter->k = 0;
124 :
125 16 : if (filter_count == USHRT_MAX)
126 0 : errno = ENOMEM, err(EXIT_FAILURE, "too many filters");
127 :
128 16 : if (!VPREFIX(s2, pos, len, "{ "))
129 0 : errx(EXIT_FAILURE, "parse error 1, line %u", line_count);
130 16 : len -= strlen("{ "); pos += strlen("{ ");
131 :
132 16 : filter->code = vstr_parse_uint(s2, pos, len, num_flags, &num_len, NULL);
133 16 : len -= num_len; pos += num_len;
134 :
135 16 : if (!num_len)
136 : {
137 16 : filter->code = ex_sock_filter_parse_op(s2, pos, len, &num_len, &js, &ks);
138 16 : len -= num_len; pos += num_len;
139 : }
140 16 : if (!num_len)
141 0 : errx(EXIT_FAILURE, "parse error 2, line %u", line_count);
142 :
143 16 : if ((filter->code == 0x30) || (filter->code == 0x28) ||
144 : (filter->code == 0x20))
145 0 : special_ld_cmd = TRUE;
146 :
147 16 : if (js)
148 : {
149 0 : if (!VPREFIX(s2, pos, len, ", "))
150 0 : errx(EXIT_FAILURE, "parse error 3, line %u", line_count);
151 0 : len -= strlen(", "); pos += strlen(", ");
152 :
153 0 : filter->jt = vstr_parse_uint(s2, pos, len, num_flags, &num_len, NULL);
154 0 : len -= num_len; pos += num_len;
155 :
156 0 : if (!VPREFIX(s2, pos, len, ", "))
157 0 : errx(EXIT_FAILURE, "parse error 4, line %u", line_count);
158 0 : len -= strlen(", "); pos += strlen(", ");
159 :
160 0 : filter->jf = vstr_parse_uint(s2, pos, len, num_flags, &num_len, NULL);
161 0 : len -= num_len; pos += num_len;
162 : }
163 :
164 16 : if (ks)
165 : {
166 0 : if (!VPREFIX(s2, pos, len, ", "))
167 0 : errx(EXIT_FAILURE, "parse error 5, line %u", line_count);
168 0 : len -= strlen(", "); pos += strlen(", ");
169 :
170 0 : num_len = vstr_spn_cstr_chrs_fwd(s2, pos, len, " ");
171 0 : len -= num_len; pos += num_len;
172 :
173 0 : num_len = 0;
174 : /* see linux/filter.h ... magic off sets as we start at TCP */
175 0 : if (!special_ld_cmd) { }
176 0 : else if (VPREFIX(s2, pos, len, "ad:"))
177 : {
178 0 : num_len = strlen("ad:");
179 0 : filter->k = -0x1000;
180 : }
181 0 : else if (VPREFIX(s2, pos, len, "net:"))
182 : {
183 0 : num_len = strlen("net:");
184 0 : filter->k = -0x100000;
185 : }
186 0 : else if (VPREFIX(s2, pos, len, "ll:"))
187 : {
188 0 : num_len = strlen("ll:");
189 0 : filter->k = -0x200000;
190 : }
191 0 : len -= num_len; pos += num_len;
192 :
193 0 : filter->k += vstr_parse_uint(s2, pos, len, num_flags, &num_len, NULL);
194 0 : len -= num_len; pos += num_len;
195 : }
196 :
197 16 : if (!VPREFIX(s2, pos, len, " },"))
198 0 : errx(EXIT_FAILURE, "parse error 6, line %u", line_count);
199 :
200 16 : ++filter_count;
201 16 : vstr_del(s2, 1, srch);
202 16 : ret = TRUE;
203 :
204 16 : if (!vstr_add_buf(s1, s1->len, filter, sizeof(filter)))
205 0 : errno = ENOMEM, err(EXIT_FAILURE, "adding data");
206 :
207 16 : if (s1->len > EX_MAX_W_DATA_INCORE)
208 0 : return (ret);
209 : }
210 :
211 8 : return (ret);
212 : }
213 :
214 : static void ex_sock_filter_process_limit(Vstr_base *s1, Vstr_base *s2,
215 : unsigned int lim)
216 16 : {
217 32 : while (s2->len > lim)
218 : { /* Finish processing read data (try writing if we need memory) */
219 0 : int proc_data = ex_sock_filter_process(s1, s2);
220 :
221 0 : if (!proc_data && (io_put(s1, STDOUT_FILENO) == IO_BLOCK))
222 0 : io_block(-1, STDOUT_FILENO);
223 : }
224 16 : }
225 :
226 : static void ex_sock_filter_read_fd_write_stdout(Vstr_base *s1, Vstr_base *s2,
227 : int fd)
228 16 : {
229 : /* read/process/write loop */
230 : while (TRUE)
231 : {
232 16 : int io_w_state = IO_OK;
233 16 : int io_r_state = io_get(s2, fd);
234 :
235 16 : if (io_r_state == IO_EOF)
236 8 : break;
237 :
238 8 : ex_sock_filter_process(s1, s2);
239 :
240 8 : io_w_state = io_put(s1, STDOUT_FILENO);
241 :
242 8 : io_limit(io_r_state, fd, io_w_state, STDOUT_FILENO, s1);
243 8 : }
244 :
245 : /* write out all of the end of the file,
246 : * so the next file starts on a new line */
247 8 : ex_sock_filter_process_limit(s1, s2, 0);
248 8 : }
249 :
250 : int main(int argc, char *argv[])
251 16 : {
252 16 : Vstr_base *s2 = NULL;
253 16 : Vstr_base *s1 = ex_init(&s2);
254 16 : int count = 1;
255 16 : int use_mmap = CONF_USE_MMAP_DEF;
256 :
257 : /* parse command line arguments... */
258 32 : while (count < argc)
259 : { /* quick hack getopt_long */
260 16 : if (!strcmp("--", argv[count]))
261 : {
262 8 : ++count;
263 8 : break;
264 : }
265 8 : else if (!strcmp("--mmap", argv[count])) /* toggle use of mmap */
266 0 : use_mmap = !use_mmap;
267 8 : else if (!strcmp("--version", argv[count]))
268 : { /* print version and exit */
269 4 : vstr_add_fmt(s1, 0, "%s", "\
270 : jsock_filter 1.0.0\n\
271 : Written by James Antill\n\
272 : \n\
273 : Uses Vstr string library.\n\
274 : ");
275 4 : goto out;
276 : }
277 4 : else if (!strcmp("--help", argv[count]))
278 : { /* print version and exit */
279 4 : vstr_add_fmt(s1, 0, "%s", "\
280 : Usage: jsock_filter [FILENAME]...\n\
281 : or: jsock_filter OPTION\n\
282 : Output filenames.\n\
283 : \n\
284 : --help Display this help and exit\n\
285 : --version Output version information and exit\n\
286 : --mmap Toggle use of mmap() to load input files\n\
287 : -- Treat rest of cmd line as input filenames\n\
288 : \n\
289 : Report bugs to James Antill <james@and.org>.\n\
290 : ");
291 4 : goto out;
292 : }
293 : else
294 0 : break;
295 0 : ++count;
296 : }
297 :
298 : /* if no arguments are given just do stdin to stdout */
299 8 : if (count >= argc)
300 : {
301 0 : io_fd_set_o_nonblock(STDIN_FILENO);
302 0 : ex_sock_filter_read_fd_write_stdout(s1, s2, STDIN_FILENO);
303 : }
304 :
305 : /* loop through all arguments, open the file specified
306 : * and do the read/write loop */
307 20 : while (count < argc)
308 : {
309 8 : unsigned int ern = 0;
310 :
311 8 : if (use_mmap)
312 0 : vstr_sc_mmap_file(s2, s2->len, argv[count], 0, 0, &ern);
313 :
314 12 : if (!use_mmap ||
315 : (ern == VSTR_TYPE_SC_MMAP_FILE_ERR_FSTAT_ERRNO) ||
316 : (ern == VSTR_TYPE_SC_MMAP_FILE_ERR_MMAP_ERRNO) ||
317 : (ern == VSTR_TYPE_SC_MMAP_FILE_ERR_TOO_LARGE))
318 : {
319 8 : int fd = io_open(argv[count]);
320 :
321 8 : ex_sock_filter_read_fd_write_stdout(s1, s2, fd);
322 :
323 8 : if (close(fd) == -1)
324 0 : warn("close(%s)", argv[count]);
325 : }
326 0 : else if (ern && (ern != VSTR_TYPE_SC_MMAP_FILE_ERR_CLOSE_ERRNO))
327 0 : err(EXIT_FAILURE, "add");
328 : else
329 0 : ex_sock_filter_process_limit(s1, s2, EX_MAX_R_DATA_INCORE);
330 :
331 8 : ++count;
332 : }
333 :
334 8 : ex_sock_filter_process_limit(s1, s2, 0);
335 :
336 16 : out:
337 16 : io_put_all(s1, STDOUT_FILENO);
338 :
339 16 : exit (ex_exit(s1, s2));
340 : }
341 :
|