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 */
4493  ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
4494  map->dead = 0;
4495  } else if (x >= 4) {
4496  ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
4497  ast_copy_string(map->lcontext, fields[0], sizeof(map->lcontext));
4498  if ((sscanf(fields[1], "%30d", &map->_weight) == 1) && (map->_weight >= 0) && (map->_weight <= MAX_WEIGHT)) {
4499  ast_copy_string(map->dest, fields[3], sizeof(map->dest));
4500  if ((map->tech = str2tech(fields[2])))
4501  map->dead = 0;
4502  } else if (!strncmp(fields[1], "${", 2) && fields[1][strlen(fields[1]) - 1] == '}') {
4503  map->weightstr = ast_strdup(fields[1]);
4504  ast_copy_string(map->dest, fields[3], sizeof(map->dest));
4505  if ((map->tech = str2tech(fields[2])))
4506  map->dead = 0;
4507  } else {
4508  ast_log(LOG_WARNING, "Invalid weight '%s' specified, deleting entry '%s/%s'\n", fields[1], map->dcontext, map->lcontext);
4509  }
4510  for (y = 4;y < x; y++) {
4511  if (!strcasecmp(fields[y], "nounsolicited"))
4513  else if (!strcasecmp(fields[y], "nocomunsolicit"))
4515  else if (!strcasecmp(fields[y], "residential"))
4517  else if (!strcasecmp(fields[y], "commercial"))
4519  else if (!strcasecmp(fields[y], "mobile"))
4520  map->options |= DUNDI_FLAG_MOBILE;
4521  else if (!strcasecmp(fields[y], "nopartial"))
4523  else
4524  ast_log(LOG_WARNING, "Don't know anything about option '%s'\n", fields[y]);
4525  }
4526  } else
4527  ast_log(LOG_WARNING, "Expected at least %d arguments in map, but got only %d\n", 4, x);
4528 }
4529 
4530 /* \note Called with the peers list already locked */
4531 static int do_register(const void *data)
4532 {
4533  struct dundi_ie_data ied;
4534  struct dundi_peer *peer = (struct dundi_peer *)data;
4535  char eid_str[20];
4536  char eid_str2[20];
4537  ast_debug(1, "Register us as '%s' to '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->us_eid), ast_eid_to_str(eid_str2, sizeof(eid_str2), &peer->eid));
4538  peer->registerid = ast_sched_add(sched, default_expiration * 1000, do_register, data);
4539  /* Destroy old transaction if there is one */
4540  if (peer->regtrans)
4541  destroy_trans(peer->regtrans, 0);
4542  peer->regtrans = create_transaction(peer);
4543  if (peer->regtrans) {
4545  memset(&ied, 0, sizeof(ied));
4548  dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
4549  dundi_send(peer->regtrans, DUNDI_COMMAND_REGREQ, 0, 0, &ied);
4550 
4551  } else
4552  ast_log(LOG_NOTICE, "Unable to create new transaction for registering to '%s'!\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4553 
4554  return 0;
4555 }
4556 
4557 static int do_qualify(const void *data)
4558 {
4559  struct dundi_peer *peer = (struct dundi_peer *)data;
4560  peer->qualifyid = -1;
4561  qualify_peer(peer, 0);
4562  return 0;
4563 }
4564 
4565 static void qualify_peer(struct dundi_peer *peer, int schedonly)
4566 {
4567  int when;
4568  AST_SCHED_DEL(sched, peer->qualifyid);
4569  if (peer->qualtrans)
4570  destroy_trans(peer->qualtrans, 0);
4571  peer->qualtrans = NULL;
4572  if (peer->maxms > 0) {
4573  when = 60000;
4574  if (peer->lastms < 0)
4575  when = 10000;
4576  if (schedonly)
4577  when = 5000;
4578  peer->qualifyid = ast_sched_add(sched, when, do_qualify, peer);
4579  if (!schedonly)
4580  peer->qualtrans = create_transaction(peer);
4581  if (peer->qualtrans) {
4582  peer->qualtx = ast_tvnow();
4585  }
4586  }
4587 }
4588 static void populate_addr(struct dundi_peer *peer, dundi_eid *eid)
4589 {
4590  char data[256];
4591  char *c;
4592  int port, expire;
4593  char eid_str[20];
4594  ast_eid_to_str(eid_str, sizeof(eid_str), eid);
4595  if (!ast_db_get("dundi/dpeers", eid_str, data, sizeof(data))) {
4596  /*
4597  * data is in the form:
4598  * IPv6 address: [ffff:ffff::ffff:ffff]:port:expire
4599  * IPv4 address: a.b.c.d:port:expire
4600  */
4601  c = data;
4602  if (*c == '[') {
4603  /* Need to skip over the IPv6 address. */
4604  c = strchr(c, ']');
4605  }
4606  if (c) {
4607  c = strchr(c, ':');
4608  }
4609  if (c) {
4610  *c = '\0';
4611  c++;
4612  if (sscanf(c, "%5d:%30d", &port, &expire) == 2) {
4613  /* Got it! */
4614  struct ast_sockaddr *addrs;
4615 
4616  if (ast_sockaddr_resolve(&addrs, data, PARSE_PORT_FORBID, AF_UNSPEC) > 0){
4617  ast_sockaddr_copy(&peer->addr, &addrs[0]);
4618  ast_free(addrs);
4619  }
4620  ast_sockaddr_set_port(&peer->addr, port);
4621  peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
4622  }
4623  }
4624  }
4625 }
4626 
4627 
4628 static void build_peer(dundi_eid *eid, struct ast_variable *v, int *globalpcmode)
4629 {
4630  struct dundi_peer *peer;
4631  dundi_eid testeid;
4632  int needregister=0;
4633  char eid_str[20];
4634  int port = 0;
4635 
4636  AST_LIST_LOCK(&peers);
4637  AST_LIST_TRAVERSE(&peers, peer, list) {
4638  if (!ast_eid_cmp(&peer->eid, eid)) {
4639  break;
4640  }
4641  }
4642  if (!peer) {
4643  /* Add us into the list */
4644  if (!(peer = ast_calloc(1, sizeof(*peer)))) {
4646  return;
4647  }
4648  peer->registerid = -1;
4649  peer->registerexpire = -1;
4650  peer->qualifyid = -1;
4651  populate_addr(peer, eid);
4652  AST_LIST_INSERT_HEAD(&peers, peer, list);
4653  }
4654  peer->dead = 0;
4655  peer->eid = *eid;
4656  peer->us_eid = global_eid;
4657  destroy_permissions(&peer->permit);
4658  destroy_permissions(&peer->include);
4659  AST_SCHED_DEL(sched, peer->registerid);
4660  for (; v; v = v->next) {
4661  if (!strcasecmp(v->name, "inkey")) {
4662  ast_copy_string(peer->inkey, v->value, sizeof(peer->inkey));
4663  } else if (!strcasecmp(v->name, "outkey")) {
4664  ast_copy_string(peer->outkey, v->value, sizeof(peer->outkey));
4665  } else if (!strcasecmp(v->name, "port")) {
4666  port = atoi(v->value);
4667  } else if (!strcasecmp(v->name, "host")) {
4668  if (!strcasecmp(v->value, "dynamic")) {
4669  peer->dynamic = 1;
4670  } else {
4671  struct ast_sockaddr *addrs;
4672 
4673  if (ast_sockaddr_resolve(&addrs, v->value, PARSE_PORT_FORBID, AF_UNSPEC) > 0) {
4674  ast_sockaddr_copy(&peer->addr, &addrs[0]);
4675  peer->dynamic = 0;
4676  ast_free(addrs);
4677  } else {
4678  ast_log(LOG_WARNING, "Unable to find host '%s' at line %d\n", v->value, v->lineno);
4679  peer->dead = 1;
4680  }
4681  }
4682  } else if (!strcasecmp(v->name, "ustothem")) {
4683  if (!ast_str_to_eid(&testeid, v->value))
4684  peer->us_eid = testeid;
4685  else
4686  ast_log(LOG_WARNING, "'%s' is not a valid DUNDi Entity Identifier at line %d\n", v->value, v->lineno);
4687  } else if (!strcasecmp(v->name, "include")) {
4688  append_permission(&peer->include, v->value, 1);
4689  } else if (!strcasecmp(v->name, "permit")) {
4690  append_permission(&peer->permit, v->value, 1);
4691  } else if (!strcasecmp(v->name, "noinclude")) {
4692  append_permission(&peer->include, v->value, 0);
4693  } else if (!strcasecmp(v->name, "deny")) {
4694  append_permission(&peer->permit, v->value, 0);
4695  } else if (!strcasecmp(v->name, "register")) {
4696  needregister = ast_true(v->value);
4697  } else if (!strcasecmp(v->name, "order")) {
4698  if (!strcasecmp(v->value, "primary"))
4699  peer->order = 0;
4700  else if (!strcasecmp(v->value, "secondary"))
4701  peer->order = 1;
4702  else if (!strcasecmp(v->value, "tertiary"))
4703  peer->order = 2;
4704  else if (!strcasecmp(v->value, "quartiary"))
4705  peer->order = 3;
4706  else {
4707  ast_log(LOG_WARNING, "'%s' is not a valid order, should be primary, secondary, tertiary or quartiary at line %d\n", v->value, v->lineno);
4708  }
4709  } else if (!strcasecmp(v->name, "qualify")) {
4710  if (!strcasecmp(v->value, "no")) {
4711  peer->maxms = 0;
4712  } else if (!strcasecmp(v->value, "yes")) {
4713  peer->maxms = DEFAULT_MAXMS;
4714  } else if (sscanf(v->value, "%30d", &peer->maxms) != 1) {
4715  ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of dundi.conf\n",
4716  ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), v->lineno);
4717  peer->maxms = 0;
4718  }
4719  } else if (!strcasecmp(v->name, "model")) {
4720  if (!strcasecmp(v->value, "inbound"))
4721  peer->model = DUNDI_MODEL_INBOUND;
4722  else if (!strcasecmp(v->value, "outbound"))
4723  peer->model = DUNDI_MODEL_OUTBOUND;
4724  else if (!strcasecmp(v->value, "symmetric"))
4725  peer->model = DUNDI_MODEL_SYMMETRIC;
4726  else if (!strcasecmp(v->value, "none"))
4727  peer->model = 0;
4728  else {
4729  ast_log(LOG_WARNING, "Unknown model '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
4730  v->value, v->lineno);
4731  }
4732  } else if (!strcasecmp(v->name, "precache")) {
4733  if (!strcasecmp(v->value, "inbound"))
4734  peer->pcmodel = DUNDI_MODEL_INBOUND;
4735  else if (!strcasecmp(v->value, "outbound"))
4736  peer->pcmodel = DUNDI_MODEL_OUTBOUND;
4737  else if (!strcasecmp(v->value, "symmetric"))
4739  else if (!strcasecmp(v->value, "none"))
4740  peer->pcmodel = 0;
4741  else {
4742  ast_log(LOG_WARNING, "Unknown pcmodel '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
4743  v->value, v->lineno);
4744  }
4745  }
4746  }
4747 
4748  if (!ast_sockaddr_isnull(&peer->addr)) {
4749  ast_sockaddr_set_port(&peer->addr, (0 < port) ? port : DUNDI_PORT);
4750  }
4751 
4752  (*globalpcmode) |= peer->pcmodel;
4753  if (!peer->model && !peer->pcmodel) {
4754  ast_log(LOG_WARNING, "Peer '%s' lacks a model or pcmodel, discarding!\n",
4755  ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4756  peer->dead = 1;
4757  } else if ((peer->model & DUNDI_MODEL_INBOUND) && (peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
4758  ast_log(LOG_WARNING, "Peer '%s' may not be both inbound/symmetric model and outbound/symmetric precache model, discarding!\n",
4759  ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4760  peer->dead = 1;
4761  } else if ((peer->model & DUNDI_MODEL_OUTBOUND) && (peer->pcmodel & DUNDI_MODEL_INBOUND)) {
4762  ast_log(LOG_WARNING, "Peer '%s' may not be both outbound/symmetric model and inbound/symmetric precache model, discarding!\n",
4763  ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4764  peer->dead = 1;
4765  } else if (!AST_LIST_EMPTY(&peer->include) && !(peer->model & DUNDI_MODEL_OUTBOUND) && !(peer->pcmodel & DUNDI_MODEL_INBOUND)) {
4766  ast_log(LOG_WARNING, "Peer '%s' is supposed to be included in outbound searches but isn't an outbound peer or inbound precache!\n",
4767  ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4768  } else if (!AST_LIST_EMPTY(&peer->permit) && !(peer->model & DUNDI_MODEL_INBOUND) && !(peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
4769  ast_log(LOG_WARNING, "Peer '%s' is supposed to have permission for some inbound searches but isn't an inbound peer or outbound precache!\n",
4770  ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4771  } else {
4772  if (ast_eid_cmp(&peer->eid, &empty_eid)) {
4773  /* Schedule any items for explicitly configured peers. */
4774  if (needregister) {
4775  peer->registerid = ast_sched_add(sched, 2000, do_register, peer);
4776  }
4777  qualify_peer(peer, 1);
4778  }
4779  }
4781 }
4782 
4783 static int dundi_helper(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *data, int flag)
4784 {
4785  struct dundi_result results[MAX_RESULTS];
4786  int res;
4787  int x;
4788  int found = 0;
4789  if (!strncasecmp(context, "macro-", 6)) {
4790  if (!chan) {
4791  ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
4792  return -1;
4793  }
4794  /* If done as a macro, use macro extension */
4795  if (!strcasecmp(exten, "s")) {
4796  exten = pbx_builtin_getvar_helper(chan, "ARG1");
4797  if (ast_strlen_zero(exten))
4798  exten = ast_channel_macroexten(chan);
4799  if (ast_strlen_zero(exten))
4800  exten = ast_channel_exten(chan);
4801  if (ast_strlen_zero(exten)) {
4802  ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
4803  return -1;
4804  }
4805  }
4806  if (ast_strlen_zero(data))
4807  data = "e164";
4808  } else {
4809  if (ast_strlen_zero(data))
4810  data = context;
4811  }
4812  res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
4813  for (x=0;x<res;x++) {
4814  if (ast_test_flag(results + x, flag))
4815  found++;
4816  }
4817  if (found >= priority)
4818  return 1;
4819  return 0;
4820 }
4821 
4822 static int dundi_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4823 {
4824  return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_EXISTS);
4825 }
4826 
4827 static int dundi_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4828 {
4829  return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_CANMATCH);
4830 }
4831 
4832 static int dundi_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4833 {
4834  struct dundi_result results[MAX_RESULTS];
4835  int res;
4836  int x=0;
4837  char req[1024];
4838  const char *dundiargs;
4839  struct ast_app *dial;
4840 
4841  if (!strncasecmp(context, "macro-", 6)) {
4842  if (!chan) {
4843  ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
4844  return -1;
4845  }
4846  /* If done as a macro, use macro extension */
4847  if (!strcasecmp(exten, "s")) {
4848  exten = pbx_builtin_getvar_helper(chan, "ARG1");
4849  if (ast_strlen_zero(exten))
4850  exten = ast_channel_macroexten(chan);
4851  if (ast_strlen_zero(exten))
4852  exten = ast_channel_exten(chan);
4853  if (ast_strlen_zero(exten)) {
4854  ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
4855  return -1;
4856  }
4857  }
4858  if (ast_strlen_zero(data))
4859  data = "e164";
4860  } else {
4861  if (ast_strlen_zero(data))
4862  data = context;
4863  }
4864  res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
4865  if (res > 0) {
4866  sort_results(results, res);
4867  for (x=0;x<res;x++) {
4868  if (ast_test_flag(results + x, DUNDI_FLAG_EXISTS)) {
4869  if (!--priority)
4870  break;
4871  }
4872  }
4873  }
4874  if (x < res) {
4875  /* Got a hit! */
4876  dundiargs = pbx_builtin_getvar_helper(chan, "DUNDIDIALARGS");
4877  snprintf(req, sizeof(req), "%s/%s,,%s", results[x].tech, results[x].dest,
4878  S_OR(dundiargs, ""));
4879  dial = pbx_findapp("Dial");
4880  if (dial)
4881  res = pbx_exec(chan, dial, req);
4882  } else
4883  res = -1;
4884  return res;
4885 }
4886 
4887 static int dundi_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4888 {
4889  return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_MATCHMORE);
4890 }
4891 
4892 static struct ast_switch dundi_switch = {
4893  .name = "DUNDi",
4894  .description = "DUNDi Discovered Dialplan Switch",
4895  .exists = dundi_exists,
4896  .canmatch = dundi_canmatch,
4897  .exec = dundi_exec,
4898  .matchmore = dundi_matchmore,
4899 };
4900 
4901 static int get_ipaddress(char *ip, size_t size, const char *str, int family)
4902 {
4903  struct ast_sockaddr *addrs;
4904 
4905  if (!ast_sockaddr_resolve(&addrs, str, 0, family)) {
4906  return -1;
4907  }
4908 
4909  ast_copy_string(ip, ast_sockaddr_stringify_host(&addrs[0]), size);
4910  ast_free(addrs);
4911 
4912  return 0;
4913 }
4914 
4915 static void set_host_ipaddr(struct ast_sockaddr *sin)
4916 {
4917  char hn[MAXHOSTNAMELEN];
4918  struct addrinfo hints;
4919  int family;
4920 
4921  memset(&hints, 0, sizeof(hints));
4922 
4923  if (ast_sockaddr_is_ipv6(sin)) {
4924  family = AF_INET6;
4925  } else {
4926  family = AF_INET;
4927  }
4928 
4929  if (gethostname(hn, sizeof(hn) - 1) < 0) {
4930  ast_log(LOG_WARNING, "Unable to get host name!\n");
4931  return;
4932  }
4933 
4934  get_ipaddress(ipaddr, sizeof(ipaddr), hn, family);
4935 }
4936 
4937 static int set_config(char *config_file, struct ast_sockaddr *sin, int reload, struct ast_sockaddr *sin2)
4938 {
4939  struct ast_config *cfg;
4940  struct ast_variable *v;
4941  char *cat;
4942  int x;
4943  struct ast_flags config_flags = { 0 };
4944  static int last_port = 0;
4945  int port = 0;
4946  int globalpcmodel = 0;
4947  dundi_eid testeid;
4948  char bind_addr[80]={0,};
4949  char bind_addr2[80]={0,};
4950 
4951  if (!(cfg = ast_config_load(config_file, config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
4952  ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
4953  return -1;
4954  }
4955 
4956  dundi_ttl = DUNDI_DEFAULT_TTL;
4957  dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
4958  any_peer = NULL;
4959 
4960  AST_LIST_LOCK(&peers);
4961 
4963  ast_log(LOG_WARNING, "Entity ID is not set.\n");
4964  }
4965  memcpy(&global_eid, &ast_eid_default, sizeof(global_eid));
4966 
4967  global_storehistory = 0;
4968  ast_copy_string(secretpath, "dundi", sizeof(secretpath));
4969  v = ast_variable_browse(cfg, "general");
4970  while(v) {
4971  if (!strcasecmp(v->name, "port")){
4972  port = atoi(v->value);
4973  } else if (!strcasecmp(v->name, "bindaddr")) {
4974  if (get_ipaddress(bind_addr, sizeof(bind_addr), v->value, AF_UNSPEC) == 0) {
4975  if (!ast_sockaddr_parse(sin, bind_addr, 0)) {
4976  ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
4977  }
4978  }
4979  } else if (!strcasecmp(v->name, "bindaddr2")) {
4980  if (get_ipaddress(bind_addr2, sizeof(bind_addr2), v->value, AF_UNSPEC) == 0) {
4981  if (!ast_sockaddr_parse(sin2, bind_addr2, 0)) {
4982  ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
4983  }
4984  }
4985  } else if (!strcasecmp(v->name, "authdebug")) {
4986  authdebug = ast_true(v->value);
4987  } else if (!strcasecmp(v->name, "ttl")) {
4988  if ((sscanf(v->value, "%30d", &x) == 1) && (x > 0) && (x < DUNDI_DEFAULT_TTL)) {
4989  dundi_ttl = x;
4990  } else {
4991  ast_log(LOG_WARNING, "'%s' is not a valid TTL at line %d, must be number from 1 to %d\n",
4992  v->value, v->lineno, DUNDI_DEFAULT_TTL);
4993  }
4994  } else if (!strcasecmp(v->name, "autokill")) {
4995  if (sscanf(v->value, "%30d", &x) == 1) {
4996  if (x >= 0)
4997  global_autokilltimeout = x;
4998  else
4999  ast_log(LOG_NOTICE, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno);
5000  } else if (ast_true(v->value)) {
5001  global_autokilltimeout = DEFAULT_MAXMS;
5002  } else {
5003  global_autokilltimeout = 0;
5004  }
5005  } else if (!strcasecmp(v->name, "entityid")) {
5006  if (!ast_str_to_eid(&testeid, v->value))
5007  global_eid = testeid;
5008  else
5009  ast_log(LOG_WARNING, "Invalid global endpoint identifier '%s' at line %d\n", v->value, v->lineno);
5010  } else if (!strcasecmp(v->name, "tos")) {
5011  if (ast_str2tos(v->value, &tos))
5012  ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
5013  } else if (!strcasecmp(v->name, "department")) {
5014  ast_copy_string(dept, v->value, sizeof(dept));
5015  } else if (!strcasecmp(v->name, "organization")) {
5016  ast_copy_string(org, v->value, sizeof(org));
5017  } else if (!strcasecmp(v->name, "locality")) {
5018  ast_copy_string(locality, v->value, sizeof(locality));
5019  } else if (!strcasecmp(v->name, "stateprov")) {
5020  ast_copy_string(stateprov, v->value, sizeof(stateprov));
5021  } else if (!strcasecmp(v->name, "country")) {
5022  ast_copy_string(country, v->value, sizeof(country));
5023  } else if (!strcasecmp(v->name, "email")) {
5024  ast_copy_string(email, v->value, sizeof(email));
5025  } else if (!strcasecmp(v->name, "phone")) {
5026  ast_copy_string(phone, v->value, sizeof(phone));
5027  } else if (!strcasecmp(v->name, "storehistory")) {
5028  global_storehistory = ast_true(v->value);
5029  } else if (!strcasecmp(v->name, "cachetime")) {
5030  if ((sscanf(v->value, "%30d", &x) == 1)) {
5031  dundi_cache_time = x;
5032  } else {
5033  ast_log(LOG_WARNING, "'%s' is not a valid cache time at line %d. Using default value '%d'.\n",
5035  }
5036  }
5037  v = v->next;
5038  }
5039 
5040  if (port == 0) {
5041  port = DUNDI_PORT;
5042  }
5043 
5044  if (ast_sockaddr_isnull(sin)) {
5045  sprintf(bind_addr, "0.0.0.0:%d", port);
5046  ast_sockaddr_parse(sin, bind_addr, 0);
5047  } else {
5048  ast_sockaddr_set_port(sin, port);
5049  }
5050 
5051  if (last_port == 0) {
5052  last_port = port;
5053  } else if (last_port != port) {
5054  ast_log(LOG_WARNING, "change to port ignored until next asterisk re-start\n");
5055  }
5056 
5057  set_host_ipaddr(sin);
5058 
5059  if (!ast_sockaddr_isnull(sin2)) {
5060  ast_sockaddr_set_port(sin2, port);
5061  }
5062 
5064 
5065  mark_mappings();
5066  v = ast_variable_browse(cfg, "mappings");
5067  while (v) {
5068  AST_LIST_LOCK(&peers);
5069  build_mapping(v->name, v->value);
5071  v = v->next;
5072  }
5073  prune_mappings();
5074 
5075  mark_peers();
5076  cat = ast_category_browse(cfg, NULL);
5077  while(cat) {
5078  if (strcasecmp(cat, "general") && strcasecmp(cat, "mappings")) {
5079  /* Entries */
5080  if (!ast_str_to_eid(&testeid, cat))
5081  build_peer(&testeid, ast_variable_browse(cfg, cat), &globalpcmodel);
5082  else if (!strcasecmp(cat, "*")) {
5083  build_peer(&empty_eid, ast_variable_browse(cfg, cat), &globalpcmodel);
5084  any_peer = find_peer(NULL);
5085  } else
5086  ast_log(LOG_NOTICE, "Ignoring invalid EID entry '%s'\n", cat);
5087  }
5088  cat = ast_category_browse(cfg, cat);
5089  }
5090  prune_peers();
5091 
5092  ast_config_destroy(cfg);
5093  load_password();
5094  if (globalpcmodel & DUNDI_MODEL_OUTBOUND)
5096  return 0;
5097 }
5098 
5099 static int unload_module(void)
5100 {
5101  ast_cli_unregister_multiple(cli_dundi, ARRAY_LEN(cli_dundi));
5102  ast_unregister_switch(&dundi_switch);
5103  ast_custom_function_unregister(&dundi_function);
5104  ast_custom_function_unregister(&dundi_query_function);
5105  ast_custom_function_unregister(&dundi_result_function);
5106 
5107  /* Stop all currently running threads */
5108  dundi_shutdown = 1;
5109  if (netthreadid != AST_PTHREADT_NULL) {
5110  pthread_kill(netthreadid, SIGURG);
5111  pthread_join(netthreadid, NULL);
5112  netthreadid = AST_PTHREADT_NULL;
5113  }
5114  if (precachethreadid != AST_PTHREADT_NULL) {
5115  pthread_kill(precachethreadid, SIGURG);
5116  pthread_join(precachethreadid, NULL);
5117  precachethreadid = AST_PTHREADT_NULL;
5118  }
5119  if (clearcachethreadid != AST_PTHREADT_NULL) {
5120  pthread_cancel(clearcachethreadid);
5121  pthread_join(clearcachethreadid, NULL);
5122  clearcachethreadid = AST_PTHREADT_NULL;
5123  }
5124 
5125  if (netsocket >= 0) {
5126  close(netsocket);
5127  }
5128 
5129  if (netsocket2 >= 0) {
5130  close(netsocket2);
5131  }
5132 
5133  mark_mappings();
5134  prune_mappings();
5135  mark_peers();
5136  prune_peers();
5137 
5138  if (-1 < netsocket) {
5139  close(netsocket);
5140  netsocket = -1;
5141  }
5142  if (io) {
5143  io_context_destroy(io);
5144  io = NULL;
5145  }
5146 
5147  if (sched) {
5149  sched = NULL;
5150  }
5151 
5152  return 0;
5153 }
5154 
5155 static int reload(void)
5156 {
5157  struct ast_sockaddr sin;
5158  struct ast_sockaddr sin2;
5159 
5160  ast_sockaddr_setnull(&sin);
5161  ast_sockaddr_setnull(&sin2);
5162 
5163  if (set_config("dundi.conf", &sin, 1, &sin2))
5164  return AST_MODULE_LOAD_FAILURE;
5165 
5166  return AST_MODULE_LOAD_SUCCESS;
5167 }
5168 
5169 static int load_module(void)
5170 {
5171  struct ast_sockaddr sin;
5172  struct ast_sockaddr sin2;
5173 
5176 
5177  /* Make a UDP socket */
5178  io = io_context_create();
5179  sched = ast_sched_context_create();
5180 
5181  if (!io || !sched) {
5182  goto declined;
5183  }
5184 
5185  ast_sockaddr_setnull(&sin);
5186  ast_sockaddr_setnull(&sin2);
5187 
5188  if (set_config("dundi.conf", &sin, 0, &sin2)) {
5189  goto declined;
5190  }
5191 
5192  if (!ast_sockaddr_isnull(&sin2)) {
5193  if ((ast_sockaddr_is_ipv4(&sin) == ast_sockaddr_is_ipv4(&sin2)) || (ast_sockaddr_is_ipv6(&sin) == ast_sockaddr_is_ipv6(&sin2))) {
5194  ast_log(LOG_ERROR, "bindaddr & bindaddr2 should be different IP protocols.\n");
5195  goto declined;
5196  }
5197 
5198  /*bind netsocket to ipv4, netsocket2 to ipv6 */
5199 
5200  netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
5201  netsocket2 = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
5202  if (netsocket < 0 || netsocket2 < 0) {
5203  ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
5204  goto declined;
5205  }
5206  if (ast_sockaddr_is_ipv4(&sin)) {
5207  if (ast_bind(netsocket, &sin)) {
5208  ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
5209  ast_sockaddr_stringify(&sin), strerror(errno));
5210  goto declined;
5211  }
5212  if (ast_bind(netsocket2, &sin2)) {
5213  ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
5214  ast_sockaddr_stringify(&sin2), strerror(errno));
5215  goto declined;
5216  }
5217  } else {
5218  if (ast_bind(netsocket, &sin2)) {
5219  ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
5220  ast_sockaddr_stringify(&sin2), strerror(errno));
5221  goto declined;
5222  }
5223  if (ast_bind(netsocket2, &sin)) {
5224  ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
5225  ast_sockaddr_stringify(&sin), strerror(errno));
5226  goto declined;
5227  }
5228  }
5229  ast_set_qos(netsocket, tos, 0, "DUNDi");
5230  ast_set_qos(netsocket2, tos, 0, "DUNDi");
5231  } else {
5232  if (ast_sockaddr_is_ipv6(&sin)) {
5233  netsocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
5234  } else {
5235  netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
5236  }
5237  if (netsocket < 0) {
5238  ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
5239  goto declined;
5240  }
5241  if (ast_bind(netsocket, &sin)) {
5242  ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
5243  ast_sockaddr_stringify(&sin), strerror(errno));
5244  goto declined;
5245  }
5246  ast_set_qos(netsocket, tos, 0, "DUNDi");
5247  }
5248 
5249  if (start_network_thread()) {
5250  ast_log(LOG_ERROR, "Unable to start network thread\n");
5251  goto declined;
5252  }
5253 
5254  ast_cli_register_multiple(cli_dundi, ARRAY_LEN(cli_dundi));
5255  if (ast_register_switch(&dundi_switch))
5256  ast_log(LOG_ERROR, "Unable to register DUNDi switch\n");
5257  ast_custom_function_register(&dundi_function);
5258  ast_custom_function_register(&dundi_query_function);
5259  ast_custom_function_register(&dundi_result_function);
5260 
5261  ast_verb(2, "DUNDi Ready and Listening on %s\n", ast_sockaddr_stringify(&sin));
5262  if (!ast_sockaddr_isnull(&sin2))
5263  ast_verb(2, "DUNDi Ready and Listening on %s\n", ast_sockaddr_stringify(&sin2));
5264 
5265  return AST_MODULE_LOAD_SUCCESS;
5266 
5267 declined:
5268  unload_module();
5269  return AST_MODULE_LOAD_DECLINE;
5270 }
5271 
5272 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Distributed Universal Number Discovery (DUNDi)",
5273  .support_level = AST_MODULE_SUPPORT_EXTENDED,
5274  .load = load_module,
5275  .unload = unload_module,
5276  .reload = reload,
5277  .optional_modules = "res_crypto",
5278 );
static char * ast_sockaddr_stringify_addr(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() to return an address only.
Definition: netsock2.h:290
static void dundi_precache_full(void)
Definition: pbx_dundi.c:3972
int ast_io_wait(struct io_context *ioc, int howlong)
Waits for IO.
Definition: io.c:278
const char * name
Definition: pbx.h:119
const char * type
Definition: datastore.h:32
static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
Definition: pbx_dundi.c:1295
struct ast_variable * next
static float dr[4]
Definition: tdd.c:58
static char pass[512]
static int discover_transactions(struct dundi_request *dr)
Definition: pbx_dundi.c:3525
int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid)
Retrieve information on a specific EID.
Definition: pbx_dundi.c:4121
int ast_unlock_context(struct ast_context *con)
Definition: pbx.c:8530
static char * dundi_sockaddr_stringify_host(const struct ast_sockaddr *addr)
Definition: pbx_dundi.c:2601
char locality[80]
Definition: dundi.h:239
char phone[80]
Definition: dundi.h:243
#define DUNDI_IE_EXPIRATION
Definition: dundi.h:189
#define ast_channel_lock(chan)
Definition: channel.h:2945
int ast_matchmore_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Looks to see if adding anything to this extension might match something. (exists ^ canmatch) ...
Definition: pbx.c:4199
static char exten[AST_MAX_EXTENSION]
Definition: chan_alsa.c:118
Main Channel structure associated with a channel.
Definition: pbx_dundi.c:342
char dest[512]
Definition: pbx_dundi.c:299
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
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)
Definition: pbx_dundi.c:578
int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass)
Lookup the given number in the given dundi context. Lookup number in a given dundi context (if unspec...
Definition: pbx_dundi.c:3926
ssize_t ast_sendto(int sockfd, const void *buf, size_t len, int flags, const struct ast_sockaddr *dest_addr)
Wrapper around sendto(2) that uses ast_sockaddr.
Definition: netsock2.c:614
#define DUNDI_FLUFF_TIME
Definition: dundi.h:211
#define DUNDI_DEFAULT_RETRANS
Definition: dundi.h:214
static char * complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
Definition: pbx_dundi.c:2417
char org[80]
Definition: dundi.h:240
dundi_eid * reqeid
Definition: dundi-parser.h:24
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
#define DUNDI_IE_EID_DIRECT
Definition: dundi.h:185
Asterisk locking-related definitions:
static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
Definition: pbx_dundi.c:1087
Asterisk main include file. File version handling, generic pbx functions.
ast_exten: An extension The dialplan is saved as a linked list with each context having it&#39;s own link...
Definition: pbx.c:237
static const char config_file[]
Definition: cdr_odbc.c:57
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:420
#define DUNDI_MODEL_SYMMETRIC
Definition: pbx_dundi.c:161
char dcontext[AST_MAX_EXTENSION]
Definition: pbx_dundi.c:292
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
int pfds[2]
Definition: pbx_dundi.c:285
Distributed Universal Number Discovery (DUNDi) See also.
static char * dundi_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2697
char * lookups[DUNDI_TIMING_HISTORY]
Definition: pbx_dundi.c:327
unsigned char aseqno
Definition: pbx_dundi.c:265
const char * ast_get_context_name(struct ast_context *con)
Definition: ael_main.c:421
static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
Definition: pbx_dundi.c:3536
static int reset_transaction(struct dundi_transaction *trans)
Definition: pbx_dundi.c:499
static int register_request(struct dundi_request *dr, struct dundi_request **pending)
Definition: pbx_dundi.c:3758
static struct ast_sched_context * sched
Definition: pbx_dundi.c:185
char country[80]
Definition: dundi.h:237
char called_number[AST_MAX_EXTENSION]
Definition: pbx_dundi.c:548
static char * dundi_show_requests(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2850
static int set_config(char *config_file, struct ast_sockaddr *sin, int reload, struct ast_sockaddr *sin2)
Definition: pbx_dundi.c:4937
int pbx_exec(struct ast_channel *c, struct ast_app *app, const char *data)
Execute an application.
Definition: pbx_app.c:471
static void prune_mappings(void)
Definition: pbx_dundi.c:4427
ast_aes_encrypt_key ecx
Definition: pbx_dundi.c:252
static char locality[80]
Definition: pbx_dundi.c:203
struct dundi_request * parent
Definition: pbx_dundi.c:268
int dundi_ie_append_eid(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid)
Definition: dundi-parser.c:610
static int get_ipaddress(char *ip, size_t size, const char *str, int family)
Definition: pbx_dundi.c:4901
#define DUNDI_IE_IPADDR
Definition: dundi.h:206
static char * dundi_show_trans(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2796
int weight
Definition: dundi.h:227
#define DUNDI_IE_CACHEBYPASS
Definition: dundi.h:207
static pthread_t precachethreadid
Definition: pbx_dundi.c:189
int ast_sched_runq(struct ast_sched_context *con)
Runs the queue.
Definition: sched.c:755
char lcontext[AST_MAX_EXTENSION]
Definition: pbx_dundi.c:293
#define ast_pthread_create_detached(a, b, c, d)
Definition: utils.h:563
int ast_sockaddr_parse(struct ast_sockaddr *addr, const char *str, int flags)
Parse an IPv4 or IPv6 address string.
Definition: netsock2.c:230
static int dundi_result_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: pbx_dundi.c:4279
static void sort_results(struct dundi_result *results, int count)
Definition: pbx_dundi.c:2451
static char * dundi_show_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2955
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
static int dundi_cache_time
Definition: pbx_dundi.c:196
#define DUNDI_COMMAND_ENCREJ
Definition: dundi.h:173
char * ast_eid_to_str(char *s, int maxlen, struct ast_eid *eid)
Convert an EID to a string.
Definition: main/utils.c:2587
static int do_register_expire(const void *data)
Definition: pbx_dundi.c:1321
struct ast_eid dundi_eid
Definition: dundi.h:32
static dundi_eid global_eid
Definition: pbx_dundi.c:198
#define ast_test_flag(p, flag)
Definition: utils.h:63
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1216
char orgunit[80]
Definition: dundi.h:241
struct dundi_entity_info * dei
Definition: pbx_dundi.c:279
static void * process_precache(void *ign)
Definition: pbx_dundi.c:2261
#define BEGIN_OPTIONS
static void ast_sockaddr_copy(struct ast_sockaddr *dst, const struct ast_sockaddr *src)
Copies the data from one ast_sockaddr to another.
Definition: netsock2.h:171
static void * dundi_lookup_thread(void *data)
Definition: pbx_dundi.c:657
unsigned char encdata[0]
Definition: dundi.h:99
static void destroy_map(struct dundi_mapping *map)
Definition: pbx_dundi.c:4406
int qualifyid
Definition: pbx_dundi.c:313
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#define ast_clear_flag_nonstd(p, flag)
Definition: utils.h:180
static int * map
Definition: misdn_config.c:438
#define DUNDI_PORT
Definition: dundi.h:30
static void * network_thread(void *ignore)
Definition: pbx_dundi.c:2194
#define DUNDI_TIMING_HISTORY
Definition: pbx_dundi.c:164
static void set_host_ipaddr(struct ast_sockaddr *sin)
Definition: pbx_dundi.c:4915
dundi_eid eids[DUNDI_MAX_STACK+1]
Definition: pbx_dundi.c:248
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
struct dundi_answer * answers[DUNDI_MAX_ANSWERS+1]
Definition: dundi-parser.h:28
static char * tech2str(int tech)
Definition: pbx_dundi.c:380
char * q_org
Definition: dundi-parser.h:38
descriptor for a cli entry.
Definition: cli.h:171
const int argc
Definition: cli.h:160
#define LOG_WARNING
Definition: logger.h:274
#define DUNDI_DEFAULT_CACHE_TIME
Definition: dundi.h:218
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
dundi_eid * eids[DUNDI_MAX_STACK+1]
Definition: pbx_dundi.c:544
char ipaddr[80]
Definition: dundi.h:244
#define DUNDI_IE_SHAREDKEY
Definition: dundi.h:194
static int dundi_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
Definition: pbx_dundi.c:4832
char * q_locality
Definition: dundi-parser.h:39
void ast_db_freetree(struct ast_db_entry *entry)
Free structure created by ast_db_gettree()
Definition: main/db.c:598
Provide cryptographic signature routines.
unsigned int flags
Definition: pbx_dundi.c:254
#define CONFIG_STATUS_FILEINVALID
static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_encrypt_key *ecx)
Definition: pbx_dundi.c:1373
struct dundi_mapping * maps
Definition: pbx_dundi.c:549
static int timeout
Definition: cdr_mysql.c:86
static void populate_addr(struct dundi_peer *peer, dundi_eid *eid)
Definition: pbx_dundi.c:4588
struct dundi_transaction * trans
Definition: pbx_dundi.c:552
static int tmp()
Definition: bt_open.c:389
static char email[80]
Definition: pbx_dundi.c:206
void dundi_set_error(void(*func)(const char *))
Definition: dundi-parser.c:630
static int start_network_thread(void)
Definition: pbx_dundi.c:2296
static char phone[80]
Definition: pbx_dundi.c:207
static struct io_context * io
Definition: pbx_dundi.c:184
unsigned int flags
Definition: utils.h:200
Structure for variables, used for configurations and for channel variables.
unsigned char rxenckey[256]
Definition: pbx_dundi.c:317
char * dundi_eid_to_str_short(char *s, int maxlen, dundi_eid *eid)
Definition: dundi-parser.c:54
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:438
#define DUNDI_IE_CAUSE
Definition: dundi.h:191
#define MAX_RESULTS
Definition: pbx_dundi.c:153
static void cancel_request(struct dundi_request *dr)
Definition: pbx_dundi.c:3674
#define AST_IO_IN
Definition: io.h:34
static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, uint32_t crc, int *lowexpiration)
Definition: pbx_dundi.c:1242
struct ast_context * ast_walk_contexts(struct ast_context *con)
Definition: conf2ael.c:618
Definition: cli.h:152
if(!yyg->yy_init)
Definition: ast_expr2f.c:868
static void destroy_permissions(struct permissionlist *permlist)
Definition: pbx_dundi.c:4374
struct dundi_result * dr
Definition: pbx_dundi.c:278
int registerexpire
Definition: pbx_dundi.c:325
int ast_ignore_pattern(const char *context, const char *pattern)
Checks to see if a number should be ignored.
Definition: pbx.c:6921
Structure for a data store type.
Definition: datastore.h:31
static char * dundi_do_query(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2550
int * ast_io_add(struct io_context *ioc, int fd, ast_io_cb callback, short events, void *data)
Adds an IO context.
Definition: io.c:162
void ast_unregister_switch(struct ast_switch *sw)
Unregister an alternative switch.
Definition: pbx_switch.c:76
static const struct adsi_event events[]
Definition: app_adsiprog.c:85
static void deep_copy_peer(struct dundi_peer *peer_dst, const struct dundi_peer *peer_src)
Definition: pbx_dundi.c:1545
#define DUNDI_IE_EMAIL
Definition: dundi.h:204
#define DUNDI_IE_REQEID
Definition: dundi.h:192
static char * dundi_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2621
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
char dest[256]
Definition: dundi.h:233
int ast_rdlock_contexts(void)
Read locks the context list.
Definition: pbx.c:8507
#define DUNDI_COMMAND_ENCRYPT
Definition: dundi.h:172
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
static int dundi_key_ttl
Definition: pbx_dundi.c:195
static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
Definition: pbx_dundi.c:3308
int eid_direct[DUNDI_MAX_STACK+1]
Definition: dundi-parser.h:23
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:449
char * called_number
Definition: dundi-parser.h:27
static struct test_val c
char outkey[80]
Definition: pbx_dundi.c:310
static void qualify_peer(struct dundi_peer *peer, int schedonly)
Definition: pbx_dundi.c:4565
#define ast_copy_flags(dest, src, flagz)
Definition: utils.h:84
static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
Definition: pbx_dundi.c:940
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:98
dundi_eid reqeid
Definition: pbx_dundi.c:546
void ast_verbose(const char *fmt,...)
Definition: extconf.c:2207
#define MAXHOSTNAMELEN
Definition: network.h:69
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
Structure for a data store object.
Definition: datastore.h:68
unsigned char cmdresp
Definition: dundi.h:39
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2404
const char * str
Definition: app_jack.c:147
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
int ast_str2tos(const char *value, unsigned int *tos)
Convert a string to the appropriate TOS value.
Definition: acl.c:967
int lookuptimes[DUNDI_TIMING_HISTORY]
Definition: pbx_dundi.c:326
int dundi_ie_append_hint(struct dundi_ie_data *ied, unsigned char ie, unsigned short flags, char *data)
Definition: dundi-parser.c:518
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3328
const char * args
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)
Definition: pbx_dundi.c:1406
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[])
Definition: pbx_dundi.c:3824
#define NULL
Definition: resample.c:96
struct packetlist lasttrans
Definition: pbx_dundi.c:267
int ast_sockaddr_cmp(const struct ast_sockaddr *a, const struct ast_sockaddr *b)
Compares two ast_sockaddr structures.
Definition: netsock2.c:388
static void load_password(void)
Definition: pbx_dundi.c:2138
dundi_eid us_eid
Definition: pbx_dundi.c:250
I/O Management (derived from Cheops-NG)
char dcontext[AST_MAX_EXTENSION]
Definition: pbx_dundi.c:274
static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
Definition: pbx_dundi.c:3391
char * q_country
Definition: dundi-parser.h:41
#define DUNDI_IE_VERSION
Definition: dundi.h:188
static int reload(void)
Definition: pbx_dundi.c:5155
int value
Definition: syslog.c:37
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
Definition: linkedlists.h:855
static char * dundi_do_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2512
unsigned char oseqno
Definition: dundi.h:38
struct dundi_transaction::@439 all
static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
Definition: pbx_dundi.c:3203
#define DUNDI_IE_DEPARTMENT
Definition: dundi.h:199
struct dundi_mapping::@442 list
dundi_eid * eids[DUNDI_MAX_STACK+1]
Definition: dundi-parser.h:22
static int do_register(const void *data)
Definition: pbx_dundi.c:4531
dundi_eid them_eid
Definition: pbx_dundi.c:251
#define DUNDI_COMMAND_ACK
Definition: dundi.h:159
static int priority
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:614
static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
Definition: pbx_dundi.c:3437
Socket address structure.
Definition: netsock2.h:97
unsigned char data[0]
Definition: pbx_dundi.c:228
int ast_bind(int sockfd, const struct ast_sockaddr *addr)
Wrapper around bind(2) that uses struct ast_sockaddr.
Definition: netsock2.c:590
An Entity ID is essentially a MAC address, brief and unique.
Definition: utils.h:786
#define ast_verb(level,...)
Definition: logger.h:463
ast_aes_decrypt_key them_dcx
Definition: pbx_dundi.c:323
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition: lock.h:755
const char * line
Definition: cli.h:162
static int query_transactions(struct dundi_request *dr)
Definition: pbx_dundi.c:3571
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Looks for a valid matching extension.
Definition: pbx.c:4194
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
static int authdebug
Definition: pbx_dundi.c:193
int ast_eid_cmp(const struct ast_eid *eid1, const struct ast_eid *eid2)
Compare two EIDs.
Definition: main/utils.c:2842
int lastms
Definition: pbx_dundi.c:335
static char secretpath[80]
Definition: pbx_dundi.c:208
static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_decrypt_key *dcx)
Definition: pbx_dundi.c:1389
int dundi_ie_append_raw(struct dundi_ie_data *ied, unsigned char ie, void *data, int datalen)
Definition: dundi-parser.c:484
Utility functions.
#define AST_LIST_INSERT_AFTER(head, listelm, elm, field)
Inserts a list entry after a given entry.
Definition: linkedlists.h:694
static void ast_sockaddr_setnull(struct ast_sockaddr *addr)
Sets address addr to null.
Definition: netsock2.h:140
struct dundi_mapping * next
Definition: pbx_dundi.c:300
int args
This gets set in ast_cli_register()
Definition: cli.h:185
static int has_permission(struct permissionlist *permlist, char *cont)
Definition: pbx_dundi.c:367
#define ast_strlen_zero(foo)
Definition: strings.h:52
const char * name
Definition: pbx.h:162
static pthread_t netthreadid
Definition: pbx_dundi.c:188
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
#define ast_pthread_create_background(a, b, c, d)
Definition: utils.h:567
Number structure.
Definition: app_followme.c:154
static int ast_sockaddr_isnull(const struct ast_sockaddr *addr)
Checks if the ast_sockaddr is null. "null" in this sense essentially means uninitialized, or having a 0 length.
Definition: netsock2.h:127
#define ast_sockaddr_port(addr)
Get the port number of a socket address.
Definition: netsock2.h:521
unsigned char data[0]
Definition: dundi.h:112
int registerid
Definition: pbx_dundi.c:312
time_t keyexpire
Definition: pbx_dundi.c:324
static int netsocket
Definition: pbx_dundi.c:186
Configuration File Parser.
static char * dundi_flush(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2357
unsigned char buf[8192]
Definition: dundi-parser.h:56
#define DUNDI_FLAG_INTERNAL_NOPARTIAL
Definition: pbx_dundi.c:176
int eidcount
Definition: dundi-parser.h:25
AES_KEY ast_aes_encrypt_key
Definition: crypto.h:35
static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
Definition: pbx_dundi.c:1170
int ast_get_time_t(const char *src, time_t *dst, time_t _default, int *consumed)
get values from config variables.
Definition: main/utils.c:2198
uint32_t us_keycrc32
Definition: pbx_dundi.c:318
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define DUNDI_COMMAND_DPRESPONSE
Definition: dundi.h:161
#define ast_log
Definition: astobj2.c:42
#define DUNDI_IE_LOCALITY
Definition: dundi.h:201
#define DUNDI_IE_UNKNOWN
Definition: dundi.h:190
struct ast_db_entry * next
Definition: astdb.h:32
unsigned short flags
Definition: dundi.h:105
struct timeval qualtx
Definition: pbx_dundi.c:337
#define ast_config_load(filename, flags)
Load a config file.
#define DUNDI_IE_KEYCRC32
Definition: dundi.h:196
static int dundi_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
Definition: pbx_dundi.c:4827
#define DUNDI_IE_SIGNATURE
Definition: dundi.h:195
static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
Definition: pbx_dundi.c:864
int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception)
Waits for input on an fd.
Definition: channel.c:2989
unsigned char protocol
Definition: dundi.h:104
static void save_secret(const char *newkey, const char *oldkey)
Definition: pbx_dundi.c:2125
static struct dundi_peer * any_peer
Wildcard peer.
Definition: pbx_dundi.c:352
General Asterisk PBX channel definitions.
static struct dundi_transaction * create_transaction(struct dundi_peer *p)
Definition: pbx_dundi.c:3137
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[])
Definition: pbx_dundi.c:4074
#define AST_SCHED_DEL(sched, id)
Remove a scheduler entry.
Definition: sched.h:46
int dundi_ie_append_encdata(struct dundi_ie_data *ied, unsigned char ie, unsigned char *iv, void *data, int datalen)
Definition: dundi-parser.c:539
int dundi_ie_append(struct dundi_ie_data *ied, unsigned char ie)
Definition: dundi-parser.c:620
#define DUNDI_DEFAULT_TTL
Definition: dundi.h:216
void io_context_destroy(struct io_context *ioc)
Destroys a context.
Definition: io.c:107
struct dundi_encblock * encblock
Definition: dundi-parser.h:49
const int fd
Definition: cli.h:159
ast_aes_decrypt_key us_dcx
Definition: pbx_dundi.c:320
void dundi_set_output(void(*func)(const char *))
Definition: dundi-parser.c:625
#define DUNDI_DEFAULT_RETRANS_TIMER
Definition: dundi.h:215
static int dundidebug
Definition: pbx_dundi.c:192
integer order
Definition: analys.c:66
#define AST_PTHREADT_NULL
Definition: lock.h:66
unsigned char cmdflags
Definition: dundi.h:40
const int n
Definition: cli.h:165
#define DUNDI_MODEL_INBOUND
Definition: pbx_dundi.c:159
struct sla_ringing_trunk * last
Definition: app_meetme.c:1092
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
Access Control of various sorts.
Global IO variables are now in a struct in order to be made threadsafe.
Definition: io.c:71
char * q_ipaddr
Definition: dundi-parser.h:44
#define AST_MAX_EXTENSION
Definition: channel.h:135
Scheduler Routines (derived from cheops)
char tech[10]
Definition: dundi.h:232
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:556
Asterisk internal frame definitions.
unsigned long keycrc32
Definition: dundi-parser.h:48
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: extconf.c:1290
struct dundi_transaction::@438 parentlist
long int ast_random(void)
Definition: main/utils.c:2064
static int global_storehistory
Definition: pbx_dundi.c:200
#define DUNDI_IE_PHONE
Definition: dundi.h:205
static void build_iv(unsigned char *iv)
Definition: pbx_dundi.c:533
int ast_str_to_eid(struct ast_eid *eid, const char *s)
Convert a string into an EID.
Definition: main/utils.c:2825
static uint16_t dundi_sockaddr_port(const struct ast_sockaddr *addr)
Definition: pbx_dundi.c:2609
unsigned char oiseqno
Definition: pbx_dundi.c:263
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
dundi_eid query_eid
Definition: pbx_dundi.c:276
static void append_permission(struct permissionlist *permlist, const char *s, int allow)
Definition: pbx_dundi.c:4442
static int get_trans_id(void)
Definition: pbx_dundi.c:480
static dundi_eid empty_eid
Definition: pbx_dundi.c:212
void AST_OPTIONAL_API_NAME() ast_aes_encrypt(const unsigned char *in, unsigned char *out, const ast_aes_encrypt_key *ctx)
Definition: res_crypto.c:476
#define DUNDI_IE_EID
Definition: dundi.h:182
static int dundi_discover(struct dundi_transaction *trans)
Definition: pbx_dundi.c:3413
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:193
char eid_str[20]
Definition: dundi.h:231
#define DUNDI_SECRET_TIME
Definition: pbx_dundi.c:181
#define AST_KEY_PRIVATE
Definition: crypto.h:43
dundi_eid us_eid
Definition: pbx_dundi.c:308
static int do_autokill(const void *data)
Definition: pbx_dundi.c:3380
static void destroy_peer(struct dundi_peer *peer)
Definition: pbx_dundi.c:4382
static int default_expiration
Definition: pbx_dundi.c:199
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:832
int expiration
Definition: dundi.h:228
AST_LIST_HEAD_NOLOCK(contactliststruct, contact)
struct dundi_transaction::packetlist packets
static char * dundi_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2330
unsigned char ies[0]
Definition: dundi.h:41
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
Definition: pbx.c:4179
static int dundi_query(struct dundi_transaction *trans)
Definition: pbx_dundi.c:3503
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:2906
static void dundi_debug_output(const char *data)
Definition: pbx_dundi.c:356
unsigned char * encsharedkey
Definition: dundi-parser.h:46
const char * ast_channel_exten(const struct ast_channel *chan)
static struct ast_custom_function dundi_function
Definition: pbx_dundi.c:4186
int expiration
Definition: dundi-parser.h:33
Network socket handling.
Core PBX routines and definitions.
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
ast_aes_encrypt_key them_ecx
Definition: pbx_dundi.c:322
static char ipaddr[80]
Definition: pbx_dundi.c:210
Wrapper for network related headers, masking differences between various operating systems...
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:290
struct ast_sched_context * ast_sched_context_create(void)
Create a scheduler context.
Definition: sched.c:236
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:290
const char *const * argv
Definition: cli.h:161
char * q_stateprov
Definition: dundi-parser.h:40
static char * dundi_show_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2883
static char * dundi_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2304
static void reschedule_precache(const char *number, const char *context, int expiration)
Definition: pbx_dundi.c:3937
#define DUNDI_TTL_TIME
Definition: dundi.h:212
static void destroy_packet(struct dundi_packet *pack, int needfree)
Definition: pbx_dundi.c:3194
static int dundifunc_read(struct ast_channel *chan, const char *cmd, char *num, char *buf, size_t len)
Definition: pbx_dundi.c:4137
static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
Definition: pbx_dundi.c:809
unsigned int dynamic
Definition: pbx_dundi.c:334
unsigned short strans
Definition: pbx_dundi.c:260
int dundi_ie_append_str(struct dundi_ie_data *ied, unsigned char ie, char *str)
Definition: dundi-parser.c:605
static unsigned long avoid_crc32(dundi_eid *avoid[])
Definition: pbx_dundi.c:3809
#define AST_LIST_HEAD_NOLOCK_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:345
#define DUNDI_COMMAND_UNKNOWN
Definition: dundi.h:167
static unsigned int dundi_result_id
Definition: pbx_dundi.c:4191
static int dundi_rexmit(const void *data)
Definition: pbx_dundi.c:3285
static char * dundi_show_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2917
#define LOG_ERROR
Definition: logger.h:285
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: main/utils.c:1951
static const struct ast_datastore_info dundi_result_datastore_info
Definition: pbx_dundi.c:4210
static void unregister_request(struct dundi_request *dr)
Definition: pbx_dundi.c:3788
static int dundi_query_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: pbx_dundi.c:4215
int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max)
Encode data in base64.
Definition: main/utils.c:404
char number[AST_MAX_EXTENSION]
Definition: pbx_dundi.c:275
static struct dundi_peer * find_peer(dundi_eid *eid)
Definition: pbx_dundi.c:515
struct ast_db_entry * ast_db_gettree(const char *family, const char *keytree)
Get a list of values within the astdb tree.
Definition: main/db.c:531
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define ast_sockaddr_set_port(addr, port)
Sets the port number of a socket address.
Definition: netsock2.h:537
static pthread_t clearcachethreadid
Definition: pbx_dundi.c:190
static char country[80]
Definition: pbx_dundi.c:205
static int global_autokilltimeout
Definition: pbx_dundi.c:197
int AST_OPTIONAL_API_NAME() ast_encrypt_bin(unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key)
encrypt a message
Definition: res_crypto.c:368
int dundi_ie_append_cause(struct dundi_ie_data *ied, unsigned char ie, unsigned char cause, char *data)
Definition: dundi-parser.c:499
char weight
int ast_set_qos(int sockfd, int tos, int cos, const char *desc)
Set type of service.
Definition: netsock2.c:621
int ast_unlock_contexts(void)
Unlocks contexts.
Definition: pbx.c:8512
static void check_password(void)
Definition: pbx_dundi.c:2177
void ast_var_delete(struct ast_var_t *var)
Definition: extconf.c:2473
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
struct dundi_transaction * qualtrans
Definition: pbx_dundi.c:330
int errno
static int load_module(void)
Definition: pbx_dundi.c:5169
#define DUNDI_IE_CALLED_CONTEXT
Definition: dundi.h:183
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:260
unsigned char * encsig
Definition: dundi-parser.h:47
def ignore(key=None, val=None, section=None, pjsip=None, nmapped=None, type='endpoint')
Definition: sip_to_pjsip.py:48
ssize_t ast_recvfrom(int sockfd, void *buf, size_t len, int flags, struct ast_sockaddr *src_addr)
Wrapper around recvfrom(2) that uses struct ast_sockaddr.
Definition: netsock2.c:606
#define DUNDI_IE_ORGANIZATION
Definition: dundi.h:200
int techint
Definition: dundi.h:229
unsigned char oseqno
Definition: pbx_dundi.c:264
#define DUNDI_IE_HINT
Definition: dundi.h:197
int anscount
Definition: dundi-parser.h:30
const char * ast_get_extension_name(struct ast_exten *exten)
Definition: pbx.c:8548
#define LOG_NOTICE
Definition: logger.h:263
#define DUNDI_DEFAULT_KEY_EXPIRE
Definition: dundi.h:219
int dundi_precache(const char *context, const char *number)
Pre-cache to push upstream peers.
Definition: pbx_dundi.c:4068
uint32_t crc32
Definition: pbx_dundi.c:286
#define DUNDI_DEFAULT_VERSION
Definition: dundi.h:217
Definition: astdb.h:31
struct ast_exten * ast_walk_context_extensions(struct ast_context *con, struct ast_exten *priority)
Definition: ael_main.c:427
int dundi_ie_append_short(struct dundi_ie_data *ied, unsigned char ie, unsigned short value)
Definition: dundi-parser.c:598
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
dundi_eid eid
Definition: dundi.h:230
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
static int get_mapping_weight(struct dundi_mapping *map, struct varshead *headp)
Definition: pbx_dundi.c:559
static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, uint32_t keycrc32)
Definition: pbx_dundi.c:1492
long int flag
Definition: f2c.h:83
static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
Definition: pbx_dundi.c:3642
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:710
unsigned short weight
Definition: dundi.h:106
#define ast_channel_unlock(chan)
Definition: channel.h:2946
unsigned short flags
Definition: pbx_dundi.c:232
unsigned short dtrans
Definition: pbx_dundi.c:261
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1872
#define DEFAULT_MAXMS
Definition: chan_iax2.c:390
static void drds_destroy_cb(void *data)
Definition: pbx_dundi.c:4204
char data[0]
Definition: astdb.h:34
static unsigned int tos
Definition: pbx_dundi.c:191
char * called_context
Definition: dundi-parser.h:26
#define ast_free(a)
Definition: astmm.h:182
char * command
Definition: cli.h:186
int directs[DUNDI_MAX_STACK+1]
Definition: pbx_dundi.c:545
unsigned short strans
Definition: dundi.h:35
static void * dundi_precache_thread(void *data)
Definition: pbx_dundi.c:721
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
uint32_t them_keycrc32
Definition: pbx_dundi.c:321
static void * dundi_query_thread(void *data)
Definition: pbx_dundi.c:754
AES_KEY ast_aes_decrypt_key
Definition: crypto.h:36
struct ast_var_t::@249 entries
int AST_OPTIONAL_API_NAME() ast_sign_bin(struct ast_key *key, const char *msg, int msglen, unsigned char *dsig)
signs outgoing message with public key
Definition: res_crypto.c:304
#define AST_FLAGS_ALL
Definition: utils.h:196
#define ast_var_assign(name, value)
Definition: chanvars.h:40
int ast_register_switch(struct ast_switch *sw)
Register an alternative dialplan switch.
Definition: pbx_switch.c:58
static void abort_request(struct dundi_request *dr)
Definition: pbx_dundi.c:3688
struct dundi_hint_metadata * hmd
Definition: pbx_dundi.c:280
static void build_secret(char *secret, int seclen)
Definition: pbx_dundi.c:2110
Module could not be loaded properly.
Definition: module.h:102
unsigned int flags
Definition: dundi.h:226
static int dundi_ttl
Definition: pbx_dundi.c:194
int AST_OPTIONAL_API_NAME() ast_check_signature_bin(struct ast_key *key, const char *msg, int msglen, const unsigned char *dsig)
check signature of a message
Definition: res_crypto.c:416
dundi_eid eid
Definition: pbx_dundi.c:304
const char * word
Definition: cli.h:163
struct dundi_request::@440 trans
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
char inkey[80]
Definition: pbx_dundi.c:309
static struct ast_cli_entry cli_dundi[]
Definition: pbx_dundi.c:3119
char exten[AST_MAX_EXTENSION]
Definition: pbx_dundi.c:233
struct dundi_peer::permissionlist permit
int ast_db_get(const char *family, const char *key, char *value, int valuelen)
Get key value specified by family/key.
Definition: main/db.c:412
void dundi_showframe(struct dundi_hdr *fhi, int rx, struct ast_sockaddr *sin, int datalen)
Definition: dundi-parser.c:432
int ast_sched_add(struct ast_sched_context *con, int when, ast_sched_cb callback, const void *data) attribute_warn_unused_result
Adds a scheduled event.
Definition: sched.c:565
static int dundi_shutdown
Definition: pbx_dundi.c:213
char name[0]
Definition: pbx_dundi.c:218
Structure used to handle boolean flags.
Definition: utils.h:199
#define ast_clear_flag(p, flag)
Definition: utils.h:77
static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[])
Definition: pbx_dundi.c:3996
int ast_io_remove(struct io_context *ioc, int *id)
Removes an IO context.
Definition: io.c:245
Support for logging to various files, console and syslog Configuration in file logger.conf.
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
#define DUNDI_MAX_STACK
Definition: dundi-parser.h:18
#define DUNDI_IE_CALLED_NUMBER
Definition: dundi.h:184
#define DUNDI_COMMAND_PRECACHERQ
Definition: dundi.h:164
char * weightstr
Definition: pbx_dundi.c:295
const char * usage
Definition: cli.h:177
int pcmodel
Definition: pbx_dundi.c:332
#define DUNDI_COMMAND_PRECACHERP
Definition: dundi.h:165
static int dundi_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
Definition: pbx_dundi.c:4822
struct ast_eid ast_eid_default
Global EID.
Definition: options.c:93
unsigned char iv[16]
Definition: dundi.h:98
char stateprov[80]
Definition: dundi.h:238
ast_aes_encrypt_key us_ecx
Definition: pbx_dundi.c:319
#define CLI_SUCCESS
Definition: cli.h:44
char email[80]
Definition: dundi.h:242
char * q_dept
Definition: dundi-parser.h:37
struct permissionlist include
Definition: pbx_dundi.c:307
#define DUNDI_COMMAND_REGREQ
Definition: dundi.h:169
#define DUNDI_COMMAND_FINAL
Definition: dundi.h:157
dundi_eid root_eid
Definition: pbx_dundi.c:277
static void destroy_packets(struct packetlist *p)
Definition: pbx_dundi.c:2010
static void dundi_reject(struct dundi_hdr *h, struct ast_sockaddr *sin)
Definition: pbx_dundi.c:455
#define FORMAT
void * data
Definition: datastore.h:70
static void dundi_error_output(const char *data)
Definition: pbx_dundi.c:362
static int update_key(struct dundi_peer *peer)
Definition: pbx_dundi.c:1334
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:680
unsigned char txenckey[256]
Definition: pbx_dundi.c:316
static int check_request(struct dundi_request *dr)
Definition: pbx_dundi.c:3795
#define DUNDI_COMMAND_EIDRESPONSE
Definition: dundi.h:163
static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
Definition: pbx_dundi.c:1428
static void mark_peers(void)
Definition: pbx_dundi.c:4353
Standard Command Line Interface.
#define DUNDI_IE_STATE_PROV
Definition: dundi.h:202
int ast_db_del(const char *family, const char *key)
Delete entry in astdb.
Definition: main/db.c:429
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
static int dundi_helper(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *data, int flag)
Definition: pbx_dundi.c:4783
static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
Definition: pbx_dundi.c:1586
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
ast_app: A registered application
Definition: pbx_app.c:45
const char * ast_channel_name(const struct ast_channel *chan)
int dundi_eid_zero(dundi_eid *eid)
Definition: dundi-parser.c:82
static int str2tech(char *str)
Definition: pbx_dundi.c:396
struct dundi_result results[MAX_RESULTS]
Definition: pbx_dundi.c:4194
const int pos
Definition: cli.h:164
#define MAX_OPTS
Definition: pbx_dundi.c:4455
int AST_OPTIONAL_API_NAME() ast_aes_set_encrypt_key(const unsigned char *key, ast_aes_encrypt_key *ctx)
Definition: res_crypto.c:466
static void build_peer(dundi_eid *eid, struct ast_variable *v, int *globalpcmode)
Definition: pbx_dundi.c:4628
#define END_OPTIONS
static char org[80]
Definition: pbx_dundi.c:202
#define DUNDI_COMMAND_EIDQUERY
Definition: dundi.h:162
static ENTRY retval
Definition: hsearch.c:50
struct ast_sockaddr addr
Definition: pbx_dundi.c:305
void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
Definition: ael_main.c:211
static PGresult * result
Definition: cel_pgsql.c:88
static int unload_module(void)
Definition: pbx_dundi.c:5099
int ast_sockaddr_is_ipv4(const struct ast_sockaddr *addr)
Determine if the address is an IPv4 address.
Definition: netsock2.c:497
static void mark_mappings(void)
Definition: pbx_dundi.c:4363
int ast_sched_wait(struct ast_sched_context *con) attribute_warn_unused_result
Determines number of seconds until the next outstanding event to take place.
Definition: sched.c:431
dundi_eid eid
Definition: dundi.h:103
static time_t rotatetime
Definition: pbx_dundi.c:211
static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
Definition: pbx_dundi.c:899
static int ack_trans(struct dundi_transaction *trans, int iseqno)
Definition: pbx_dundi.c:2021
static int do_qualify(const void *data)
Definition: pbx_dundi.c:4557
static int socket_read(int *id, int fd, short events, void *sock)
Definition: pbx_dundi.c:2083
#define DUNDI_COMMAND_INVALID
Definition: dundi.h:166
struct ast_key *AST_OPTIONAL_API_NAME() ast_key_get(const char *kname, int ktype)
return the ast_key structure for name
Definition: res_crypto.c:141
char * q_phone
Definition: dundi-parser.h:43
struct dundi_transaction * regtrans
Definition: pbx_dundi.c:329
static struct test_val b
int ast_db_put(const char *family, const char *key, const char *value)
Store value addressed by family/key.
Definition: main/db.c:327
static char cursecret[80]
Definition: pbx_dundi.c:209
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:89
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528
#define DUNDI_COMMAND_DPDISCOVER
Definition: dundi.h:160
#define FORMAT2
struct timeval start
Definition: pbx_dundi.c:247
static void build_mapping(const char *name, const char *value)
Definition: pbx_dundi.c:4457
static char * dundi_do_lookup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2456
#define DUNDI_IE_COUNTRY
Definition: dundi.h:203
int ast_eid_is_empty(const struct ast_eid *eid)
Check if EID is empty.
Definition: main/utils.c:2847
static int dundi_ack(struct dundi_transaction *trans, int final)
Definition: pbx_dundi.c:451
static struct test_options options
static const struct ast_app_option dundi_query_opts[128]
Definition: pbx_dundi.c:4135
int dundi_parse_ies(struct dundi_ies *ies, unsigned char *data, int datalen)
Definition: dundi-parser.c:635
static char context[AST_MAX_CONTEXT]
Definition: chan_alsa.c:116
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
unsigned short dtrans
Definition: dundi.h:36
static struct ast_custom_function dundi_query_function
Definition: pbx_dundi.c:4274
unsigned char iseqno
Definition: pbx_dundi.c:262
struct dundi_transaction * parent
Definition: pbx_dundi.c:225
static struct dundi_transaction * find_transaction(struct dundi_hdr *hdr, struct ast_sockaddr *sin)
Definition: pbx_dundi.c:412
struct ast_app * pbx_findapp(const char *app)
Look up an application.
Definition: ael_main.c:165
#define ast_set_flag_nonstd(p, flag)
Definition: utils.h:176
struct ast_sockaddr addr
Definition: pbx_dundi.c:246
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
static int optimize_transactions(struct dundi_request *dr, int order)
Definition: pbx_dundi.c:3584
int dundi_ie_append_byte(struct dundi_ie_data *ied, unsigned char ie, unsigned char dat)
Definition: dundi-parser.c:615
#define MAX_PACKET_SIZE
Definition: pbx_dundi.c:155
Asterisk module definitions.
#define MAX_WEIGHT
Definition: pbx_dundi.c:157
static int dundi_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
Definition: pbx_dundi.c:4887
int AST_OPTIONAL_API_NAME() ast_aes_set_decrypt_key(const unsigned char *key, ast_aes_decrypt_key *ctx)
Definition: res_crypto.c:471
MD5 digest functions.
static char * dundi_show_entityid(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:2827
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[])
Definition: pbx_dundi.c:3700
int dundi_ie_append_int(struct dundi_ie_data *ied, unsigned char ie, unsigned int value)
Definition: dundi-parser.c:591
#define DUNDI_IE_ENCDATA
Definition: dundi.h:193
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2390
Persistant data storage (akin to *doze registry)
struct dundi_hint * hint
Definition: dundi-parser.h:29
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
ast_context: An extension context - must remain in sync with fake_context
Definition: pbx.c:284
ast_aes_decrypt_key dcx
Definition: pbx_dundi.c:253
int dundi_str_short_to_eid(dundi_eid *eid, const char *s)
Definition: dundi-parser.c:70
void ast_sched_context_destroy(struct ast_sched_context *c)
destroys a schedule context
Definition: sched.c:269
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1508
#define DUNDI_COMMAND_NULL
Definition: dundi.h:168
#define DUNDI_MODEL_OUTBOUND
Definition: pbx_dundi.c:160
const char * ast_channel_macroexten(const struct ast_channel *chan)
void AST_OPTIONAL_API_NAME() ast_aes_decrypt(const unsigned char *in, unsigned char *out, const ast_aes_decrypt_key *ctx)
Definition: res_crypto.c:481
int ast_sockaddr_is_ipv6(const struct ast_sockaddr *addr)
Determine if this is an IPv6 address.
Definition: netsock2.c:524
static struct ast_switch dundi_switch
Definition: pbx_dundi.c:4892
#define AST_KEY_PUBLIC
Definition: crypto.h:42
#define ast_test_flag_nonstd(p, flag)
Definition: utils.h:173
int ast_db_deltree(const char *family, const char *keytree)
Delete one or more entries in astdb.
Definition: main/db.c:457
char * q_email
Definition: dundi-parser.h:42
struct dundi_hdr * h
Definition: pbx_dundi.c:223
static int rescomp(const void *a, const void *b)
Definition: pbx_dundi.c:2439
void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
#define DUNDI_IE_ANSWER
Definition: dundi.h:186
int sentfullkey
Definition: pbx_dundi.c:314
static void * process_clearcache(void *ignore)
Definition: pbx_dundi.c:2228
char * dundi_flags2str(char *buf, int bufsiz, int flags)
Definition: dundi-parser.c:246
#define DUNDI_COMMAND_REGRESPONSE
Definition: dundi.h:170
int dundi_ie_append_answer(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid, unsigned char protocol, unsigned short flags, unsigned short weight, char *data)
Definition: dundi-parser.c:559
unsigned char data[0]
Definition: dundi.h:107
char called_context[AST_MAX_EXTENSION]
Definition: pbx_dundi.c:547
static int netsocket2
Definition: pbx_dundi.c:187
static char * model2str(int model)
Definition: pbx_dundi.c:2403
jack_status_t status
Definition: app_jack.c:146
struct permission::@435 list
static char * ast_sockaddr_stringify_host(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() to return an address only, suitable for a URL (with brack...
Definition: netsock2.h:331
int ast_rdlock_context(struct ast_context *con)
Read locks a given context.
Definition: pbx.c:8525
static void prune_peers(void)
Definition: pbx_dundi.c:4412
short word
int ast_sockaddr_resolve(struct ast_sockaddr **addrs, const char *str, int flags, int family)
Parses a string with an IPv4 or IPv6 address and place results into an array.
Definition: netsock2.c:280
static struct ast_custom_function dundi_result_function
Definition: pbx_dundi.c:4348
char * key
Definition: astdb.h:33
static int handle_frame(struct dundi_hdr *h, struct ast_sockaddr *sin, int datalen)
Definition: pbx_dundi.c:2042
static char dept[80]
Definition: pbx_dundi.c:201
#define AST_APP_ARG(name)
Define an application argument.
int AST_OPTIONAL_API_NAME() ast_decrypt_bin(unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key)
decrypt a message
Definition: res_crypto.c:336
#define DUNDI_COMMAND_CANCEL
Definition: dundi.h:171
unsigned char iseqno
Definition: dundi.h:37
static char stateprov[80]
Definition: pbx_dundi.c:204
static char * dundi_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: pbx_dundi.c:3050
static int dundi_xmit(struct dundi_packet *pack)
Definition: pbx_dundi.c:3169
static struct test_val a
struct io_context * io_context_create(void)
Creates a context Create a context for I/O operations Basically mallocs an IO structure and sets up s...
Definition: io.c:81
#define DUNDI_IE_TTL
Definition: dundi.h:187
static void drds_destroy(struct dundi_result_datastore *drds)
Definition: pbx_dundi.c:4199
#define max(a, b)
Definition: f2c.h:198