1 /*
2 output logging/reporting
3
4 This is the file that formats the output files -- that is to say,
5 where we report everything we find.
6
7 PLUGINS
8
9 The various types of output (XML, binary, Redis, etc.) are written vaguely
10 as "plugins", which means as a structure with function pointers. In the
11 future, it should be possible to write plugins as DDLs/shared-objects
12 and load them at runtime, but right now, they are just hard coded.
13
14 ROTATE
15
16 Files can be "rotated". This is done by prefixing the file with the
17 date/time when the file was created.
18
19 A key feature of this design is to prevent files being lost during
20 rotation. Therefore, the files are renamed while they are still open.
21 If the rename function fails, then the file is left in-place and still
22 open for writing, with continued appending to the file.
23
24 Thus, you could start the program logging to "--rotate-dir ../foobar"
25 and then notice the error messages saying that rotating isn't working,
26 then go create the "foobar" directory, at which point rotating will now
27 work -- it's just that the first rotated file will contain several
28 periods of data.
29 */
30
31 /* Needed for Linux to make offsets 64 bits */
32 #define _FILE_OFFSET_BITS 64
33
34 #include "output.h"
35 #include "masscan.h"
36 #include "masscan-status.h"
37 #include "string_s.h"
38 #include "logger.h"
39 #include "proto-banner1.h"
40 #include "masscan-app.h"
41 #include "main-globals.h"
42 #include "pixie-file.h"
43 #include "pixie-sockets.h"
44 #include "util-malloc.h"
45
46 #include <limits.h>
47 #include <ctype.h>
48 #include <string.h>
49
50
51 /*****************************************************************************
52 *****************************************************************************/
ftell_x(FILE * fp)53 static int64_t ftell_x(FILE *fp)
54 {
55 #if defined(WIN32) && defined(__GNUC__)
56 return ftello64(fp);
57 #elif defined(WIN32) && defined(_MSC_VER)
58 return _ftelli64(fp);
59 #else
60 return ftello(fp);
61 #endif
62 }
63
64 /*****************************************************************************
65 * The 'status' variable contains both the open/closed info as well as the
66 * protocol info. This splits it back out into two values.
67 *****************************************************************************/
68 const char *
name_from_ip_proto(unsigned ip_proto)69 name_from_ip_proto(unsigned ip_proto)
70 {
71 switch (ip_proto) {
72 case 0: return "arp";
73 case 1: return "icmp";
74 case 6: return "tcp";
75 case 17: return "udp";
76 case 132: return "sctp";
77 default: return "err";
78 }
79 }
80
81
82 /*****************************************************************************
83 * The actual 'status' variable is narrowly defined depending on the
84 * underlying protocol. This function creates a gross "open" v. "closed"
85 * string based on the narrow variable.
86 *****************************************************************************/
87 const char *
status_string(enum PortStatus status)88 status_string(enum PortStatus status)
89 {
90 switch (status) {
91 case PortStatus_Open: return "open";
92 case PortStatus_Closed: return "closed";
93 case PortStatus_Arp: return "up";
94 default: return "unknown";
95 }
96 }
97
98
99 /*****************************************************************************
100 * Convert TCP flags into an nmap-style "reason" string
101 *****************************************************************************/
102 const char *
reason_string(int x,char * buffer,size_t sizeof_buffer)103 reason_string(int x, char *buffer, size_t sizeof_buffer)
104 {
105 sprintf_s(buffer, sizeof_buffer, "%s%s%s%s%s%s%s%s",
106 (x&0x01)?"fin-":"",
107 (x&0x02)?"syn-":"",
108 (x&0x04)?"rst-":"",
109 (x&0x08)?"psh-":"",
110 (x&0x10)?"ack-":"",
111 (x&0x20)?"urg-":"",
112 (x&0x40)?"ece-":"",
113 (x&0x80)?"cwr-":""
114 );
115 if (buffer[0] == '\0')
116 return "none";
117 else
118 buffer[strlen(buffer)-1] = '\0';
119 return buffer;
120 }
121
122
123 /*****************************************************************************
124 * Remove bad characters from the banner, especially new lines and HTML
125 * control codes.
126 *****************************************************************************/
127 const char *
normalize_string(const unsigned char * px,size_t length,char * buf,size_t buf_len)128 normalize_string(const unsigned char *px, size_t length,
129 char *buf, size_t buf_len)
130 {
131 size_t i=0;
132 size_t offset = 0;
133
134
135 for (i=0; i<length; i++) {
136 unsigned char c = px[i];
137
138 if (isprint(c) && c != '<' && c != '>' && c != '&' && c != '\\' && c != '\"' && c != '\'') {
139 if (offset + 2 < buf_len)
140 buf[offset++] = px[i];
141 } else {
142 if (offset + 5 < buf_len) {
143 buf[offset++] = '\\';
144 buf[offset++] = 'x';
145 buf[offset++] = "0123456789abcdef"[px[i]>>4];
146 buf[offset++] = "0123456789abcdef"[px[i]&0xF];
147 }
148 }
149 }
150
151 buf[offset] = '\0';
152
153 return buf;
154 }
155
156
157 /*****************************************************************************
158 * PORTABILITY: WINDOWS
159 *
160 * Windows POSIX functions open the file without the "share-delete" flag,
161 * meaning they can't be renamed while open. Therefore, we need to
162 * construct our own open flag.
163 *****************************************************************************/
164 static FILE *
open_rotate(struct Output * out,const char * filename)165 open_rotate(struct Output *out, const char *filename)
166 {
167 FILE *fp = 0;
168 unsigned is_append = out->is_append;
169 int x;
170
171 /*
172 * KLUDGE: do something special for redis
173 */
174 if (out->format == Output_Redis) {
175 ptrdiff_t fd = out->redis.fd;
176 if (fd < 1) {
177 struct sockaddr_in sin = {0};
178 fd = (ptrdiff_t)socket(AF_INET, SOCK_STREAM, 0);
179 if (fd == -1) {
180 LOG(0, "redis: socket() failed to create socket\n");
181 exit(1);
182 }
183 sin.sin_addr.s_addr = htonl(out->redis.ip.ipv4); /* TODO: ipv6 */
184 sin.sin_port = htons((unsigned short)out->redis.port);
185 sin.sin_family = AF_INET;
186 x = connect((SOCKET)fd, (struct sockaddr*)&sin, sizeof(sin));
187 if (x != 0) {
188 LOG(0, "redis: connect() failed\n");
189 perror("connect");
190 }
191 out->redis.fd = fd;
192 }
193 out->funcs->open(out, (FILE*)fd);
194
195 return (FILE*)fd;
196 }
197
198 /* Do something special for the "-" filename */
199 if (filename[0] == '-' && filename[1] == '\0')
200 fp = stdout;
201
202 /* open a "shareable" file. On Windows, by default files can't be renamed
203 * while they are open, so we need a special function that takes care
204 * of this. */
205 if (fp == 0) {
206 x = pixie_fopen_shareable(&fp, filename, is_append);
207 if (x != 0 || fp == NULL) {
208 fprintf(stderr, "out: could not open file for %s\n",
209 is_append?"appending":"writing");
210 perror(filename);
211 is_tx_done = 1;
212 return NULL;
213 }
214 }
215
216 /*
217 * Mark the file as newly opened. That way, before writing any data
218 * to it, we'll first have to write headers
219 */
220 out->is_virgin_file = 1;
221
222 return fp;
223 }
224
225
226 /*****************************************************************************
227 * Write the remaining data the file and close it. This function is
228 * called "rotate", but it doesn't actually rotate, this name just reflects
229 * how it's used in the rotate process.
230 *****************************************************************************/
231 static void
close_rotate(struct Output * out,FILE * fp)232 close_rotate(struct Output *out, FILE *fp)
233 {
234 if (out == NULL)
235 return;
236 if (fp == NULL)
237 return;
238
239 /*
240 * Write the format-specific trailers, like </xml>
241 */
242 if (!out->is_virgin_file)
243 out->funcs->close(out, fp);
244
245 memset(&out->counts, 0, sizeof(out->counts));
246
247 /* Redis Kludge*/
248 if (out->format == Output_Redis)
249 return;
250
251 fflush(fp);
252 fclose(fp);
253 }
254
255
256 /*****************************************************************************
257 * Returns the time when the next rotate should occur. Rotations are
258 * aligned to the period, which means that if you rotate hourly, it's done
259 * on the hour every hour, like at 9:00:00 o'clock exactly. In other words,
260 * a period of "hourly" doesn't really mean "every 60 minutes", but
261 * on the hour". Since the program will be launched midway in a period,
262 * that means the first rotation will happen in less than a full period.
263 *****************************************************************************/
264 static time_t
next_rotate_time(time_t last_rotate,unsigned period,unsigned offset)265 next_rotate_time(time_t last_rotate, unsigned period, unsigned offset)
266 {
267 time_t next;
268
269 next = last_rotate - (last_rotate % period) + period + offset;
270
271 return next;
272 }
273
274
275 #if 0
276 /*****************************************************************************
277 *****************************************************************************/
278 static int
279 ends_with(const char *filename, const char *extension)
280 {
281 if (filename == NULL || extension == NULL)
282 return 0;
283 if (strlen(filename) + 1 < strlen(extension))
284 return 0;
285 if (memcmp(filename + strlen(filename) - strlen(extension),
286 extension, strlen(extension)) != 0)
287 return 0;
288 if (filename[strlen(filename) - strlen(extension) - 1] != '.')
289 return 0;
290
291 return 1;
292 }
293 #endif
294
295 /*****************************************************************************
296 * strdup(): compilers don't like strdup(), so I just write my own here. I
297 * should probably find a better solution.
298 *****************************************************************************/
299 static char *
duplicate_string(const char * str)300 duplicate_string(const char *str)
301 {
302 size_t length;
303 char *result;
304
305 /* Find the length of the string. We allow NULL strings, in which case
306 * the length is zero */
307 if (str == NULL)
308 length = 0;
309 else
310 length = strlen(str);
311
312 /* Allocate memory for the string */
313 result = MALLOC(length + 1);
314
315
316 /* Copy the string */
317 if (str)
318 memcpy(result, str, length+1);
319 result[length] = '\0';
320
321 return result;
322 }
323
324 /*****************************************************************************
325 * Adds the index variable to just before the file extension. For example,
326 * if the original filename is "foo.bar", and the index is 1, then the
327 * new filename becomes "foo.01.bar". By putting the index before the
328 * extension, it preserves the file type. By prepending a zero on the index,
329 * it allows up to 100 files while still being able to easily sort the files.
330 *****************************************************************************/
331 static char *
indexed_filename(const char * filename,unsigned index)332 indexed_filename(const char *filename, unsigned index)
333 {
334 size_t len = strlen(filename);
335 size_t ext;
336 char *new_filename;
337 size_t new_length = strlen(filename) + 32;
338
339 /* find the extension */
340 ext = len;
341 while (ext) {
342 ext--;
343 if (filename[ext] == '.')
344 break;
345 if (filename[ext] == '/' || filename[ext] == '\\') {
346 /* no dot found, so ext is end of file */
347 ext = len;
348 break;
349 }
350 }
351 if (ext == 0 && len > 0 && filename[0] != '.')
352 ext = len;
353
354 /* allocate memory */
355 new_filename = MALLOC(new_length);
356
357
358 /* format the new name */
359 sprintf_s(new_filename, new_length, "%.*s.%02u%s",
360 (unsigned)ext, filename,
361 index,
362 filename+ext);
363
364 return new_filename;
365
366 }
367
368 /*****************************************************************************
369 * Create an "output" structure. If we are writing a file, we create the
370 * file now, so that any errors creating the file are caught immediately,
371 * rather than later in the scan when it might fail.
372 *****************************************************************************/
373 struct Output *
output_create(const struct Masscan * masscan,unsigned thread_index)374 output_create(const struct Masscan *masscan, unsigned thread_index)
375 {
376 struct Output *out;
377 unsigned i;
378
379 /* allocate/initialize memory */
380 out = CALLOC(1, sizeof(*out));
381 out->masscan = masscan;
382 out->when_scan_started = time(0);
383 out->is_virgin_file = 1;
384
385 /*
386 * Copy the configuration information from the 'masscan' structure.
387 */
388 out->rotate.period = masscan->output.rotate.timeout;
389 out->rotate.offset = masscan->output.rotate.offset;
390 out->rotate.filesize = masscan->output.rotate.filesize;
391 out->redis.port = masscan->redis.port;
392 out->redis.ip = masscan->redis.ip;
393 out->is_banner = masscan->is_banners;
394 out->is_gmt = masscan->is_gmt;
395 out->is_interactive = masscan->output.is_interactive;
396 out->is_show_open = masscan->output.is_show_open;
397 out->is_show_closed = masscan->output.is_show_closed;
398 out->is_show_host = masscan->output.is_show_host;
399 out->is_append = masscan->output.is_append;
400 out->xml.stylesheet = duplicate_string(masscan->output.stylesheet);
401 out->rotate.directory = duplicate_string(masscan->output.rotate.directory);
402 if (masscan->nic_count <= 1)
403 out->filename = duplicate_string(masscan->output.filename);
404 else
405 out->filename = indexed_filename(masscan->output.filename, thread_index);
406
407 for (i=0; i<8; i++) {
408 out->src[i] = masscan->nic[i].src;
409 }
410
411 /*
412 * Link the appropriate output module.
413 * TODO: support multiple output modules
414 */
415 out->format = masscan->output.format;
416 switch (out->format) {
417 case Output_List:
418 out->funcs = &text_output;
419 break;
420 case Output_Unicornscan:
421 out->funcs = &unicornscan_output;
422 break;
423 case Output_XML:
424 out->funcs = &xml_output;
425 break;
426 case Output_JSON:
427 out->funcs = &json_output;
428 break;
429 case Output_NDJSON:
430 out->funcs = &ndjson_output;
431 break;
432 case Output_Certs:
433 out->funcs = &certs_output;
434 break;
435 case Output_Binary:
436 out->funcs = &binary_output;
437 break;
438 case Output_Grepable:
439 out->funcs = &grepable_output;
440 break;
441 case Output_Redis:
442 out->funcs = &redis_output;
443 break;
444 case Output_Hostonly:
445 out->funcs = &hostonly_output;
446 break;
447 case Output_None:
448 out->funcs = &null_output;
449 break;
450 default:
451 out->funcs = &null_output;
452 break;
453 }
454
455 /*
456 * Open the desired output file. We do this now at the start of the scan
457 * so that we can immediately notify the user of an error, rather than
458 * waiting midway through a long scan and have it fail.
459 */
460 if (masscan->output.filename[0] && out->funcs != &null_output) {
461 FILE *fp;
462
463 fp = open_rotate(out, masscan->output.filename);
464 if (fp == NULL) {
465 perror(masscan->output.filename);
466 exit(1);
467 }
468
469 out->fp = fp;
470 out->rotate.last = time(0);
471 }
472
473 /*
474 * Set the time of the next rotation. If we aren't rotating files, then
475 * this time will be set at "infinity" in the future.
476 * TODO: this code isn't Y2036 compliant.
477 */
478 if (masscan->output.rotate.timeout == 0) {
479 /* TODO: how does one find the max time_t value??*/
480 out->rotate.next = (time_t)LONG_MAX;
481 } else {
482 if (out->rotate.offset > 1) {
483 out->rotate.next = next_rotate_time(
484 out->rotate.last-out->rotate.period,
485 out->rotate.period, out->rotate.offset);
486 } else {
487 out->rotate.next = next_rotate_time(
488 out->rotate.last,
489 out->rotate.period, out->rotate.offset);
490 }
491 }
492
493
494
495 return out;
496 }
497
498
499 /*****************************************************************************
500 * Rotate the file, moving it from the local directory to a remote directory
501 * and changing the name to include the timestamp. This is done while the file
502 * is still open: we move the file and rename it first, then close it.
503 *****************************************************************************/
504 static FILE *
output_do_rotate(struct Output * out,int is_closing)505 output_do_rotate(struct Output *out, int is_closing)
506 {
507 const char *dir;
508 const char *filename;
509 char *new_filename;
510 size_t new_filename_size;
511 struct tm tm;
512 int err;
513
514 /* Don't do anything if there is no file */
515 if (out == NULL || out->fp == NULL)
516 return NULL;
517
518 dir = out->rotate.directory;
519 filename = out->filename;
520
521 /* Make sure that all output has been flushed to the file */
522 fflush(out->fp);
523
524 /* Remove directory prefix from filename, we just want the root filename
525 * to start with */
526 while (strchr(filename, '/')) {
527 filename = strchr(filename, '/');
528 if (*filename == '/')
529 filename++;
530 }
531
532 while (strchr(filename, '\\')) {
533 filename = strchr(filename, '\\');
534 if (*filename == '\\')
535 filename++;
536 }
537
538 /* Allocate memory for the new filename */
539 new_filename_size = strlen(dir)
540 + strlen("/")
541 + strlen(filename)
542 + strlen("1308201101-")
543 + strlen(filename)
544 + 1 /* - */
545 + 1; /* nul */
546 new_filename = MALLOC(new_filename_size);
547
548 /* Get the proper timestamp for the file */
549 if (out->is_gmt) {
550 err = gmtime_s(&tm, &out->rotate.last);
551 } else {
552 err = localtime_s(&tm, &out->rotate.last);
553 }
554 if (err != 0) {
555 free(new_filename);
556 perror("gmtime(): file rotation ended");
557 return out->fp;
558 }
559
560
561 /* Look for a name that doesn't collide with an exist name. If the desired
562 * file already exists, then increment the filename. This should never
563 * happen. */
564 err = 0;
565 again:
566 if (out->rotate.filesize) {
567 size_t x_off=0, x_len=0;
568 if (strrchr(filename, '.')) {
569 x_off = strrchr(filename, '.') - filename;
570 x_len = strlen(filename + x_off);
571 } else {
572 x_off = strlen(filename);
573 x_len = 0;
574 }
575 sprintf_s(new_filename, new_filename_size,
576 "%s/%.*s-%05u%.*s",
577 dir,
578 (unsigned)x_off, filename,
579 out->rotate.filecount++,
580 (unsigned)x_len, filename + x_off
581 );
582 } else {
583 sprintf_s(new_filename, new_filename_size,
584 "%s/%02u%02u%02u-%02u%02u%02u" "-%s",
585 dir,
586 tm.tm_year % 100,
587 tm.tm_mon+1,
588 tm.tm_mday,
589 tm.tm_hour,
590 tm.tm_min,
591 tm.tm_sec,
592 filename);
593 if (access(new_filename, 0) == 0) {
594 tm.tm_sec++;
595 if (err++ == 0)
596 goto again;
597 }
598 }
599 filename = out->filename;
600
601 /*
602 * Move the file
603 */
604 err = rename(filename, new_filename);
605 if (err) {
606 LOG(0, "rename(\"%s\", \"%s\"): failed\n", filename, new_filename);
607 perror("rename()");
608 free(new_filename);
609 return out->fp;
610 }
611
612 /*
613 * Set the next rotate time, which is the current time plus the period
614 * length
615 */
616 out->rotate.bytes_written = 0;
617
618 if (out->rotate.period) {
619 out->rotate.next = next_rotate_time(time(0),
620 out->rotate.period, out->rotate.offset);
621 }
622
623 LOG(1, "rotated: %s\n", new_filename);
624 free(new_filename);
625
626 /*
627 * Now create a new file
628 */
629 if (is_closing)
630 out->fp = NULL; /* program shutting down, so don't create new file */
631 else {
632 FILE *fp;
633
634 fp = open_rotate(out, filename);
635 if (fp == NULL) {
636 LOG(0, "rotate: %s: failed: %s\n", filename, strerror_x(errno));
637 } else {
638 close_rotate(out, out->fp);
639 out->fp = fp;
640 out->rotate.last = time(0);
641 LOG(1, "rotate: started new file: %s\n", filename);
642 }
643 }
644 return out->fp;
645 }
646
647 /***************************************************************************
648 ***************************************************************************/
649 static int
is_rotate_time(const struct Output * out,time_t now,FILE * fp)650 is_rotate_time(const struct Output *out, time_t now, FILE *fp)
651 {
652 if (out->is_virgin_file)
653 return 0;
654 if (now >= out->rotate.next)
655 return 1;
656 if (out->rotate.filesize != 0 &&
657 ftell_x(fp) >= (int64_t)out->rotate.filesize)
658 return 1;
659 return 0;
660 }
661
662 /***************************************************************************
663 * Return the vendor/OUI string matchng the first three bytes of a
664 * MAC address.
665 * TODO: this should be read in from a file
666 ***************************************************************************/
667 static const char *
oui_from_mac(const unsigned char mac[6])668 oui_from_mac(const unsigned char mac[6])
669 {
670 unsigned oui = mac[0]<<16 | mac[1]<<8 | mac[2];
671 switch (oui) {
672 case 0x0001c0: return "Compulab";
673 case 0x000732: return "Aaeon";
674 case 0x000c29: return "VMware";
675 case 0x001075: return "Seagate";
676 case 0x001132: return "Synology";
677 case 0x022618: return "Asus";
678 case 0x0022b0: return "D-Link";
679 case 0x00236c: return "Apple";
680 case 0x0016CB: return "Apple";
681 case 0x001e06: return "Odroid";
682 case 0x001ff3: return "Apple";
683 case 0x002590: return "Supermicro";
684 case 0x08cc68: return "Cisco";
685 case 0x0C9D92: return "Asus";
686 case 0x244CE3: return "Amazon";
687 case 0x2c27d7: return "HP";
688 case 0x3497f6: return "Asus";
689 case 0x38f73d: return "Amazon";
690 case 0x404a03: return "Zyxel";
691 case 0x4C9EFF: return "Zyxel";
692 case 0x5855CA: return "Apple";
693 case 0x60a44c: return "Asus";
694 case 0x6c72e7: return "Apple";
695 case 0x9003b7: return "Parrot";
696 case 0x94dbc9: return "Azurewave";
697 case 0xacbc32: return "Apple";
698 case 0xb827eb: return "Raspberry Pi";
699 case 0xc05627: return "Belkin";
700 case 0xc0c1c0: return "Cisco-Linksys";
701 case 0xDCA4CA: return "Apple";
702 case 0xe4956e: return "[random]";
703 default: return "";
704 }
705 }
706
707 /***************************************************************************
708 * Report simply "open" or "closed", with little additional information.
709 * This is called directly from the receive thread when responses come
710 * back.
711 ***************************************************************************/
712 void
output_report_status(struct Output * out,time_t timestamp,int status,ipaddress ip,unsigned ip_proto,unsigned port,unsigned reason,unsigned ttl,const unsigned char mac[6])713 output_report_status(struct Output *out, time_t timestamp, int status,
714 ipaddress ip, unsigned ip_proto, unsigned port, unsigned reason, unsigned ttl,
715 const unsigned char mac[6])
716 {
717 FILE *fp = out->fp;
718 time_t now = time(0);
719 ipaddress_formatted_t fmt = ipaddress_fmt(ip);
720
721 global_now = now;
722
723 /* if "--open"/"--open-only" parameter specified on command-line, then
724 * don't report the status of closed-ports */
725 if (!out->is_show_closed && status == PortStatus_Closed)
726 return;
727 if (!out->is_show_open && status == PortStatus_Open)
728 return;
729
730 /* If in "--interactive" mode, then print the banner to the command
731 * line screen */
732 if (out->is_interactive || out->format == 0 || out->format == Output_Interactive) {
733 unsigned count;
734
735 switch (ip_proto) {
736 case 0: /* ARP */
737 count = fprintf(stdout, "Discovered %s port %u/%s on %s (%02x:%02x:%02x:%02x:%02x:%02x) %s",
738 status_string(status),
739 port,
740 name_from_ip_proto(ip_proto),
741 fmt.string,
742 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
743 oui_from_mac(mac)
744 );
745 break;
746 default:
747 count = fprintf(stdout, "Discovered %s port %u/%s on %s",
748 status_string(status),
749 port,
750 name_from_ip_proto(ip_proto),
751 fmt.string
752 );
753 }
754
755 /* Because this line may overwrite the "%done" status line, print
756 * some spaces afterward to completely cover up the line */
757 if (count < 80)
758 fprintf(stdout, "%.*s", (int)(79-count),
759 " "
760 " ");
761
762 fprintf(stdout, "\n");
763 fflush(stdout);
764
765 }
766
767
768 if (fp == NULL)
769 return;
770
771 /* Rotate, if we've pass the time limit. Rotating the log files happens
772 * inline while writing output, whenever there's output to write to the
773 * file, rather than in a separate thread right at the time interval.
774 * Thus, if results are coming in slowly, the rotation won't happen
775 * on precise boundaries */
776 if (is_rotate_time(out, now, fp)) {
777 fp = output_do_rotate(out, 0);
778 if (fp == NULL)
779 return;
780 }
781
782
783 /* Keep some statistics so that the user can monitor how much stuff is
784 * being found. */
785 switch (status) {
786 case PortStatus_Open:
787 switch (ip_proto) {
788 case 1:
789 out->counts.icmp.echo++;
790 break;
791 case 6:
792 out->counts.tcp.open++;
793 break;
794 case 17:
795 out->counts.udp.open++;
796 break;
797 case 132:
798 out->counts.sctp.open++;
799 break;
800 default:
801 out->counts.oproto.open++;
802 break;
803 }
804 if (!out->is_show_open)
805 return;
806 break;
807 case PortStatus_Closed:
808 switch (ip_proto) {
809 case 6:
810 out->counts.tcp.closed++;
811 break;
812 case 17:
813 out->counts.udp.closed++;
814 break;
815 case 132:
816 out->counts.sctp.closed++;
817 break;
818 }
819 if (!out->is_show_closed)
820 return;
821 break;
822 case PortStatus_Arp:
823 out->counts.arp.open++;
824 break;
825 default:
826 LOG(0, "unknown status type: %u\n", status);
827 return;
828 }
829
830 /*
831 * If this is a newly opened file, then write file headers
832 */
833 if (out->is_virgin_file) {
834 out->funcs->open(out, fp);
835 out->is_virgin_file = 0;
836 }
837
838 /*
839 * Now do the actual output, whether it be XML, binary, JSON, ndjson, Redis,
840 * and so on.
841 */
842 out->funcs->status(out, fp, timestamp, status, ip, ip_proto, port, reason, ttl);
843 }
844
845
846 /***************************************************************************
847 ***************************************************************************/
848 void
output_report_banner(struct Output * out,time_t now,ipaddress ip,unsigned ip_proto,unsigned port,unsigned proto,unsigned ttl,const unsigned char * px,unsigned length)849 output_report_banner(struct Output *out, time_t now,
850 ipaddress ip, unsigned ip_proto, unsigned port,
851 unsigned proto,
852 unsigned ttl,
853 const unsigned char *px, unsigned length)
854 {
855 FILE *fp = out->fp;
856 ipaddress_formatted_t fmt = ipaddress_fmt(ip);
857
858 /* If we aren't doing banners, then don't do anything. That's because
859 * when doing UDP scans, we'll still get banner information from
860 * decoding the response packets, even if the user isn't interested */
861 if (!out->is_banner)
862 return;
863
864 /* If in "--interactive" mode, then print the banner to the command
865 * line screen */
866 if (out->is_interactive || out->format == 0 || out->format == Output_Interactive) {
867 unsigned count;
868 char banner_buffer[4096];
869
870 count = fprintf(stdout, "Banner on port %u/%s on %s: [%s] %s",
871 port,
872 name_from_ip_proto(ip_proto),
873 fmt.string,
874 masscan_app_to_string(proto),
875 normalize_string(px, length, banner_buffer, sizeof(banner_buffer))
876 );
877
878 /* Because this line may overwrite the "%done" status line, print
879 * some spaces afterward to completely cover up the line */
880 if (count < 80)
881 fprintf(stdout, "%.*s", (int)(79-count),
882 " "
883 " ");
884
885 fprintf(stdout, "\n");
886 }
887
888 /* If not outputing to a file, then don't do anything */
889 if (fp == NULL)
890 return;
891
892 /* Rotate, if we've pass the time limit. Rotating the log files happens
893 * inline while writing output, whenever there's output to write to the
894 * file, rather than in a separate thread right at the time interval.
895 * Thus, if results are coming in slowly, the rotation won't happen
896 * on precise boundaries */
897 if (is_rotate_time(out, now, fp)) {
898 fp = output_do_rotate(out, 0);
899 if (fp == NULL)
900 return;
901 }
902
903 /*
904 * If this is a newly opened file, then write file headers
905 */
906 if (out->is_virgin_file) {
907 out->funcs->open(out, fp);
908 out->is_virgin_file = 0;
909 }
910
911 /*
912 * Now do the actual output, whether it be XML, binary, JSON, ndjson, Redis,
913 * and so on.
914 */
915 out->funcs->banner(out, fp, now, ip, ip_proto, port, proto, ttl, px, length);
916
917 }
918
919
920 /***************************************************************************
921 * Called on exit of the program to close/free everything
922 ***************************************************************************/
923 void
output_destroy(struct Output * out)924 output_destroy(struct Output *out)
925 {
926 if (out == NULL)
927 return;
928
929 /* If rotating files, then do one last rotate of this file to the
930 * destination directory */
931 if (out->rotate.period || out->rotate.filesize) {
932 LOG(1, "doing finale rotate\n");
933 output_do_rotate(out, 1);
934 }
935
936 /* If not rotating files, then simply close this file. Remember
937 * that some files will write closing information before closing
938 * the file */
939 if (out->fp)
940 close_rotate(out, out->fp);
941
942
943
944 free(out->xml.stylesheet);
945 free(out->rotate.directory);
946 free(out->filename);
947
948 free(out);
949 }
950
951
952 /*****************************************************************************
953 * Regression tests for this unit.
954 *****************************************************************************/
955 int
output_selftest(void)956 output_selftest(void)
957 {
958 char *f;
959
960 f = indexed_filename("foo.bar", 1);
961 if (strcmp(f, "foo.01.bar") != 0) {
962 fprintf(stderr, "output: failed selftest\n");
963 return 1;
964 }
965 free(f);
966
967 f = indexed_filename("foo.b/ar", 2);
968 if (strcmp(f, "foo.b/ar.02") != 0) {
969 fprintf(stderr, "output: failed selftest\n");
970 return 1;
971 }
972 free(f);
973
974 f = indexed_filename(".foobar", 3);
975 if (strcmp(f, ".03.foobar") != 0) {
976 fprintf(stderr, "output: failed selftest\n");
977 return 1;
978 }
979 free(f);
980
981 return 0;
982 }
983
984