1 /*
2  * scamper_task.c
3  *
4  * $Id: scamper_task.c,v 1.71 2020/03/28 20:45:52 mjl Exp $
5  *
6  * Copyright (C) 2005-2006 Matthew Luckie
7  * Copyright (C) 2006-2011 The University of Waikato
8  * Copyright (C) 2012-2015 The Regents of the University of California
9  * Copyright (C) 2016-2020 Matthew Luckie
10  * Author: Matthew Luckie
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation, version 2.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  *
25  */
26 
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30 #include "internal.h"
31 
32 #include "scamper.h"
33 #include "scamper_addr.h"
34 #include "scamper_icmp_resp.h"
35 #include "scamper_fds.h"
36 #include "scamper_task.h"
37 #include "scamper_queue.h"
38 #include "scamper_debug.h"
39 #include "scamper_list.h"
40 #include "scamper_cyclemon.h"
41 #include "scamper_outfiles.h"
42 #include "scamper_sources.h"
43 #include "scamper_file.h"
44 #include "scamper_rtsock.h"
45 #include "scamper_dl.h"
46 #include "mjl_list.h"
47 #include "mjl_splaytree.h"
48 #include "mjl_patricia.h"
49 #include "utils.h"
50 
51 struct scamper_task
52 {
53   /* the data pointer points to the collected data */
54   void                     *data;
55 
56   /* any state kept during the data collection is kept here */
57   void                     *state;
58 
59   /* state / details kept internally to the task */
60   dlist_t                  *onhold;
61 
62   /* various callbacks that scamper uses to handle this task */
63   scamper_task_funcs_t     *funcs;
64 
65   /* pointer to a queue structure that manages this task in the queues */
66   scamper_queue_t          *queue;
67 
68   /* pointer to where the task came from */
69   scamper_sourcetask_t     *sourcetask;
70 
71   /* pointer to cycle monitor structure, if used */
72   struct scamper_cyclemon  *cyclemon;
73 
74   /* signature of probes sent by this task */
75   slist_t                  *siglist;
76 
77   /* list of ancillary data */
78   dlist_t                  *ancillary;
79 
80   /* file descriptors held by the task */
81   scamper_fd_t            **fds;
82   int                       fdc;
83 };
84 
85 struct scamper_task_anc
86 {
87   void         *data;
88   void        (*freedata)(void *);
89   dlist_node_t *node;
90 };
91 
92 typedef struct s2t
93 {
94   scamper_task_sig_t *sig;
95   scamper_task_t     *task;
96   void               *node;
97 } s2t_t;
98 
99 typedef struct task_onhold
100 {
101   void          (*unhold)(void *param);
102   void           *param;
103 } task_onhold_t;
104 
105 static patricia_t  *tx_ip4 = NULL;
106 static patricia_t  *tx_ip6 = NULL;
107 static patricia_t  *tx_nd4 = NULL;
108 static patricia_t  *tx_nd6 = NULL;
109 static dlist_t     *sniff = NULL;
110 static splaytree_t *host = NULL;
111 
tx_ip_cmp(const s2t_t * a,const s2t_t * b)112 static int tx_ip_cmp(const s2t_t *a, const s2t_t *b)
113 {
114   assert(a->sig->sig_type == SCAMPER_TASK_SIG_TYPE_TX_IP);
115   assert(b->sig->sig_type == SCAMPER_TASK_SIG_TYPE_TX_IP);
116   return scamper_addr_cmp(a->sig->sig_tx_ip_dst, b->sig->sig_tx_ip_dst);
117 }
118 
tx_ip_bit(const s2t_t * s2t,int bit)119 static int tx_ip_bit(const s2t_t *s2t, int bit)
120 {
121   assert(s2t->sig->sig_type == SCAMPER_TASK_SIG_TYPE_TX_IP);
122   return scamper_addr_bit(s2t->sig->sig_tx_ip_dst, bit);
123 }
124 
tx_ip_fbd(const s2t_t * a,const s2t_t * b)125 static int tx_ip_fbd(const s2t_t *a, const s2t_t *b)
126 {
127   assert(a->sig->sig_type == SCAMPER_TASK_SIG_TYPE_TX_IP);
128   assert(b->sig->sig_type == SCAMPER_TASK_SIG_TYPE_TX_IP);
129   return scamper_addr_fbd(a->sig->sig_tx_ip_dst, b->sig->sig_tx_ip_dst);
130 }
131 
tx_nd_cmp(const s2t_t * a,const s2t_t * b)132 static int tx_nd_cmp(const s2t_t *a, const s2t_t *b)
133 {
134   assert(a->sig->sig_type == SCAMPER_TASK_SIG_TYPE_TX_ND);
135   assert(b->sig->sig_type == SCAMPER_TASK_SIG_TYPE_TX_ND);
136   return scamper_addr_cmp(a->sig->sig_tx_nd_ip, b->sig->sig_tx_nd_ip);
137 }
138 
tx_nd_bit(const s2t_t * s2t,int bit)139 static int tx_nd_bit(const s2t_t *s2t, int bit)
140 {
141   assert(s2t->sig->sig_type == SCAMPER_TASK_SIG_TYPE_TX_ND);
142   return scamper_addr_bit(s2t->sig->sig_tx_nd_ip, bit);
143 }
144 
tx_nd_fbd(const s2t_t * a,const s2t_t * b)145 static int tx_nd_fbd(const s2t_t *a, const s2t_t *b)
146 {
147   assert(a->sig->sig_type == SCAMPER_TASK_SIG_TYPE_TX_ND);
148   assert(b->sig->sig_type == SCAMPER_TASK_SIG_TYPE_TX_ND);
149   return scamper_addr_fbd(a->sig->sig_tx_nd_ip, b->sig->sig_tx_nd_ip);
150 }
151 
host_cmp(const s2t_t * a,const s2t_t * b)152 static int host_cmp(const s2t_t *a, const s2t_t *b)
153 {
154   int i;
155   assert(a->sig->sig_type == SCAMPER_TASK_SIG_TYPE_HOST);
156   assert(b->sig->sig_type == SCAMPER_TASK_SIG_TYPE_HOST);
157   if((i = strcasecmp(a->sig->sig_host_name, b->sig->sig_host_name)) != 0)
158     return i;
159   if(a->sig->sig_host_type < b->sig->sig_host_type)
160     return -1;
161   if(a->sig->sig_host_type > b->sig->sig_host_type)
162     return 1;
163   return 0;
164 }
165 
tx_ip_check(scamper_dl_rec_t * dl)166 static void tx_ip_check(scamper_dl_rec_t *dl)
167 {
168   scamper_task_sig_t sig;
169   scamper_addr_t addr, addr2buf, *addr2 = NULL;
170   patricia_t *pt;
171   s2t_t fm, *s2t;
172 
173   if(SCAMPER_DL_IS_IPV4(dl))
174     {
175       addr.type = SCAMPER_ADDR_TYPE_IPV4;
176       pt = tx_ip4;
177     }
178   else if(SCAMPER_DL_IS_IPV6(dl))
179     {
180       addr.type = SCAMPER_ADDR_TYPE_IPV6;
181       pt = tx_ip6;
182     }
183   else return;
184 
185   if(dl->dl_ip_off != 0)
186     {
187       addr.addr = dl->dl_ip_src;
188     }
189   else if(SCAMPER_DL_IS_TCP(dl))
190     {
191       if((dl->dl_tcp_flags & TH_SYN) && (dl->dl_tcp_flags & TH_ACK) == 0)
192 	addr.addr = dl->dl_ip_dst;
193       else
194 	addr.addr = dl->dl_ip_src;
195     }
196   else if(SCAMPER_DL_IS_ICMP(dl))
197     {
198       if(SCAMPER_DL_IS_ICMP_ECHO_REQUEST(dl))
199 	addr.addr = dl->dl_ip_dst;
200       else if(SCAMPER_DL_IS_ICMP_ECHO_REPLY(dl))
201 	addr.addr = dl->dl_ip_src;
202       else if(SCAMPER_DL_IS_ICMP_TTL_EXP(dl))
203 	addr.addr = dl->dl_icmp_ip_dst;
204       else if(SCAMPER_DL_IS_ICMP_UNREACH(dl))
205 	addr.addr = dl->dl_icmp_ip_dst;
206       else if(SCAMPER_DL_IS_ICMP_PACKET_TOO_BIG(dl))
207 	addr.addr = dl->dl_icmp_ip_dst;
208       else
209 	return;
210     }
211   else if(SCAMPER_DL_IS_UDP(dl))
212     {
213       addr.addr = dl->dl_ip_dst;
214       addr2buf.type = addr.type;
215       addr2buf.addr = dl->dl_ip_src; addr2 = &addr2buf;
216     }
217   else
218     {
219       addr.addr = dl->dl_ip_dst;
220     }
221 
222   fm.sig = &sig;
223   sig.sig_type = SCAMPER_TASK_SIG_TYPE_TX_IP;
224   sig.sig_tx_ip_dst = &addr;
225 
226   if((s2t = patricia_find(pt, &fm)) != NULL &&
227      s2t->task->funcs->handle_dl != NULL)
228     {
229       s2t->task->funcs->handle_dl(s2t->task, dl);
230     }
231   else if(addr2 != NULL)
232     {
233       sig.sig_tx_ip_dst = addr2;
234       if((s2t = patricia_find(pt, &fm)) != NULL &&
235 	 s2t->task->funcs->handle_dl != NULL)
236 	{
237 	  s2t->task->funcs->handle_dl(s2t->task, dl);
238 	}
239     }
240 
241   return;
242 }
243 
tx_nd_check(scamper_dl_rec_t * dl)244 static void tx_nd_check(scamper_dl_rec_t *dl)
245 {
246   scamper_task_sig_t sig;
247   scamper_addr_t ip;
248   struct in_addr ip4;
249   struct in6_addr ip6;
250   patricia_t *pt;
251   s2t_t fm, *s2t;
252 
253   if(SCAMPER_DL_IS_ARP_OP_REPLY(dl) && SCAMPER_DL_IS_ARP_PRO_IPV4(dl))
254     {
255       if(patricia_count(tx_nd4) <= 0)
256 	return;
257       ip.type = SCAMPER_ADDR_TYPE_IPV4;
258       memcpy(&ip4, dl->dl_arp_spa, sizeof(ip4));
259       ip.addr = &ip4;
260       pt = tx_nd4;
261     }
262   else if(SCAMPER_DL_IS_ICMP6_ND_NADV(dl))
263     {
264       if(patricia_count(tx_nd6) <= 0)
265 	return;
266       ip.type = SCAMPER_ADDR_TYPE_IPV6;
267       memcpy(&ip6, dl->dl_icmp6_nd_target, sizeof(ip6));
268       ip.addr = &ip6;
269       pt = tx_nd6;
270     }
271   else return;
272 
273   sig.sig_type = SCAMPER_TASK_SIG_TYPE_TX_ND;
274   sig.sig_tx_nd_ip = &ip;
275   fm.sig = &sig;
276   if((s2t = patricia_find(pt, &fm)) == NULL)
277     return;
278 
279   if(s2t->task->funcs->handle_dl != NULL)
280     s2t->task->funcs->handle_dl(s2t->task, dl);
281 
282   return;
283 }
284 
sniff_check(scamper_dl_rec_t * dl)285 static void sniff_check(scamper_dl_rec_t *dl)
286 {
287   scamper_task_sig_t *sig;
288   s2t_t *s2t;
289   dlist_node_t *n;
290   scamper_addr_t src;
291   uint16_t id;
292 
293   if(dlist_count(sniff) <= 0)
294     return;
295 
296   if(SCAMPER_DL_IS_ICMP_ECHO_REPLY(dl))
297     id = dl->dl_icmp_id;
298   else if(SCAMPER_DL_IS_ICMP_Q_ICMP_ECHO(dl))
299     id = dl->dl_icmp_icmp_id;
300   else
301     return;
302 
303   if(SCAMPER_DL_IS_IPV4(dl))
304     src.type = SCAMPER_ADDR_TYPE_IPV4;
305   else if(SCAMPER_DL_IS_IPV6(dl))
306     src.type = SCAMPER_ADDR_TYPE_IPV6;
307   else
308     return;
309   src.addr = dl->dl_ip_dst;
310 
311   for(n = dlist_head_node(sniff); n != NULL; n = dlist_node_next(n))
312     {
313       s2t = dlist_node_item(n); sig = s2t->sig;
314       if(sig->sig_sniff_icmp_id != id)
315 	continue;
316       if(scamper_addr_cmp(sig->sig_sniff_src, &src) != 0)
317 	continue;
318 
319       if(s2t->task->funcs->handle_dl != NULL)
320 	s2t->task->funcs->handle_dl(s2t->task, dl);
321     }
322 
323   return;
324 }
325 
s2t_free(s2t_t * s2t)326 static void s2t_free(s2t_t *s2t)
327 {
328   scamper_task_sig_t *sig = s2t->sig;
329 
330   if(s2t == NULL)
331     return;
332 
333   if(s2t->node != NULL)
334     {
335       if(sig->sig_type == SCAMPER_TASK_SIG_TYPE_TX_IP)
336 	{
337 	  if(sig->sig_tx_ip_dst->type == SCAMPER_ADDR_TYPE_IPV4)
338 	    patricia_remove_node(tx_ip4, s2t->node);
339 	  else
340 	    patricia_remove_node(tx_ip6, s2t->node);
341 	}
342       else if(sig->sig_type == SCAMPER_TASK_SIG_TYPE_TX_ND)
343 	{
344 	  if(sig->sig_tx_nd_ip->type == SCAMPER_ADDR_TYPE_IPV4)
345 	    patricia_remove_node(tx_nd4, s2t->node);
346 	  else
347 	    patricia_remove_node(tx_nd6, s2t->node);
348 	}
349       else if(sig->sig_type == SCAMPER_TASK_SIG_TYPE_SNIFF)
350 	dlist_node_pop(sniff, s2t->node);
351       else if(sig->sig_type == SCAMPER_TASK_SIG_TYPE_HOST)
352 	splaytree_remove_node(host, s2t->node);
353     }
354 
355   free(s2t);
356   return;
357 }
358 
scamper_task_sig_tostr(scamper_task_sig_t * sig,char * buf,size_t len)359 char *scamper_task_sig_tostr(scamper_task_sig_t *sig, char *buf, size_t len)
360 {
361   char tmp[64];
362   size_t off = 0;
363 
364   if(sig->sig_type == SCAMPER_TASK_SIG_TYPE_TX_IP)
365     string_concat(buf, len, &off, "ip %s",
366 		  scamper_addr_tostr(sig->sig_tx_ip_dst, tmp, sizeof(tmp)));
367   else if(sig->sig_type == SCAMPER_TASK_SIG_TYPE_TX_ND)
368     string_concat(buf, len, &off, "nd %s",
369 		  scamper_addr_tostr(sig->sig_tx_nd_ip, tmp, sizeof(tmp)));
370   else if(sig->sig_type == SCAMPER_TASK_SIG_TYPE_SNIFF)
371     string_concat(buf, len, &off, "sniff %s icmp-id %04x",
372 		  scamper_addr_tostr(sig->sig_sniff_src, tmp, sizeof(tmp)),
373 		  sig->sig_sniff_icmp_id);
374   else if(sig->sig_type == SCAMPER_TASK_SIG_TYPE_HOST)
375     string_concat(buf, len, &off, "host %s %u",
376 		  sig->sig_host_name, sig->sig_host_type);
377   else
378     return NULL;
379 
380   return buf;
381 }
382 
scamper_task_sig_alloc(uint8_t type)383 scamper_task_sig_t *scamper_task_sig_alloc(uint8_t type)
384 {
385   scamper_task_sig_t *sig;
386   if((sig = malloc_zero(sizeof(scamper_task_sig_t))) != NULL)
387     sig->sig_type = type;
388   return sig;
389 }
390 
scamper_task_sig_free(scamper_task_sig_t * sig)391 void scamper_task_sig_free(scamper_task_sig_t *sig)
392 {
393   if(sig == NULL)
394     return;
395 
396   switch(sig->sig_type)
397     {
398     case SCAMPER_TASK_SIG_TYPE_TX_IP:
399       if(sig->sig_tx_ip_dst != NULL) scamper_addr_free(sig->sig_tx_ip_dst);
400       if(sig->sig_tx_ip_src != NULL) scamper_addr_free(sig->sig_tx_ip_src);
401       break;
402 
403     case SCAMPER_TASK_SIG_TYPE_TX_ND:
404       if(sig->sig_tx_nd_ip != NULL) scamper_addr_free(sig->sig_tx_nd_ip);
405       break;
406 
407     case SCAMPER_TASK_SIG_TYPE_SNIFF:
408       if(sig->sig_sniff_src != NULL) scamper_addr_free(sig->sig_sniff_src);
409       break;
410 
411     case SCAMPER_TASK_SIG_TYPE_HOST:
412       if(sig->sig_host_name != NULL) free(sig->sig_host_name);
413       break;
414     }
415 
416   free(sig);
417   return;
418 }
419 
scamper_task_anc_add(scamper_task_t * task,void * data,void (* freedata)(void *))420 scamper_task_anc_t *scamper_task_anc_add(scamper_task_t *task, void *data,
421 					 void (*freedata)(void *))
422 {
423   scamper_task_anc_t *anc = NULL;
424   if(task->ancillary == NULL && (task->ancillary = dlist_alloc()) == NULL)
425     return NULL;
426   if((anc = malloc_zero(sizeof(scamper_task_anc_t))) == NULL)
427     return NULL;
428   anc->data = data;
429   anc->freedata = freedata;
430   if((anc->node = dlist_tail_push(task->ancillary, anc)) == NULL)
431     {
432       free(anc);
433       return NULL;
434     }
435   return anc;
436 }
437 
scamper_task_anc_del(scamper_task_t * task,scamper_task_anc_t * anc)438 void scamper_task_anc_del(scamper_task_t *task, scamper_task_anc_t *anc)
439 {
440   if(anc == NULL)
441     return;
442   dlist_node_pop(task->ancillary, anc->node);
443   free(anc);
444   return;
445 }
446 
scamper_task_sig_add(scamper_task_t * task,scamper_task_sig_t * sig)447 int scamper_task_sig_add(scamper_task_t *task, scamper_task_sig_t *sig)
448 {
449   s2t_t *s2t;
450   if((s2t = malloc_zero(sizeof(s2t_t))) == NULL)
451     return -1;
452   s2t->sig = sig;
453   s2t->task = task;
454   if(slist_tail_push(task->siglist, s2t) == NULL)
455     {
456       free(s2t);
457       return -1;
458     }
459   return 0;
460 }
461 
scamper_task_find(scamper_task_sig_t * sig)462 scamper_task_t *scamper_task_find(scamper_task_sig_t *sig)
463 {
464   s2t_t fm, *s2t;
465 
466   fm.sig = sig;
467   if(sig->sig_type == SCAMPER_TASK_SIG_TYPE_TX_IP)
468     {
469       if(sig->sig_tx_ip_dst->type == SCAMPER_ADDR_TYPE_IPV4)
470 	s2t = patricia_find(tx_ip4, &fm);
471       else
472 	s2t = patricia_find(tx_ip6, &fm);
473     }
474   else if(sig->sig_type == SCAMPER_TASK_SIG_TYPE_TX_ND)
475     {
476       if(sig->sig_tx_nd_ip->type == SCAMPER_ADDR_TYPE_IPV4)
477 	s2t = patricia_find(tx_nd4, &fm);
478       else
479 	s2t = patricia_find(tx_nd6, &fm);
480     }
481   else
482     return NULL;
483 
484   if(s2t != NULL)
485     return s2t->task;
486   return NULL;
487 }
488 
scamper_task_sig_deinstall(scamper_task_t * task)489 void scamper_task_sig_deinstall(scamper_task_t *task)
490 {
491   s2t_t *s2t;
492   scamper_task_sig_t *sig;
493   slist_node_t *n;
494 
495   for(n=slist_head_node(task->siglist); n != NULL; n = slist_node_next(n))
496     {
497       s2t = slist_node_item(n); sig = s2t->sig;
498       s2t_free(s2t);
499       scamper_task_sig_free(sig);
500     }
501 
502   return;
503 }
504 
scamper_task_sig_install(scamper_task_t * task)505 int scamper_task_sig_install(scamper_task_t *task)
506 {
507   scamper_task_sig_t *sig;
508   scamper_task_t *tf;
509   s2t_t *s2t;
510   slist_node_t *n;
511 
512   if(slist_count(task->siglist) < 1)
513     {
514       printerror(__func__, "no signatures for task");
515       return -1;
516     }
517 
518   for(n=slist_head_node(task->siglist); n != NULL; n = slist_node_next(n))
519     {
520       s2t = slist_node_item(n); sig = s2t->sig;
521 
522       /* check if another task has this signature already */
523       if((tf = scamper_task_find(sig)) != NULL)
524 	{
525 	  if(tf != task)
526 	    goto err;
527 	  continue;
528 	}
529 
530       if(sig->sig_type == SCAMPER_TASK_SIG_TYPE_TX_IP)
531 	{
532 	  if(sig->sig_tx_ip_dst->type == SCAMPER_ADDR_TYPE_IPV4)
533 	    s2t->node = patricia_insert(tx_ip4, s2t);
534 	  else
535 	    s2t->node = patricia_insert(tx_ip6, s2t);
536 	}
537       else if(sig->sig_type == SCAMPER_TASK_SIG_TYPE_TX_ND)
538 	{
539 	  if(sig->sig_tx_nd_ip->type == SCAMPER_ADDR_TYPE_IPV4)
540 	    s2t->node = patricia_insert(tx_nd4, s2t);
541 	  else
542 	    s2t->node = patricia_insert(tx_nd6, s2t);
543 	}
544       else if(sig->sig_type == SCAMPER_TASK_SIG_TYPE_SNIFF)
545 	s2t->node = dlist_tail_push(sniff, s2t);
546       else if(sig->sig_type == SCAMPER_TASK_SIG_TYPE_HOST)
547 	s2t->node = splaytree_insert(host, s2t);
548 
549       if(s2t->node == NULL)
550 	{
551 	  scamper_debug(__func__, "could not install sig");
552 	  goto err;
553 	}
554     }
555 
556   return 0;
557 
558  err:
559   scamper_task_sig_deinstall(task);
560   return -1;
561 }
562 
563 /*
564  * scamper_task_sig_block
565  *
566  * go through the signatures and see if any conflict with other tasks.
567  * if there is a conflict, return the task, otherwise return NULL.
568  * scamper_task_sig_install assumes that this function has been called.
569  */
scamper_task_sig_block(scamper_task_t * task)570 scamper_task_t *scamper_task_sig_block(scamper_task_t *task)
571 {
572   scamper_task_sig_t *sig;
573   scamper_task_t *tf;
574   slist_node_t *n;
575   s2t_t *s2t;
576 
577   for(n=slist_head_node(task->siglist); n != NULL; n = slist_node_next(n))
578     {
579       s2t = slist_node_item(n); sig = s2t->sig;
580       if((tf = scamper_task_find(sig)) != NULL && tf != task)
581 	return tf;
582     }
583 
584   return NULL;
585 }
586 
scamper_task_onhold(scamper_task_t * task,void * param,void (* unhold)(void * param))587 void *scamper_task_onhold(scamper_task_t *task, void *param,
588 			  void (*unhold)(void *param))
589 {
590   task_onhold_t *toh = NULL;
591   dlist_node_t *cookie;
592 
593   if(task->onhold == NULL && (task->onhold = dlist_alloc()) == NULL)
594     goto err;
595   if((toh = malloc_zero(sizeof(task_onhold_t))) == NULL)
596     goto err;
597   if((cookie = dlist_tail_push(task->onhold, toh)) == NULL)
598     goto err;
599 
600   toh->param = param;
601   toh->unhold = unhold;
602 
603   return cookie;
604 
605  err:
606   if(toh != NULL) free(toh);
607   return NULL;
608 }
609 
scamper_task_dehold(scamper_task_t * task,void * cookie)610 int scamper_task_dehold(scamper_task_t *task, void *cookie)
611 {
612   task_onhold_t *toh;
613   assert(task->onhold != NULL);
614   if((toh = dlist_node_pop(task->onhold, cookie)) == NULL)
615     return -1;
616   free(toh);
617   return 0;
618 }
619 
620 /*
621  * scamper_task_alloc
622  *
623  * allocate and initialise a task object.
624  */
scamper_task_alloc(void * data,scamper_task_funcs_t * funcs)625 scamper_task_t *scamper_task_alloc(void *data, scamper_task_funcs_t *funcs)
626 {
627   scamper_task_t *task;
628 
629   assert(data  != NULL);
630   assert(funcs != NULL);
631 
632   if((task = malloc_zero(sizeof(scamper_task_t))) == NULL)
633     {
634       printerror(__func__, "could not malloc task");
635       goto err;
636     }
637 
638   if((task->queue = scamper_queue_alloc(task)) == NULL)
639     goto err;
640 
641   if((task->siglist = slist_alloc()) == NULL)
642     goto err;
643 
644   task->funcs = funcs;
645   task->data = data;
646 
647   return task;
648 
649  err:
650   scamper_task_free(task);
651   return NULL;
652 }
653 
654 /*
655  * scamper_task_free
656  *
657  * free a task structure.
658  * this involves freeing the task using the free pointer provided,
659  * freeing the queue data structure, unholding any tasks blocked, and
660  * finally freeing the task structure itself.
661  */
scamper_task_free(scamper_task_t * task)662 void scamper_task_free(scamper_task_t *task)
663 {
664   scamper_task_anc_t *anc;
665   task_onhold_t *toh;
666   int i;
667 
668   if(task->funcs != NULL)
669     task->funcs->task_free(task);
670 
671   if(task->queue != NULL)
672     {
673       scamper_queue_free(task->queue);
674       task->queue = NULL;
675     }
676 
677   if(task->onhold != NULL)
678     {
679       while((toh = dlist_head_pop(task->onhold)) != NULL)
680 	{
681 	  toh->unhold(toh->param);
682 	  free(toh);
683 	}
684       dlist_free(task->onhold);
685     }
686 
687   if(task->cyclemon != NULL)
688     {
689       scamper_cyclemon_unuse(task->cyclemon);
690       task->cyclemon = NULL;
691     }
692 
693   if(task->sourcetask != NULL)
694     {
695       scamper_sourcetask_free(task->sourcetask);
696       task->sourcetask = NULL;
697     }
698 
699   if(task->siglist != NULL)
700     {
701       scamper_task_sig_deinstall(task);
702       slist_free(task->siglist);
703     }
704 
705   if(task->ancillary != NULL)
706     {
707       while((anc = dlist_head_pop(task->ancillary)) != NULL)
708 	{
709 	  anc->node = NULL;
710 	  anc->freedata(anc->data);
711 	  free(anc);
712 	}
713       dlist_free(task->ancillary);
714     }
715 
716   if(task->fds != NULL)
717     {
718       for(i=0; i<task->fdc; i++)
719 	scamper_fd_free(task->fds[i]);
720       free(task->fds);
721     }
722 
723   free(task);
724   return;
725 }
726 
scamper_task_getdata(const scamper_task_t * task)727 void *scamper_task_getdata(const scamper_task_t *task)
728 {
729   return task->data;
730 }
731 
scamper_task_getstate(const scamper_task_t * task)732 void *scamper_task_getstate(const scamper_task_t *task)
733 {
734   return task->state;
735 }
736 
scamper_task_setdatanull(scamper_task_t * task)737 void scamper_task_setdatanull(scamper_task_t *task)
738 {
739   task->data = NULL;
740   return;
741 }
742 
scamper_task_setstate(scamper_task_t * task,void * state)743 void scamper_task_setstate(scamper_task_t *task, void *state)
744 {
745   task->state = state;
746   return;
747 }
748 
scamper_task_getsource(scamper_task_t * task)749 scamper_source_t *scamper_task_getsource(scamper_task_t *task)
750 {
751   if(task->sourcetask == NULL) return NULL;
752   return scamper_sourcetask_getsource(task->sourcetask);
753 }
754 
scamper_task_setsourcetask(scamper_task_t * task,scamper_sourcetask_t * st)755 void scamper_task_setsourcetask(scamper_task_t *task, scamper_sourcetask_t *st)
756 {
757   assert(task->sourcetask == NULL);
758   task->sourcetask = st;
759   return;
760 }
761 
scamper_task_setcyclemon(scamper_task_t * task,scamper_cyclemon_t * cm)762 void scamper_task_setcyclemon(scamper_task_t *task, scamper_cyclemon_t *cm)
763 {
764   task->cyclemon = scamper_cyclemon_use(cm);
765   return;
766 }
767 
scamper_task_write(scamper_task_t * task,scamper_file_t * file)768 void scamper_task_write(scamper_task_t *task, scamper_file_t *file)
769 {
770   task->funcs->write(file, task);
771   return;
772 }
773 
scamper_task_probe(scamper_task_t * task)774 void scamper_task_probe(scamper_task_t *task)
775 {
776   task->funcs->probe(task);
777   return;
778 }
779 
scamper_task_halt(scamper_task_t * task)780 void scamper_task_halt(scamper_task_t *task)
781 {
782   task->funcs->halt(task);
783   return;
784 }
785 
scamper_task_handleicmp(scamper_task_t * task,scamper_icmp_resp_t * resp)786 void scamper_task_handleicmp(scamper_task_t *task, scamper_icmp_resp_t *resp)
787 {
788   if(task->funcs->handle_icmp != NULL)
789     task->funcs->handle_icmp(task, resp);
790   return;
791 }
792 
scamper_task_handledl(scamper_dl_rec_t * dl)793 void scamper_task_handledl(scamper_dl_rec_t *dl)
794 {
795   tx_ip_check(dl);
796   tx_nd_check(dl);
797   sniff_check(dl);
798   return;
799 }
800 
scamper_task_handletimeout(scamper_task_t * task)801 void scamper_task_handletimeout(scamper_task_t *task)
802 {
803   if(task->funcs->handle_timeout != NULL)
804     task->funcs->handle_timeout(task);
805   return;
806 }
807 
scamper_task_queue_probe(scamper_task_t * task)808 int scamper_task_queue_probe(scamper_task_t *task)
809 {
810   return scamper_queue_probe(task->queue);
811 }
812 
scamper_task_queue_probe_head(scamper_task_t * task)813 int scamper_task_queue_probe_head(scamper_task_t *task)
814 {
815   return scamper_queue_probe_head(task->queue);
816 }
817 
scamper_task_queue_wait(scamper_task_t * task,int ms)818 int scamper_task_queue_wait(scamper_task_t *task, int ms)
819 {
820   return scamper_queue_wait(task->queue, ms);
821 }
822 
scamper_task_queue_wait_tv(scamper_task_t * task,struct timeval * tv)823 int scamper_task_queue_wait_tv(scamper_task_t *task, struct timeval *tv)
824 {
825   return scamper_queue_wait_tv(task->queue, tv);
826 }
827 
scamper_task_queue_done(scamper_task_t * task,int ms)828 int scamper_task_queue_done(scamper_task_t *task, int ms)
829 {
830   return scamper_queue_done(task->queue, ms);
831 }
832 
scamper_task_queue_isprobe(scamper_task_t * task)833 int scamper_task_queue_isprobe(scamper_task_t *task)
834 {
835   return scamper_queue_isprobe(task->queue);
836 }
837 
scamper_task_queue_isdone(scamper_task_t * task)838 int scamper_task_queue_isdone(scamper_task_t *task)
839 {
840   return scamper_queue_isdone(task->queue);
841 }
842 
task_fd_cmp(const scamper_fd_t * a,const scamper_fd_t * b)843 static int task_fd_cmp(const scamper_fd_t *a, const scamper_fd_t *b)
844 {
845   if(a < b) return -1;
846   if(a > b) return  1;
847   return 0;
848 }
849 
850 /*
851  * task_fd
852  *
853  * make sure the task has a hold on this fd.
854  */
task_fd(scamper_task_t * t,scamper_fd_t * fd)855 static scamper_fd_t *task_fd(scamper_task_t *t, scamper_fd_t *fd)
856 {
857   if(fd == NULL)
858     return NULL;
859 
860   if(array_find((void **)t->fds, t->fdc, fd, (array_cmp_t)task_fd_cmp) == NULL)
861     {
862       if(array_insert((void ***)&t->fds, &t->fdc, fd,
863 		      (array_cmp_t)task_fd_cmp) != 0)
864 	{
865 	  scamper_fd_free(fd);
866 	  return NULL;
867 	}
868     }
869   else
870     {
871       /* already have a hold of the fd */
872       scamper_fd_free(fd);
873     }
874   return fd;
875 }
876 
scamper_task_fd_icmp4(scamper_task_t * task,void * addr)877 scamper_fd_t *scamper_task_fd_icmp4(scamper_task_t *task, void *addr)
878 {
879   scamper_fd_t *fd = scamper_fd_icmp4(addr);
880   return task_fd(task, fd);
881 }
882 
scamper_task_fd_icmp6(scamper_task_t * task,void * addr)883 scamper_fd_t *scamper_task_fd_icmp6(scamper_task_t *task, void *addr)
884 {
885   scamper_fd_t *fd = scamper_fd_icmp6(addr);
886   return task_fd(task, fd);
887 }
888 
scamper_task_fd_udp4(scamper_task_t * task,void * a,uint16_t sp)889 scamper_fd_t *scamper_task_fd_udp4(scamper_task_t *task, void *a, uint16_t sp)
890 {
891   scamper_fd_t *fd = scamper_fd_udp4(a, sp);
892   return task_fd(task, fd);
893 }
894 
scamper_task_fd_udp6(scamper_task_t * task,void * a,uint16_t sp)895 scamper_fd_t *scamper_task_fd_udp6(scamper_task_t *task, void *a, uint16_t sp)
896 {
897   scamper_fd_t *fd = scamper_fd_udp6(a, sp);
898   return task_fd(task, fd);
899 }
900 
scamper_task_fd_tcp4(scamper_task_t * task,void * a,uint16_t sp)901 scamper_fd_t *scamper_task_fd_tcp4(scamper_task_t *task, void *a, uint16_t sp)
902 {
903   scamper_fd_t *fd = scamper_fd_tcp4(a, sp);
904   return task_fd(task, fd);
905 }
906 
scamper_task_fd_tcp6(scamper_task_t * task,void * a,uint16_t sp)907 scamper_fd_t *scamper_task_fd_tcp6(scamper_task_t *task, void *a, uint16_t sp)
908 {
909   scamper_fd_t *fd = scamper_fd_tcp6(a, sp);
910   return task_fd(task, fd);
911 }
912 
scamper_task_fd_dl(scamper_task_t * task,int ifindex)913 scamper_fd_t *scamper_task_fd_dl(scamper_task_t *task, int ifindex)
914 {
915   scamper_fd_t *fd = scamper_fd_dl(ifindex);
916   return task_fd(task, fd);
917 }
918 
scamper_task_fd_ip4(scamper_task_t * task)919 scamper_fd_t *scamper_task_fd_ip4(scamper_task_t *task)
920 {
921   scamper_fd_t *fd = scamper_fd_ip4();
922   return task_fd(task, fd);
923 }
924 
925 #ifndef _WIN32
scamper_task_fd_rtsock(scamper_task_t * task)926 scamper_fd_t *scamper_task_fd_rtsock(scamper_task_t *task)
927 {
928   scamper_fd_t *fd = scamper_fd_rtsock();
929   return task_fd(task, fd);
930 }
931 #endif
932 
scamper_task_init(void)933 int scamper_task_init(void)
934 {
935   if((tx_ip4 = patricia_alloc((patricia_bit_t)tx_ip_bit,
936 			      (patricia_cmp_t)tx_ip_cmp,
937 			      (patricia_fbd_t)tx_ip_fbd)) == NULL)
938     return -1;
939   if((tx_ip6 = patricia_alloc((patricia_bit_t)tx_ip_bit,
940 			      (patricia_cmp_t)tx_ip_cmp,
941 			      (patricia_fbd_t)tx_ip_fbd)) == NULL)
942     return -1;
943   if((tx_nd4 = patricia_alloc((patricia_bit_t)tx_nd_bit,
944 			      (patricia_cmp_t)tx_nd_cmp,
945 			      (patricia_fbd_t)tx_nd_fbd)) == NULL)
946     return -1;
947   if((tx_nd6 = patricia_alloc((patricia_bit_t)tx_nd_bit,
948 			      (patricia_cmp_t)tx_nd_cmp,
949 			      (patricia_fbd_t)tx_nd_fbd)) == NULL)
950     return -1;
951   if((host = splaytree_alloc((splaytree_cmp_t)host_cmp)) == NULL)
952     return -1;
953   if((sniff = dlist_alloc()) == NULL)
954     return -1;
955   return 0;
956 }
957 
scamper_task_cleanup(void)958 void scamper_task_cleanup(void)
959 {
960   if(tx_ip4 != NULL) { patricia_free(tx_ip4); tx_ip4 = NULL; }
961   if(tx_ip6 != NULL) { patricia_free(tx_ip6); tx_ip6 = NULL; }
962   if(tx_nd4 != NULL) { patricia_free(tx_nd4); tx_nd4 = NULL; }
963   if(tx_nd6 != NULL) { patricia_free(tx_nd6); tx_nd6 = NULL; }
964   if(host != NULL)   { splaytree_free(host, NULL);   host   = NULL; }
965   if(sniff != NULL)  { dlist_free(sniff); sniff = NULL; }
966   return;
967 }
968