1 /*
2 * Copyright (c) 2016-2021, OARC, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 *
17 * 3. Neither the name of the copyright holder nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include "config.h"
36
37 #include "dumper.h"
38 #include "iaddr.h"
39 #include "log.h"
40 #include "pcaps.h"
41
42 /*
43 * when flags & DNSCAP_OUTPUT_ISDNS, payload points to a DNS packet
44 */
output(const char * descr,iaddr from,iaddr to,uint8_t proto,unsigned flags,unsigned sport,unsigned dport,my_bpftimeval ts,const u_char * pkt_copy,const unsigned olen,const u_char * payload,const unsigned payloadlen)45 void output(const char* descr, iaddr from, iaddr to, uint8_t proto, unsigned flags,
46 unsigned sport, unsigned dport, my_bpftimeval ts,
47 const u_char* pkt_copy, const unsigned olen,
48 const u_char* payload, const unsigned payloadlen)
49 {
50 struct plugin* p;
51
52 for (p = HEAD(plugins); p != NULL; p = NEXT(p, link)) {
53 if (p->filter && (*p->filter)(descr, &from, &to, proto, flags, sport, dport, ts, pkt_copy, olen, payload, payloadlen)) {
54 if (dumptrace >= 3) {
55 fprintf(stderr, "filtered: capturedbytes=%zu, proto=%d, isfrag=%s, isdns=%s, olen=%u, payloadlen=%u\n",
56 capturedbytes,
57 proto,
58 flags & DNSCAP_OUTPUT_ISFRAG ? "yes" : "no",
59 flags & DNSCAP_OUTPUT_ISDNS ? "yes" : "no",
60 olen,
61 payloadlen);
62 }
63 return;
64 }
65 }
66
67 msgcount++;
68 capturedbytes += olen;
69
70 if (dumptrace >= 3) {
71 fprintf(stderr, "output: capturedbytes=%zu, proto=%d, isfrag=%s, isdns=%s, olen=%u, payloadlen=%u\n",
72 capturedbytes,
73 proto,
74 flags & DNSCAP_OUTPUT_ISFRAG ? "yes" : "no",
75 flags & DNSCAP_OUTPUT_ISDNS ? "yes" : "no",
76 olen,
77 payloadlen);
78 }
79
80 /* Output stage. */
81 if (preso) {
82 fputs(descr, stderr);
83 if (flags & DNSCAP_OUTPUT_ISFRAG) {
84 fprintf(stderr, ";: [%s] ", ia_str(from));
85 fprintf(stderr, "-> [%s] (frag)\n", ia_str(to));
86 } else {
87 fprintf(stderr, "\t[%s].%u ", ia_str(from), sport);
88 fprintf(stderr, "[%s].%u ", ia_str(to), dport);
89 if ((flags & DNSCAP_OUTPUT_ISDNS) && payload)
90 dump_dns(payload, payloadlen, stderr, "\\\n\t");
91 }
92 putc('\n', stderr);
93 }
94 if (dump_type != nowhere) {
95 if (options.dump_format == pcap) {
96 struct pcap_pkthdr h;
97
98 memset(&h, 0, sizeof h);
99 h.ts = ts;
100 h.len = h.caplen = olen;
101 pcap_dump((u_char*)dumper, &h, pkt_copy);
102 if (flush)
103 pcap_dump_flush(dumper);
104 } else if (options.dump_format == cbor && (flags & DNSCAP_OUTPUT_ISDNS) && payload) {
105 int ret = output_cbor(from, to, proto, flags, sport, dport, ts, payload, payloadlen);
106
107 if (ret == DUMP_CBOR_FLUSH) {
108 if (dumper_close(ts)) {
109 fprintf(stderr, "%s: dumper_close() failed\n", ProgramName);
110 exit(1);
111 }
112 if (dumper_open(ts)) {
113 fprintf(stderr, "%s: dumper_open() failed\n", ProgramName);
114 exit(1);
115 }
116 } else if (ret != DUMP_CBOR_OK) {
117 fprintf(stderr, "%s: output to cbor failed [%u]\n", ProgramName, ret);
118 exit(1);
119 }
120 } else if (options.dump_format == cds) {
121 int ret = output_cds(from, to, proto, flags, sport, dport, ts, pkt_copy, olen, payload, payloadlen);
122
123 if (ret == DUMP_CDS_FLUSH) {
124 if (dumper_close(ts)) {
125 fprintf(stderr, "%s: dumper_close() failed\n", ProgramName);
126 exit(1);
127 }
128 if (dumper_open(ts)) {
129 fprintf(stderr, "%s: dumper_open() failed\n", ProgramName);
130 exit(1);
131 }
132 } else if (ret != DUMP_CDS_OK) {
133 fprintf(stderr, "%s: output to cds failed [%u]\n", ProgramName, ret);
134 exit(1);
135 }
136 }
137 }
138 for (p = HEAD(plugins); p != NULL; p = NEXT(p, link))
139 if (p->output)
140 (*p->output)(descr, from, to, proto, flags, sport, dport, ts, pkt_copy, olen, payload, payloadlen);
141 return;
142 }
143
dumper_open(my_bpftimeval ts)144 int dumper_open(my_bpftimeval ts)
145 {
146 const char* t = NULL;
147 struct plugin* p;
148
149 assert(dump_state == dumper_closed);
150
151 while (ts.tv_usec >= MILLION) {
152 ts.tv_sec++;
153 ts.tv_usec -= MILLION;
154 }
155 if (limit_seconds != 0U)
156 next_interval = ts.tv_sec
157 - (ts.tv_sec % limit_seconds)
158 + limit_seconds;
159
160 if (dump_type == to_stdout) {
161 t = "-";
162 } else if (dump_type == to_file) {
163 char sbuf[64];
164 struct tm tm;
165
166 gmtime_r((time_t*)&ts.tv_sec, &tm);
167 strftime(sbuf, 64, "%Y%m%d.%H%M%S", &tm);
168 if (asprintf(&dumpname, "%s.%s.%06lu%s",
169 dump_base, sbuf,
170 (u_long)ts.tv_usec, dump_suffix ? dump_suffix : "")
171 < 0
172 || asprintf(&dumpnamepart, "%s.part", dumpname) < 0) {
173 logerr("asprintf: %s", strerror(errno));
174 return (TRUE);
175 }
176 t = dumpnamepart;
177 }
178 if (NULL != t) {
179 if (options.dump_format == pcap) {
180 dumper = dnscap_pcap_dump_open(pcap_dead, t);
181 if (dumper == NULL) {
182 logerr("pcap dump open: %s",
183 pcap_geterr(pcap_dead));
184 return (TRUE);
185 }
186 }
187 }
188 dumpstart = ts.tv_sec;
189 if (limit_seconds != 0U) {
190 struct timeval now;
191 u_int seconds;
192 time_t targ;
193
194 gettimeofday(&now, NULL);
195 while (now.tv_usec >= MILLION) {
196 now.tv_sec++;
197 now.tv_usec -= MILLION;
198 }
199 targ = (((now.tv_sec + (limit_seconds / 2))
200 / limit_seconds)
201 + 1)
202 * limit_seconds;
203 assert(targ > now.tv_sec);
204 seconds = targ - now.tv_sec;
205 if (next_interval == 0) {
206 alarm(seconds);
207 alarm_set = TRUE;
208 }
209 }
210 for (p = HEAD(plugins); p != NULL; p = NEXT(p, link)) {
211 int x;
212 if (!p->open)
213 continue;
214 x = (*p->open)(ts);
215 if (0 == x)
216 continue;
217 logerr("%s_open returned %d", p->name, x);
218 }
219 dump_state = dumper_opened;
220 return (FALSE);
221 }
222
dumper_close(my_bpftimeval ts)223 int dumper_close(my_bpftimeval ts)
224 {
225 int ret = FALSE;
226 struct plugin* p;
227
228 assert(dump_state == dumper_opened);
229
230 if (print_pcap_stats)
231 do_pcap_stats();
232
233 if (alarm_set) {
234 alarm(0);
235 alarm_set = FALSE;
236 }
237
238 if (options.dump_format == pcap) {
239 if (dumper) {
240 pcap_dump_close(dumper);
241 dumper = FALSE;
242 }
243 } else if (options.dump_format == cbor) {
244 int ret;
245
246 if (dump_type == to_stdout) {
247 ret = dump_cbor(stdout);
248
249 if (ret != DUMP_CBOR_OK) {
250 fprintf(stderr, "%s: output to cbor failed [%u]\n", ProgramName, ret);
251 exit(1);
252 }
253 } else if (dump_type == to_file) {
254 FILE* fp;
255
256 if (!(fp = fopen(dumpnamepart, "w"))) {
257 fprintf(stderr, "%s: fopen(%s) failed: %s\n", ProgramName, dumpnamepart, strerror(errno));
258 exit(1);
259 }
260 ret = dump_cbor(fp);
261 fclose(fp);
262 if (ret != DUMP_CBOR_OK) {
263 fprintf(stderr, "%s: output to cbor failed [%u]\n", ProgramName, ret);
264 exit(1);
265 }
266 }
267 } else if (options.dump_format == cds) {
268 int ret;
269
270 if (dump_type == to_stdout) {
271 ret = dump_cds(stdout);
272
273 if (ret != DUMP_CDS_OK) {
274 fprintf(stderr, "%s: output to cds failed [%u]\n", ProgramName, ret);
275 exit(1);
276 }
277 } else if (dump_type == to_file) {
278 FILE* fp;
279
280 if (!(fp = fopen(dumpnamepart, "w"))) {
281 fprintf(stderr, "%s: fopen(%s) failed: %s\n", ProgramName, dumpnamepart, strerror(errno));
282 exit(1);
283 }
284 ret = dump_cds(fp);
285 fclose(fp);
286 if (ret != DUMP_CDS_OK) {
287 fprintf(stderr, "%s: output to cds failed [%u]\n", ProgramName, ret);
288 exit(1);
289 }
290 }
291 }
292
293 if (dump_type == to_stdout) {
294 assert(dumpname == NULL);
295 assert(dumpnamepart == NULL);
296 if (dumptrace >= 1)
297 fprintf(stderr, "%s: breaking\n", ProgramName);
298 ret = TRUE;
299 } else if (dump_type == to_file) {
300 char* cmd = NULL;
301 ;
302
303 if (dumptrace >= 1)
304 fprintf(stderr, "%s: closing %s\n",
305 ProgramName, dumpname);
306 if (rename(dumpnamepart, dumpname)) {
307 logerr("rename: %s", strerror(errno));
308 return ret;
309 }
310 if (kick_cmd != NULL)
311 if (asprintf(&cmd, "%s %s &", kick_cmd, dumpname) < 0) {
312 logerr("asprintf: %s", strerror(errno));
313 cmd = NULL;
314 }
315 free(dumpnamepart);
316 dumpnamepart = NULL;
317 free(dumpname);
318 dumpname = NULL;
319 if (cmd != NULL) {
320 int x = system(cmd);
321 if (x)
322 logerr("system: \"%s\" returned %d", cmd, x);
323 free(cmd);
324 }
325 if (kick_cmd == NULL && options.dump_format != cbor && options.dump_format != cds)
326 ret = TRUE;
327 }
328 for (p = HEAD(plugins); p != NULL; p = NEXT(p, link)) {
329 int x;
330 if (!p->close)
331 continue;
332 x = (*p->close)(ts);
333 if (x)
334 logerr("%s_close returned %d", p->name, x);
335 }
336 dump_state = dumper_closed;
337 return (ret);
338 }
339
340 #if HAVE_ZLIB_H
341 #if HAVE_FUNOPEN
342 static int
gzip_cookie_write(void * cookie,const char * buf,int size)343 gzip_cookie_write(void* cookie, const char* buf, int size)
344 {
345 return gzwrite((gzFile)cookie, (voidpc)buf, (unsigned)size);
346 }
347 #elif HAVE_FOPENCOOKIE
348 static ssize_t
gzip_cookie_write(void * cookie,const char * buf,size_t size)349 gzip_cookie_write(void* cookie, const char* buf, size_t size)
350 {
351 return gzwrite((gzFile)cookie, (voidpc)buf, (unsigned)size);
352 }
353 #endif
354
355 static int
gzip_cookie_close(void * cookie)356 gzip_cookie_close(void* cookie)
357 {
358 return gzclose((gzFile)cookie);
359 }
360 #endif /* HAVE_ZLIB_H */
361
dnscap_pcap_dump_open(pcap_t * pcap,const char * path)362 pcap_dumper_t* dnscap_pcap_dump_open(pcap_t* pcap, const char* path)
363 {
364 #if HAVE_ZLIB_H
365 #if HAVE_GZOPEN
366 if (wantgzip) {
367 FILE* fp = NULL;
368 gzFile z = gzopen(path, "w");
369 if (z == NULL) {
370 perror("gzopen");
371 return NULL;
372 }
373
374 #if HAVE_FUNOPEN
375 fp = funopen(z, NULL, gzip_cookie_write, NULL, gzip_cookie_close);
376 if (fp == NULL) {
377 perror("funopen");
378 return NULL;
379 }
380 #elif HAVE_FOPENCOOKIE
381 {
382 static cookie_io_functions_t cookiefuncs = {
383 NULL, gzip_cookie_write, NULL, gzip_cookie_close
384 };
385
386 fp = fopencookie(z, "w", cookiefuncs);
387 if (fp == NULL) {
388 perror("fopencookie");
389 return NULL;
390 }
391 }
392 #endif
393 return pcap_dump_fopen(pcap, fp);
394 }
395 #endif /* HAVE_GZOPEN */
396 #endif /* HAVE_ZLIB_H */
397
398 return pcap_dump_open(pcap, path);
399 }
400