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