1 /*
2  * sc_filterpolicy : check filter congruity of different addresses for the
3  *                 : same device
4  *
5  * Authors         : Matthew Luckie, Jakub Czyz
6  *
7  * Copyright (C) 2014-2015 The Regents of the University of California
8  * Copyright (C) 2015      Matthew Luckie
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, version 2.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 #include "internal.h"
29 
30 #include "scamper_addr.h"
31 #include "scamper_list.h"
32 #include "ping/scamper_ping.h"
33 #include "trace/scamper_trace.h"
34 #include "scamper_file.h"
35 #include "scamper_writebuf.h"
36 #include "scamper_linepoll.h"
37 #include "mjl_list.h"
38 #include "mjl_splaytree.h"
39 #include "mjl_heap.h"
40 #include "utils.h"
41 
42 #define OPT_HELP        0x0001
43 #define OPT_INFILE      0x0002
44 #define OPT_OUTFILE     0x0004
45 #define OPT_PORT        0x0008
46 #define OPT_UNIX        0x0010
47 #define OPT_LOG         0x0020
48 #define OPT_DAEMON      0x0040
49 #define OPT_OPTIONS     0x0080
50 #define OPT_TYPE        0x0100
51 #define OPT_READ        0x0200
52 #define OPT_TEST        0x0400
53 #define OPT_ALL         0xffff
54 
55 #define FLAG_IMPATIENT   0x0001
56 #define FLAG_TRACEROUTE  0x0002
57 #define FLAG_TUPLES      0x0004
58 #define FLAG_INCONGRUENT 0x0008
59 
60 typedef struct sc_iditem
61 {
62   scamper_addr_t *addr;
63   uint32_t        tests;
64   uint32_t        results;
65 } sc_iditem_t;
66 
67 typedef struct sc_idset
68 {
69   uint32_t        userid;
70   uint32_t        tests;
71   sc_iditem_t   **items;
72   int             itemc;
73 } sc_idset_t;
74 
75 typedef struct sc_name2ips
76 {
77   uint32_t          id;
78   char             *name;
79   slist_t          *addrs;
80   slist_t          *methods;
81   splaytree_node_t *tree_node;
82   sc_idset_t       *set;
83 } sc_name2ips_t;
84 
85 typedef struct sc_ip2n2i
86 {
87   scamper_addr_t   *ip;
88   sc_name2ips_t    *n2i;
89   splaytree_node_t *tree_node;
90 } sc_ip2n2i_t;
91 
92 typedef struct sc_wait
93 {
94   struct timeval  tv;
95   sc_name2ips_t  *n2i;
96 } sc_wait_t;
97 
98 typedef struct sc_policytest
99 {
100   const char     *name;
101   size_t          namelen;
102   const uint32_t  id;
103   const char     *payload;
104   const uint16_t  port;
105   uint8_t         flags;
106 } sc_policytest_t;
107 
108 #define PT_FLAG_TCP    0x01
109 #define PT_FLAG_ICMP   0x02
110 #define PT_FLAG_UDP    0x04
111 #define PT_FLAG_ROUTER 0x08
112 #define PT_FLAG_SERVER 0x10
113 #define PT_FLAG_USE    0x20
114 
115 static splaytree_t           *n2i_tree      = NULL;
116 static slist_t               *n2i_list      = NULL;
117 static uint32_t               n2i_id        = 1;
118 static sc_name2ips_t         *n2i_last      = NULL;
119 static uint32_t               options       = 0;
120 static unsigned int           port          = 0;
121 static char                  *unix_name     = NULL;
122 static scamper_writebuf_t    *scamper_wb    = NULL;
123 static int                    scamper_fd    = -1;
124 static scamper_linepoll_t    *scamper_lp    = NULL;
125 static scamper_writebuf_t    *decode_wb     = NULL;
126 static scamper_file_t        *decode_in     = NULL;
127 static int                    decode_in_fd  = -1;
128 static int                    decode_out_fd = -1;
129 static char                  *infile        = NULL;
130 static char                  *datafile      = NULL;
131 static char                  *outfile_name  = NULL;
132 static scamper_file_t        *outfile       = NULL;
133 static scamper_file_filter_t *ffilter       = NULL;
134 static int                    data_left     = 0;
135 static int                    more          = 0;
136 static int                    probing       = 0;
137 static int                    flags         = 0;
138 static struct timeval         now;
139 static FILE                  *logfile       = NULL;
140 static heap_t                *waiting       = NULL;
141 
142 #define PT_METHOD_ICMP    1
143 #define PT_METHOD_NETBIOS 2
144 #define PT_METHOD_MSSQL   3
145 #define PT_METHOD_FTP     4
146 #define PT_METHOD_SSH     5
147 #define PT_METHOD_TELNET  6
148 #define PT_METHOD_MYSQL   7
149 #define PT_METHOD_RDP     8
150 #define PT_METHOD_HTTPS   9
151 #define PT_METHOD_SMB     10
152 #define PT_METHOD_VNC     11
153 #define PT_METHOD_HTTP    12
154 #define PT_METHOD_BGP     13
155 #define PT_METHOD_NTP     14
156 #define PT_METHOD_DNS     15
157 #define PT_METHOD_SNMP    16
158 
159 #define PT_METHOD_MAX     16
160 
161 static sc_policytest_t methods[] = {
162   {"ICMP", 4, PT_METHOD_ICMP,
163    NULL, 0, PT_FLAG_ICMP | PT_FLAG_SERVER | PT_FLAG_ROUTER},
164   {"NetBIOS", 7, PT_METHOD_NETBIOS,
165    NULL, 139, PT_FLAG_TCP},
166   {"MSSQL", 5, PT_METHOD_MSSQL,
167    NULL, 1433, PT_FLAG_TCP},
168   {"FTP", 3, PT_METHOD_FTP,
169    NULL, 21, PT_FLAG_TCP | PT_FLAG_SERVER},
170   {"SSH", 3, PT_METHOD_SSH,
171    NULL, 22, PT_FLAG_TCP | PT_FLAG_SERVER | PT_FLAG_ROUTER},
172   {"Telnet", 6, PT_METHOD_TELNET,
173    NULL, 23, PT_FLAG_TCP | PT_FLAG_SERVER | PT_FLAG_ROUTER},
174   {"MySQL", 5, PT_METHOD_MYSQL,
175    NULL, 3306, PT_FLAG_TCP | PT_FLAG_SERVER},
176   {"RDP", 3, PT_METHOD_RDP,
177    NULL, 3389, PT_FLAG_TCP | PT_FLAG_SERVER},
178   {"HTTPS", 5, PT_METHOD_HTTPS,
179    NULL, 443, PT_FLAG_TCP | PT_FLAG_SERVER | PT_FLAG_ROUTER},
180   {"SMB", 3, PT_METHOD_SMB,
181    NULL, 445, PT_FLAG_TCP | PT_FLAG_SERVER},
182   {"VNC", 3, PT_METHOD_VNC,
183    NULL, 5900, PT_FLAG_TCP},
184   {"HTTP", 4, PT_METHOD_HTTP,
185    NULL, 80, PT_FLAG_TCP | PT_FLAG_SERVER | PT_FLAG_ROUTER},
186   {"BGP", 3, PT_METHOD_BGP,
187    NULL, 179, PT_FLAG_TCP | PT_FLAG_ROUTER},
188   {"NTP", 3, PT_METHOD_NTP,
189    "160200010000000000000000",
190   123, PT_FLAG_UDP | PT_FLAG_SERVER | PT_FLAG_ROUTER},
191   {"DNS", 3, PT_METHOD_DNS,
192    "a980010000010000000000000377777706676f6f676c6503636f6d0000010001",
193    53, PT_FLAG_UDP | PT_FLAG_SERVER | PT_FLAG_ROUTER},
194   {"SNMP", 4, PT_METHOD_SNMP,
195    "302902010104067075626c6963a01c02046aebe0a002"
196    "0100020100300e300c06082b060102010105000500",
197    161, PT_FLAG_UDP | PT_FLAG_SERVER | PT_FLAG_ROUTER},
198 };
199 static const int methodc = sizeof(methods) / sizeof(sc_policytest_t);
200 
usage(uint32_t opt_mask)201 static void usage(uint32_t opt_mask)
202 {
203   int i;
204 
205   fprintf(stderr,
206     "usage: sc_filterpolicy [-D] [-a infile] [-o outfile] [-p port] [-U unix]\n"
207     "                       [-l log] [-O options] [-t type] [-T test]\n"
208     "\n"
209     "       sc_filterpolicy [-r datafile]\n"
210     "\n");
211 
212   if(opt_mask == 0)
213     {
214       fprintf(stderr, "       sc_filterpolicy -?\n\n");
215       return;
216     }
217 
218   if(opt_mask & OPT_HELP)
219     fprintf(stderr, "   -? give an overview of the usage of sc_filterpolicy\n");
220 
221   if(opt_mask & OPT_INFILE)
222     fprintf(stderr, "   -a input file\n");
223 
224   if(opt_mask & OPT_DAEMON)
225     fprintf(stderr, "   -D run as a daemon\n");
226 
227   if(opt_mask & OPT_OUTFILE)
228     fprintf(stderr, "   -o output warts data file\n");
229 
230   if(opt_mask & OPT_OPTIONS)
231     fprintf(stderr, "   -O options [impatient | incongruent | trace | tuples]\n");
232 
233   if(opt_mask & OPT_PORT)
234     fprintf(stderr, "   -p port to find scamper on\n");
235 
236   if(opt_mask & OPT_LOG)
237     fprintf(stderr, "   -l output logfile\n");
238 
239   if(opt_mask & OPT_READ)
240     fprintf(stderr, "   -r input warts data file\n");
241 
242   if(opt_mask & OPT_TYPE)
243     fprintf(stderr, "   -t type of probes: router, server, or all\n");
244 
245   if(opt_mask & OPT_TEST)
246     {
247       fprintf(stderr, "   -T adjust test schedule; e.g. -http or +vnc\n");
248       for(i=0; i<methodc; i++)
249 	{
250 	  if((i % 4) == 0)
251 	    {
252 	      if(i != 0) printf(",\n");
253 	      printf("     ");
254 	    }
255 	  else printf(",");
256 	  printf(" %s", methods[i].name);
257 	  if(methods[i].flags & PT_FLAG_TCP)
258 	    printf(" (tcp/%u)", methods[i].port);
259 	  else if(methods[i].flags & PT_FLAG_UDP)
260 	    printf(" (udp/%u)", methods[i].port);
261 	}
262       printf("\n");
263     }
264 
265   if(opt_mask & OPT_UNIX)
266     fprintf(stderr, "   -U unix domain to find scamper on\n");
267 
268   return;
269 }
270 
check_options(int argc,char * argv[])271 static int check_options(int argc, char *argv[])
272 {
273   char *opts = "?a:Dl:o:O:p:r:t:T:U:";
274   char *opt_port = NULL, *opt_unix = NULL, *opt_log = NULL, *opt_type = NULL;
275   slist_t *test_list = NULL;
276   uint32_t u32;
277   char *test;
278   int i, ch;
279   long lo;
280 
281   while((ch = getopt(argc, argv, opts)) != -1)
282     {
283       switch(ch)
284 	{
285 	case 'a':
286 	  options |= OPT_INFILE;
287 	  infile = optarg;
288 	  break;
289 
290 	case 'D':
291 	  options |= OPT_DAEMON;
292 	  break;
293 
294 	case 'l':
295 	  options |= OPT_LOG;
296 	  opt_log = optarg;
297 	  break;
298 
299 	case 'o':
300 	  options |= OPT_OUTFILE;
301 	  outfile_name = optarg;
302 	  break;
303 
304 	case 'O':
305 	  if(strcasecmp(optarg, "impatient") == 0)
306 	    flags |= FLAG_IMPATIENT;
307 	  else if(strcasecmp(optarg, "incongruent") == 0)
308 	    flags |= FLAG_INCONGRUENT;
309 	  else if(strcasecmp(optarg, "trace") == 0)
310 	    flags |= FLAG_TRACEROUTE;
311 	  else if(strcasecmp(optarg, "tuples") == 0)
312 	    flags |= FLAG_TUPLES;
313 	  else
314 	    {
315 	      usage(OPT_OPTIONS);
316 	      goto err;
317 	    }
318 	  break;
319 
320 	case 'p':
321 	  options |= OPT_PORT;
322 	  opt_port = optarg;
323 	  break;
324 
325 	case 'r':
326 	  options |= OPT_READ;
327 	  datafile = optarg;
328 	  break;
329 
330 	case 't':
331 	  options |= OPT_TYPE;
332 	  opt_type = optarg;
333 	  break;
334 
335 	case 'T':
336 	  options |= OPT_TEST;
337 	  if(test_list == NULL && (test_list = slist_alloc()) == NULL)
338 	    {
339 	      fprintf(stderr, "could not alloc test_list\n");
340 	      goto err;
341 	    }
342 	  if(slist_tail_push(test_list, optarg) == NULL)
343 	    {
344 	      fprintf(stderr, "could not push %s to test_list\n", optarg);
345 	      goto err;
346 	    }
347 	  break;
348 
349 	case 'U':
350 	  options |= OPT_UNIX;
351 	  opt_unix = optarg;
352 	  break;
353 
354 	case '?':
355 	default:
356 	  usage(OPT_ALL);
357 	  goto err;
358 	}
359     }
360 
361   u32 = options & (OPT_INFILE|OPT_OUTFILE|OPT_READ);
362   if(u32 != OPT_READ &&
363      u32 != (OPT_INFILE|OPT_OUTFILE))
364     {
365       usage(0);
366       goto err;
367     }
368 
369   if(options & (OPT_INFILE|OPT_OUTFILE))
370     {
371       if((options & (OPT_PORT|OPT_UNIX)) == 0 ||
372 	 (options & (OPT_PORT|OPT_UNIX)) == (OPT_PORT|OPT_UNIX) ||
373 	 argc - optind > 0)
374 	{
375 	  usage(OPT_INFILE|OPT_OUTFILE|OPT_PORT|OPT_UNIX);
376 	  goto err;
377 	}
378 
379       if(options & OPT_PORT)
380 	{
381 	  if(string_tolong(opt_port, &lo) != 0 || lo < 1 || lo > 65535)
382 	    {
383 	      usage(OPT_PORT);
384 	      goto err;
385 	    }
386 	  port = lo;
387 	}
388       else if(options & OPT_UNIX)
389 	{
390 	  unix_name = opt_unix;
391 	}
392 
393       /* validate the -t parameter */
394       if(opt_type != NULL)
395 	{
396 	  if(strcasecmp(opt_type, "router") == 0)
397 	    {
398 	      for(i=0; i<methodc; i++)
399 		if(methods[i].flags & PT_FLAG_ROUTER)
400 		  methods[i].flags |= PT_FLAG_USE;
401 	    }
402 	  else if(strcasecmp(opt_type, "server") == 0)
403 	    {
404 	      for(i=0; i<methodc; i++)
405 		if(methods[i].flags & PT_FLAG_SERVER)
406 		  methods[i].flags |= PT_FLAG_USE;
407 	    }
408 	  else if(strcasecmp(opt_type, "all") == 0)
409 	    {
410 	      for(i=0; i<methodc; i++)
411 		methods[i].flags |= PT_FLAG_USE;
412 	    }
413 	}
414 
415       /*
416        * if the user adjusted the test schedule, incorporate
417        * their changes now
418        */
419       if(test_list != NULL)
420 	{
421 	  while((test = slist_head_pop(test_list)) != NULL)
422 	    {
423 	      if(test[0] != '+' && test[0] != '-')
424 		{
425 		  usage(OPT_TEST);
426 		  goto err;
427 		}
428 	      for(i=0; i<methodc; i++)
429 		if(strcasecmp(methods[i].name, test+1) == 0)
430 		  break;
431 	      if(i == methodc)
432 		{
433 		  usage(OPT_TEST);
434 		  fprintf(stderr, "unknown test %s\n", test+1);
435 		  goto err;
436 		}
437 
438 	      if(test[0] == '+')
439 		methods[i].flags |= PT_FLAG_USE;
440 	      else
441 		methods[i].flags &= (~PT_FLAG_USE);
442 	    }
443 	}
444 
445       /* check that at least one protocol will be tested */
446       for(i=0; i<methodc; i++)
447 	if(methods[i].flags & PT_FLAG_USE)
448 	  break;
449       if(i == methodc)
450 	{
451 	  usage(OPT_TYPE|OPT_TEST);
452 	  goto err;
453 	}
454 
455       if(opt_log != NULL)
456 	{
457 	  if((logfile = fopen(opt_log, "w")) == NULL)
458 	    {
459 	      usage(OPT_LOG);
460 	      fprintf(stderr, "could not open %s\n", opt_log);
461 	      goto err;
462 	    }
463 	}
464     }
465   else
466     {
467       if((options & (OPT_PORT|OPT_UNIX|OPT_LOG|OPT_TYPE|OPT_TEST|OPT_DAEMON)) != 0 ||
468 	 (flags & (FLAG_TRACEROUTE|FLAG_IMPATIENT|FLAG_TUPLES)) != 0)
469 	{
470 	  usage(OPT_READ);
471 	  goto err;
472 	}
473     }
474 
475   if(test_list != NULL) slist_free(test_list);
476   return 0;
477 
478  err:
479   if(test_list != NULL) slist_free(test_list);
480   return -1;
481 }
482 
logprint(char * format,...)483 static void logprint(char *format, ...)
484 {
485   va_list ap;
486   char msg[131072];
487 
488   if(logfile == NULL)
489     return;
490 
491   va_start(ap, format);
492   vsnprintf(msg, sizeof(msg), format, ap);
493   va_end(ap);
494 
495   fprintf(logfile, "%ld: %s", (long int)now.tv_sec, msg);
496   fflush(logfile);
497 
498   return;
499 }
500 
ping_to_method(const scamper_ping_t * ping)501 static sc_policytest_t *ping_to_method(const scamper_ping_t *ping)
502 {
503   uint32_t id = 0;
504 
505   if(ping->probe_method == SCAMPER_PING_METHOD_ICMP_ECHO)
506     {
507       id = PT_METHOD_ICMP;
508     }
509   else if(ping->probe_method == SCAMPER_PING_METHOD_TCP_SYN)
510     {
511       switch(ping->probe_dport)
512 	{
513 	case 21:   id = PT_METHOD_FTP;     break;
514 	case 22:   id = PT_METHOD_SSH;     break;
515 	case 23:   id = PT_METHOD_TELNET;  break;
516 	case 80:   id = PT_METHOD_HTTP;    break;
517 	case 139:  id = PT_METHOD_NETBIOS; break;
518 	case 179:  id = PT_METHOD_BGP;     break;
519 	case 443:  id = PT_METHOD_HTTPS;   break;
520 	case 445:  id = PT_METHOD_SMB;     break;
521 	case 1433: id = PT_METHOD_MSSQL;   break;
522 	case 3306: id = PT_METHOD_MYSQL;   break;
523 	case 3389: id = PT_METHOD_RDP;     break;
524 	case 5900: id = PT_METHOD_VNC;     break;
525 	}
526     }
527   else if(ping->probe_method == SCAMPER_PING_METHOD_UDP)
528     {
529       switch(ping->probe_dport)
530 	{
531 	case 53:   id = PT_METHOD_DNS;  break;
532 	case 123:  id = PT_METHOD_NTP;  break;
533 	case 161:  id = PT_METHOD_SNMP; break;
534 	}
535     }
536 
537   if(id == 0)
538     return NULL;
539 
540   return &methods[id-1];
541 }
542 
ping_r(const sc_policytest_t * method,const scamper_ping_t * ping)543 static int ping_r(const sc_policytest_t *method, const scamper_ping_t *ping)
544 {
545   scamper_ping_reply_t *r;
546   uint16_t i;
547 
548   for(i=0; i<ping->ping_sent; i++)
549     {
550       for(r = ping->ping_replies[i]; r != NULL; r = r->next)
551 	{
552 	  if(method->flags & PT_FLAG_TCP)
553 	    {
554 	      if(SCAMPER_PING_REPLY_IS_TCP(r) &&
555 		 (r->tcp_flags & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK))
556 		return 1;
557 	    }
558 	  else if(method->flags & PT_FLAG_UDP)
559 	    {
560 	      if(SCAMPER_PING_REPLY_IS_UDP(r))
561 		return 1;
562 	    }
563 	  else if(method->flags & PT_FLAG_ICMP)
564 	    {
565 	      if(SCAMPER_PING_REPLY_IS_ICMP_ECHO_REPLY(r))
566 		return 1;
567 	    }
568 	  else return -1;
569 	}
570     }
571 
572   return 0;
573 }
574 
sc_wait_cmp(const void * a,const void * b)575 static int sc_wait_cmp(const void *a, const void *b)
576 {
577   return timeval_cmp(&((sc_wait_t *)b)->tv, &((sc_wait_t *)a)->tv);
578 }
579 
sc_wait(struct timeval * tv,sc_name2ips_t * n2i)580 static int sc_wait(struct timeval *tv, sc_name2ips_t *n2i)
581 {
582   sc_wait_t *w;
583   if((w = malloc_zero(sizeof(sc_wait_t))) == NULL)
584     return -1;
585   timeval_cpy(&w->tv, tv);
586   w->n2i = n2i;
587   if(heap_insert(waiting, w) == NULL)
588     return -1;
589   return 0;
590 }
591 
sc_iditem_cmp(const sc_iditem_t * a,const sc_iditem_t * b)592 static int sc_iditem_cmp(const sc_iditem_t *a, const sc_iditem_t *b)
593 {
594   return scamper_addr_cmp(a->addr, b->addr);
595 }
596 
sc_iditem_free(sc_iditem_t * item)597 static void sc_iditem_free(sc_iditem_t *item)
598 {
599   if(item == NULL)
600     return;
601   if(item->addr != NULL) scamper_addr_free(item->addr);
602   free(item);
603   return;
604 }
605 
sc_iditem_get(sc_idset_t * set,scamper_addr_t * addr)606 static sc_iditem_t *sc_iditem_get(sc_idset_t *set, scamper_addr_t *addr)
607 {
608   sc_iditem_t fm, *item;
609 
610   fm.addr = addr;
611   if((item = array_find((void **)set->items, set->itemc, &fm,
612 			(array_cmp_t)sc_iditem_cmp)) != NULL)
613     return item;
614 
615   if((item = malloc_zero(sizeof(sc_iditem_t))) == NULL)
616     return NULL;
617   item->addr = scamper_addr_use(addr);
618   if(array_insert((void ***)&set->items, &set->itemc, item,
619 		  (array_cmp_t)sc_iditem_cmp) != 0)
620     {
621       sc_iditem_free(item);
622       return NULL;
623     }
624 
625   return item;
626 }
627 
sc_idset_cmp(const sc_idset_t * a,const sc_idset_t * b)628 static int sc_idset_cmp(const sc_idset_t *a, const sc_idset_t *b)
629 {
630   if(a->userid < b->userid) return -1;
631   if(a->userid > b->userid) return  1;
632   return 0;
633 }
634 
sc_idset_free(sc_idset_t * set)635 static void sc_idset_free(sc_idset_t *set)
636 {
637   int i;
638 
639   if(set == NULL)
640     return;
641 
642   if(set->items != NULL)
643     {
644       for(i=0; i<set->itemc; i++)
645 	sc_iditem_free(set->items[i]);
646       free(set->items);
647     }
648   free(set);
649   return;
650 }
651 
sc_idset_get(splaytree_t * tree,uint32_t id)652 static sc_idset_t *sc_idset_get(splaytree_t *tree, uint32_t id)
653 {
654   sc_idset_t fm, *set; fm.userid = id;
655 
656   /* see if we already have a collection for this id */
657   if((set = splaytree_find(tree, &fm)) != NULL)
658     return set;
659 
660   /* no collection, alloc a new one */
661   if((set = malloc_zero(sizeof(sc_idset_t))) == NULL)
662     return NULL;
663   set->userid = id;
664 
665   /* add it to the tree.  if it fails, free the memory */
666   if(splaytree_insert(tree, set) == NULL)
667     {
668       free(set);
669       return NULL;
670     }
671 
672   return set;
673 }
674 
sc_idset_incongruent(const sc_idset_t * set)675 static int sc_idset_incongruent(const sc_idset_t *set)
676 {
677   sc_iditem_t *first, *item;
678   int i;
679 
680   first = set->items[0];
681   for(i=1; i<set->itemc; i++)
682     {
683       item = set->items[i];
684       if(first->results != item->results)
685 	return 1;
686     }
687 
688   return 0;
689 }
690 
sc_idset_print(const sc_idset_t * set)691 static void sc_idset_print(const sc_idset_t *set)
692 {
693   char fsaddr[30], buf[128];
694   size_t maxaddr, max, len;
695   sc_policytest_t *pt;
696   sc_iditem_t *item;
697   uint32_t o;
698   int i, j;
699 
700   /* should we print this at all? */
701   if((flags & FLAG_INCONGRUENT) != 0 && sc_idset_incongruent(set) == 0)
702     return;
703 
704   /*
705    * first, figure out the maximum width IP address in the set, and
706    * which protocols are open at all
707    */
708   maxaddr = 0; o = 0;
709   for(i=0; i<set->itemc; i++)
710     {
711       item = set->items[i];
712       scamper_addr_tostr(item->addr, buf, sizeof(buf));
713       if((len = strlen(buf)) > maxaddr)
714 	maxaddr = len;
715       o |= item->results;
716     }
717   snprintf(fsaddr, sizeof(fsaddr), "%%%ds :", (int)maxaddr);
718 
719   /* next, figure out the maximum width protocol name tested */
720   max = 0;
721   for(j=0; j<PT_METHOD_MAX; j++)
722     {
723       if((set->tests & (1 << j)) == 0)
724 	continue;
725       pt = &methods[j];
726       if(pt->namelen > max)
727 	max = pt->namelen;
728     }
729 
730   /* print the header that goes at the top of the router's results */
731   for(len=0; len<max; len++)
732     {
733       printf(fsaddr, "");
734       for(j=0; j<PT_METHOD_MAX; j++)
735 	{
736 	  if((set->tests & (1 << j)) == 0)
737 	    continue;
738 	  pt = &methods[j];
739 	  if(len < max - pt->namelen)
740 	    {
741 	      printf("   ");
742 	      continue;
743 	    }
744 	  printf("  %c", pt->name[len-(max-pt->namelen)]);
745 	}
746       printf("\n");
747     }
748 
749   /* print a line under the header */
750   for(len=0; len<maxaddr; len++)
751     printf("=");
752   printf("==");
753   for(j=0; j<PT_METHOD_MAX; j++)
754     {
755       if((set->tests & (1 << j)) == 0)
756 	continue;
757       printf("===");
758     }
759   printf("\n");
760 
761   /* report the open ports */
762   for(i=0; i<set->itemc; i++)
763     {
764       item = set->items[i];
765       printf(fsaddr, scamper_addr_tostr(item->addr, buf, sizeof(buf)));
766       for(j=0; j<PT_METHOD_MAX; j++)
767 	{
768 	  if((set->tests & (1 << j)) == 0)
769 	    continue;
770 	  if(item->results & (1 << j))
771 	    printf("  O");
772 	  else if((item->tests & (1 << j)) == 0)
773 	    printf("  ?");
774 	  else if(o & (1 << j))
775 	    printf("  X");
776 	  else
777 	    printf("   ");
778 	}
779       printf("\n");
780     }
781   printf("\n");
782 
783   return;
784 }
785 
sc_ip2n2i_cmp(const sc_ip2n2i_t * a,const sc_ip2n2i_t * b)786 static int sc_ip2n2i_cmp(const sc_ip2n2i_t *a, const sc_ip2n2i_t *b)
787 {
788   return scamper_addr_cmp(a->ip, b->ip);
789 }
790 
sc_ip2n2i_free(sc_ip2n2i_t * ip2n2i)791 static void sc_ip2n2i_free(sc_ip2n2i_t *ip2n2i)
792 {
793   if(ip2n2i == NULL)
794     return;
795   if(ip2n2i->tree_node != NULL)
796     splaytree_remove_node(n2i_tree, ip2n2i->tree_node);
797   if(ip2n2i->ip != NULL)
798     scamper_addr_free(ip2n2i->ip);
799   free(ip2n2i);
800   return;
801 }
802 
sc_ip2n2i_find(scamper_addr_t * ip)803 static sc_ip2n2i_t *sc_ip2n2i_find(scamper_addr_t *ip)
804 {
805   sc_ip2n2i_t fm; fm.ip = ip;
806   return splaytree_find(n2i_tree, &fm);
807 }
808 
sc_ip2n2i_get(scamper_addr_t * ip,sc_name2ips_t * n2i)809 static sc_ip2n2i_t *sc_ip2n2i_get(scamper_addr_t *ip, sc_name2ips_t *n2i)
810 {
811   sc_ip2n2i_t *ip2n2i;
812 
813   if((ip2n2i = sc_ip2n2i_find(ip)) != NULL)
814     {
815       assert(ip2n2i->n2i == n2i);
816       return ip2n2i;
817     }
818 
819   if((ip2n2i = malloc_zero(sizeof(sc_ip2n2i_t))) == NULL)
820     return NULL;
821   ip2n2i->ip = scamper_addr_use(ip);
822   ip2n2i->n2i = n2i;
823   if((ip2n2i->tree_node = splaytree_insert(n2i_tree, ip2n2i)) == NULL)
824     {
825       sc_ip2n2i_free(ip2n2i);
826       return NULL;
827     }
828 
829   return ip2n2i;
830 }
831 
sc_name2ips_methods(sc_name2ips_t * n2i)832 static void sc_name2ips_methods(sc_name2ips_t *n2i)
833 {
834   int i;
835   slist_empty(n2i->methods);
836   for(i=0; i<methodc; i++)
837     if(methods[i].flags & PT_FLAG_USE)
838       slist_tail_push(n2i->methods, &methods[i]);
839   slist_shuffle(n2i->methods);
840   return;
841 }
842 
sc_name2ips_name_cmp(const sc_name2ips_t * a,const sc_name2ips_t * b)843 static int sc_name2ips_name_cmp(const sc_name2ips_t *a, const sc_name2ips_t *b)
844 {
845   return strcmp(a->name, b->name);
846 }
847 
sc_name2ips_addr_cmp(const sc_name2ips_t * a,const sc_name2ips_t * b)848 static int sc_name2ips_addr_cmp(const sc_name2ips_t *a, const sc_name2ips_t *b)
849 {
850   int ac = slist_count(a->addrs);
851   int bc = slist_count(b->addrs);
852   if(ac > bc) return -1;
853   if(ac < bc) return  1;
854   return 0;
855 }
856 
sc_name2ips_free(sc_name2ips_t * n2i)857 static void sc_name2ips_free(sc_name2ips_t *n2i)
858 {
859   scamper_addr_t *addr;
860   if(n2i == NULL) return;
861   if(n2i->tree_node != NULL)
862     splaytree_remove_node(n2i_tree, n2i->tree_node);
863   if(n2i->addrs != NULL)
864     {
865       while((addr = slist_head_pop(n2i->addrs)) != NULL)
866 	scamper_addr_free(addr);
867       slist_free(n2i->addrs);
868     }
869   if(n2i->methods != NULL)
870     slist_free(n2i->methods);
871   if(n2i->name != NULL)
872     free(n2i->name);
873   if(n2i->set != NULL)
874     sc_idset_free(n2i->set);
875   free(n2i);
876   return;
877 }
878 
sc_name2ips_get(char * name)879 static sc_name2ips_t *sc_name2ips_get(char *name)
880 {
881   sc_name2ips_t fm, *n2i = NULL;
882 
883   fm.name = name;
884   if((n2i = splaytree_find(n2i_tree, &fm)) != NULL)
885     return n2i;
886 
887   if((n2i = malloc_zero(sizeof(sc_name2ips_t))) == NULL ||
888      (name != NULL && (n2i->name = strdup(name)) == NULL) ||
889      (n2i->addrs = slist_alloc()) == NULL ||
890      (n2i->methods = slist_alloc()) == NULL ||
891      (n2i->tree_node = splaytree_insert(n2i_tree, n2i)) == NULL ||
892      slist_tail_push(n2i_list, n2i) == NULL)
893     {
894       fprintf(stderr, "sc_name2ips_get: could not alloc node\n");
895       goto err;
896     }
897   n2i->id = n2i_id++;
898 
899   return n2i;
900 
901  err:
902   if(n2i != NULL) sc_name2ips_free(n2i);
903   return NULL;
904 }
905 
do_method(void)906 static int do_method(void)
907 {
908   scamper_addr_t *addr;
909   sc_name2ips_t *n2i;
910   sc_policytest_t *pt;
911   sc_wait_t *w;
912   char buf[128], cmd[512];
913   size_t off = 0;
914 
915   if(more < 1)
916     return 0;
917 
918   if((w = heap_head_item(waiting)) != NULL && timeval_cmp(&now, &w->tv) >= 0)
919     {
920       heap_remove(waiting);
921       n2i = w->n2i;
922       free(w);
923     }
924   else if((n2i = slist_head_pop(n2i_list)) != NULL)
925     {
926       sc_name2ips_methods(n2i);
927     }
928   else return 0;
929 
930   addr = slist_head_item(n2i->addrs);
931   scamper_addr_tostr(addr, buf, sizeof(buf));
932 
933   if(sc_ip2n2i_get(addr, n2i) == NULL)
934     {
935       logprint("%s: could not sc_ip2n2i_get %s\n", __func__, buf);
936       return -1;
937     }
938 
939   pt = slist_head_pop(n2i->methods);
940   if(flags & FLAG_TRACEROUTE)
941     {
942       string_concat(cmd, sizeof(cmd), &off, "trace -q 1 -U %u", n2i->id);
943       if(pt->flags & PT_FLAG_ICMP)
944 	string_concat(cmd, sizeof(cmd), &off, " -P icmp-paris");
945       else if(pt->flags & PT_FLAG_TCP)
946 	string_concat(cmd, sizeof(cmd), &off, " -P tcp -d %u", pt->port);
947       else if(pt->flags & PT_FLAG_UDP)
948 	string_concat(cmd, sizeof(cmd), &off,
949 		      " -P udp-paris -O dl -O const-payload -d %u -p %s",
950 		      pt->port, pt->payload);
951       else
952 	return -1;
953       string_concat(cmd, sizeof(cmd), &off, " %s\n", buf);
954     }
955   else
956     {
957       string_concat(cmd, sizeof(cmd), &off, "ping -i 5 -U %u -c 1", n2i->id);
958       if(pt->flags & PT_FLAG_ICMP)
959 	string_concat(cmd, sizeof(cmd), &off, " -P icmp-echo");
960       else if(pt->flags & PT_FLAG_TCP)
961 	string_concat(cmd, sizeof(cmd), &off, " -P tcp-syn -d %u", pt->port);
962       else if(pt->flags & PT_FLAG_UDP)
963 	string_concat(cmd, sizeof(cmd), &off,
964 		      " -P udp -O dl -d %u -B %s",
965 		      pt->port, pt->payload);
966       string_concat(cmd, sizeof(cmd), &off, " %s\n", buf);
967     }
968   if(scamper_writebuf_send(scamper_wb, cmd, off) != 0)
969     {
970       fprintf(stderr, "could not send %s\n", cmd);
971       return -1;
972     }
973   n2i_last = n2i;
974 
975   probing++;
976   more--;
977 
978   logprint("p %d, w %d, l %d : %s", probing, heap_count(waiting),
979 	   slist_count(n2i_list), cmd);
980   return 0;
981 }
982 
983 /*
984  * infile_tuples_line
985  *
986  * read the input file, which contains <name, ip> tuples.
987  */
infile_tuples_line(char * line,void * param)988 static int infile_tuples_line(char *line, void *param)
989 {
990   splaytree_t *addrtree = param;
991   scamper_addr_t *addr = NULL;
992   sc_name2ips_t *n2i = NULL;
993   char *name, *ip;
994 
995   if(line[0] == '#' || line[0] == '\0')
996     return 0;
997 
998   name = line;
999   if((ip = string_nextword(line)) == NULL ||
1000      (addr = scamper_addr_resolve(AF_UNSPEC, ip)) == NULL)
1001     {
1002       fprintf(stderr, "malformed line in input file\n");
1003       goto err;
1004     }
1005 
1006   if(splaytree_find(addrtree, addr) != NULL)
1007     {
1008       fprintf(stderr, "%s in list multiple times, aborting\n", ip);
1009       goto err;
1010     }
1011 
1012   if(splaytree_insert(addrtree, scamper_addr_use(addr)) == NULL ||
1013      (n2i = sc_name2ips_get(name)) == NULL ||
1014      slist_tail_push(n2i->addrs, addr) == NULL)
1015     {
1016       fprintf(stderr, "could not stuff %s:%s\n", name, ip);
1017       goto err;
1018     }
1019 
1020   return 0;
1021 
1022  err:
1023   if(addr != NULL) scamper_addr_free(addr);
1024   return -1;
1025 }
1026 
infile_rows_line(char * line,void * param)1027 static int infile_rows_line(char *line, void *param)
1028 {
1029   splaytree_t *addrtree = param;
1030   scamper_addr_t *addr = NULL;
1031   sc_name2ips_t *n2i = NULL;
1032   char *name, *ip, *ptr;
1033 
1034   if(line[0] == '#' || line[0] == '\0')
1035     return 0;
1036 
1037   name = line;
1038   if((ip = string_nextword(line)) == NULL)
1039     {
1040       fprintf(stderr, "malformed line in input file\n");
1041       goto err;
1042     }
1043 
1044   if((addr = scamper_addr_resolve(AF_UNSPEC, name)) != NULL)
1045     n2i = sc_name2ips_get(name);
1046   else
1047     n2i = sc_name2ips_get(NULL);
1048   if(n2i == NULL)
1049     goto err;
1050 
1051   if(addr != NULL)
1052     {
1053       if(splaytree_find(addrtree, addr) != NULL)
1054 	{
1055 	  fprintf(stderr, "%s in list multiple times, aborting\n", ip);
1056 	  goto err;
1057 	}
1058       if(splaytree_insert(addrtree, scamper_addr_use(addr)) == NULL ||
1059 	 slist_tail_push(n2i->addrs, addr) == NULL)
1060 	{
1061 	  fprintf(stderr, "could not stuff %s:%s\n", name, ip);
1062 	  goto err;
1063 	}
1064     }
1065   ptr = string_nextword(ip);
1066 
1067   for(;;)
1068     {
1069       if((addr = scamper_addr_resolve(AF_UNSPEC, ip)) == NULL)
1070 	{
1071 	  fprintf(stderr, "%s is not an IP address\n", ip);
1072 	  goto err;
1073 	}
1074       if(splaytree_find(addrtree, addr) != NULL)
1075 	{
1076 	  fprintf(stderr, "%s in list multiple times, aborting\n", ip);
1077 	  goto err;
1078 	}
1079       if(splaytree_insert(addrtree, scamper_addr_use(addr)) == NULL ||
1080 	 slist_tail_push(n2i->addrs, addr) == NULL)
1081 	{
1082 	  fprintf(stderr, "could not stuff %s:%s\n", name, ip);
1083 	  goto err;
1084 	}
1085       if((ip = ptr) == NULL)
1086 	break;
1087       ptr = string_nextword(ip);
1088     }
1089 
1090   return 0;
1091 
1092  err:
1093   if(addr != NULL) scamper_addr_free(addr);
1094   return -1;
1095 }
1096 
do_infile(void)1097 static int do_infile(void)
1098 {
1099   splaytree_t *addrtree;
1100   slist_node_t *node;
1101   sc_name2ips_t *n2i;
1102 
1103   if((addrtree = splaytree_alloc((splaytree_cmp_t)scamper_addr_cmp)) == NULL)
1104     {
1105       fprintf(stderr, "could not alloc addrtree\n");
1106       return -1;
1107     }
1108 
1109   if(flags & FLAG_TUPLES)
1110     {
1111       if(file_lines(infile, infile_tuples_line, addrtree) != 0)
1112 	goto err;
1113     }
1114   else
1115     {
1116       if(file_lines(infile, infile_rows_line, addrtree) != 0)
1117 	goto err;
1118     }
1119   splaytree_free(addrtree, (splaytree_free_t)scamper_addr_free);
1120   addrtree = NULL;
1121 
1122   for(node=slist_head_node(n2i_list); node != NULL; node=slist_node_next(node))
1123     {
1124       n2i = slist_node_item(node);
1125       slist_shuffle(n2i->addrs);
1126       n2i->tree_node = NULL;
1127     }
1128   splaytree_free(n2i_tree, NULL);
1129 
1130   if(flags & FLAG_IMPATIENT)
1131     slist_qsort(n2i_list, (slist_cmp_t)sc_name2ips_addr_cmp);
1132   else
1133     slist_shuffle(n2i_list);
1134 
1135   if((n2i_tree = splaytree_alloc((splaytree_cmp_t)sc_ip2n2i_cmp)) == NULL)
1136     {
1137       fprintf(stderr, "could not alloc n2i_tree\n");
1138       return -1;
1139     }
1140 
1141   return 0;
1142 
1143  err:
1144   if(addrtree != NULL)
1145     splaytree_free(addrtree, (splaytree_free_t)scamper_addr_free);
1146   return -1;
1147 }
1148 
sc_name2ips_find(scamper_addr_t * dst)1149 static sc_name2ips_t *sc_name2ips_find(scamper_addr_t *dst)
1150 {
1151   sc_ip2n2i_t *ip2n2i;
1152   if((ip2n2i = sc_ip2n2i_find(dst)) == NULL)
1153     return NULL;
1154   return ip2n2i->n2i;
1155 }
1156 
do_decoderead_addr(scamper_addr_t * dst)1157 static int do_decoderead_addr(scamper_addr_t *dst)
1158 {
1159   scamper_addr_t *addr;
1160   struct timeval tv;
1161   sc_ip2n2i_t *ip2n2i;
1162   sc_name2ips_t *n2i;
1163   char buf[128];
1164 
1165   if((ip2n2i = sc_ip2n2i_find(dst)) == NULL)
1166     {
1167       logprint("%s: could not find %s\n", __func__,
1168 	       scamper_addr_tostr(dst, buf, sizeof(buf)));
1169       return -1;
1170     }
1171   n2i = ip2n2i->n2i;
1172 
1173   if(slist_count(n2i->methods) == 0)
1174     {
1175       sc_ip2n2i_free(ip2n2i);
1176       addr = slist_head_pop(n2i->addrs);
1177       scamper_addr_free(addr);
1178       if(slist_count(n2i->addrs) == 0)
1179 	{
1180 	  if(n2i->set != NULL)
1181 	    sc_idset_print(n2i->set);
1182 	  sc_name2ips_free(n2i);
1183 	  n2i = NULL;
1184 	}
1185       else
1186 	{
1187 	  sc_name2ips_methods(n2i);
1188 	}
1189     }
1190 
1191   if(n2i != NULL)
1192     {
1193       timeval_add_s(&tv, &now, 1);
1194       if(sc_wait(&tv, n2i) != 0)
1195 	{
1196 	  logprint("%s: could not wait\n", __func__);
1197 	  return -1;
1198 	}
1199     }
1200 
1201   return 0;
1202 }
1203 
do_decoderead_ping(scamper_ping_t * ping)1204 static int do_decoderead_ping(scamper_ping_t *ping)
1205 {
1206   sc_policytest_t *method;
1207   sc_name2ips_t *n2i;
1208   char buf[128];
1209   sc_iditem_t *item;
1210   int rc, code;
1211 
1212   if((options & OPT_DAEMON) == 0)
1213     {
1214       if((n2i = sc_name2ips_find(ping->dst)) == NULL)
1215 	{
1216 	  logprint("%s: could not find %s\n", __func__,
1217 		   scamper_addr_tostr(ping->dst, buf, sizeof(buf)));
1218 	  goto err;
1219 	}
1220 
1221       if(n2i->set == NULL &&
1222 	 (n2i->set = malloc_zero(sizeof(sc_idset_t))) == NULL)
1223 	{
1224 	  logprint("%s: could not malloc idset: %s\n",
1225 		   __func__, strerror(errno));
1226 	  goto err;
1227 	}
1228 
1229       if((method = ping_to_method(ping)) == NULL)
1230 	{
1231 	  logprint("%s: unhandled method\n", __func__);
1232 	  goto err;
1233 	}
1234       code = ping_r(method, ping);
1235       n2i->set->tests |= (1 << (method->id-1));
1236       if((item = sc_iditem_get(n2i->set, ping->dst)) == NULL)
1237 	{
1238 	  logprint("%s: could not get item for %s: %s\n", __func__,
1239 		   scamper_addr_tostr(ping->dst, buf, sizeof(buf)),
1240 		   strerror(errno));
1241 	  goto err;
1242 	}
1243       item->tests |= (1 << (method->id-1));
1244       if(code == 1)
1245 	item->results |= (1 << (method->id-1));
1246     }
1247 
1248   rc = do_decoderead_addr(ping->dst);
1249   scamper_ping_free(ping);
1250   return rc;
1251 
1252  err:
1253   scamper_ping_free(ping);
1254   return -1;
1255 }
1256 
do_decoderead_trace(scamper_trace_t * trace)1257 static int do_decoderead_trace(scamper_trace_t *trace)
1258 {
1259   int rc = do_decoderead_addr(trace->dst);
1260   scamper_trace_free(trace);
1261   return rc;
1262 }
1263 
do_n2i_next(void)1264 static int do_n2i_next(void)
1265 {
1266   scamper_addr_t *addr;
1267   struct timeval tv;
1268   sc_ip2n2i_t *ip2n2i;
1269   char buf[128];
1270 
1271   assert(n2i_last != NULL);
1272 
1273   addr = slist_head_item(n2i_last->addrs);
1274   scamper_addr_tostr(addr, buf, sizeof(buf));
1275 
1276   if((ip2n2i = sc_ip2n2i_find(addr)) == NULL)
1277     {
1278       logprint("%s: could not find %s\n", __func__, buf);
1279       return -1;
1280     }
1281   if(ip2n2i->n2i != n2i_last)
1282     {
1283       logprint("%s: different n2i %u than expected %u\n", __func__,
1284 	       ip2n2i->n2i->id, n2i_last->id);
1285       return -1;
1286     }
1287 
1288   logprint("%s: skipping %s\n", __func__, buf);
1289 
1290   sc_ip2n2i_free(ip2n2i);
1291   addr = slist_head_pop(n2i_last->addrs);
1292   scamper_addr_free(addr);
1293   if(slist_count(n2i_last->addrs) == 0)
1294     {
1295       sc_name2ips_free(n2i_last);
1296       n2i_last = NULL;
1297     }
1298   else
1299     {
1300       sc_name2ips_methods(n2i_last);
1301     }
1302 
1303   if(n2i_last != NULL)
1304     {
1305       timeval_add_s(&tv, &now, 1);
1306       if(sc_wait(&tv, n2i_last) != 0)
1307 	{
1308 	  logprint("%s: could not wait %s\n", __func__, buf);
1309 	  return -1;
1310 	}
1311     }
1312   return 0;
1313 }
1314 
do_decoderead(void)1315 static int do_decoderead(void)
1316 {
1317   void     *data;
1318   uint16_t  type;
1319 
1320   /* try and read a traceroute from the warts decoder */
1321   if(scamper_file_read(decode_in, ffilter, &type, &data) != 0)
1322     {
1323       fprintf(stderr, "do_decoderead: scamper_file_read errno %d\n", errno);
1324       return -1;
1325     }
1326   if(data == NULL)
1327     {
1328       if(scamper_file_geteof(decode_in) != 0)
1329 	{
1330 	  scamper_file_close(decode_in);
1331 	  decode_in = NULL;
1332 	  decode_in_fd = -1;
1333 	}
1334       return 0;
1335     }
1336   probing--;
1337 
1338   if(scamper_file_write_obj(outfile, type, data) != 0)
1339     {
1340       fprintf(stderr, "do_decoderead: could not write obj %d\n", type);
1341       /* XXX: free data */
1342       return -1;
1343     }
1344 
1345   if(type == SCAMPER_FILE_OBJ_PING)
1346     return do_decoderead_ping(data);
1347   else if(type == SCAMPER_FILE_OBJ_TRACE)
1348     return do_decoderead_trace(data);
1349 
1350   logprint("%s: unknown type %d\n", __func__, type);
1351   return -1;
1352 }
1353 
do_scamperread_line(void * param,uint8_t * buf,size_t linelen)1354 static int do_scamperread_line(void *param, uint8_t *buf, size_t linelen)
1355 {
1356   char *head = (char *)buf;
1357   uint8_t uu[64];
1358   size_t uus;
1359   long lo;
1360 
1361   /* skip empty lines */
1362   if(head[0] == '\0')
1363     return 0;
1364 
1365   /* if currently decoding data, then pass it to uudecode */
1366   if(data_left > 0)
1367     {
1368       uus = sizeof(uu);
1369       if(uudecode_line(head, linelen, uu, &uus) != 0)
1370 	{
1371 	  fprintf(stderr, "could not uudecode_line\n");
1372 	  return -1;
1373 	}
1374       if(uus != 0)
1375 	scamper_writebuf_send(decode_wb, uu, uus);
1376       data_left -= (linelen + 1);
1377       return 0;
1378     }
1379 
1380   /* feedback letting us know that the command was accepted */
1381   if(linelen >= 2 && strncasecmp(head, "OK", 2) == 0)
1382     return 0;
1383 
1384   /* if the scamper process is asking for more tasks, give it more */
1385   if(linelen == 4 && strncasecmp(head, "MORE", linelen) == 0)
1386     {
1387       more++;
1388       if(do_method() != 0)
1389 	return -1;
1390       return 0;
1391     }
1392 
1393   /* new piece of data */
1394   if(linelen > 5 && strncasecmp(head, "DATA ", 5) == 0)
1395     {
1396       if(string_isnumber(head+5) == 0 || string_tolong(head+5, &lo) != 0)
1397 	{
1398 	  fprintf(stderr, "could not parse %s\n", head);
1399 	  return -1;
1400 	}
1401       data_left = lo;
1402       return 0;
1403     }
1404 
1405   /* feedback letting us know that the command was not accepted */
1406   if(linelen >= 3 && strncasecmp(head, "ERR", 3) == 0)
1407     {
1408       more++;
1409       if(do_n2i_next() != 0 || do_method() != 0)
1410 	return -1;
1411       return 0;
1412     }
1413 
1414   fprintf(stderr, "unknown response '%s'\n", head);
1415   return -1;
1416 }
1417 
do_scamperread(void)1418 static int do_scamperread(void)
1419 {
1420   ssize_t rc;
1421   uint8_t buf[512];
1422 
1423   if((rc = read(scamper_fd, buf, sizeof(buf))) > 0)
1424     {
1425       scamper_linepoll_handle(scamper_lp, buf, rc);
1426       return 0;
1427     }
1428   else if(rc == 0)
1429     {
1430       close(scamper_fd);
1431       scamper_fd = -1;
1432       return 0;
1433     }
1434   else if(errno == EINTR || errno == EAGAIN)
1435     {
1436       return 0;
1437     }
1438 
1439   fprintf(stderr, "could not read: errno %d\n", errno);
1440   return -1;
1441 }
1442 
1443 /*
1444  * do_scamperconnect
1445  *
1446  * allocate socket and connect to scamper process listening on the port
1447  * specified.
1448  */
do_scamperconnect(void)1449 static int do_scamperconnect(void)
1450 {
1451   struct sockaddr *sa;
1452   struct sockaddr_un sun;
1453   struct sockaddr_in sin;
1454   struct in_addr in;
1455   socklen_t sl;
1456 
1457   if(options & OPT_PORT)
1458     {
1459       inet_aton("127.0.0.1", &in);
1460       sockaddr_compose((struct sockaddr *)&sin, AF_INET, &in, port);
1461       sa = (struct sockaddr *)&sin; sl = sizeof(sin);
1462       if((scamper_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
1463 	{
1464 	  fprintf(stderr, "could not allocate new socket\n");
1465 	  return -1;
1466 	}
1467     }
1468   else if(options & OPT_UNIX)
1469     {
1470       if(sockaddr_compose_un((struct sockaddr *)&sun, unix_name) != 0)
1471 	{
1472 	  fprintf(stderr, "could not build sockaddr_un\n");
1473 	  return -1;
1474 	}
1475       sa = (struct sockaddr *)&sun; sl = sizeof(sun);
1476       if((scamper_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
1477 	{
1478 	  fprintf(stderr, "could not allocate unix domain socket\n");
1479 	  return -1;
1480 	}
1481     }
1482   else return -1;
1483 
1484   if(connect(scamper_fd, sa, sl) != 0)
1485     {
1486       fprintf(stderr, "could not connect to scamper process\n");
1487       return -1;
1488     }
1489 
1490   if(fcntl_set(scamper_fd, O_NONBLOCK) == -1)
1491     {
1492       fprintf(stderr, "could not set nonblock on scamper_fd\n");
1493       return -1;
1494     }
1495 
1496   return 0;
1497 }
1498 
fp_data(void)1499 static int fp_data(void)
1500 {
1501   struct timeval tv, *tv_ptr;
1502   fd_set rfds, wfds, *wfdsp;
1503   sc_wait_t *w;
1504   int pair[2];
1505   int nfds;
1506 
1507   random_seed();
1508 
1509   if((n2i_tree = splaytree_alloc((splaytree_cmp_t)sc_name2ips_name_cmp)) == NULL ||
1510      (n2i_list = slist_alloc()) == NULL ||
1511      (waiting = heap_alloc(sc_wait_cmp)) == NULL ||
1512      (scamper_wb = scamper_writebuf_alloc()) == NULL ||
1513      (scamper_lp = scamper_linepoll_alloc(do_scamperread_line,NULL)) == NULL ||
1514      (decode_wb = scamper_writebuf_alloc()) == NULL ||
1515      do_infile() != 0 || do_scamperconnect() != 0 ||
1516      (outfile = scamper_file_open(outfile_name, 'w', "warts")) == NULL ||
1517      socketpair(AF_UNIX, SOCK_STREAM, 0, pair) != 0 ||
1518      (decode_in = scamper_file_openfd(pair[0], NULL, 'r', "warts")) == NULL ||
1519      fcntl_set(pair[0], O_NONBLOCK) == -1 ||
1520      fcntl_set(pair[1], O_NONBLOCK) == -1)
1521     return -1;
1522 
1523   decode_in_fd = pair[0];
1524   decode_out_fd = pair[1];
1525   scamper_writebuf_send(scamper_wb, "attach\n", 7);
1526 
1527   for(;;)
1528     {
1529       nfds = 0; FD_ZERO(&rfds); FD_ZERO(&wfds); wfdsp = NULL;
1530       if(scamper_fd < 0 && decode_in_fd < 0)
1531 	break;
1532 
1533       if(scamper_fd >= 0)
1534 	{
1535 	  FD_SET(scamper_fd, &rfds);
1536 	  if(nfds < scamper_fd) nfds = scamper_fd;
1537 	  if(scamper_writebuf_len(scamper_wb) > 0)
1538 	    {
1539 	      FD_SET(scamper_fd, &wfds);
1540 	      wfdsp = &wfds;
1541 	    }
1542 	}
1543 
1544       if(decode_in_fd >= 0)
1545 	{
1546 	  FD_SET(decode_in_fd, &rfds);
1547 	  if(nfds < decode_in_fd) nfds = decode_in_fd;
1548 	}
1549 
1550       if(decode_out_fd >= 0 && scamper_writebuf_len(decode_wb) > 0)
1551 	{
1552 	  FD_SET(decode_out_fd, &wfds);
1553 	  wfdsp = &wfds;
1554 	  if(nfds < decode_out_fd) nfds = decode_out_fd;
1555 	}
1556 
1557       /*
1558        * need to set a timeout on select if scamper's processing window is
1559        * not full and there is a trace in the waiting queue.
1560        */
1561       tv_ptr = NULL;
1562       if(more > 0)
1563 	{
1564 	  gettimeofday_wrap(&now);
1565 
1566 	  /*
1567 	   * if there is something ready to probe now, then try and
1568 	   * do it.
1569 	   */
1570 	  w = heap_head_item(waiting);
1571 	  if(slist_count(n2i_list) > 0 ||
1572 	     (w != NULL && timeval_cmp(&w->tv, &now) <= 0))
1573 	    {
1574 	      if(do_method() != 0)
1575 		return -1;
1576 	    }
1577 
1578 	  /*
1579 	   * if we could not send a new command just yet, but scamper
1580 	   * wants one, then wait for an appropriate length of time.
1581 	   */
1582 	  w = heap_head_item(waiting);
1583 	  if(more > 0 && tv_ptr == NULL && w != NULL)
1584 	    {
1585 	      tv_ptr = &tv;
1586 	      if(timeval_cmp(&w->tv, &now) > 0)
1587 		timeval_diff_tv(&tv, &now, &w->tv);
1588 	      else
1589 		memset(&tv, 0, sizeof(tv));
1590 	    }
1591 	}
1592 
1593       if(splaytree_count(n2i_tree) == 0 && slist_count(n2i_list) == 0 &&
1594 	 heap_count(waiting) == 0)
1595 	{
1596 	  logprint("done\n");
1597 	  break;
1598 	}
1599 
1600       if(select(nfds+1, &rfds, wfdsp, NULL, tv_ptr) < 0)
1601 	{
1602 	  if(errno == EINTR) continue;
1603 	  fprintf(stderr, "select error\n");
1604 	  break;
1605 	}
1606 
1607       gettimeofday_wrap(&now);
1608 
1609       if(more > 0)
1610 	{
1611 	  if(do_method() != 0)
1612 	    return -1;
1613 	}
1614 
1615       if(scamper_fd >= 0)
1616 	{
1617 	  if(FD_ISSET(scamper_fd, &rfds) && do_scamperread() != 0)
1618 	    return -1;
1619 	  if(wfdsp != NULL && FD_ISSET(scamper_fd, wfdsp) &&
1620 	     scamper_writebuf_write(scamper_fd, scamper_wb) != 0)
1621 	    {
1622 	      logprint("could not write to scamper_fd: %d %s\n",
1623 		       errno, strerror(errno));
1624 	      return -1;
1625 	    }
1626 	}
1627 
1628       if(decode_in_fd >= 0)
1629 	{
1630 	  if(FD_ISSET(decode_in_fd, &rfds) && do_decoderead() != 0)
1631 	    return -1;
1632 	}
1633 
1634       if(decode_out_fd >= 0)
1635 	{
1636 	  if(wfdsp != NULL && FD_ISSET(decode_out_fd, wfdsp) &&
1637 	     scamper_writebuf_write(decode_out_fd, decode_wb) != 0)
1638 	    {
1639 	      logprint("could not write to decode_out_fd: %d %s\n",
1640 		       errno, strerror(errno));
1641 	      return -1;
1642 	    }
1643 
1644 	  if(scamper_fd < 0 && scamper_writebuf_len(decode_wb) == 0)
1645 	    {
1646 	      close(decode_out_fd);
1647 	      decode_out_fd = -1;
1648 	    }
1649 	}
1650     }
1651 
1652   return 0;
1653 }
1654 
sc_idset_print_cb(void * ptr,void * item)1655 static int sc_idset_print_cb(void *ptr, void *item)
1656 {
1657   sc_idset_print(item);
1658   return 0;
1659 }
1660 
fp_read(void)1661 static int fp_read(void)
1662 {
1663   splaytree_t *tree = NULL;
1664   scamper_file_t *in = NULL;
1665   sc_policytest_t *method;
1666   scamper_ping_t *ping;
1667   sc_idset_t *set;
1668   sc_iditem_t *item;
1669   uint16_t type;
1670   void *data;
1671   int code;
1672 
1673   if(string_isdash(datafile) != 0)
1674     in = scamper_file_openfd(STDIN_FILENO, "-", 'r', "warts");
1675   else
1676     in = scamper_file_open(datafile, 'r', NULL);
1677   if(in == NULL)
1678     {
1679       fprintf(stderr, "could not open %s: %s\n", datafile, strerror(errno));
1680       goto err;
1681     }
1682 
1683   if((tree = splaytree_alloc((splaytree_cmp_t)sc_idset_cmp)) == NULL)
1684     goto err;
1685 
1686   while(scamper_file_read(in, ffilter, &type, &data) == 0)
1687     {
1688       if(data == NULL)
1689 	break;
1690 
1691       ping = data;
1692       if((method = ping_to_method(ping)) == NULL)
1693 	goto err;
1694       code = ping_r(method, ping);
1695 
1696       if((set = sc_idset_get(tree, ping->userid)) == NULL)
1697 	goto err;
1698       set->tests |= (1 << (method->id-1));
1699 
1700       if((item = sc_iditem_get(set, ping->dst)) == NULL)
1701 	goto err;
1702       item->tests |= (1 << (method->id-1));
1703       if(code == 1)
1704 	item->results |= (1 << (method->id-1));
1705 
1706       scamper_ping_free(ping);
1707     }
1708   scamper_file_close(in);
1709 
1710   splaytree_inorder(tree, sc_idset_print_cb, NULL);
1711   splaytree_free(tree, (splaytree_free_t)sc_idset_free);
1712 
1713   return 0;
1714 
1715  err:
1716   return -1;
1717 }
1718 
fp_init(void)1719 static int fp_init(void)
1720 {
1721   uint16_t type;
1722 
1723   if(flags & FLAG_TRACEROUTE)
1724     type = SCAMPER_FILE_OBJ_TRACE;
1725   else
1726     type = SCAMPER_FILE_OBJ_PING;
1727 
1728   if((ffilter = scamper_file_filter_alloc(&type, 1)) == NULL)
1729     return -1;
1730 
1731   return 0;
1732 }
1733 
cleanup(void)1734 static void cleanup(void)
1735 {
1736   if(n2i_tree != NULL) splaytree_free(n2i_tree, NULL);
1737   if(n2i_list != NULL) slist_free(n2i_list);
1738   if(waiting != NULL) heap_free(waiting, free);
1739   if(scamper_wb != NULL) scamper_writebuf_free(scamper_wb);
1740   if(scamper_lp != NULL) scamper_linepoll_free(scamper_lp, 0);
1741   if(decode_wb != NULL) scamper_writebuf_free(decode_wb);
1742   if(outfile != NULL) scamper_file_close(outfile);
1743   if(decode_in != NULL) scamper_file_close(decode_in);
1744   if(ffilter != NULL) scamper_file_filter_free(ffilter);
1745   if(logfile != NULL) fclose(logfile);
1746   if(decode_in_fd != -1) close(decode_in_fd);
1747   if(decode_out_fd != -1) close(decode_out_fd);
1748   if(scamper_fd != -1) close(scamper_fd);
1749   return;
1750 }
1751 
main(int argc,char * argv[])1752 int main(int argc, char *argv[])
1753 {
1754 #if defined(DMALLOC)
1755   free(malloc(1));
1756 #endif
1757 
1758   atexit(cleanup);
1759 
1760   if(check_options(argc, argv) != 0)
1761     return -1;
1762 
1763   /* start a daemon if asked to */
1764   if((options & OPT_DAEMON) != 0 && daemon(1, 0) != 0)
1765     return -1;
1766 
1767   if(fp_init() != 0)
1768     return -1;
1769 
1770   if(options & OPT_READ)
1771     return fp_read();
1772 
1773   return fp_data();
1774 }
1775