Asterisk - The Open Source Telephony Project  18.5.0
pbx_dundi.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <[email protected]>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*! \file
20  *
21  * \brief Distributed Universal Number Discovery (DUNDi)
22  */
23 
24 /*! \li \ref pbx_dundi.c uses configuration file \ref dundi.conf
25  * \addtogroup configuration_file Configuration Files
26  */
27 
28 /*!
29  * \page dundi.conf dundi.conf
30  * \verbinclude dundi.conf.sample
31  */
32 
33 /*** MODULEINFO
34  <depend>zlib</depend>
35  <use type="module">res_crypto</use>
36  <use type="external">crypto</use>
37  <support_level>extended</support_level>
38  ***/
39 
40 #include "asterisk.h"
41 
42 #include "asterisk/network.h"
43 #include <sys/ioctl.h>
44 #include <zlib.h>
45 #include <signal.h>
46 #include <pthread.h>
47 #include <net/if.h>
48 
49 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
50 #include <net/if_dl.h>
51 #include <ifaddrs.h>
52 #include <signal.h>
53 #endif
54 
55 #include "asterisk/file.h"
56 #include "asterisk/logger.h"
57 #include "asterisk/channel.h"
58 #include "asterisk/config.h"
59 #include "asterisk/pbx.h"
60 #include "asterisk/module.h"
61 #include "asterisk/frame.h"
62 #include "asterisk/cli.h"
63 #include "asterisk/lock.h"
64 #include "asterisk/md5.h"
65 #include "asterisk/dundi.h"
66 #include "asterisk/sched.h"
67 #include "asterisk/io.h"
68 #include "asterisk/utils.h"
69 #include "asterisk/netsock2.h"
70 #include "asterisk/crypto.h"
71 #include "asterisk/astdb.h"
72 #include "asterisk/acl.h"
73 #include "asterisk/app.h"
74 
75 #include "dundi-parser.h"
76 
77 /*** DOCUMENTATION
78  <function name="DUNDILOOKUP" language="en_US">
79  <synopsis>
80  Do a DUNDi lookup of a phone number.
81  </synopsis>
82  <syntax>
83  <parameter name="number" required="true"/>
84  <parameter name="context">
85  <para>If not specified the default will be <literal>e164</literal>.</para>
86  </parameter>
87  <parameter name="options">
88  <optionlist>
89  <option name="b">
90  <para>Bypass the internal DUNDi cache</para>
91  </option>
92  </optionlist>
93  </parameter>
94  </syntax>
95  <description>
96  <para>This will do a DUNDi lookup of the given phone number.</para>
97  <para>This function will return the Technology/Resource found in the first result
98  in the DUNDi lookup. If no results were found, the result will be blank.</para>
99  </description>
100  </function>
101 
102 
103  <function name="DUNDIQUERY" language="en_US">
104  <synopsis>
105  Initiate a DUNDi query.
106  </synopsis>
107  <syntax>
108  <parameter name="number" required="true"/>
109  <parameter name="context">
110  <para>If not specified the default will be <literal>e164</literal>.</para>
111  </parameter>
112  <parameter name="options">
113  <optionlist>
114  <option name="b">
115  <para>Bypass the internal DUNDi cache</para>
116  </option>
117  </optionlist>
118  </parameter>
119  </syntax>
120  <description>
121  <para>This will do a DUNDi lookup of the given phone number.</para>
122  <para>The result of this function will be a numeric ID that can be used to retrieve
123  the results with the <literal>DUNDIRESULT</literal> function.</para>
124  </description>
125  </function>
126 
127  <function name="DUNDIRESULT" language="en_US">
128  <synopsis>
129  Retrieve results from a DUNDIQUERY.
130  </synopsis>
131  <syntax>
132  <parameter name="id" required="true">
133  <para>The identifier returned by the <literal>DUNDIQUERY</literal> function.</para>
134  </parameter>
135  <parameter name="resultnum">
136  <optionlist>
137  <option name="number">
138  <para>The number of the result that you want to retrieve, this starts at <literal>1</literal></para>
139  </option>
140  <option name="getnum">
141  <para>The total number of results that are available.</para>
142  </option>
143  </optionlist>
144  </parameter>
145  </syntax>
146  <description>
147  <para>This function will retrieve results from a previous use\n"
148  of the <literal>DUNDIQUERY</literal> function.</para>
149  </description>
150  </function>
151  ***/
152 
153 #define MAX_RESULTS 64
154 
155 #define MAX_PACKET_SIZE 8192
156 
157 #define MAX_WEIGHT 59999
158 
159 #define DUNDI_MODEL_INBOUND (1 << 0)
160 #define DUNDI_MODEL_OUTBOUND (1 << 1)
161 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
162 
163 /*! Keep times of last 10 lookups */
164 #define DUNDI_TIMING_HISTORY 10
165 
166 enum {
167  FLAG_ISREG = (1 << 0), /*!< Transaction is register request */
168  FLAG_DEAD = (1 << 1), /*!< Transaction is dead */
169  FLAG_FINAL = (1 << 2), /*!< Transaction has final message sent */
170  FLAG_ISQUAL = (1 << 3), /*!< Transaction is a qualification */
171  FLAG_ENCRYPT = (1 << 4), /*!< Transaction is encrypted wiht ECX/DCX */
172  FLAG_SENDFULLKEY = (1 << 5), /*!< Send full key on transaction */
173  FLAG_STOREHIST = (1 << 6), /*!< Record historic performance */
174 };
175 
176 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
177 
178 #if 0
179 #define DUNDI_SECRET_TIME 15 /* Testing only */
180 #else
181 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
182 #endif
183 
184 static struct io_context *io;
185 static struct ast_sched_context *sched;
186 static int netsocket = -1; /* Socket for bindaddr if only one bindaddr. Otherwise the IPv4 socket when bindaddr2 given. */
187 static int netsocket2 = -1; /* IPv6 socket when bindaddr2 given. */
188 static pthread_t netthreadid = AST_PTHREADT_NULL;
191 static unsigned int tos = 0;
192 static int dundidebug = 0;
193 static int authdebug = 0;
197 static int global_autokilltimeout = 0;
199 static int default_expiration = 60;
200 static int global_storehistory = 0;
201 static char dept[80];
202 static char org[80];
203 static char locality[80];
204 static char stateprov[80];
205 static char country[80];
206 static char email[80];
207 static char phone[80];
208 static char secretpath[80];
209 static char cursecret[80];
210 static char ipaddr[80];
211 static time_t rotatetime;
212 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
213 static int dundi_shutdown = 0;
214 
215 struct permission {
217  int allow;
218  char name[0];
219 };
220 
221 struct dundi_packet {
223  struct dundi_hdr *h;
224  int datalen;
227  int retrans;
228  unsigned char data[0];
229 };
230 
232  unsigned short flags;
234 };
235 
238  char *context;
239  time_t expiration;
240  char number[0];
241 };
242 
243 struct dundi_request;
244 
246  struct ast_sockaddr addr; /*!< Other end of transaction */
247  struct timeval start; /*!< When this transaction was created */
249  int eidcount; /*!< Number of eids in eids */
250  dundi_eid us_eid; /*!< Our EID, to them */
251  dundi_eid them_eid; /*!< Their EID, to us */
252  ast_aes_encrypt_key ecx; /*!< AES 128 Encryption context */
253  ast_aes_decrypt_key dcx; /*!< AES 128 Decryption context */
254  unsigned int flags; /*!< Has final packet been sent */
255  int ttl; /*!< Remaining TTL for queries on this one */
256  int thread; /*!< We have a calling thread */
257  int retranstimer; /*!< How long to wait before retransmissions */
258  int autokillid; /*!< ID to kill connection if answer doesn't come back fast enough */
259  int autokilltimeout; /*!< Recommended timeout for autokill */
260  unsigned short strans; /*!< Our transaction identifier */
261  unsigned short dtrans; /*!< Their transaction identifer */
262  unsigned char iseqno; /*!< Next expected received seqno */
263  unsigned char oiseqno; /*!< Last received incoming seqno */
264  unsigned char oseqno; /*!< Next transmitted seqno */
265  unsigned char aseqno; /*!< Last acknowledge seqno */
266  AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets; /*!< Packets to be retransmitted */
267  struct packetlist lasttrans; /*!< Last transmitted / ACK'd packet */
268  struct dundi_request *parent; /*!< Parent request (if there is one) */
269  AST_LIST_ENTRY(dundi_transaction) parentlist; /*!< Next with respect to the parent */
270  AST_LIST_ENTRY(dundi_transaction) all; /*!< Next with respect to all DUNDi transactions */
271 };
272 
274  char dcontext[AST_MAX_EXTENSION];
278  struct dundi_result *dr;
281  int maxcount;
284  int cbypass;
285  int pfds[2];
286  uint32_t crc32; /*!< CRC-32 of all but root EID's in avoid list */
287  AST_LIST_HEAD_NOLOCK(, dundi_transaction) trans; /*!< Transactions */
289 };
290 
292  char dcontext[AST_MAX_EXTENSION];
293  char lcontext[AST_MAX_EXTENSION];
294  int _weight;
295  char *weightstr;
296  int options;
297  int tech;
298  int dead;
299  char dest[512];
301 };
302 
303 struct dundi_peer {
305  struct ast_sockaddr addr; /*!< Address of DUNDi peer */
307  struct permissionlist include;
309  char inkey[80];
310  char outkey[80];
311  int dead;
315  int order;
316  unsigned char txenckey[256]; /*!< Transmitted encrypted key + sig */
317  unsigned char rxenckey[256]; /*!< Cache received encrypted key + sig */
318  uint32_t us_keycrc32; /*!< CRC-32 of our key */
319  ast_aes_encrypt_key us_ecx; /*!< Cached AES 128 Encryption context */
320  ast_aes_decrypt_key us_dcx; /*!< Cached AES 128 Decryption context */
321  uint32_t them_keycrc32; /*!< CRC-32 of our key */
322  ast_aes_encrypt_key them_ecx; /*!< Cached AES 128 Encryption context */
323  ast_aes_decrypt_key them_dcx; /*!< Cached AES 128 Decryption context */
324  time_t keyexpire; /*!< When to expire/recreate key */
326  int lookuptimes[DUNDI_TIMING_HISTORY];
327  char *lookups[DUNDI_TIMING_HISTORY];
328  int avgms;
329  struct dundi_transaction *regtrans; /*!< Registration transaction */
330  struct dundi_transaction *qualtrans; /*!< Qualify transaction */
331  int model; /*!< Pull model */
332  int pcmodel; /*!< Push/precache model */
333  /*! Dynamic peers register with us */
334  unsigned int dynamic:1;
335  int lastms; /*!< Last measured latency */
336  int maxms; /*!< Max permissible latency */
337  struct timeval qualtx; /*!< Time of transmit */
339 };
340 
346 
347 /*!
348  * \brief Wildcard peer
349  *
350  * This peer is created if the [*] entry is specified in dundi.conf
351  */
352 static struct dundi_peer *any_peer;
353 
354 static int dundi_xmit(struct dundi_packet *pack);
355 
356 static void dundi_debug_output(const char *data)
357 {
358  if (dundidebug)
359  ast_verbose("%s", data);
360 }
361 
362 static void dundi_error_output(const char *data)
363 {
364  ast_log(LOG_WARNING, "%s", data);
365 }
366 
367 static int has_permission(struct permissionlist *permlist, char *cont)
368 {
369  struct permission *perm;
370  int res = 0;
371 
372  AST_LIST_TRAVERSE(permlist, perm, list) {
373  if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont))
374  res = perm->allow;
375  }
376 
377  return res;
378 }
379 
380 static char *tech2str(int tech)
381 {
382  switch(tech) {
383  case DUNDI_PROTO_NONE:
384  return "None";
385  case DUNDI_PROTO_IAX:
386  return "IAX2";
387  case DUNDI_PROTO_SIP:
388  return "SIP";
389  case DUNDI_PROTO_H323:
390  return "H323";
391  default:
392  return "Unknown";
393  }
394 }
395 
396 static int str2tech(char *str)
397 {
398  if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
399  return DUNDI_PROTO_IAX;
400  else if (!strcasecmp(str, "SIP"))
401  return DUNDI_PROTO_SIP;
402  else if (!strcasecmp(str, "H323"))
403  return DUNDI_PROTO_H323;
404  else
405  return -1;
406 }
407 
408 static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int ttl, int blockempty, struct dundi_hint_metadata *md, int *expiration, int cybpass, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int direct[]);
409 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
410 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
411 
412 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct ast_sockaddr *sin)
413 {
414  struct dundi_transaction *trans;
415 
416  /* Look for an exact match first */
417  AST_LIST_TRAVERSE(&alltrans, trans, all) {
418  if (!ast_sockaddr_cmp(&trans->addr, sin) &&
419  ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
420  ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
421  if (hdr->strans) {
422  trans->dtrans = ntohs(hdr->strans) & 32767;
423  }
424  return trans;
425  }
426  }
427 
428  switch(hdr->cmdresp & 0x7f) {
433  case DUNDI_COMMAND_NULL:
435  if (!hdr->strans)
436  break;
437  /* Create new transaction */
438  if (!(trans = create_transaction(NULL)))
439  break;
440  ast_sockaddr_copy(&trans->addr, sin);
441  trans->dtrans = ntohs(hdr->strans) & 32767;
442  default:
443  break;
444  }
445 
446  return trans;
447 }
448 
449 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
450 
451 static int dundi_ack(struct dundi_transaction *trans, int final)
452 {
453  return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
454 }
455 static void dundi_reject(struct dundi_hdr *h, struct ast_sockaddr *sin)
456 {
457  struct {
458  struct dundi_packet pack;
459  struct dundi_hdr hdr;
460  } tmp;
461  struct dundi_transaction trans;
462  /* Never respond to an INVALID with another INVALID */
463  if (h->cmdresp == DUNDI_COMMAND_INVALID)
464  return;
465  memset(&tmp, 0, sizeof(tmp));
466  memset(&trans, 0, sizeof(trans));
467  ast_sockaddr_copy(&trans.addr, sin);
468  tmp.hdr.strans = h->dtrans;
469  tmp.hdr.dtrans = h->strans;
470  tmp.hdr.iseqno = h->oseqno;
471  tmp.hdr.oseqno = h->iseqno;
472  tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
473  tmp.hdr.cmdflags = 0;
474  tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
475  tmp.pack.datalen = sizeof(struct dundi_hdr);
476  tmp.pack.parent = &trans;
477  dundi_xmit(&tmp.pack);
478 }
479 
480 static int get_trans_id(void)
481 {
482  struct dundi_transaction *t;
483  int stid = (ast_random() % 32766) + 1;
484  int tid = stid;
485 
486  do {
488  if (t->strans == tid)
489  break;
490  }
491  if (!t)
492  return tid;
493  tid = (tid % 32766) + 1;
494  } while (tid != stid);
495 
496  return 0;
497 }
498 
499 static int reset_transaction(struct dundi_transaction *trans)
500 {
501  int tid;
502  tid = get_trans_id();
503  if (tid < 1)
504  return -1;
505  trans->strans = tid;
506  trans->dtrans = 0;
507  trans->iseqno = 0;
508  trans->oiseqno = 0;
509  trans->oseqno = 0;
510  trans->aseqno = 0;
511  ast_clear_flag(trans, FLAG_FINAL);
512  return 0;
513 }
514 
516 {
517  struct dundi_peer *cur = NULL;
518 
519  if (!eid)
520  eid = &empty_eid;
521 
522  AST_LIST_TRAVERSE(&peers, cur, list) {
523  if (!ast_eid_cmp(&cur->eid,eid))
524  break;
525  }
526 
527  if (!cur && any_peer)
528  cur = any_peer;
529 
530  return cur;
531 }
532 
533 static void build_iv(unsigned char *iv)
534 {
535  /* XXX Would be nice to be more random XXX */
536  unsigned int *fluffy;
537  int x;
538  fluffy = (unsigned int *)(iv);
539  for (x=0;x<4;x++)
540  fluffy[x] = ast_random();
541 }
542 
545  int directs[DUNDI_MAX_STACK + 1];
547  char called_context[AST_MAX_EXTENSION];
548  char called_number[AST_MAX_EXTENSION];
550  int nummaps;
551  int nocache;
553  void *chal;
554  int challen;
555  int ttl;
556  char fluffy[0];
557 };
558 
559 static int get_mapping_weight(struct dundi_mapping *map, struct varshead *headp)
560 {
561  char buf[32];
562 
563  buf[0] = 0;
564  if (map->weightstr) {
565  if (headp) {
566  pbx_substitute_variables_varshead(headp, map->weightstr, buf, sizeof(buf) - 1);
567  } else {
568  pbx_substitute_variables_helper(NULL, map->weightstr, buf, sizeof(buf) - 1);
569  }
570 
571  if (sscanf(buf, "%30d", &map->_weight) != 1)
572  map->_weight = MAX_WEIGHT;
573  }
574 
575  return map->_weight;
576 }
577 
578 static int dundi_lookup_local(struct dundi_result *dr, struct dundi_mapping *map, char *called_number, dundi_eid *us_eid, int anscnt, struct dundi_hint_metadata *hmd)
579 {
580  struct ast_flags flags = {0};
581  int x;
582  if (!ast_strlen_zero(map->lcontext)) {
583  if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
585  if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
587  if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
589  if (ast_ignore_pattern(map->lcontext, called_number))
591 
592  /* Clearly we can't say 'don't ask' anymore if we found anything... */
593  if (ast_test_flag(&flags, AST_FLAGS_ALL))
595 
597  /* Skip partial answers */
599  }
600  if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
601  struct varshead headp;
602  struct ast_var_t *newvariable;
603  ast_set_flag(&flags, map->options & 0xffff);
604  ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
605  dr[anscnt].techint = map->tech;
606  dr[anscnt].expiration = dundi_cache_time;
607  ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
608  dr[anscnt].eid = *us_eid;
609  ast_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
610  if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
612  if ((newvariable = ast_var_assign("NUMBER", called_number))) {
613  AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
614  }
615  if ((newvariable = ast_var_assign("EID", dr[anscnt].eid_str))) {
616  AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
617  }
618  if ((newvariable = ast_var_assign("SECRET", cursecret))) {
619  AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
620  }
621  if ((newvariable = ast_var_assign("IPADDR", ipaddr))) {
622  AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
623  }
624  pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
625  dr[anscnt].weight = get_mapping_weight(map, &headp);
626  while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
627  ast_var_delete(newvariable);
628  } else {
629  dr[anscnt].dest[0] = '\0';
630  dr[anscnt].weight = get_mapping_weight(map, NULL);
631  }
632  anscnt++;
633  } else {
634  /* No answers... Find the fewest number of digits from the
635  number for which we have no answer. */
636  char tmp[AST_MAX_EXTENSION + 1] = "";
637  for (x = 0; x < (sizeof(tmp) - 1); x++) {
638  tmp[x] = called_number[x];
639  if (!tmp[x])
640  break;
641  if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
642  /* Oops found something we can't match. If this is longer
643  than the running hint, we have to consider it */
644  if (strlen(tmp) > strlen(hmd->exten)) {
645  ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
646  }
647  break;
648  }
649  }
650  }
651  }
652  return anscnt;
653 }
654 
655 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
656 
657 static void *dundi_lookup_thread(void *data)
658 {
659  struct dundi_query_state *st = data;
660  struct dundi_result dr[MAX_RESULTS];
661  struct dundi_ie_data ied;
662  struct dundi_hint_metadata hmd;
663  char eid_str[20];
664  int res, x;
665  int ouranswers=0;
666  int max = 999999;
667  int expiration = dundi_cache_time;
668 
669  ast_debug(1, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
670  st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
671  memset(&ied, 0, sizeof(ied));
672  memset(&dr, 0, sizeof(dr));
673  memset(&hmd, 0, sizeof(hmd));
674  /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
676  for (x=0;x<st->nummaps;x++)
677  ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
678  if (ouranswers < 0)
679  ouranswers = 0;
680  for (x=0;x<ouranswers;x++) {
681  if (dr[x].weight < max)
682  max = dr[x].weight;
683  }
684 
685  if (max) {
686  /* If we do not have a canonical result, keep looking */
687  res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, st->called_context, st->called_number, st->ttl, 1, &hmd, &expiration, st->nocache, 0, NULL, st->eids, st->directs);
688  if (res > 0) {
689  /* Append answer in result */
690  ouranswers += res;
691  } else {
692  if ((res < -1) && (!ouranswers))
693  dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
694  }
695  }
697  /* Truncate if "don't ask" isn't present */
699  hmd.exten[0] = '\0';
700  if (ast_test_flag(st->trans, FLAG_DEAD)) {
701  ast_debug(1, "Our transaction went away!\n");
702  st->trans->thread = 0;
703  destroy_trans(st->trans, 0);
704  } else {
705  for (x=0;x<ouranswers;x++) {
706  /* Add answers */
707  if (dr[x].expiration && (expiration > dr[x].expiration))
708  expiration = dr[x].expiration;
709  dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
710  }
712  dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
713  dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
714  st->trans->thread = 0;
715  }
717  ast_free(st);
718  return NULL;
719 }
720 
721 static void *dundi_precache_thread(void *data)
722 {
723  struct dundi_query_state *st = data;
724  struct dundi_ie_data ied;
725  struct dundi_hint_metadata hmd = {0};
726  char eid_str[20];
727 
728  ast_debug(1, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
729  st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
730  memset(&ied, 0, sizeof(ied));
731 
732  /* Now produce precache */
734 
736  /* Truncate if "don't ask" isn't present */
738  hmd.exten[0] = '\0';
739  if (ast_test_flag(st->trans, FLAG_DEAD)) {
740  ast_debug(1, "Our transaction went away!\n");
741  st->trans->thread = 0;
742  destroy_trans(st->trans, 0);
743  } else {
744  dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
745  st->trans->thread = 0;
746  }
748  ast_free(st);
749  return NULL;
750 }
751 
752 static int dundi_query_eid_internal(struct dundi_entity_info *dei, const char *dcontext, dundi_eid *eid, struct dundi_hint_metadata *hmd, int ttl, int blockempty, dundi_eid *avoid[]);
753 
754 static void *dundi_query_thread(void *data)
755 {
756  struct dundi_query_state *st = data;
757  struct dundi_entity_info dei;
758  struct dundi_ie_data ied;
759  struct dundi_hint_metadata hmd;
760  char eid_str[20];
761  int res;
762 
763  ast_debug(1, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
764  st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
765  memset(&ied, 0, sizeof(ied));
766  memset(&dei, 0, sizeof(dei));
767  memset(&hmd, 0, sizeof(hmd));
768  if (!ast_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
769  /* Ooh, it's us! */
770  ast_debug(1, "Neat, someone look for us!\n");
771  ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
772  ast_copy_string(dei.org, org, sizeof(dei.org));
773  ast_copy_string(dei.locality, locality, sizeof(dei.locality));
774  ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
775  ast_copy_string(dei.country, country, sizeof(dei.country));
776  ast_copy_string(dei.email, email, sizeof(dei.email));
777  ast_copy_string(dei.phone, phone, sizeof(dei.phone));
778  res = 1;
779  } else {
780  /* If we do not have a canonical result, keep looking */
781  res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
782  }
784  if (ast_test_flag(st->trans, FLAG_DEAD)) {
785  ast_debug(1, "Our transaction went away!\n");
786  st->trans->thread = 0;
787  destroy_trans(st->trans, 0);
788  } else {
789  if (res) {
797  if (!ast_strlen_zero(dei.ipaddr))
799  }
801  dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
802  st->trans->thread = 0;
803  }
805  ast_free(st);
806  return NULL;
807 }
808 
809 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
810 {
811  struct dundi_query_state *st;
812  int totallen;
813  int x;
814  int skipfirst=0;
815  char eid_str[20];
816  char *s;
817  pthread_t lookupthread;
818 
819  if (ies->eidcount > 1) {
820  /* Since it is a requirement that the first EID is the authenticating host
821  and the last EID is the root, it is permissible that the first and last EID
822  could be the same. In that case, we should go ahead copy only the "root" section
823  since we will not need it for authentication. */
824  if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
825  skipfirst = 1;
826  }
827  totallen = sizeof(struct dundi_query_state);
828  totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
829  st = ast_calloc(1, totallen);
830  if (st) {
832  memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
833  st->trans = trans;
834  st->ttl = ies->ttl - 1;
835  if (st->ttl < 0)
836  st->ttl = 0;
837  s = st->fluffy;
838  for (x=skipfirst;ies->eids[x];x++) {
839  st->eids[x-skipfirst] = (dundi_eid *)s;
840  *st->eids[x-skipfirst] = *ies->eids[x];
841  s += sizeof(dundi_eid);
842  }
843  ast_debug(1, "Answering EID query for '%s@%s'!\n", ast_eid_to_str(eid_str, sizeof(eid_str), ies->reqeid), ies->called_context);
844 
845  trans->thread = 1;
846  if (ast_pthread_create_detached(&lookupthread, NULL, dundi_query_thread, st)) {
847  struct dundi_ie_data ied = { 0, };
848  trans->thread = 0;
849  ast_log(LOG_WARNING, "Unable to create thread!\n");
850  ast_free(st);
851  dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
852  dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
853  return -1;
854  }
855  } else {
856  struct dundi_ie_data ied = { 0, };
858  dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
859  return -1;
860  }
861  return 0;
862 }
863 
864 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
865 {
866  int unaffected;
867  char key1[256];
868  char key2[256];
869  char eidpeer_str[20];
870  char eidroot_str[20];
871  char data[80];
872  time_t timeout;
873 
874  if (expiration < 0)
875  expiration = dundi_cache_time;
876 
877  /* Only cache hint if "don't ask" is there... */
878  if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))
879  return 0;
880 
881  unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
882 
883  dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
884  dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
885  snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08x", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
886  snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
887 
888  time(&timeout);
889  timeout += expiration;
890  snprintf(data, sizeof(data), "%ld|", (long)(timeout));
891 
892  ast_db_put("dundi/cache", key1, data);
893  ast_debug(1, "Caching hint at '%s'\n", key1);
894  ast_db_put("dundi/cache", key2, data);
895  ast_debug(1, "Caching hint at '%s'\n", key2);
896  return 0;
897 }
898 
899 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
900 {
901  int x;
902  char key1[256];
903  char key2[256];
904  char data[1024];
905  char eidpeer_str[20];
906  char eidroot_str[20];
907  time_t timeout;
908 
909  if (expiration < 1)
910  expiration = dundi_cache_time;
911 
912  /* Keep pushes a little longer, cut pulls a little short */
913  if (push)
914  expiration += 10;
915  else
916  expiration -= 10;
917  if (expiration < 1)
918  expiration = 1;
919  dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
920  dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
921  snprintf(key1, sizeof(key1), "%s/%s/%s/e%08x", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
922  snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
923  /* Build request string */
924  time(&timeout);
925  timeout += expiration;
926  snprintf(data, sizeof(data), "%ld|", (long)(timeout));
927  for (x=start;x<req->respcount;x++) {
928  /* Skip anything with an illegal pipe in it */
929  if (strchr(req->dr[x].dest, '|'))
930  continue;
931  snprintf(data + strlen(data), sizeof(data) - strlen(data), "%u/%d/%d/%s/%s|",
932  req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
933  dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
934  }
935  ast_db_put("dundi/cache", key1, data);
936  ast_db_put("dundi/cache", key2, data);
937  return 0;
938 }
939 
940 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
941 {
942  struct dundi_query_state *st;
943  int totallen;
944  int x,z;
945  struct dundi_ie_data ied;
946  char *s;
947  struct dundi_result dr2[MAX_RESULTS];
948  struct dundi_request dr;
949  struct dundi_hint_metadata hmd;
950 
951  struct dundi_mapping *cur;
952  int mapcount;
953  int skipfirst = 0;
954 
955  pthread_t lookupthread;
956 
957  memset(&dr2, 0, sizeof(dr2));
958  memset(&dr, 0, sizeof(dr));
959  memset(&hmd, 0, sizeof(hmd));
960 
961  /* Forge request structure to hold answers for cache */
963  dr.dr = dr2;
964  dr.maxcount = MAX_RESULTS;
966  dr.hmd = &hmd;
967  dr.pfds[0] = dr.pfds[1] = -1;
968  trans->parent = &dr;
969  ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
970  ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
971 
972  for (x=0;x<ies->anscount;x++) {
973  if (trans->parent->respcount < trans->parent->maxcount) {
974  /* Make sure it's not already there */
975  for (z=0;z<trans->parent->respcount;z++) {
976  if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
977  !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data))
978  break;
979  }
980  if (z == trans->parent->respcount) {
981  /* Copy into parent responses */
982  trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
983  trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
984  trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
985  trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
986  if (ies->expiration > 0)
987  trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
988  else
990  ast_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
991  sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
992  &ies->answers[x]->eid);
993  ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
994  sizeof(trans->parent->dr[trans->parent->respcount].dest));
995  ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
996  sizeof(trans->parent->dr[trans->parent->respcount].tech));
997  trans->parent->respcount++;
999  } else if (trans->parent->dr[z].weight > ntohs(ies->answers[x]->weight)) {
1000  /* Update weight if appropriate */
1001  trans->parent->dr[z].weight = ntohs(ies->answers[x]->weight);
1002  }
1003  } else
1004  ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
1005  trans->parent->number, trans->parent->dcontext);
1006 
1007  }
1008  /* Save all the results (if any) we had. Even if no results, still cache lookup. */
1009  cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
1010  if (ies->hint)
1011  cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
1012 
1013  totallen = sizeof(struct dundi_query_state);
1014  /* Count matching map entries */
1015  mapcount = 0;
1016  AST_LIST_TRAVERSE(&mappings, cur, list) {
1017  if (!strcasecmp(cur->dcontext, ccontext))
1018  mapcount++;
1019  }
1020 
1021  /* If no maps, return -1 immediately */
1022  if (!mapcount)
1023  return -1;
1024 
1025  if (ies->eidcount > 1) {
1026  /* Since it is a requirement that the first EID is the authenticating host
1027  and the last EID is the root, it is permissible that the first and last EID
1028  could be the same. In that case, we should go ahead copy only the "root" section
1029  since we will not need it for authentication. */
1030  if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1031  skipfirst = 1;
1032  }
1033 
1034  /* Prepare to run a query and then propagate that as necessary */
1035  totallen += mapcount * sizeof(struct dundi_mapping);
1036  totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1037  st = ast_calloc(1, totallen);
1038  if (st) {
1039  ast_copy_string(st->called_context, dr.dcontext, sizeof(st->called_context));
1040  ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1041  st->trans = trans;
1042  st->ttl = ies->ttl - 1;
1043  st->nocache = ies->cbypass;
1044  if (st->ttl < 0)
1045  st->ttl = 0;
1046  s = st->fluffy;
1047  for (x=skipfirst;ies->eids[x];x++) {
1048  st->eids[x-skipfirst] = (dundi_eid *)s;
1049  *st->eids[x-skipfirst] = *ies->eids[x];
1050  st->directs[x-skipfirst] = ies->eid_direct[x];
1051  s += sizeof(dundi_eid);
1052  }
1053  /* Append mappings */
1054  x = 0;
1055  st->maps = (struct dundi_mapping *)s;
1056  AST_LIST_TRAVERSE(&mappings, cur, list) {
1057  if (!strcasecmp(cur->dcontext, ccontext)) {
1058  if (x < mapcount) {
1059  st->maps[x] = *cur;
1060  st->maps[x].list.next = NULL;
1061  x++;
1062  }
1063  }
1064  }
1065  st->nummaps = mapcount;
1066  ast_debug(1, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
1067  trans->thread = 1;
1068  if (ast_pthread_create_detached(&lookupthread, NULL, dundi_precache_thread, st)) {
1069  trans->thread = 0;
1070  ast_log(LOG_WARNING, "Unable to create thread!\n");
1071  ast_free(st);
1072  memset(&ied, 0, sizeof(ied));
1073  dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1074  dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1075  return -1;
1076  }
1077  } else {
1078  ast_log(LOG_WARNING, "Out of memory!\n");
1079  memset(&ied, 0, sizeof(ied));
1080  dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1081  dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1082  return -1;
1083  }
1084  return 0;
1085 }
1086 
1087 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
1088 {
1089  struct dundi_query_state *st;
1090  int totallen;
1091  int x;
1092  struct dundi_ie_data ied;
1093  char *s;
1094  struct dundi_mapping *cur;
1095  int mapcount = 0;
1096  int skipfirst = 0;
1097 
1098  pthread_t lookupthread;
1099  totallen = sizeof(struct dundi_query_state);
1100  /* Count matching map entries */
1101  AST_LIST_TRAVERSE(&mappings, cur, list) {
1102  if (!strcasecmp(cur->dcontext, ccontext))
1103  mapcount++;
1104  }
1105  /* If no maps, return -1 immediately */
1106  if (!mapcount)
1107  return -1;
1108 
1109  if (ies->eidcount > 1) {
1110  /* Since it is a requirement that the first EID is the authenticating host
1111  and the last EID is the root, it is permissible that the first and last EID
1112  could be the same. In that case, we should go ahead copy only the "root" section
1113  since we will not need it for authentication. */
1114  if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1115  skipfirst = 1;
1116  }
1117 
1118  totallen += mapcount * sizeof(struct dundi_mapping);
1119  totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1120  st = ast_calloc(1, totallen);
1121  if (st) {
1123  ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1124  st->trans = trans;
1125  st->ttl = ies->ttl - 1;
1126  st->nocache = ies->cbypass;
1127  if (st->ttl < 0)
1128  st->ttl = 0;
1129  s = st->fluffy;
1130  for (x=skipfirst;ies->eids[x];x++) {
1131  st->eids[x-skipfirst] = (dundi_eid *)s;
1132  *st->eids[x-skipfirst] = *ies->eids[x];
1133  st->directs[x-skipfirst] = ies->eid_direct[x];
1134  s += sizeof(dundi_eid);
1135  }
1136  /* Append mappings */
1137  x = 0;
1138  st->maps = (struct dundi_mapping *)s;
1139  AST_LIST_TRAVERSE(&mappings, cur, list) {
1140  if (!strcasecmp(cur->dcontext, ccontext)) {
1141  if (x < mapcount) {
1142  st->maps[x] = *cur;
1143  st->maps[x].list.next = NULL;
1144  x++;
1145  }
1146  }
1147  }
1148  st->nummaps = mapcount;
1149  ast_debug(1, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
1150  trans->thread = 1;
1151  if (ast_pthread_create_detached(&lookupthread, NULL, dundi_lookup_thread, st)) {
1152  trans->thread = 0;
1153  ast_log(LOG_WARNING, "Unable to create thread!\n");
1154  ast_free(st);
1155  memset(&ied, 0, sizeof(ied));
1156  dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1157  dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1158  return -1;
1159  }
1160  } else {
1161  ast_log(LOG_WARNING, "Out of memory!\n");
1162  memset(&ied, 0, sizeof(ied));
1163  dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1164  dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1165  return -1;
1166  }
1167  return 0;
1168 }
1169 
1170 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
1171 {
1172  char data[1024];
1173  char *ptr, *term, *src;
1174  int tech;
1175  struct ast_flags flags;
1176  int weight;
1177  int length;
1178  int z;
1179  char fs[256];
1180 
1181  /* Build request string */
1182  if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
1183  time_t timeout;
1184  ptr = data;
1185  if (!ast_get_time_t(ptr, &timeout, 0, &length)) {
1186  int expiration = timeout - now;
1187  if (expiration > 0) {
1188  ast_debug(1, "Found cache expiring in %d seconds!\n", expiration);
1189  ptr += length + 1;
1190  while((sscanf(ptr, "%30d/%30d/%30d/%n", (int *)&(flags.flags), &weight, &tech, &length) == 3)) {
1191  ptr += length;
1192  term = strchr(ptr, '|');
1193  if (term) {
1194  *term = '\0';
1195  src = strrchr(ptr, '/');
1196  if (src) {
1197  *src = '\0';
1198  src++;
1199  } else
1200  src = "";
1201  ast_debug(1, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
1202  tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
1203  /* Make sure it's not already there */
1204  for (z=0;z<req->respcount;z++) {
1205  if ((req->dr[z].techint == tech) &&
1206  !strcmp(req->dr[z].dest, ptr))
1207  break;
1208  }
1209  if (z == req->respcount) {
1210  /* Copy into parent responses */
1211  ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
1212  req->dr[req->respcount].weight = weight;
1213  req->dr[req->respcount].techint = tech;
1214  req->dr[req->respcount].expiration = expiration;
1215  dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
1216  ast_eid_to_str(req->dr[req->respcount].eid_str,
1217  sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
1218  ast_copy_string(req->dr[req->respcount].dest, ptr,
1219  sizeof(req->dr[req->respcount].dest));
1220  ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
1221  sizeof(req->dr[req->respcount].tech));
1222  req->respcount++;
1224  } else if (req->dr[z].weight > weight)
1225  req->dr[z].weight = weight;
1226  ptr = term + 1;
1227  }
1228  }
1229  /* We found *something* cached */
1230  if (expiration < *lowexpiration)
1231  *lowexpiration = expiration;
1232  return 1;
1233  } else
1234  ast_db_del("dundi/cache", key);
1235  } else
1236  ast_db_del("dundi/cache", key);
1237  }
1238 
1239  return 0;
1240 }
1241 
1242 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, uint32_t crc, int *lowexpiration)
1243 {
1244  char eid_str[20];
1245  char eidroot_str[20];
1246  time_t now;
1247  int res=0;
1248  int res2=0;
1249  char eid_str_full[20];
1250  char tmp[256]="";
1251  /* Enough space for largest value that can be stored in key. */
1252  char key[sizeof(eid_str) + sizeof(tmp) + sizeof(req->dcontext) + sizeof(eidroot_str) + sizeof("hint////r")];
1253  int x;
1254 
1255  time(&now);
1256  dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
1257  dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
1258  ast_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
1259  snprintf(key, sizeof(key), "%s/%s/%s/e%08x", eid_str, req->number, req->dcontext, crc);
1260  res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1261  snprintf(key, sizeof(key), "%s/%s/%s/e%08x", eid_str, req->number, req->dcontext, (unsigned)0);
1262  res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1263  snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1264  res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1265  x = 0;
1266  if (!req->respcount) {
1267  while(!res2) {
1268  /* Look and see if we have a hint that would preclude us from looking at this
1269  peer for this number. */
1270  if (!(tmp[x] = req->number[x]))
1271  break;
1272  x++;
1273  /* Check for hints */
1274  snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08x", eid_str, tmp, req->dcontext, crc);
1275  res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1276  snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08x", eid_str, tmp, req->dcontext, (unsigned)0);
1277  res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1278  snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1279  res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1280  if (res2) {
1281  if (strlen(tmp) > strlen(req->hmd->exten)) {
1282  /* Update meta data if appropriate */
1283  ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
1284  }
1285  }
1286  }
1287  res |= res2;
1288  }
1289 
1290  return res;
1291 }
1292 
1293 static void qualify_peer(struct dundi_peer *peer, int schedonly);
1294 
1295 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
1296 {
1297  if (ast_sockaddr_isnull(&trans->addr)) {
1298  ast_sockaddr_copy(&trans->addr, &p->addr);
1299  }
1300  trans->us_eid = p->us_eid;
1301  trans->them_eid = p->eid;
1302  /* Enable encryption if appropriate */
1303  if (!ast_strlen_zero(p->inkey))
1304  ast_set_flag(trans, FLAG_ENCRYPT);
1305  if (p->maxms) {
1306  trans->autokilltimeout = p->maxms;
1308  if (p->lastms > 1) {
1309  trans->retranstimer = p->lastms * 2;
1310  /* Keep it from being silly */
1311  if (trans->retranstimer < 150)
1312  trans->retranstimer = 150;
1313  }
1316  } else
1318 }
1319 
1320 /*! \note Called with the peers list already locked */
1321 static int do_register_expire(const void *data)
1322 {
1323  struct dundi_peer *peer = (struct dundi_peer *)data;
1324  char eid_str[20];
1325 
1326  ast_debug(1, "Register expired for '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1327  ast_db_del("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid));
1328  peer->registerexpire = -1;
1329  peer->lastms = 0;
1330  ast_sockaddr_setnull(&peer->addr);
1331  return 0;
1332 }
1333 
1334 static int update_key(struct dundi_peer *peer)
1335 {
1336  unsigned char key[16];
1337  struct ast_key *ekey, *skey;
1338  char eid_str[20];
1339  int res;
1340  if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1341  build_iv(key);
1342  ast_aes_set_encrypt_key(key, &peer->us_ecx);
1343  ast_aes_set_decrypt_key(key, &peer->us_dcx);
1344  ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1345  if (!ekey) {
1346  ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1347  peer->inkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1348  return -1;
1349  }
1350  skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1351  if (!skey) {
1352  ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1353  peer->outkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1354  return -1;
1355  }
1356  if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1357  ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1358  return -1;
1359  }
1360  if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
1361  ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1362  return -1;
1363  }
1364  peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1365  peer->sentfullkey = 0;
1366  /* Looks good */
1367  time(&peer->keyexpire);
1368  peer->keyexpire += dundi_key_ttl;
1369  }
1370  return 0;
1371 }
1372 
1373 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_encrypt_key *ecx)
1374 {
1375  unsigned char curblock[16];
1376  int x;
1377  memcpy(curblock, iv, sizeof(curblock));
1378  while(len > 0) {
1379  for (x=0;x<16;x++)
1380  curblock[x] ^= src[x];
1381  ast_aes_encrypt(curblock, dst, ecx);
1382  memcpy(curblock, dst, sizeof(curblock));
1383  dst += 16;
1384  src += 16;
1385  len -= 16;
1386  }
1387  return 0;
1388 }
1389 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_decrypt_key *dcx)
1390 {
1391  unsigned char lastblock[16];
1392  int x;
1393  memcpy(lastblock, iv, sizeof(lastblock));
1394  while(len > 0) {
1395  ast_aes_decrypt(src, dst, dcx);
1396  for (x=0;x<16;x++)
1397  dst[x] ^= lastblock[x];
1398  memcpy(lastblock, src, sizeof(lastblock));
1399  dst += 16;
1400  src += 16;
1401  len -= 16;
1402  }
1403  return 0;
1404 }
1405 
1406 static struct dundi_hdr *dundi_decrypt(struct dundi_transaction *trans, unsigned char *dst, int *dstlen, struct dundi_hdr *ohdr, struct dundi_encblock *src, int srclen)
1407 {
1408  int space = *dstlen;
1409  unsigned long bytes;
1410  struct dundi_hdr *h;
1411  unsigned char *decrypt_space;
1412  decrypt_space = ast_alloca(srclen);
1413  decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1414  /* Setup header */
1415  h = (struct dundi_hdr *)dst;
1416  *h = *ohdr;
1417  bytes = space - 6;
1418  if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1419  ast_debug(1, "Ouch, uncompress failed :(\n");
1420  return NULL;
1421  }
1422  /* Update length */
1423  *dstlen = bytes + 6;
1424  /* Return new header */
1425  return h;
1426 }
1427 
1428 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1429 {
1430  unsigned char *compress_space;
1431  int len;
1432  int res;
1433  unsigned long bytes;
1434  struct dundi_ie_data ied;
1435  struct dundi_peer *peer;
1436  unsigned char iv[16];
1437  len = pack->datalen + pack->datalen / 100 + 42;
1438  compress_space = ast_alloca(len);
1439  memset(compress_space, 0, len);
1440  /* We care about everthing save the first 6 bytes of header */
1441  bytes = len;
1442  res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1443  if (res != Z_OK) {
1444  ast_debug(1, "Ouch, compression failed!\n");
1445  return -1;
1446  }
1447  memset(&ied, 0, sizeof(ied));
1448  /* Say who we are */
1449  if (!pack->h->iseqno && !pack->h->oseqno) {
1450  /* Need the key in the first copy */
1451  if (!(peer = find_peer(&trans->them_eid)))
1452  return -1;
1453  if (update_key(peer))
1454  return -1;
1455  if (!peer->sentfullkey)
1457  /* Append key data */
1458  dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1459  if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
1460  dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1461  dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1462  } else {
1464  }
1465  /* Setup contexts */
1466  trans->ecx = peer->us_ecx;
1467  trans->dcx = peer->us_dcx;
1468 
1469  /* We've sent the full key */
1470  peer->sentfullkey = 1;
1471  }
1472  /* Build initialization vector */
1473  build_iv(iv);
1474  /* Add the field, rounded up to 16 bytes */
1475  dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1476  /* Copy the data */
1477  if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1478  ast_log(LOG_NOTICE, "Final packet too large!\n");
1479  return -1;
1480  }
1481  encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1482  ied.pos += ((bytes + 15) / 16) * 16;
1483  /* Reconstruct header */
1484  pack->datalen = sizeof(struct dundi_hdr);
1485  pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
1486  pack->h->cmdflags = 0;
1487  memcpy(pack->h->ies, ied.buf, ied.pos);
1488  pack->datalen += ied.pos;
1489  return 0;
1490 }
1491 
1492 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, uint32_t keycrc32)
1493 {
1494  unsigned char dst[128];
1495  int res;
1496  struct ast_key *key, *skey;
1497  char eid_str[20];
1498  ast_debug(1, "Expected '%08x' got '%08x'\n", peer->them_keycrc32, keycrc32);
1499  if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1500  /* A match */
1501  return 1;
1502  } else if (!newkey || !newsig)
1503  return 0;
1504  if (!memcmp(peer->rxenckey, newkey, 128) &&
1505  !memcmp(peer->rxenckey + 128, newsig, 128)) {
1506  /* By definition, a match */
1507  return 1;
1508  }
1509  /* Decrypt key */
1510  key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1511  if (!key) {
1512  ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1513  peer->outkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1514  return -1;
1515  }
1516 
1517  skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1518  if (!skey) {
1519  ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1520  peer->inkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1521  return -1;
1522  }
1523 
1524  /* First check signature */
1525  res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
1526  if (res)
1527  return 0;
1528 
1529  res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1530  if (res != 16) {
1531  if (res >= 0)
1532  ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1533  return 0;
1534  }
1535  /* Decrypted, passes signature */
1536  ast_debug(1, "Wow, new key combo passed signature and decrypt!\n");
1537  memcpy(peer->rxenckey, newkey, 128);
1538  memcpy(peer->rxenckey + 128, newsig, 128);
1539  peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1540  ast_aes_set_decrypt_key(dst, &peer->them_dcx);
1541  ast_aes_set_encrypt_key(dst, &peer->them_ecx);
1542  return 1;
1543 }
1544 
1545 static void deep_copy_peer(struct dundi_peer *peer_dst, const struct dundi_peer *peer_src)
1546 {
1547  struct permission *cur, *perm;
1548 
1549  *peer_dst = *peer_src;
1550  AST_LIST_NEXT(peer_dst, list) = NULL;
1551 
1552  /* Scheduled items cannot go with the copy */
1553  peer_dst->registerid = -1;
1554  peer_dst->qualifyid = -1;
1555  peer_dst->registerexpire = -1;
1556 
1557  /* Transactions and lookup history cannot go with the copy either */
1558  peer_dst->regtrans = NULL;
1559  peer_dst->qualtrans = NULL;
1560  memset(&peer_dst->lookups, 0, sizeof(peer_dst->lookups));
1561 
1562  memset(&peer_dst->permit, 0, sizeof(peer_dst->permit));
1563  memset(&peer_dst->include, 0, sizeof(peer_dst->permit));
1564 
1565  AST_LIST_TRAVERSE(&peer_src->permit, cur, list) {
1566  if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1567  continue;
1568 
1569  perm->allow = cur->allow;
1570  strcpy(perm->name, cur->name);
1571 
1572  AST_LIST_INSERT_HEAD(&peer_dst->permit, perm, list);
1573  }
1574 
1575  AST_LIST_TRAVERSE(&peer_src->include, cur, list) {
1576  if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1577  continue;
1578 
1579  perm->allow = cur->allow;
1580  strcpy(perm->name, cur->name);
1581 
1582  AST_LIST_INSERT_HEAD(&peer_dst->include, perm, list);
1583  }
1584 }
1585 
1586 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1587 {
1588  /* Handle canonical command / response */
1589  int final = hdr->cmdresp & 0x80;
1590  int cmd = hdr->cmdresp & 0x7f;
1591  int x,y,z;
1592  int resp;
1593  int res;
1594  int authpass=0;
1595  unsigned char *bufcpy;
1596 #ifdef LOW_MEMORY
1597  struct dundi_ie_data *ied = ast_calloc(1, sizeof(*ied));
1598 #else
1599  struct dundi_ie_data _ied = {
1600  .pos = 0,
1601  };
1602  struct dundi_ie_data *ied = &_ied;
1603 #endif
1604  struct dundi_ies ies = {
1605  .eidcount = 0,
1606  };
1607  struct dundi_peer *peer = NULL;
1608  char eid_str[20];
1609  char eid_str2[20];
1610  int retval = -1;
1611 
1612  if (!ied) {
1613  return -1;
1614  }
1615 
1616  if (datalen) {
1617  bufcpy = ast_alloca(datalen);
1618  /* Make a copy for parsing */
1619  memcpy(bufcpy, hdr->ies, datalen);
1620  ast_debug(1, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1621  if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1622  ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1623  goto return_cleanup;
1624  }
1625  }
1626  switch(cmd) {
1630  if (cmd == DUNDI_COMMAND_EIDQUERY)
1632  else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1633  resp = DUNDI_COMMAND_PRECACHERP;
1634  else
1635  resp = DUNDI_COMMAND_DPRESPONSE;
1636  /* A dialplan or entity discover -- qualify by highest level entity */
1637  peer = find_peer(ies.eids[0]);
1638  if (!peer) {
1640  dundi_send(trans, resp, 0, 1, ied);
1641  } else {
1642  int hasauth = 0;
1643  trans->us_eid = peer->us_eid;
1644  if (strlen(peer->inkey)) {
1645  hasauth = encrypted;
1646  } else
1647  hasauth = 1;
1648  if (hasauth) {
1649  /* Okay we're authentiated and all, now we check if they're authorized */
1650  if (!ies.called_context)
1651  ies.called_context = "e164";
1652  if (cmd == DUNDI_COMMAND_EIDQUERY) {
1653  res = dundi_answer_entity(trans, &ies, ies.called_context);
1654  } else {
1655  if (ast_strlen_zero(ies.called_number)) {
1656  /* They're not permitted to access that context */
1657  dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1658  dundi_send(trans, resp, 0, 1, ied);
1659  } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
1660  (peer->model & DUNDI_MODEL_INBOUND) &&
1661  has_permission(&peer->permit, ies.called_context)) {
1662  res = dundi_answer_query(trans, &ies, ies.called_context);
1663  if (res < 0) {
1664  /* There is no such dundi context */
1665  dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1666  dundi_send(trans, resp, 0, 1, ied);
1667  }
1668  } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
1669  (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
1670  has_permission(&peer->include, ies.called_context)) {
1671  res = dundi_prop_precache(trans, &ies, ies.called_context);
1672  if (res < 0) {
1673  /* There is no such dundi context */
1674  dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1675  dundi_send(trans, resp, 0, 1, ied);
1676  }
1677  } else {
1678  /* They're not permitted to access that context */
1679  dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1680  dundi_send(trans, resp, 0, 1, ied);
1681  }
1682  }
1683  } else {
1684  /* They're not permitted to access that context */
1685  dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1686  dundi_send(trans, resp, 0, 1, ied);
1687  }
1688  }
1689  break;
1690  case DUNDI_COMMAND_REGREQ:
1691  /* A register request -- should only have one entity */
1692  peer = find_peer(ies.eids[0]);
1693 
1694  /* if the peer is not found and we have a valid 'any_peer' setting */
1695  if (any_peer && peer == any_peer) {
1696  /* copy any_peer into a new peer object */
1697  peer = ast_calloc(1, sizeof(*peer));
1698  if (peer) {
1699  deep_copy_peer(peer, any_peer);
1700 
1701  /* set EID to remote EID */
1702  peer->eid = *ies.eids[0];
1703 
1704  AST_LIST_LOCK(&peers);
1705  AST_LIST_INSERT_HEAD(&peers, peer, list);
1707  }
1708  }
1709 
1710  if (!peer || !peer->dynamic) {
1712  dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
1713  } else {
1714  int hasauth = 0;
1715  trans->us_eid = peer->us_eid;
1716  if (!ast_strlen_zero(peer->inkey)) {
1717  hasauth = encrypted;
1718  } else
1719  hasauth = 1;
1720  if (hasauth) {
1721  int expire = default_expiration;
1722  char data[256];
1723  int needqual = 0;
1724  AST_SCHED_DEL(sched, peer->registerexpire);
1725  peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1726  snprintf(data, sizeof(data), "%s:%d", ast_sockaddr_stringify(&trans->addr), expire);
1727  ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1728  if (ast_sockaddr_cmp(&peer->addr, &trans->addr)) {
1729  ast_verb(3, "Registered DUNDi peer '%s' at '%s'\n",
1730  ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
1731  ast_sockaddr_stringify(&trans->addr));
1732  needqual = 1;
1733  }
1734 
1735  ast_sockaddr_copy(&peer->addr, &trans->addr);
1736  dundi_ie_append_short(ied, DUNDI_IE_EXPIRATION, default_expiration);
1737  dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
1738  if (needqual)
1739  qualify_peer(peer, 1);
1740  }
1741  }
1742  break;
1744  /* A dialplan response, lets see what we got... */
1745  if (ies.cause < 1) {
1746  /* Success of some sort */
1747  ast_debug(1, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1748  if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1749  authpass = encrypted;
1750  } else
1751  authpass = 1;
1752  if (authpass) {
1753  /* Pass back up answers */
1754  if (trans->parent && trans->parent->dr) {
1755  y = trans->parent->respcount;
1756  for (x=0;x<ies.anscount;x++) {
1757  if (trans->parent->respcount < trans->parent->maxcount) {
1758  /* Make sure it's not already there */
1759  for (z=0;z<trans->parent->respcount;z++) {
1760  if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1761  !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
1762  break;
1763  }
1764  if (z == trans->parent->respcount) {
1765  /* Copy into parent responses */
1766  trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1767  trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1768  trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1769  trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1770  if (ies.expiration > 0)
1771  trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1772  else
1773  trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
1774  ast_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
1775  sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1776  &ies.answers[x]->eid);
1777  ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1778  sizeof(trans->parent->dr[trans->parent->respcount].dest));
1779  ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1780  sizeof(trans->parent->dr[trans->parent->respcount].tech));
1781  trans->parent->respcount++;
1783  } else if (trans->parent->dr[z].weight > ntohs(ies.answers[x]->weight)) {
1784  /* Update weight if appropriate */
1785  trans->parent->dr[z].weight = ntohs(ies.answers[x]->weight);
1786  }
1787  } else
1788  ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1789  trans->parent->number, trans->parent->dcontext);
1790  }
1791  /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1792  the cache know if this request was unaffected by our entity list. */
1793  cache_save(&trans->them_eid, trans->parent, y,
1794  ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1795  if (ies.hint) {
1796  cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1799  if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
1800  if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1801  ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
1802  sizeof(trans->parent->hmd->exten));
1803  }
1804  } else {
1806  }
1807  }
1808  if (ies.expiration > 0) {
1809  if (trans->parent->expiration > ies.expiration) {
1810  trans->parent->expiration = ies.expiration;
1811  }
1812  }
1813  }
1814  /* Close connection if not final */
1815  if (!final)
1816  dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1817  }
1818 
1819  } else {
1820  /* Auth failure, check for data */
1821  if (!final) {
1822  /* Cancel if they didn't already */
1823  dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1824  }
1825  }
1826  break;
1828  /* A dialplan response, lets see what we got... */
1829  if (ies.cause < 1) {
1830  /* Success of some sort */
1831  ast_debug(1, "Looks like success of some sort (%d)\n", ies.cause);
1832  if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1833  authpass = encrypted;
1834  } else
1835  authpass = 1;
1836  if (authpass) {
1837  /* Pass back up answers */
1838  if (trans->parent && trans->parent->dei && ies.q_org) {
1839  if (!trans->parent->respcount) {
1840  trans->parent->respcount++;
1841  if (ies.q_dept)
1842  ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1843  if (ies.q_org)
1844  ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1845  if (ies.q_locality)
1846  ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1847  if (ies.q_stateprov)
1848  ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1849  if (ies.q_country)
1850  ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1851  if (ies.q_email)
1852  ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1853  if (ies.q_phone)
1854  ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1855  if (ies.q_ipaddr)
1856  ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1857  if (!ast_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1858  /* If it's them, update our address */
1859  ast_copy_string(trans->parent->dei->ipaddr, ast_sockaddr_stringify_addr(&trans->addr), sizeof(trans->parent->dei->ipaddr));
1860  }
1861  }
1862  if (ies.hint) {
1865  }
1866  }
1867  /* Close connection if not final */
1868  if (!final)
1869  dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1870  }
1871 
1872  } else {
1873  /* Auth failure, check for data */
1874  if (!final) {
1875  /* Cancel if they didn't already */
1876  dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1877  }
1878  }
1879  break;
1881  /* A dialplan response, lets see what we got... */
1882  if (ies.cause < 1) {
1883  int hasauth;
1884  /* Success of some sort */
1885  if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1886  hasauth = encrypted;
1887  } else
1888  hasauth = 1;
1889 
1890  if (!hasauth) {
1891  ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1892  if (!final) {
1893  dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1894  dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, ied);
1895  }
1896  } else {
1897  ast_debug(1, "Yay, we've registered as '%s' to '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &trans->us_eid),
1898  ast_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1899  /* Close connection if not final */
1900  if (!final)
1901  dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1902  }
1903  } else {
1904  /* Auth failure, cancel if they didn't for some reason */
1905  if (!final) {
1906  dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1907  }
1908  }
1909  break;
1910  case DUNDI_COMMAND_INVALID:
1911  case DUNDI_COMMAND_NULL:
1913  /* Do nothing special */
1914  if (!final)
1915  dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1916  break;
1917  case DUNDI_COMMAND_ENCREJ:
1918  if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
1919  /* No really, it's over at this point */
1920  if (!final)
1921  dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1922  } else {
1923  /* Send with full key */
1925  if (final) {
1926  /* Ooops, we got a final message, start by sending ACK... */
1927  dundi_ack(trans, hdr->cmdresp & 0x80);
1928  trans->aseqno = trans->iseqno;
1929  /* Now, we gotta create a new transaction */
1930  if (!reset_transaction(trans)) {
1931  /* Make sure handle_frame doesn't destroy us */
1932  hdr->cmdresp &= 0x7f;
1933  /* Parse the message we transmitted */
1934  memset(&ies, 0, sizeof(ies));
1935  dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
1936  /* Reconstruct outgoing encrypted packet */
1937  memset(ied, 0, sizeof(*ied));
1938  dundi_ie_append_eid(ied, DUNDI_IE_EID, &trans->us_eid);
1940  dundi_ie_append_raw(ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1941  if (ies.encblock)
1943  dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, ied);
1944  peer->sentfullkey = 1;
1945  }
1946  }
1947  }
1948  break;
1949  case DUNDI_COMMAND_ENCRYPT:
1950  if (!encrypted) {
1951  /* No nested encryption! */
1952  if ((trans->iseqno == 1) && !trans->oseqno) {
1953  if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
1954  ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1955  (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1956  if (!final) {
1957  dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1958  }
1959  break;
1960  }
1961  apply_peer(trans, peer);
1962  /* Key passed, use new contexts for this session */
1963  trans->ecx = peer->them_ecx;
1964  trans->dcx = peer->them_dcx;
1965  }
1966  if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1967  struct dundi_hdr *dhdr;
1968  unsigned char decoded[MAX_PACKET_SIZE];
1969  int ddatalen;
1970  ddatalen = sizeof(decoded);
1971  dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1972  if (dhdr) {
1973  /* Handle decrypted response */
1974  if (dundidebug)
1975  dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1976  handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1977  /* Carry back final flag */
1978  hdr->cmdresp |= dhdr->cmdresp & 0x80;
1979  break;
1980  } else {
1981  ast_debug(1, "Ouch, decrypt failed :(\n");
1982  }
1983  }
1984  }
1985  if (!final) {
1986  /* Turn off encryption */
1987  ast_clear_flag(trans, FLAG_ENCRYPT);
1988  dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1989  }
1990  break;
1991  default:
1992  /* Send unknown command if we don't know it, with final flag IFF it's the
1993  first command in the dialog and only if we haven't received final notification */
1994  if (!final) {
1996  dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, ied);
1997  }
1998  }
1999 
2000  retval = 0;
2001 
2002 return_cleanup:
2003 #ifdef LOW_MEMORY
2004  ast_free(ied);
2005 #endif
2006  return retval;
2007 }
2008 
2009 static void destroy_packet(struct dundi_packet *pack, int needfree);
2010 static void destroy_packets(struct packetlist *p)
2011 {
2012  struct dundi_packet *pack;
2013 
2014  while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
2015  AST_SCHED_DEL(sched, pack->retransid);
2016  ast_free(pack);
2017  }
2018 }
2019 
2020 
2021 static int ack_trans(struct dundi_transaction *trans, int iseqno)
2022 {
2023  struct dundi_packet *pack;
2024 
2025  /* Ack transmitted packet corresponding to iseqno */
2026  AST_LIST_TRAVERSE(&trans->packets, pack, list) {
2027  if ((pack->h->oseqno + 1) % 255 == iseqno) {
2028  destroy_packet(pack, 0);
2029  if (!AST_LIST_EMPTY(&trans->lasttrans)) {
2030  ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
2031  destroy_packets(&trans->lasttrans);
2032  }
2033  AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
2034  AST_SCHED_DEL(sched, trans->autokillid);
2035  return 1;
2036  }
2037  }
2038 
2039  return 0;
2040 }
2041 
2042 static int handle_frame(struct dundi_hdr *h, struct ast_sockaddr *sin, int datalen)
2043 {
2044  struct dundi_transaction *trans;
2045  trans = find_transaction(h, sin);
2046  if (!trans) {
2047  dundi_reject(h, sin);
2048  return 0;
2049  }
2050  /* Got a transaction, see where this header fits in */
2051  if (h->oseqno == trans->iseqno) {
2052  /* Just what we were looking for... Anything but ack increments iseqno */
2053  if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
2054  /* If final, we're done */
2055  destroy_trans(trans, 0);
2056  return 0;
2057  }
2058  if (h->cmdresp != DUNDI_COMMAND_ACK) {
2059  trans->oiseqno = trans->iseqno;
2060  trans->iseqno++;
2061  handle_command_response(trans, h, datalen, 0);
2062  }
2063  if (trans->aseqno != trans->iseqno) {
2064  dundi_ack(trans, h->cmdresp & 0x80);
2065  trans->aseqno = trans->iseqno;
2066  }
2067  /* Delete any saved last transmissions */
2068  destroy_packets(&trans->lasttrans);
2069  if (h->cmdresp & 0x80) {
2070  /* Final -- destroy now */
2071  destroy_trans(trans, 0);
2072  }
2073  } else if (h->oseqno == trans->oiseqno) {
2074  /* Last incoming sequence number -- send ACK without processing */
2075  dundi_ack(trans, 0);
2076  } else {
2077  /* Out of window -- simply drop */
2078  ast_debug(1, "Dropping packet out of window!\n");
2079  }
2080  return 0;
2081 }
2082 
2083 static int socket_read(int *id, int fd, short events, void *sock)
2084 {
2085  struct ast_sockaddr sin;
2086  int res;
2087  struct dundi_hdr *h;
2088  char buf[MAX_PACKET_SIZE];
2089 
2090  res = ast_recvfrom(*((int *)sock), buf, sizeof(buf), 0, &sin);
2091  if (res < 0) {
2092  if (errno != ECONNREFUSED)
2093  ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
2094  return 1;
2095  }
2096  if (res < sizeof(struct dundi_hdr)) {
2097  ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
2098  return 1;
2099  }
2100  buf[res] = '\0';
2101  h = (struct dundi_hdr *) buf;
2102  if (dundidebug)
2103  dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
2104  AST_LIST_LOCK(&peers);
2105  handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
2107  return 1;
2108 }
2109 
2110 static void build_secret(char *secret, int seclen)
2111 {
2112  unsigned char tmp[16];
2113  char *s;
2114  build_iv(tmp);
2115  secret[0] = '\0';
2116  ast_base64encode(secret, tmp, sizeof(tmp), seclen);
2117  /* Eliminate potential bad characters */
2118  while((s = strchr(secret, ';'))) *s = '+';
2119  while((s = strchr(secret, '/'))) *s = '+';
2120  while((s = strchr(secret, ':'))) *s = '+';
2121  while((s = strchr(secret, '@'))) *s = '+';
2122 }
2123 
2124 
2125 static void save_secret(const char *newkey, const char *oldkey)
2126 {
2127  char tmp[350];
2128  if (oldkey)
2129  snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2130  else
2131  snprintf(tmp, sizeof(tmp), "%s", newkey);
2132  rotatetime = time(NULL) + DUNDI_SECRET_TIME;
2133  ast_db_put(secretpath, "secret", tmp);
2134  snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
2135  ast_db_put(secretpath, "secretexpiry", tmp);
2136 }
2137 
2138 static void load_password(void)
2139 {
2140  char *current=NULL;
2141  char *last=NULL;
2142  char tmp[256];
2143  time_t expired;
2144 
2145  ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2146  if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
2147  ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2148  current = strchr(tmp, ';');
2149  if (!current)
2150  current = tmp;
2151  else {
2152  *current = '\0';
2153  current++;
2154  };
2155  if ((time(NULL) - expired) < 0) {
2156  if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2157  expired = time(NULL) + DUNDI_SECRET_TIME;
2158  } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2159  last = current;
2160  current = NULL;
2161  } else {
2162  last = NULL;
2163  current = NULL;
2164  }
2165  }
2166  if (current) {
2167  /* Current key is still valid, just setup rotatation properly */
2168  ast_copy_string(cursecret, current, sizeof(cursecret));
2169  rotatetime = expired;
2170  } else {
2171  /* Current key is out of date, rotate or eliminate all together */
2172  build_secret(cursecret, sizeof(cursecret));
2173  save_secret(cursecret, last);
2174  }
2175 }
2176 
2177 static void check_password(void)
2178 {
2179  char oldsecret[80];
2180  time_t now;
2181 
2182  time(&now);
2183 #if 0
2184  printf("%ld/%ld\n", now, rotatetime);
2185 #endif
2186  if ((now - rotatetime) >= 0) {
2187  /* Time to rotate keys */
2188  ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
2189  build_secret(cursecret, sizeof(cursecret));
2190  save_secret(cursecret, oldsecret);
2191  }
2192 }
2193 
2194 static void *network_thread(void *ignore)
2195 {
2196  /* Our job is simple: Send queued messages, retrying if necessary. Read frames
2197  from the network, and queue them for delivery to the channels */
2198  int res;
2199  /* Establish I/O callback for socket read */
2200  int *socket_read_id = ast_io_add(io, netsocket, socket_read, AST_IO_IN, &netsocket);
2201  int *socket_read_id2 = NULL;
2202  if (netsocket2 >= 0) {
2203  socket_read_id2 = ast_io_add(io, netsocket2, socket_read, AST_IO_IN, &netsocket2);
2204  }
2205 
2206  while (!dundi_shutdown) {
2207  res = ast_sched_wait(sched);
2208  if ((res > 1000) || (res < 0))
2209  res = 1000;
2210  res = ast_io_wait(io, res);
2211  if (res >= 0) {
2212  AST_LIST_LOCK(&peers);
2213  ast_sched_runq(sched);
2215  }
2216  check_password();
2217  }
2218 
2219  ast_io_remove(io, socket_read_id);
2220 
2221  if (socket_read_id2) {
2222  ast_io_remove(io, socket_read_id2);
2223  }
2224 
2225  return NULL;
2226 }
2227 
2228 static void *process_clearcache(void *ignore)
2229 {
2230  struct ast_db_entry *db_entry, *db_tree;
2231  int striplen = sizeof("/dundi/cache");
2232  time_t now;
2233 
2234  while (!dundi_shutdown) {
2235  pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
2236 
2237  time(&now);
2238 
2239  db_entry = db_tree = ast_db_gettree("dundi/cache", NULL);
2240  for (; db_entry; db_entry = db_entry->next) {
2241  time_t expiry;
2242 
2243  if (!ast_get_time_t(db_entry->data, &expiry, 0, NULL)) {
2244  if (expiry < now) {
2245  ast_debug(1, "clearing expired DUNDI cache entry: %s\n", db_entry->key);
2246  ast_db_del("dundi/cache", db_entry->key + striplen);
2247  }
2248  }
2249  }
2250  ast_db_freetree(db_tree);
2251 
2252  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
2253  pthread_testcancel();
2254  sleep(60);
2255  pthread_testcancel();
2256  }
2257 
2258  return NULL;
2259 }
2260 
2261 static void *process_precache(void *ign)
2262 {
2263  struct dundi_precache_queue *qe;
2264  time_t now;
2265  char context[256];
2266  char number[256];
2267  int run;
2268 
2269  while (!dundi_shutdown) {
2270  time(&now);
2271  run = 0;
2272  AST_LIST_LOCK(&pcq);
2273  if ((qe = AST_LIST_FIRST(&pcq))) {
2274  if (!qe->expiration) {
2275  /* Gone... Remove... */
2277  ast_free(qe);
2278  } else if (qe->expiration < now) {
2279  /* Process this entry */
2280  qe->expiration = 0;
2281  ast_copy_string(context, qe->context, sizeof(context));
2282  ast_copy_string(number, qe->number, sizeof(number));
2283  run = 1;
2284  }
2285  }
2286  AST_LIST_UNLOCK(&pcq);
2287  if (run) {
2288  dundi_precache(context, number);
2289  } else
2290  sleep(1);
2291  }
2292 
2293  return NULL;
2294 }
2295 
2296 static int start_network_thread(void)
2297 {
2301  return 0;
2302 }
2303 
2304 static char *dundi_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2305 {
2306  switch (cmd) {
2307  case CLI_INIT:
2308  e->command = "dundi set debug {on|off}";
2309  e->usage =
2310  "Usage: dundi set debug {on|off}\n"
2311  " Enables/Disables dumping of DUNDi packets for debugging purposes\n";
2312  return NULL;
2313  case CLI_GENERATE:
2314  return NULL;
2315  }
2316 
2317  if (a->argc != e->args) {
2318  return CLI_SHOWUSAGE;
2319  }
2320  if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
2321  dundidebug = 1;
2322  ast_cli(a->fd, "DUNDi Debugging Enabled\n");
2323  } else {
2324  dundidebug = 0;
2325  ast_cli(a->fd, "DUNDi Debugging Disabled\n");
2326  }
2327  return CLI_SUCCESS;
2328 }
2329 
2330 static char *dundi_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2331 {
2332  switch (cmd) {
2333  case CLI_INIT:
2334  e->command = "dundi store history {on|off}";
2335  e->usage =
2336  "Usage: dundi store history {on|off}\n"
2337  " Enables/Disables storing of DUNDi requests and times for debugging\n"
2338  "purposes\n";
2339  return NULL;
2340  case CLI_GENERATE:
2341  return NULL;
2342  }
2343 
2344  if (a->argc != e->args) {
2345  return CLI_SHOWUSAGE;
2346  }
2347  if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
2348  global_storehistory = 1;
2349  ast_cli(a->fd, "DUNDi History Storage Enabled\n");
2350  } else {
2351  global_storehistory = 0;
2352  ast_cli(a->fd, "DUNDi History Storage Disabled\n");
2353  }
2354  return CLI_SUCCESS;
2355 }
2356 
2357 static char *dundi_flush(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2358 {
2359  int stats = 0;
2360  switch (cmd) {
2361  case CLI_INIT:
2362  e->command = "dundi flush [stats]";
2363  e->usage =
2364  "Usage: dundi flush [stats]\n"
2365  " Flushes DUNDi answer cache, used primarily for debug. If\n"
2366  "'stats' is present, clears timer statistics instead of normal\n"
2367  "operation.\n";
2368  return NULL;
2369  case CLI_GENERATE:
2370  return NULL;
2371  }
2372  if ((a->argc < 2) || (a->argc > 3)) {
2373  return CLI_SHOWUSAGE;
2374  }
2375  if (a->argc > 2) {
2376  if (!strcasecmp(a->argv[2], "stats")) {
2377  stats = 1;
2378  } else {
2379  return CLI_SHOWUSAGE;
2380  }
2381  }
2382  if (stats) {
2383  /* Flush statistics */
2384  struct dundi_peer *p;
2385  int x;
2386  AST_LIST_LOCK(&peers);
2387  AST_LIST_TRAVERSE(&peers, p, list) {
2388  for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2389  ast_free(p->lookups[x]);
2390  p->lookups[x] = NULL;
2391  p->lookuptimes[x] = 0;
2392  }
2393  p->avgms = 0;
2394  }
2396  } else {
2397  ast_db_deltree("dundi/cache", NULL);
2398  ast_cli(a->fd, "DUNDi Cache Flushed\n");
2399  }
2400  return CLI_SUCCESS;
2401 }
2402 
2403 static char *model2str(int model)
2404 {
2405  switch(model) {
2406  case DUNDI_MODEL_INBOUND:
2407  return "Inbound";
2408  case DUNDI_MODEL_OUTBOUND:
2409  return "Outbound";
2410  case DUNDI_MODEL_SYMMETRIC:
2411  return "Symmetric";
2412  default:
2413  return "Unknown";
2414  }
2415 }
2416 
2417 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
2418 {
2419  int which=0, len;
2420  char *ret = NULL;
2421  struct dundi_peer *p;
2422  char eid_str[20];
2423 
2424  if (pos != rpos)
2425  return NULL;
2426  AST_LIST_LOCK(&peers);
2427  len = strlen(word);
2428  AST_LIST_TRAVERSE(&peers, p, list) {
2429  const char *s = ast_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
2430  if (!strncasecmp(word, s, len) && ++which > state) {
2431  ret = ast_strdup(s);
2432  break;
2433  }
2434  }
2436  return ret;
2437 }
2438 
2439 static int rescomp(const void *a, const void *b)
2440 {
2441  const struct dundi_result *resa, *resb;
2442  resa = a;
2443  resb = b;
2444  if (resa->weight < resb->weight)
2445  return -1;
2446  if (resa->weight > resb->weight)
2447  return 1;
2448  return 0;
2449 }
2450 
2451 static void sort_results(struct dundi_result *results, int count)
2452 {
2453  qsort(results, count, sizeof(results[0]), rescomp);
2454 }
2455 
2456 static char *dundi_do_lookup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2457 {
2458  int res;
2459  char tmp[256];
2460  char fs[80] = "";
2461  char *context;
2462  int x;
2463  int bypass = 0;
2464  struct dundi_result dr[MAX_RESULTS];
2465  struct timeval start;
2466  switch (cmd) {
2467  case CLI_INIT:
2468  e->command = "dundi lookup";
2469  e->usage =
2470  "Usage: dundi lookup <number>[@context] [bypass]\n"
2471  " Lookup the given number within the given DUNDi context\n"
2472  "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
2473  "keyword is specified.\n";
2474  return NULL;
2475  case CLI_GENERATE:
2476  return NULL;
2477  }
2478 
2479  if ((a->argc < 3) || (a->argc > 4)) {
2480  return CLI_SHOWUSAGE;
2481  }
2482  if (a->argc > 3) {
2483  if (!strcasecmp(a->argv[3], "bypass")) {
2484  bypass=1;
2485  } else {
2486  return CLI_SHOWUSAGE;
2487  }
2488  }
2489  ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2490  context = strchr(tmp, '@');
2491  if (context) {
2492  *context = '\0';
2493  context++;
2494  }
2495  start = ast_tvnow();
2496  res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2497 
2498  if (res < 0)
2499  ast_cli(a->fd, "DUNDi lookup returned error.\n");
2500  else if (!res)
2501  ast_cli(a->fd, "DUNDi lookup returned no results.\n");
2502  else
2503  sort_results(dr, res);
2504  for (x=0;x<res;x++) {
2505  ast_cli(a->fd, "%3d. %5d %s/%s (%s)\n", x + 1, dr[x].weight, dr[x].tech, dr[x].dest, dundi_flags2str(fs, sizeof(fs), dr[x].flags));
2506  ast_cli(a->fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2507  }
2508  ast_cli(a->fd, "DUNDi lookup completed in %" PRIi64 " ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2509  return CLI_SUCCESS;
2510 }
2511 
2512 static char *dundi_do_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2513 {
2514  int res;
2515  char tmp[256];
2516  char *context;
2517  struct timeval start;
2518  switch (cmd) {
2519  case CLI_INIT:
2520  e->command = "dundi precache";
2521  e->usage =
2522  "Usage: dundi precache <number>[@context]\n"
2523  " Lookup the given number within the given DUNDi context\n"
2524  "(or e164 if none is specified) and precaches the results to any\n"
2525  "upstream DUNDi push servers.\n";
2526  return NULL;
2527  case CLI_GENERATE:
2528  return NULL;
2529  }
2530  if ((a->argc < 3) || (a->argc > 3)) {
2531  return CLI_SHOWUSAGE;
2532  }
2533  ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2534  context = strchr(tmp, '@');
2535  if (context) {
2536  *context = '\0';
2537  context++;
2538  }
2539  start = ast_tvnow();
2540  res = dundi_precache(context, tmp);
2541 
2542  if (res < 0)
2543  ast_cli(a->fd, "DUNDi precache returned error.\n");
2544  else if (!res)
2545  ast_cli(a->fd, "DUNDi precache returned no error.\n");
2546  ast_cli(a->fd, "DUNDi lookup completed in %" PRIi64 " ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2547  return CLI_SUCCESS;
2548 }
2549 
2550 static char *dundi_do_query(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2551 {
2552  int res;
2553  char tmp[256];
2554  char *context;
2555  dundi_eid eid;
2556  struct dundi_entity_info dei;
2557  switch (cmd) {
2558  case CLI_INIT:
2559  e->command = "dundi query";
2560  e->usage =
2561  "Usage: dundi query <entity>[@context]\n"
2562  " Attempts to retrieve contact information for a specific\n"
2563  "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2564  "e164 if none is specified).\n";
2565  return NULL;
2566  case CLI_GENERATE:
2567  return NULL;
2568  }
2569  if ((a->argc < 3) || (a->argc > 3)) {
2570  return CLI_SHOWUSAGE;
2571  }
2572  if (ast_str_to_eid(&eid, a->argv[2])) {
2573  ast_cli(a->fd, "'%s' is not a valid EID!\n", a->argv[2]);
2574  return CLI_SHOWUSAGE;
2575  }
2576  ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2577  context = strchr(tmp, '@');
2578  if (context) {
2579  *context = '\0';
2580  context++;
2581  }
2582  res = dundi_query_eid(&dei, context, eid);
2583  if (res < 0)
2584  ast_cli(a->fd, "DUNDi Query EID returned error.\n");
2585  else if (!res)
2586  ast_cli(a->fd, "DUNDi Query EID returned no results.\n");
2587  else {
2588  ast_cli(a->fd, "DUNDi Query EID succeeded:\n");
2589  ast_cli(a->fd, "Department: %s\n", dei.orgunit);
2590  ast_cli(a->fd, "Organization: %s\n", dei.org);
2591  ast_cli(a->fd, "City/Locality: %s\n", dei.locality);
2592  ast_cli(a->fd, "State/Province: %s\n", dei.stateprov);
2593  ast_cli(a->fd, "Country: %s\n", dei.country);
2594  ast_cli(a->fd, "E-mail: %s\n", dei.email);
2595  ast_cli(a->fd, "Phone: %s\n", dei.phone);
2596  ast_cli(a->fd, "IP Address: %s\n", dei.ipaddr);
2597  }
2598  return CLI_SUCCESS;
2599 }
2600 
2601 static char *dundi_sockaddr_stringify_host(const struct ast_sockaddr *addr)
2602 {
2603  if (ast_sockaddr_isnull(addr)) {
2604  return "(Unspecified)";
2605  }
2606  return ast_sockaddr_stringify_host(addr);
2607 }
2608 
2609 static uint16_t dundi_sockaddr_port(const struct ast_sockaddr *addr)
2610 {
2611  /*
2612  * Test to avoid a debug message complaining about addr
2613  * not being an IPv4 or IPv6 address.
2614  */
2615  if (ast_sockaddr_isnull(addr)) {
2616  return 0;
2617  }
2618  return ast_sockaddr_port(addr);
2619 }
2620 
2621 static char *dundi_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2622 {
2623  struct dundi_peer *peer;
2624  struct permission *p;
2625  char *order;
2626  char eid_str[20];
2627  int x, cnt;
2628  switch (cmd) {
2629  case CLI_INIT:
2630  e->command = "dundi show peer";
2631  e->usage =
2632  "Usage: dundi show peer [peer]\n"
2633  " Provide a detailed description of a specifid DUNDi peer.\n";
2634  return NULL;
2635  case CLI_GENERATE:
2636  return complete_peer_helper(a->line, a->word, a->pos, a->n, 3);
2637  }
2638  if (a->argc != 4) {
2639  return CLI_SHOWUSAGE;
2640  }
2641  AST_LIST_LOCK(&peers);
2642  AST_LIST_TRAVERSE(&peers, peer, list) {
2643  if (!strcasecmp(ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), a->argv[3]))
2644  break;
2645  }
2646  if (peer) {
2647  switch(peer->order) {
2648  case 0:
2649  order = "Primary";
2650  break;
2651  case 1:
2652  order = "Secondary";
2653  break;
2654  case 2:
2655  order = "Tertiary";
2656  break;
2657  case 3:
2658  order = "Quartiary";
2659  break;
2660  default:
2661  order = "Unknown";
2662  }
2663  ast_cli(a->fd, "Peer: %s\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2664  ast_cli(a->fd, "Model: %s\n", model2str(peer->model));
2665  ast_cli(a->fd, "Order: %s\n", order);
2666  ast_cli(a->fd, "Host: %s\n", ast_sockaddr_isnull(&peer->addr) ? "<Unspecified>" : ast_sockaddr_stringify_host(&peer->addr));
2667  ast_cli(a->fd, "Port: %d\n", dundi_sockaddr_port(&peer->addr));
2668  ast_cli(a->fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2669  ast_cli(a->fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
2670  ast_cli(a->fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2671  ast_cli(a->fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2672  if (!AST_LIST_EMPTY(&peer->include))
2673  ast_cli(a->fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2674  AST_LIST_TRAVERSE(&peer->include, p, list)
2675  ast_cli(a->fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2676  if (!AST_LIST_EMPTY(&peer->permit))
2677  ast_cli(a->fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2678  AST_LIST_TRAVERSE(&peer->permit, p, list)
2679  ast_cli(a->fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2680  cnt = 0;
2681  for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2682  if (peer->lookups[x]) {
2683  if (!cnt)
2684  ast_cli(a->fd, "Last few query times:\n");
2685  ast_cli(a->fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2686  cnt++;
2687  }
2688  }
2689  if (cnt)
2690  ast_cli(a->fd, "Average query time: %d ms\n", peer->avgms);
2691  } else
2692  ast_cli(a->fd, "No such peer '%s'\n", a->argv[3]);
2694  return CLI_SUCCESS;
2695 }
2696 
2697 static char *dundi_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2698 {
2699 #define FORMAT2 "%-20.20s %-41s %-6.6s %-10.10s %-8.8s %-15.15s\n"
2700 #define FORMAT "%-20.20s %-41s %s %-6d %-10.10s %-8.8s %-15.15s\n"
2701  struct dundi_peer *peer;
2702  int registeredonly=0;
2703  char avgms[20];
2704  char eid_str[20];
2705  int online_peers = 0;
2706  int offline_peers = 0;
2707  int unmonitored_peers = 0;
2708  int total_peers = 0;
2709  switch (cmd) {
2710  case CLI_INIT:
2711  e->command = "dundi show peers [registered|include|exclude|begin]";
2712  e->usage =
2713  "Usage: dundi show peers [registered|include|exclude|begin]\n"
2714  " Lists all known DUNDi peers.\n"
2715  " If 'registered' is present, only registered peers are shown.\n";
2716  return NULL;
2717  case CLI_GENERATE:
2718  return NULL;
2719  }
2720 
2721  if ((a->argc != 3) && (a->argc != 4) && (a->argc != 5)) {
2722  return CLI_SHOWUSAGE;
2723  }
2724  if ((a->argc == 4)) {
2725  if (!strcasecmp(a->argv[3], "registered")) {
2726  registeredonly = 1;
2727  } else {
2728  return CLI_SHOWUSAGE;
2729  }
2730  }
2731  AST_LIST_LOCK(&peers);
2732  ast_cli(a->fd, FORMAT2, "EID", "Host", "Port", "Model", "AvgTime", "Status");
2733  AST_LIST_TRAVERSE(&peers, peer, list) {
2734  char status[64];
2735  int print_line = -1;
2736  char srch[2000];
2737 
2738  total_peers++;
2739  if (registeredonly && ast_sockaddr_isnull(&peer->addr)) {
2740  continue;
2741  }
2742  if (peer->maxms) {
2743  if (peer->lastms < 0) {
2744  strcpy(status, "UNREACHABLE");
2745  offline_peers++;
2746  }
2747  else if (peer->lastms > peer->maxms) {
2748  snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2749  offline_peers++;
2750  }
2751  else if (peer->lastms) {
2752  snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2753  online_peers++;
2754  }
2755  else {
2756  strcpy(status, "UNKNOWN");
2757  offline_peers++;
2758  }
2759  } else {
2760  strcpy(status, "Unmonitored");
2761  unmonitored_peers++;
2762  }
2763  if (peer->avgms)
2764  snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2765  else
2766  strcpy(avgms, "Unavail");
2767  snprintf(srch, sizeof(srch), FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str),
2768  &peer->eid), dundi_sockaddr_stringify_host(&peer->addr),
2769  peer->dynamic ? "(D)" : "(S)", dundi_sockaddr_port(&peer->addr), model2str(peer->model), avgms, status);
2770 
2771  if (a->argc == 5) {
2772  if (!strcasecmp(a->argv[3],"include") && strstr(srch,a->argv[4])) {
2773  print_line = -1;
2774  } else if (!strcasecmp(a->argv[3],"exclude") && !strstr(srch,a->argv[4])) {
2775  print_line = 1;
2776  } else if (!strcasecmp(a->argv[3],"begin") && !strncasecmp(srch,a->argv[4],strlen(a->argv[4]))) {
2777  print_line = -1;
2778  } else {
2779  print_line = 0;
2780  }
2781  }
2782 
2783  if (print_line) {
2784  ast_cli(a->fd, FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2786  peer->dynamic ? "(D)" : "(S)", dundi_sockaddr_port(&peer->addr), model2str(peer->model), avgms, status);
2787  }
2788  }
2789  ast_cli(a->fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2791  return CLI_SUCCESS;
2792 #undef FORMAT
2793 #undef FORMAT2
2794 }
2795 
2796 static char *dundi_show_trans(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2797 {
2798 #define FORMAT2 "%-47s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2799 #define FORMAT "%-41s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2800  struct dundi_transaction *trans;
2801  switch (cmd) {
2802  case CLI_INIT:
2803  e->command = "dundi show trans";
2804  e->usage =
2805  "Usage: dundi show trans\n"
2806  " Lists all known DUNDi transactions.\n";
2807  return NULL;
2808  case CLI_GENERATE:
2809  return NULL;
2810  }
2811  if (a->argc != 3) {
2812  return CLI_SHOWUSAGE;
2813  }
2814  AST_LIST_LOCK(&peers);
2815  ast_cli(a->fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2816  AST_LIST_TRAVERSE(&alltrans, trans, all) {
2818  ast_sockaddr_port(&trans->addr), trans->strans, trans->dtrans,
2819  trans->oseqno, trans->iseqno, trans->aseqno);
2820  }
2822  return CLI_SUCCESS;
2823 #undef FORMAT
2824 #undef FORMAT2
2825 }
2826 
2827 static char *dundi_show_entityid(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2828 {
2829  char eid_str[20];
2830  switch (cmd) {
2831  case CLI_INIT:
2832  e->command = "dundi show entityid";
2833  e->usage =
2834  "Usage: dundi show entityid\n"
2835  " Displays the global entityid for this host.\n";
2836  return NULL;
2837  case CLI_GENERATE:
2838  return NULL;
2839  }
2840  if (a->argc != 3) {
2841  return CLI_SHOWUSAGE;
2842  }
2843  AST_LIST_LOCK(&peers);
2844  ast_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2846  ast_cli(a->fd, "Global EID for this system is '%s'\n", eid_str);
2847  return CLI_SUCCESS;
2848 }
2849 
2850 static char *dundi_show_requests(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2851 {
2852 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2853 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2854  struct dundi_request *req;
2855  char eidstr[20];
2856  switch (cmd) {
2857  case CLI_INIT:
2858  e->command = "dundi show requests";
2859  e->usage =
2860  "Usage: dundi show requests\n"
2861  " Lists all known pending DUNDi requests.\n";
2862  return NULL;
2863  case CLI_GENERATE:
2864  return NULL;
2865  }
2866  if (a->argc != 3) {
2867  return CLI_SHOWUSAGE;
2868  }
2869  AST_LIST_LOCK(&peers);
2870  ast_cli(a->fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2871  AST_LIST_TRAVERSE(&requests, req, list) {
2872  ast_cli(a->fd, FORMAT, req->number, req->dcontext,
2873  dundi_eid_zero(&req->root_eid) ? "<unspecified>" : ast_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2874  }
2876  return CLI_SUCCESS;
2877 #undef FORMAT
2878 #undef FORMAT2
2879 }
2880 
2881 /* Grok-a-dial DUNDi */
2882 
2883 static char *dundi_show_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2884 {
2885 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2886 #define FORMAT "%-12.12s %-7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2887  struct dundi_mapping *map;
2888  char fs[256];
2889  char weight[8];
2890  switch (cmd) {
2891  case CLI_INIT:
2892  e->command = "dundi show mappings";
2893  e->usage =
2894  "Usage: dundi show mappings\n"
2895  " Lists all known DUNDi mappings.\n";
2896  return NULL;
2897  case CLI_GENERATE:
2898  return NULL;
2899  }
2900  if (a->argc != 3) {
2901  return CLI_SHOWUSAGE;
2902  }
2903  AST_LIST_LOCK(&peers);
2904  ast_cli(a->fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2905  AST_LIST_TRAVERSE(&mappings, map, list) {
2906  snprintf(weight, sizeof(weight), "%d", get_mapping_weight(map, NULL));
2907  ast_cli(a->fd, FORMAT, map->dcontext, weight,
2908  ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2909  dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2910  }
2912  return CLI_SUCCESS;
2913 #undef FORMAT
2914 #undef FORMAT2
2915 }
2916 
2917 static char *dundi_show_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2918 {
2919 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2920 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2921  struct dundi_precache_queue *qe;
2922  int h,m,s;
2923  time_t now;
2924  switch (cmd) {
2925  case CLI_INIT:
2926  e->command = "dundi show precache";
2927  e->usage =
2928  "Usage: dundi show precache\n"
2929  " Lists all known DUNDi scheduled precache updates.\n";
2930  return NULL;
2931  case CLI_GENERATE:
2932  return NULL;
2933  }
2934  if (a->argc != 3) {
2935  return CLI_SHOWUSAGE;
2936  }
2937  time(&now);
2938  ast_cli(a->fd, FORMAT2, "Number", "Context", "Expiration");
2939  AST_LIST_LOCK(&pcq);
2940  AST_LIST_TRAVERSE(&pcq, qe, list) {
2941  s = qe->expiration - now;
2942  h = s / 3600;
2943  s = s % 3600;
2944  m = s / 60;
2945  s = s % 60;
2946  ast_cli(a->fd, FORMAT, qe->number, qe->context, h,m,s);
2947  }
2948  AST_LIST_UNLOCK(&pcq);
2949 
2950  return CLI_SUCCESS;
2951 #undef FORMAT
2952 #undef FORMAT2
2953 }
2954 
2955 static char *dundi_show_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2956 {
2957 #define FORMAT2 "%-12.12s %-16.16s %-10.10s %-18s %-7s %s\n"
2958 #define FORMAT "%-12.12s %-16.16s %6d sec %-18s %-7d %s/%s (%s)\n"
2959  struct ast_db_entry *db_tree, *db_entry;
2960  int cnt = 0;
2961  time_t ts, now;
2962  dundi_eid src_eid;
2963  char src_eid_str[20];
2964  int expiry, tech, weight;
2965  struct ast_flags flags;
2966  char fs[256];
2967  int length;
2968  char *ptr, *term, *src, *number, *context, *dst;
2969 
2970  switch (cmd) {
2971  case CLI_INIT:
2972  e->command = "dundi show cache";
2973  e->usage =
2974  "Usage: dundi show cache\n"
2975  " Lists all DUNDi cache entries.\n";
2976  return NULL;
2977  case CLI_GENERATE:
2978  return NULL;
2979  }
2980 
2981  if (a->argc != 3) {
2982  return CLI_SHOWUSAGE;
2983  }
2984 
2985  time(&now);
2986  db_tree = ast_db_gettree("dundi/cache", NULL);
2987  ast_cli(a->fd, FORMAT2, "Number", "Context", "Expiration", "From", "Weight", "Destination (Flags)");
2988  for (db_entry = db_tree; db_entry; db_entry = db_entry->next) {
2989  char *rest;
2990 
2991  if ((strncmp(db_entry->key, "/dundi/cache/hint/", 18) == 0) || ast_get_time_t(db_entry->data, &ts, 0, &length)) {
2992  continue;
2993  }
2994 
2995  expiry = ts - now;
2996 
2997  if (expiry <= 0) {
2998  continue;
2999  }
3000 
3001  ptr = db_entry->key + sizeof("/dundi/cache");
3002  strtok_r(ptr, "/", &rest);
3003  number = strtok_r(NULL, "/", &rest);
3004  context = strtok_r(NULL, "/", &rest);
3005  ptr = strtok_r(NULL, "/", &rest);
3006 
3007  if (*ptr != 'e') {
3008  continue;
3009  }
3010 
3011  ptr = db_entry->data + length + 1;
3012 
3013  if ((sscanf(ptr, "%30u/%30d/%30d/%n", &(flags.flags), &weight, &tech, &length) != 3)) {
3014  continue;
3015  }
3016 
3017  ptr += length;
3018  dst = ptr;
3019  term = strchr(ptr, '|');
3020 
3021  if (!term) {
3022  continue;
3023  }
3024 
3025  /* Ok, at this point we know we aren't going to skp the entry, so we go ahead and increment the count. */
3026  cnt++;
3027 
3028  *term = '\0';
3029  src = strrchr(ptr, '/');
3030  dundi_eid_zero(&src_eid);
3031 
3032  if (src) {
3033  *src = '\0';
3034  src++;
3035  dundi_str_short_to_eid(&src_eid, src);
3036  ast_eid_to_str(src_eid_str, sizeof(src_eid_str), &src_eid);
3037  }
3038 
3039  ast_cli(a->fd, FORMAT, number, context, expiry, src_eid_str, weight, tech2str(tech), dst, dundi_flags2str(fs, sizeof(fs), flags.flags));
3040  }
3041 
3042  ast_cli(a->fd, "Number of entries: %d\n", cnt);
3043  ast_db_freetree(db_tree);
3044 
3045  return CLI_SUCCESS;
3046 #undef FORMAT
3047 #undef FORMAT2
3048 }
3049 
3050 static char *dundi_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3051 {
3052 #define FORMAT2 "%-12.12s %-16.16s %-10.10s %-18s\n"
3053 #define FORMAT "%-12.12s %-16.16s %6d sec %-18s\n"
3054  struct ast_db_entry *db_tree, *db_entry;
3055  int cnt = 0;
3056  time_t ts, now;
3057  dundi_eid src_eid;
3058  char src_eid_str[20];
3059  int expiry;
3060  int length;
3061  char *ptr, *src, *number, *context;
3062 
3063  switch (cmd) {
3064  case CLI_INIT:
3065  e->command = "dundi show hints";
3066  e->usage =
3067  "Usage: dundi show hints\n"
3068  " Lists all DUNDi 'DONTASK' hints in the cache.\n";
3069  return NULL;
3070  case CLI_GENERATE:
3071  return NULL;
3072  }
3073 
3074  if (a->argc != 3) {
3075  return CLI_SHOWUSAGE;
3076  }
3077 
3078  time(&now);
3079  db_tree = ast_db_gettree("dundi/cache/hint", NULL);
3080  ast_cli(a->fd, FORMAT2, "Prefix", "Context", "Expiration", "From");
3081 
3082  for (db_entry = db_tree; db_entry; db_entry = db_entry->next) {
3083  char *rest = NULL;
3084 
3085  if (ast_get_time_t(db_entry->data, &ts, 0, &length)) {
3086  continue;
3087  }
3088 
3089  expiry = ts - now;
3090 
3091  if (expiry <= 0) {
3092  continue;
3093  }
3094 
3095  ptr = db_entry->key + sizeof("/dundi/cache/hint");
3096  src = strtok_r(ptr, "/", &rest);
3097  number = strtok_r(NULL, "/", &rest);
3098  context = strtok_r(NULL, "/", &rest);
3099  ptr = strtok_r(NULL, "/", &rest);
3100 
3101  if (*ptr != 'e') {
3102  continue;
3103  }
3104 
3105  cnt++;
3106  dundi_str_short_to_eid(&src_eid, src);
3107  ast_eid_to_str(src_eid_str, sizeof(src_eid_str), &src_eid);
3108  ast_cli(a->fd, FORMAT, number, context, expiry, src_eid_str);
3109  }
3110 
3111  ast_cli(a->fd, "Number of entries: %d\n", cnt);
3112  ast_db_freetree(db_tree);
3113 
3114  return CLI_SUCCESS;
3115 #undef FORMAT
3116 #undef FORMAT2
3117 }
3118 
3119 static struct ast_cli_entry cli_dundi[] = {
3120  AST_CLI_DEFINE(dundi_set_debug, "Enable/Disable DUNDi debugging"),
3121  AST_CLI_DEFINE(dundi_store_history, "Enable/Disable DUNDi historic records"),
3122  AST_CLI_DEFINE(dundi_flush, "Flush DUNDi cache"),
3123  AST_CLI_DEFINE(dundi_show_peers, "Show defined DUNDi peers"),
3124  AST_CLI_DEFINE(dundi_show_trans, "Show active DUNDi transactions"),
3125  AST_CLI_DEFINE(dundi_show_entityid, "Display Global Entity ID"),
3126  AST_CLI_DEFINE(dundi_show_mappings, "Show DUNDi mappings"),
3127  AST_CLI_DEFINE(dundi_show_precache, "Show DUNDi precache"),
3128  AST_CLI_DEFINE(dundi_show_requests, "Show DUNDi requests"),
3129  AST_CLI_DEFINE(dundi_show_peer, "Show info on a specific DUNDi peer"),
3130  AST_CLI_DEFINE(dundi_show_cache, "Show DUNDi cache"),
3131  AST_CLI_DEFINE(dundi_show_hints, "Show DUNDi hints in the cache"),
3132  AST_CLI_DEFINE(dundi_do_precache, "Precache a number in DUNDi"),
3133  AST_CLI_DEFINE(dundi_do_lookup, "Lookup a number in DUNDi"),
3134  AST_CLI_DEFINE(dundi_do_query, "Query a DUNDi EID"),
3135 };
3136 
3138 {
3139  struct dundi_transaction *trans;
3140  int tid;
3141 
3142  /* Don't allow creation of transactions to non-registered peers */
3143  if (p && ast_sockaddr_isnull(&p->addr)) {
3144  return NULL;
3145  }
3146  tid = get_trans_id();
3147  if (tid < 1)
3148  return NULL;
3149  if (!(trans = ast_calloc(1, sizeof(*trans))))
3150  return NULL;
3151 
3152  if (global_storehistory) {
3153  trans->start = ast_tvnow();
3154  ast_set_flag(trans, FLAG_STOREHIST);
3155  }
3157  trans->autokillid = -1;
3158  if (p) {
3159  apply_peer(trans, p);
3160  if (!p->sentfullkey)
3162  }
3163  trans->strans = tid;
3164  AST_LIST_INSERT_HEAD(&alltrans, trans, all);
3165 
3166  return trans;
3167 }
3168 
3169 static int dundi_xmit(struct dundi_packet *pack)
3170 {
3171  int res;
3172  if (dundidebug)
3173  dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
3174 
3175  if (netsocket2 < 0) {
3176  res = ast_sendto(netsocket, pack->data, pack->datalen, 0, &pack->parent->addr);
3177  } else {
3178  if (ast_sockaddr_is_ipv4(&pack->parent->addr)) {
3179  res = ast_sendto(netsocket, pack->data, pack->datalen, 0, &pack->parent->addr);
3180  } else {
3181  res = ast_sendto(netsocket2, pack->data, pack->datalen, 0, &pack->parent->addr);
3182  }
3183  }
3184 
3185  if (res < 0) {
3186  ast_log(LOG_WARNING, "Failed to transmit to '%s': %s\n",
3187  ast_sockaddr_stringify(&pack->parent->addr), strerror(errno));
3188  }
3189  if (res > 0)
3190  res = 0;
3191  return res;
3192 }
3193 
3194 static void destroy_packet(struct dundi_packet *pack, int needfree)
3195 {
3196  if (pack->parent)
3197  AST_LIST_REMOVE(&pack->parent->packets, pack, list);
3198  AST_SCHED_DEL(sched, pack->retransid);
3199  if (needfree)
3200  ast_free(pack);
3201 }
3202 
3203 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
3204 {
3205  struct dundi_peer *peer;
3206  int ms;
3207  int x;
3208  int cnt;
3209  char eid_str[20];
3211  AST_LIST_TRAVERSE(&peers, peer, list) {
3212  if (peer->regtrans == trans)
3213  peer->regtrans = NULL;
3214  if (peer->qualtrans == trans) {
3215  if (fromtimeout) {
3216  if (peer->lastms > -1)
3217  ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
3218  peer->lastms = -1;
3219  } else {
3220  ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
3221  if (ms < 1)
3222  ms = 1;
3223  if (ms < peer->maxms) {
3224  if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
3225  ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
3226  } else if (peer->lastms < peer->maxms) {
3227  ast_log(LOG_NOTICE, "Peer '%s' has become TOO LAGGED (%d ms)\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), ms);
3228  }
3229  peer->lastms = ms;
3230  }
3231  peer->qualtrans = NULL;
3232  }
3233  if (ast_test_flag(trans, FLAG_STOREHIST)) {
3234  if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
3235  if (!ast_eid_cmp(&trans->them_eid, &peer->eid)) {
3236  peer->avgms = 0;
3237  cnt = 0;
3239  for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
3240  peer->lookuptimes[x] = peer->lookuptimes[x-1];
3241  peer->lookups[x] = peer->lookups[x-1];
3242  if (peer->lookups[x]) {
3243  peer->avgms += peer->lookuptimes[x];
3244  cnt++;
3245  }
3246  }
3247  peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
3248  peer->lookups[0] = ast_malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
3249  if (peer->lookups[0]) {
3250  sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
3251  peer->avgms += peer->lookuptimes[0];
3252  cnt++;
3253  }
3254  if (cnt)
3255  peer->avgms /= cnt;
3256  }
3257  }
3258  }
3259  }
3260  }
3261  if (trans->parent) {
3262  /* Unlink from parent if appropriate */
3263  AST_LIST_REMOVE(&trans->parent->trans, trans, parentlist);
3264  if (AST_LIST_EMPTY(&trans->parent->trans)) {
3265  /* Wake up sleeper */
3266  if (trans->parent->pfds[1] > -1) {
3267  if (write(trans->parent->pfds[1], "killa!", 6) < 0) {
3268  ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
3269  }
3270  }
3271  }
3272  }
3273  /* Unlink from all trans */
3274  AST_LIST_REMOVE(&alltrans, trans, all);
3275  destroy_packets(&trans->packets);
3276  destroy_packets(&trans->lasttrans);
3277  AST_SCHED_DEL(sched, trans->autokillid);
3278  if (trans->thread) {
3279  /* If used by a thread, mark as dead and be done */
3280  ast_set_flag(trans, FLAG_DEAD);
3281  } else
3282  ast_free(trans);
3283 }
3284 
3285 static int dundi_rexmit(const void *data)
3286 {
3287  struct dundi_packet *pack = (struct dundi_packet *)data;
3288  int res;
3289  AST_LIST_LOCK(&peers);
3290  if (pack->retrans < 1) {
3291  pack->retransid = -1;
3292  if (!ast_test_flag(pack->parent, FLAG_ISQUAL)) {
3293  ast_log(LOG_NOTICE, "Max retries exceeded to host '%s' msg %d on call %d\n",
3294  ast_sockaddr_stringify(&pack->parent->addr), pack->h->oseqno, ntohs(pack->h->strans));
3295  }
3296  destroy_trans(pack->parent, 1);
3297  res = 0;
3298  } else {
3299  /* Decrement retransmission, try again */
3300  pack->retrans--;
3301  dundi_xmit(pack);
3302  res = 1;
3303  }
3305  return res;
3306 }
3307 
3308 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
3309 {
3310  struct dundi_packet *pack;
3311  int res;
3312  int len;
3313  char eid_str[20];
3314  len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
3315  /* Reserve enough space for encryption */
3316  if (ast_test_flag(trans, FLAG_ENCRYPT))
3317  len += 384;
3318  pack = ast_calloc(1, len);
3319  if (pack) {
3320  pack->h = (struct dundi_hdr *)(pack->data);
3321  pack->retransid = -1;
3322  if (cmdresp != DUNDI_COMMAND_ACK) {
3323  pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
3324  pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
3325  AST_LIST_INSERT_HEAD(&trans->packets, pack, list);
3326  }
3327  pack->parent = trans;
3328  pack->h->strans = htons(trans->strans);
3329  pack->h->dtrans = htons(trans->dtrans);
3330  pack->h->iseqno = trans->iseqno;
3331  pack->h->oseqno = trans->oseqno;
3332  pack->h->cmdresp = cmdresp;
3333  pack->datalen = sizeof(struct dundi_hdr);
3334  if (ied) {
3335  memcpy(pack->h->ies, ied->buf, ied->pos);
3336  pack->datalen += ied->pos;
3337  }
3338  if (final) {
3339  pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
3340  ast_set_flag(trans, FLAG_FINAL);
3341  }
3342  pack->h->cmdflags = flags;
3343  if (cmdresp != DUNDI_COMMAND_ACK) {
3344  trans->oseqno++;
3345  trans->oseqno = trans->oseqno % 256;
3346  }
3347  trans->aseqno = trans->iseqno;
3348  /* If we have their public key, encrypt */
3349  if (ast_test_flag(trans, FLAG_ENCRYPT)) {
3350  switch(cmdresp) {
3351  case DUNDI_COMMAND_REGREQ:
3359  if (dundidebug)
3360  dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
3361  res = dundi_encrypt(trans, pack);
3362  break;
3363  default:
3364  res = 0;
3365  }
3366  } else
3367  res = 0;
3368  if (!res)
3369  res = dundi_xmit(pack);
3370  if (res)
3371  ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3372 
3373  if (cmdresp == DUNDI_COMMAND_ACK)
3374  ast_free(pack);
3375  return res;
3376  }
3377  return -1;
3378 }
3379 
3380 static int do_autokill(const void *data)
3381 {
3382  struct dundi_transaction *trans = (struct dundi_transaction *)data;
3383  char eid_str[20];
3384  ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n",
3385  ast_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3386  trans->autokillid = -1;
3387  destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
3388  return 0;
3389 }
3390 
3391 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
3392 {
3393  struct dundi_peer *p;
3394  if (!ast_eid_cmp(eid, us)) {
3396  return;
3397  }
3398  AST_LIST_LOCK(&peers);
3399  AST_LIST_TRAVERSE(&peers, p, list) {
3400  if (!ast_eid_cmp(&p->eid, eid)) {
3401  if (has_permission(&p->include, context))
3403  else
3404  dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3405  break;
3406  }
3407  }
3408  if (!p)
3409  dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3411 }
3412 
3413 static int dundi_discover(struct dundi_transaction *trans)
3414 {
3415  struct dundi_ie_data ied;
3416  int x;
3417  if (!trans->parent) {
3418  ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3419  return -1;
3420  }
3421  memset(&ied, 0, sizeof(ied));
3423  if (!dundi_eid_zero(&trans->us_eid))
3425  for (x=0;x<trans->eidcount;x++)
3426  dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
3429  dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3430  if (trans->parent->cbypass)
3432  if (trans->autokilltimeout)
3433  trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3434  return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
3435 }
3436 
3437 static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
3438 {
3439  struct dundi_ie_data ied;
3440  int x, res;
3441  int max = 999999;
3442  int expiration = dundi_cache_time;
3443  int ouranswers=0;
3444  dundi_eid *avoid[1] = { NULL, };
3445  int direct[1] = { 0, };
3446  struct dundi_result dr[MAX_RESULTS];
3447  struct dundi_hint_metadata hmd;
3448  if (!trans->parent) {
3449  ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3450  return -1;
3451  }
3452  memset(&hmd, 0, sizeof(hmd));
3453  memset(&dr, 0, sizeof(dr));
3454  /* Look up the answers we're going to include */
3455  for (x=0;x<mapcount;x++)
3456  ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
3457  if (ouranswers < 0)
3458  ouranswers = 0;
3459  for (x=0;x<ouranswers;x++) {
3460  if (dr[x].weight < max)
3461  max = dr[x].weight;
3462  }
3463  if (max) {
3464  /* If we do not have a canonical result, keep looking */
3465  res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, trans->parent->dcontext, trans->parent->number, trans->ttl, 1, &hmd, &expiration, 0, 1, &trans->them_eid, avoid, direct);
3466  if (res > 0) {
3467  /* Append answer in result */
3468  ouranswers += res;
3469  }
3470  }
3471 
3472  if (ouranswers > 0) {
3473  *foundanswers += ouranswers;
3474  memset(&ied, 0, sizeof(ied));
3476  if (!dundi_eid_zero(&trans->us_eid))
3477  dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3478  for (x=0;x<trans->eidcount;x++)
3479  dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3482  dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3483  for (x=0;x<ouranswers;x++) {
3484  /* Add answers */
3485  if (dr[x].expiration && (expiration > dr[x].expiration))
3486  expiration = dr[x].expiration;
3487  dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
3488  }
3489  dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
3490  dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
3491  if (trans->autokilltimeout)
3492  trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3493  if (expiration < *minexp)
3494  *minexp = expiration;
3495  return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
3496  } else {
3497  /* Oops, nothing to send... */
3498  destroy_trans(trans, 0);
3499  return 0;
3500  }
3501 }
3502 
3503 static int dundi_query(struct dundi_transaction *trans)
3504 {
3505  struct dundi_ie_data ied;
3506  int x;
3507  if (!trans->parent) {
3508  ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
3509  return -1;
3510  }
3511  memset(&ied, 0, sizeof(ied));
3513  if (!dundi_eid_zero(&trans->us_eid))
3514  dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3515  for (x=0;x<trans->eidcount;x++)
3516  dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3519  dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3520  if (trans->autokilltimeout)
3521  trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3522  return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
3523 }
3524 
3526 {
3527  struct dundi_transaction *trans;
3528  AST_LIST_LOCK(&peers);
3529  AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3530  dundi_discover(trans);
3531  }
3533  return 0;
3534 }
3535 
3536 static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
3537 {
3538  struct dundi_transaction *trans;
3539 
3540  /* Mark all as "in thread" so they don't disappear */
3541  AST_LIST_LOCK(&peers);
3542  AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3543  if (trans->thread)
3544  ast_log(LOG_WARNING, "This shouldn't happen, really...\n");
3545  trans->thread = 1;
3546  }
3548 
3549  AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3550  if (!ast_test_flag(trans, FLAG_DEAD))
3551  precache_trans(trans, maps, mapcount, expiration, foundanswers);
3552  }
3553 
3554  /* Cleanup any that got destroyed in the mean time */
3555  AST_LIST_LOCK(&peers);
3557  trans->thread = 0;
3558  if (ast_test_flag(trans, FLAG_DEAD)) {
3559  ast_debug(1, "Our transaction went away!\n");
3560  /* This is going to remove the transaction from the dundi_request's list, as well
3561  * as the global transactions list */
3562  destroy_trans(trans, 0);
3563  }
3564  }
3567 
3568  return 0;
3569 }
3570 
3572 {
3573  struct dundi_transaction *trans;
3574 
3575  AST_LIST_LOCK(&peers);
3576  AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3577  dundi_query(trans);
3578  }
3580 
3581  return 0;
3582 }
3583 
3585 {
3586  /* Minimize the message propagation through DUNDi by
3587  alerting the network to hops which should be not be considered */
3588  struct dundi_transaction *trans;
3589  struct dundi_peer *peer;
3590  dundi_eid tmp;
3591  int x;
3592  int needpush;
3593 
3594  AST_LIST_LOCK(&peers);
3595  AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3596  /* Pop off the true root */
3597  if (trans->eidcount) {
3598  tmp = trans->eids[--trans->eidcount];
3599  needpush = 1;
3600  } else {
3601  tmp = trans->us_eid;
3602  needpush = 0;
3603  }
3604 
3605  AST_LIST_TRAVERSE(&peers, peer, list) {
3606  if (ast_eid_cmp(&peer->eid, &empty_eid) && /* peer's eid is not empty (in case of dynamic peers) */
3607  (peer->lastms > -1) && /* peer is reachable */
3608  has_permission(&peer->include, dr->dcontext) && /* peer has destination context */
3609  ast_eid_cmp(&peer->eid, &trans->them_eid) && /* peer is not transaction endpoint */
3610  (peer->order <= order)) {
3611  /* For each other transaction, make sure we don't
3612  ask this EID about the others if they're not
3613  already in the list */
3614  if (!ast_eid_cmp(&tmp, &peer->eid))
3615  x = -1;
3616  else {
3617  for (x=0;x<trans->eidcount;x++) {
3618  if (!ast_eid_cmp(&trans->eids[x], &peer->eid))
3619  break;
3620  }
3621  }
3622  if (x == trans->eidcount) {
3623  /* Nope not in the list, if needed, add us at the end since we're the source */
3624  if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
3625  trans->eids[trans->eidcount++] = peer->eid;
3626  /* Need to insert the real root (or us) at the bottom now as
3627  a requirement now. */
3628  needpush = 1;
3629  }
3630  }
3631  }
3632  }
3633  /* If necessary, push the true root back on the end */
3634  if (needpush)
3635  trans->eids[trans->eidcount++] = tmp;
3636  }
3638 
3639  return 0;
3640 }
3641 
3642 static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
3643 {
3644  struct dundi_transaction *trans;
3645  int x;
3646  char eid_str[20];
3647  char eid_str2[20];
3648 
3649  /* Ignore if not registered */
3650  if (ast_sockaddr_isnull(&p->addr)) {
3651  return 0;
3652  }
3653  if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
3654  return 0;
3655 
3656  if (ast_strlen_zero(dr->number))
3657  ast_debug(1, "Will query peer '%s' for '%s' (context '%s')\n", ast_eid_to_str(eid_str, sizeof(eid_str), &p->eid), ast_eid_to_str(eid_str2, sizeof(eid_str2), &dr->query_eid), dr->dcontext);
3658  else
3659  ast_debug(1, "Will query peer '%s' for '%s@%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dr->number, dr->dcontext);
3660 
3661  trans = create_transaction(p);
3662  if (!trans)
3663  return -1;
3664  trans->parent = dr;
3665  trans->ttl = ttl;
3666  for (x = 0; avoid[x] && (x < DUNDI_MAX_STACK); x++)
3667  trans->eids[x] = *avoid[x];
3668  trans->eidcount = x;
3669  AST_LIST_INSERT_HEAD(&dr->trans, trans, parentlist);
3670 
3671  return 0;
3672 }
3673 
3674 static void cancel_request(struct dundi_request *dr)
3675 {
3676  struct dundi_transaction *trans;
3677 
3678  AST_LIST_LOCK(&peers);
3679  while ((trans = AST_LIST_REMOVE_HEAD(&dr->trans, parentlist))) {
3680  /* Orphan transaction from request */
3681  trans->parent = NULL;
3682  /* Send final cancel */
3683  dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3684  }
3686 }
3687 
3688 static void abort_request(struct dundi_request *dr)
3689 {
3690  struct dundi_transaction *trans;
3691 
3692  AST_LIST_LOCK(&peers);
3693  while ((trans = AST_LIST_FIRST(&dr->trans))) {
3694  /* This will remove the transaction from the list */
3695  destroy_trans(trans, 0);
3696  }
3698 }
3699 
3700 static void build_transactions(struct dundi_request *dr, int ttl, int order, int *foundcache, int *skipped, int blockempty, int nocache, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int directs[])
3701 {
3702  struct dundi_peer *p;
3703  int x;
3704  int res;
3705  int pass;
3706  int allowconnect;
3707  char eid_str[20];
3708  AST_LIST_LOCK(&peers);
3709  AST_LIST_TRAVERSE(&peers, p, list) {
3710  if (modeselect == 1) {
3711  /* Send the precache to push upstreams only! */
3712  pass = has_permission(&p->permit, dr->dcontext) && (p->pcmodel & DUNDI_MODEL_OUTBOUND);
3713  allowconnect = 1;
3714  } else {
3715  /* Normal lookup / EID query */
3716  pass = has_permission(&p->include, dr->dcontext);
3717  allowconnect = p->model & DUNDI_MODEL_OUTBOUND;
3718  }
3719  if (skip) {
3720  if (!ast_eid_cmp(skip, &p->eid))
3721  pass = 0;
3722  }
3723  if (pass) {
3724  if (p->order <= order) {
3725  /* Check order first, then check cache, regardless of
3726  omissions, this gets us more likely to not have an
3727  affected answer. */
3728  if((nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration)))) {
3729  res = 0;
3730  /* Make sure we haven't already seen it and that it won't
3731  affect our answer */
3732  for (x=0;avoid[x];x++) {
3733  if (!ast_eid_cmp(avoid[x], &p->eid) || !ast_eid_cmp(avoid[x], &p->us_eid)) {
3734  /* If not a direct connection, it affects our answer */
3735  if (directs && !directs[x])
3737  break;
3738  }
3739  }
3740  /* Make sure we can ask */
3741  if (allowconnect) {
3742  if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) {
3743  /* Check for a matching or 0 cache entry */
3744  append_transaction(dr, p, ttl, avoid);
3745  } else {
3746  ast_debug(1, "Avoiding '%s' in transaction\n", ast_eid_to_str(eid_str, sizeof(eid_str), avoid[x]));
3747  }
3748  }
3749  }
3750  *foundcache |= res;
3751  } else if (!*skipped || (p->order < *skipped))
3752  *skipped = p->order;
3753  }
3754  }
3756 }
3757 
3758 static int register_request(struct dundi_request *dr, struct dundi_request **pending)
3759 {
3760  struct dundi_request *cur;
3761  int res=0;
3762  char eid_str[20];
3763  AST_LIST_LOCK(&peers);
3764  AST_LIST_TRAVERSE(&requests, cur, list) {
3765  ast_debug(1, "Checking '%s@%s' vs '%s@%s'\n", cur->dcontext, cur->number,
3766  dr->dcontext, dr->number);
3767  if (!strcasecmp(cur->dcontext, dr->dcontext) &&
3768  !strcasecmp(cur->number, dr->number) &&
3769  (!ast_eid_cmp(&cur->root_eid, &dr->root_eid) || (cur->crc32 == dr->crc32))) {
3770  ast_debug(1, "Found existing query for '%s@%s' for '%s' crc '%08x'\n",
3771  cur->dcontext, cur->number, ast_eid_to_str(eid_str, sizeof(eid_str), &cur->root_eid), cur->crc32);
3772  *pending = cur;
3773  res = 1;
3774  break;
3775  }
3776  }
3777  if (!res) {
3778  ast_debug(1, "Registering request for '%s@%s' on behalf of '%s' crc '%08x'\n",
3779  dr->number, dr->dcontext, ast_eid_to_str(eid_str, sizeof(eid_str), &dr->root_eid), dr->crc32);
3780  /* Go ahead and link us in since nobody else is searching for this */
3782  *pending = NULL;
3783  }
3785  return res;
3786 }
3787 
3789 {
3790  AST_LIST_LOCK(&peers);
3791  AST_LIST_REMOVE(&requests, dr, list);
3793 }
3794 
3795 static int check_request(struct dundi_request *dr)
3796 {
3797  struct dundi_request *cur;
3798 
3799  AST_LIST_LOCK(&peers);
3800  AST_LIST_TRAVERSE(&requests, cur, list) {
3801  if (cur == dr)
3802  break;
3803  }
3805 
3806  return cur ? 1 : 0;
3807 }
3808 
3809 static unsigned long avoid_crc32(dundi_eid *avoid[])
3810 {
3811  /* Idea is that we're calculating a checksum which is independent of
3812  the order that the EID's are listed in */
3813  uint32_t acrc32 = 0;
3814  int x;
3815  for (x=0;avoid[x];x++) {
3816  /* Order doesn't matter */
3817  if (avoid[x+1]) {
3818  acrc32 ^= crc32(0L, (unsigned char *)avoid[x], sizeof(dundi_eid));
3819  }
3820  }
3821  return acrc32;
3822 }
3823 
3824 static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int ttl, int blockempty, struct dundi_hint_metadata *hmd, int *expiration, int cbypass, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int direct[])
3825 {
3826  int res;
3827  struct dundi_request dr, *pending;
3828  dundi_eid *rooteid=NULL;
3829  int x;
3830  int ttlms;
3831  int ms;
3832  int foundcache;
3833  int skipped=0;
3834  int order=0;
3835  char eid_str[20];
3836  struct timeval start;
3837 
3838  /* Don't do anthing for a hungup channel */
3839  if (chan && ast_check_hangup(chan))
3840  return 0;
3841 
3842  ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
3843 
3844  for (x=0;avoid[x];x++)
3845  rooteid = avoid[x];
3846  /* Now perform real check */
3847  memset(&dr, 0, sizeof(dr));
3848  if (pipe(dr.pfds)) {
3849  ast_log(LOG_WARNING, "pipe failed: %s\n" , strerror(errno));
3850  return -1;
3851  }
3852  dr.dr = result;
3853  dr.hmd = hmd;
3854  dr.maxcount = maxret;
3855  dr.expiration = *expiration;
3856  dr.cbypass = cbypass;
3857  dr.crc32 = avoid_crc32(avoid);
3858  ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
3859  ast_copy_string(dr.number, number, sizeof(dr.number));
3860  if (rooteid)
3861  dr.root_eid = *rooteid;
3862  res = register_request(&dr, &pending);
3863  if (res) {
3864  /* Already a request */
3865  if (rooteid && !ast_eid_cmp(&dr.root_eid, &pending->root_eid)) {
3866  /* This is on behalf of someone else. Go ahead and close this out since
3867  they'll get their answer anyway. */
3868  ast_debug(1, "Oooh, duplicate request for '%s@%s' for '%s'\n",
3869  dr.number,dr.dcontext,ast_eid_to_str(eid_str, sizeof(eid_str), &dr.root_eid));
3870  close(dr.pfds[0]);
3871  close(dr.pfds[1]);
3872  return -2;
3873  } else {
3874  /* Wait for the cache to populate */
3875  ast_debug(1, "Waiting for similar request for '%s@%s' for '%s'\n",
3876  dr.number,dr.dcontext,ast_eid_to_str(eid_str, sizeof(eid_str), &pending->root_eid));
3877  start = ast_tvnow();
3878  while(check_request(pending) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !ast_check_hangup(chan))) {
3879  /* XXX Would be nice to have a way to poll/select here XXX */
3880  /* XXX this is a busy wait loop!!! */
3881  usleep(1);
3882  }
3883  /* Continue on as normal, our cache should kick in */
3884  }
3885  }
3886  /* Create transactions */
3887  do {
3888  order = skipped;
3889  skipped = 0;
3890  foundcache = 0;
3891  build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, modeselect, skip, avoid, direct);
3892  } while (skipped && !foundcache && AST_LIST_EMPTY(&dr.trans));
3893  /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
3894  do this earlier because we didn't know if we were going to have transactions
3895  or not. */
3896  if (!ttl) {
3898  abort_request(&dr);
3899  unregister_request(&dr);
3900  close(dr.pfds[0]);
3901  close(dr.pfds[1]);
3902  return 0;
3903  }
3904 
3905  /* Optimize transactions */
3906  optimize_transactions(&dr, order);
3907  /* Actually perform transactions */
3908  discover_transactions(&dr);
3909  /* Wait for transaction to come back */
3910  start = ast_tvnow();
3911  while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !ast_check_hangup(chan))) {
3912  ms = 100;
3913  ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
3914  }
3915  if (chan && ast_check_hangup(chan))
3916  ast_debug(1, "Hrm, '%s' hungup before their query for %s@%s finished\n", ast_channel_name(chan), dr.number, dr.dcontext);
3917  cancel_request(&dr);
3918  unregister_request(&dr);
3919  res = dr.respcount;
3920  *expiration = dr.expiration;
3921  close(dr.pfds[0]);
3922  close(dr.pfds[1]);
3923  return res;
3924 }
3925 
3926 int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass)
3927 {
3928  struct dundi_hint_metadata hmd;
3929  dundi_eid *avoid[1] = { NULL, };
3930  int direct[1] = { 0, };
3931  int expiration = dundi_cache_time;
3932  memset(&hmd, 0, sizeof(hmd));
3934  return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, 0, NULL, avoid, direct);
3935 }
3936 
3937 static void reschedule_precache(const char *number, const char *context, int expiration)
3938 {
3939  struct dundi_precache_queue *qe, *prev;
3940 
3941  AST_LIST_LOCK(&pcq);
3943  if (!strcmp(number, qe->number) && !strcasecmp(context, qe->context)) {
3945  break;
3946  }
3947  }
3949  if (!qe) {
3950  int len = sizeof(*qe);
3951  int num_len = strlen(number) + 1;
3952  int context_len = strlen(context) + 1;
3953  if (!(qe = ast_calloc(1, len + num_len + context_len))) {
3954  AST_LIST_UNLOCK(&pcq);
3955  return;
3956  }
3957  strcpy(qe->number, number);
3958  qe->context = qe->number + num_len + 1;
3959  ast_copy_string(qe->context, context, context_len);
3960  }
3961  time(&qe->expiration);
3962  qe->expiration += expiration;
3963  if ((prev = AST_LIST_FIRST(&pcq))) {
3964  while (AST_LIST_NEXT(prev, list) && ((AST_LIST_NEXT(prev, list))->expiration <= qe->expiration))
3965  prev = AST_LIST_NEXT(prev, list);
3966  AST_LIST_INSERT_AFTER(&pcq, prev, qe, list);
3967  } else
3968  AST_LIST_INSERT_HEAD(&pcq, qe, list);
3969  AST_LIST_UNLOCK(&pcq);
3970 }
3971 
3972 static void dundi_precache_full(void)
3973 {
3974  struct dundi_mapping *cur;
3975  struct ast_context *con;
3976  struct ast_exten *e;
3977 
3978  AST_LIST_TRAVERSE(&mappings, cur, list) {
3979  ast_log(LOG_NOTICE, "Should precache context '%s'\n", cur->dcontext);
3981  con = NULL;
3982  while ((con = ast_walk_contexts(con))) {
3983  if (strcasecmp(cur->lcontext, ast_get_context_name(con)))
3984  continue;
3985  /* Found the match, now queue them all up */
3986  ast_rdlock_context(con);
3987  e = NULL;
3988  while ((e = ast_walk_context_extensions(con, e)))
3990  ast_unlock_context(con);
3991  }
3993  }
3994 }
3995 
3996 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[])
3997 {
3998  struct dundi_request dr;
3999  struct dundi_hint_metadata hmd;
4000  struct dundi_result dr2[MAX_RESULTS];
4001  struct timeval start;
4002  struct dundi_mapping *maps = NULL, *cur;
4003  int nummaps = 0;
4004  int foundanswers;
4005  int foundcache, skipped, ttlms, ms;
4006  if (!context)
4007  context = "e164";
4008  ast_debug(1, "Precache internal (%s@%s)!\n", number, context);
4009 
4010  AST_LIST_LOCK(&peers);
4011  AST_LIST_TRAVERSE(&mappings, cur, list) {
4012  if (!strcasecmp(cur->dcontext, context))
4013  nummaps++;
4014  }
4015  if (nummaps) {
4016  maps = ast_alloca(nummaps * sizeof(*maps));
4017  nummaps = 0;
4018  AST_LIST_TRAVERSE(&mappings, cur, list) {
4019  if (!strcasecmp(cur->dcontext, context))
4020  maps[nummaps++] = *cur;
4021  }
4022  }
4024  if (!nummaps) {
4025  return -1;
4026  }
4027  ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
4028  memset(&dr2, 0, sizeof(dr2));
4029  memset(&dr, 0, sizeof(dr));
4030  memset(&hmd, 0, sizeof(hmd));
4031  dr.dr = dr2;
4032  ast_copy_string(dr.number, number, sizeof(dr.number));
4033  ast_copy_string(dr.dcontext, context ? context : "e164", sizeof(dr.dcontext));
4034  dr.maxcount = MAX_RESULTS;
4036  dr.hmd = &hmd;
4037  dr.pfds[0] = dr.pfds[1] = -1;
4038  if (pipe(dr.pfds) < 0) {
4039  ast_log(LOG_WARNING, "pipe() failed: %s\n", strerror(errno));
4040  return -1;
4041  }
4042  build_transactions(&dr, ttl, 0, &foundcache, &skipped, 0, 1, 1, NULL, avoids, NULL);
4043  optimize_transactions(&dr, 0);
4044  foundanswers = 0;
4045  precache_transactions(&dr, maps, nummaps, &dr.expiration, &foundanswers);
4046  if (foundanswers) {
4047  if (dr.expiration > 0)
4049  else
4050  ast_log(LOG_NOTICE, "Weird, expiration = %d, but need to precache for %s@%s?!\n", dr.expiration, dr.number, dr.dcontext);
4051  }
4052  start = ast_tvnow();
4053  while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms)) {
4054  if (dr.pfds[0] > -1) {
4055  ms = 100;
4056  ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
4057  } else
4058  usleep(1);
4059  }
4060  cancel_request(&dr);
4061  if (dr.pfds[0] > -1) {
4062  close(dr.pfds[0]);
4063  close(dr.pfds[1]);
4064  }
4065  return 0;
4066 }
4067 
4068 int dundi_precache(const char *context, const char *number)
4069 {
4070  dundi_eid *avoid[1] = { NULL, };
4071  return dundi_precache_internal(context, number, dundi_ttl, avoid);
4072 }
4073 
4074 static int dundi_query_eid_internal(struct dundi_entity_info *dei, const char *dcontext, dundi_eid *eid, struct dundi_hint_metadata *hmd, int ttl, int blockempty, dundi_eid *avoid[])
4075 {
4076  int res;
4077  struct dundi_request dr;
4078  dundi_eid *rooteid=NULL;
4079  int x;
4080  int ttlms;
4081  int skipped=0;
4082  int foundcache=0;
4083  struct timeval start;
4084 
4085  ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
4086 
4087  for (x=0;avoid[x];x++)
4088  rooteid = avoid[x];
4089  /* Now perform real check */
4090  memset(&dr, 0, sizeof(dr));
4091  dr.hmd = hmd;
4092  dr.dei = dei;
4093  dr.pfds[0] = dr.pfds[1] = -1;
4094  ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
4095  memcpy(&dr.query_eid, eid, sizeof(dr.query_eid));
4096  if (rooteid)
4097  dr.root_eid = *rooteid;
4098  /* Create transactions */
4099  build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, 0, 0, NULL, avoid, NULL);
4100 
4101  /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
4102  do this earlier because we didn't know if we were going to have transactions
4103  or not. */
4104  if (!ttl) {
4106  return 0;
4107  }
4108 
4109  /* Optimize transactions */
4110  optimize_transactions(&dr, 9999);
4111  /* Actually perform transactions */
4112  query_transactions(&dr);
4113  /* Wait for transaction to come back */
4114  start = ast_tvnow();
4115  while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms))
4116  usleep(1);
4117  res = dr.respcount;
4118  return res;
4119 }
4120 
4121 int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid)
4122 {
4123  dundi_eid *avoid[1] = { NULL, };
4124  struct dundi_hint_metadata hmd;
4125  memset(&hmd, 0, sizeof(hmd));
4126  return dundi_query_eid_internal(dei, dcontext, &eid, &hmd, dundi_ttl, 0, avoid);
4127 }
4128 
4129 enum {
4130  OPT_BYPASS_CACHE = (1 << 0),
4131 };
4132 
4136 
4137 static int dundifunc_read(struct ast_channel *chan, const char *cmd, char *num, char *buf, size_t len)
4138 {
4139  int results;
4140  int x;
4141  struct dundi_result dr[MAX_RESULTS];
4143  AST_APP_ARG(number);
4144  AST_APP_ARG(context);
4146  );
4147  char *parse;
4148  struct ast_flags opts = { 0, };
4149 
4150  buf[0] = '\0';
4151 
4152  if (ast_strlen_zero(num)) {
4153  ast_log(LOG_WARNING, "DUNDILOOKUP requires an argument (number)\n");
4154  return -1;
4155  }
4156 
4157  parse = ast_strdupa(num);
4158 
4159  AST_STANDARD_APP_ARGS(args, parse);
4160 
4161  if (!ast_strlen_zero(args.options)) {
4162  ast_app_parse_options(dundi_query_opts, &opts, NULL, args.options);
4163  }
4164  if (ast_strlen_zero(args.context)) {
4165  args.context = "e164";
4166  }
4167 
4168  results = dundi_lookup(dr, MAX_RESULTS, NULL, args.context, args.number, ast_test_flag(&opts, OPT_BYPASS_CACHE));
4169  if (results > 0) {
4170  sort_results(dr, results);
4171  for (x = 0; x < results; x++) {
4172  if (ast_test_flag(dr + x, DUNDI_FLAG_EXISTS)) {
4173  snprintf(buf, len, "%s/%s", dr[x].tech, dr[x].dest);
4174  break;
4175  }
4176  }
4177  }
4178 
4179  return 0;
4180 }
4181 
4182 /*! DUNDILOOKUP
4183  * \ingroup functions
4184 */
4185 
4187  .name = "DUNDILOOKUP",
4188  .read = dundifunc_read,
4189 };
4190 
4191 static unsigned int dundi_result_id;
4192 
4194  struct dundi_result results[MAX_RESULTS];
4196  unsigned int id;
4197 };
4198 
4199 static void drds_destroy(struct dundi_result_datastore *drds)
4200 {
4201  ast_free(drds);
4202 }
4203 
4204 static void drds_destroy_cb(void *data)
4205 {
4206  struct dundi_result_datastore *drds = data;
4207  drds_destroy(drds);
4208 }
4209 
4211  .type = "DUNDIQUERY",
4212  .destroy = drds_destroy_cb,
4213 };
4214 
4215 static int dundi_query_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4216 {
4218  AST_APP_ARG(number);
4219  AST_APP_ARG(context);
4221  );
4222  struct ast_flags opts = { 0, };
4223  char *parse;
4224  struct dundi_result_datastore *drds;
4225  struct ast_datastore *datastore;
4226 
4227  if (ast_strlen_zero(data)) {
4228  ast_log(LOG_WARNING, "DUNDIQUERY requires an argument (number)\n");
4229  return -1;
4230  }
4231 
4232  if (!chan) {
4233  ast_log(LOG_ERROR, "DUNDIQUERY can not be used without a channel!\n");
4234  return -1;
4235  }
4236 
4237  parse = ast_strdupa(data);
4238 
4239  AST_STANDARD_APP_ARGS(args, parse);
4240 
4241  if (!ast_strlen_zero(args.options))
4242  ast_app_parse_options(dundi_query_opts, &opts, NULL, args.options);
4243 
4244  if (ast_strlen_zero(args.context))
4245  args.context = "e164";
4246 
4247  if (!(drds = ast_calloc(1, sizeof(*drds)))) {
4248  return -1;
4249  }
4250 
4251  drds->id = ast_atomic_fetchadd_int((int *) &dundi_result_id, 1);
4252  snprintf(buf, len, "%u", drds->id);
4253 
4254  if (!(datastore = ast_datastore_alloc(&dundi_result_datastore_info, buf))) {
4255  drds_destroy(drds);
4256  return -1;
4257  }
4258 
4259  datastore->data = drds;
4260 
4261  drds->num_results = dundi_lookup(drds->results, ARRAY_LEN(drds->results), NULL, args.context,
4262  args.number, ast_test_flag(&opts, OPT_BYPASS_CACHE));
4263 
4264  if (drds->num_results > 0)
4265  sort_results(drds->results, drds->num_results);
4266 
4267  ast_channel_lock(chan);
4268  ast_channel_datastore_add(chan, datastore);
4269  ast_channel_unlock(chan);
4270 
4271  return 0;
4272 }
4273 
4275  .name = "DUNDIQUERY",
4276  .read = dundi_query_read,
4277 };
4278 
4279 static int dundi_result_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4280 {
4282  AST_APP_ARG(id);
4283  AST_APP_ARG(resultnum);
4284  );
4285  char *parse;
4286  unsigned int num;
4287  struct dundi_result_datastore *drds;
4288  struct ast_datastore *datastore;
4289  int res = -1;
4290 
4291  if (ast_strlen_zero(data)) {
4292  ast_log(LOG_WARNING, "DUNDIRESULT requires an argument (id and resultnum)\n");
4293  goto finish;
4294  }
4295 
4296  if (!chan) {
4297  ast_log(LOG_ERROR, "DUNDRESULT can not be used without a channel!\n");
4298  goto finish;
4299  }
4300 
4301  parse = ast_strdupa(data);
4302 
4303  AST_STANDARD_APP_ARGS(args, parse);
4304 
4305  if (ast_strlen_zero(args.id)) {
4306  ast_log(LOG_ERROR, "A result ID must be provided to DUNDIRESULT\n");
4307  goto finish;
4308  }
4309 
4310  if (ast_strlen_zero(args.resultnum)) {
4311  ast_log(LOG_ERROR, "A result number must be given to DUNDIRESULT!\n");
4312  goto finish;
4313  }
4314 
4315  ast_channel_lock(chan);
4316  datastore = ast_channel_datastore_find(chan, &dundi_result_datastore_info, args.id);
4317  ast_channel_unlock(chan);
4318 
4319  if (!datastore) {
4320  ast_log(LOG_WARNING, "No DUNDi results found for query ID '%s'\n", args.id);
4321  goto finish;
4322  }
4323 
4324  drds = datastore->data;
4325 
4326  if (!strcasecmp(args.resultnum, "getnum")) {
4327  snprintf(buf, len, "%d", drds->num_results < 0 ? 0 : drds->num_results);
4328  res = 0;
4329  goto finish;
4330  }
4331 
4332  if (sscanf(args.resultnum, "%30u", &num) != 1) {
4333  ast_log(LOG_ERROR, "Invalid value '%s' for resultnum to DUNDIRESULT!\n",
4334  args.resultnum);
4335  goto finish;
4336  }
4337 
4338  if (num && drds->num_results > 0 && num <= drds->num_results) {
4339  snprintf(buf, len, "%s/%s", drds->results[num - 1].tech, drds->results[num - 1].dest);
4340  res = 0;
4341  } else
4342  ast_log(LOG_WARNING, "Result number %u is not valid for DUNDi query results for ID %s!\n", num, args.id);
4343 
4344 finish:
4345  return res;
4346 }
4347 
4349  .name = "DUNDIRESULT",
4350  .read = dundi_result_read,
4351 };
4352 
4353 static void mark_peers(void)
4354 {
4355  struct dundi_peer *peer;
4356  AST_LIST_LOCK(&peers);
4357  AST_LIST_TRAVERSE(&peers, peer, list) {
4358  peer->dead = 1;
4359  }
4361 }
4362 
4363 static void mark_mappings(void)
4364 {
4365  struct dundi_mapping *map;
4366 
4367  AST_LIST_LOCK(&peers);
4368  AST_LIST_TRAVERSE(&mappings, map, list) {
4369  map->dead = 1;
4370  }
4372 }
4373 
4374 static void destroy_permissions(struct permissionlist *permlist)
4375 {
4376  struct permission *perm;
4377 
4378  while ((perm = AST_LIST_REMOVE_HEAD(permlist, list)))
4379  ast_free(perm);
4380 }
4381 
4382 static void destroy_peer(struct dundi_peer *peer)
4383 {
4384  int idx;
4385 
4386  AST_SCHED_DEL(sched, peer->registerexpire);
4387  AST_SCHED_DEL(sched, peer->registerid);
4388  if (peer->regtrans) {
4389  destroy_trans(peer->regtrans, 0);
4390  }
4391  AST_SCHED_DEL(sched, peer->qualifyid);
4392  if (peer->qualtrans) {
4393  destroy_trans(peer->qualtrans, 0);
4394  }
4395  destroy_permissions(&peer->permit);
4396  destroy_permissions(&peer->include);
4397 
4398  /* Release lookup history */
4399  for (idx = 0; idx < ARRAY_LEN(peer->lookups); ++idx) {
4400  ast_free(peer->lookups[idx]);
4401  }
4402 
4403  ast_free(peer);
4404 }
4405 
4406 static void destroy_map(struct dundi_mapping *map)
4407 {
4408  ast_free(map->weightstr);
4409  ast_free(map);
4410 }
4411 
4412 static void prune_peers(void)
4413 {
4414  struct dundi_peer *peer;
4415 
4416  AST_LIST_LOCK(&peers);
4418  if (peer->dead) {
4420  destroy_peer(peer);
4421  }
4422  }
4425 }
4426 
4427 static void prune_mappings(void)
4428 {
4429  struct dundi_mapping *map;
4430 
4431  AST_LIST_LOCK(&peers);
4433  if (map->dead) {
4435  destroy_map(map);
4436  }
4437  }
4440 }
4441 
4442 static void append_permission(struct permissionlist *permlist, const char *s, int allow)
4443 {
4444  struct permission *perm;
4445 
4446  if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(s) + 1)))
4447  return;
4448 
4449  strcpy(perm->name, s);
4450  perm->allow = allow;
4451 
4452  AST_LIST_INSERT_TAIL(permlist, perm, list);
4453 }
4454 
4455 #define MAX_OPTS 128
4456 
4457 static void build_mapping(const char *name, const char *value)
4458 {
4459  char *t, *fields[MAX_OPTS];
4460  struct dundi_mapping *map;
4461  int x;
4462  int y;
4463 
4464  t = ast_strdupa(value);
4465 
4466  AST_LIST_TRAVERSE(&mappings, map, list) {
4467  /* Find a double match */
4468  if (!strcasecmp(map->dcontext, name) &&
4469  (!strncasecmp(map->lcontext, value, strlen(map->lcontext)) &&
4470  (!value[strlen(map->lcontext)] ||
4471  (value[strlen(map->lcontext)] == ','))))
4472  break;
4473  }
4474  if (!map) {
4475  if (!(map = ast_calloc(1, sizeof(*map))))
4476  return;
4478  map->dead = 1;
4479  }
4480  map->options = 0;
4481  memset(fields, 0, sizeof(fields));
4482  x = 0;
4483  while (t && x < MAX_OPTS) {
4484  fields[x++] = t;
4485  t = strchr(t, ',');
4486  if (t) {
4487  *t = '\0';
4488  t++;
4489  }
4490  } /* Russell was here, arrrr! */
4491  if ((x == 1) && ast_strlen_zero(fields[0])) {
4492  /* Placeholder mapping */
<