Asterisk - The Open Source Telephony Project  21.4.1
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 <markster@digium.com>
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 with 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;
189 static pthread_t precachethreadid = AST_PTHREADT_NULL;
190 static pthread_t clearcachethreadid = AST_PTHREADT_NULL;
191 static unsigned int tos = 0;
192 static int dundidebug = 0;
193 static int authdebug = 0;
194 static int dundi_ttl = DUNDI_DEFAULT_TTL;
195 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
196 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
197 static int global_autokilltimeout = 0;
198 static dundi_eid global_eid;
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 int outgoing_sip_tech;
212 static char pjsip_outgoing_endpoint[80];
213 static time_t rotatetime;
214 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
215 static int dundi_shutdown = 0;
216 
217 struct permission {
219  int allow;
220  char name[0];
221 };
222 
223 struct dundi_packet {
225  struct dundi_hdr *h;
226  int datalen;
227  struct dundi_transaction *parent;
228  int retransid;
229  int retrans;
230  unsigned char data[0];
231 };
232 
234  unsigned short flags;
235  char exten[AST_MAX_EXTENSION];
236 };
237 
240  char *context;
241  time_t expiration;
242  char number[0];
243 };
244 
245 struct dundi_request;
246 
248  struct ast_sockaddr addr; /*!< Other end of transaction */
249  struct timeval start; /*!< When this transaction was created */
250  dundi_eid eids[DUNDI_MAX_STACK + 1];
251  int eidcount; /*!< Number of eids in eids */
252  dundi_eid us_eid; /*!< Our EID, to them */
253  dundi_eid them_eid; /*!< Their EID, to us */
254  ast_aes_encrypt_key ecx; /*!< AES 128 Encryption context */
255  ast_aes_decrypt_key dcx; /*!< AES 128 Decryption context */
256  unsigned int flags; /*!< Has final packet been sent */
257  int ttl; /*!< Remaining TTL for queries on this one */
258  int thread; /*!< We have a calling thread */
259  int retranstimer; /*!< How long to wait before retransmissions */
260  int autokillid; /*!< ID to kill connection if answer doesn't come back fast enough */
261  int autokilltimeout; /*!< Recommended timeout for autokill */
262  unsigned short strans; /*!< Our transaction identifier */
263  unsigned short dtrans; /*!< Their transaction identifer */
264  unsigned char iseqno; /*!< Next expected received seqno */
265  unsigned char oiseqno; /*!< Last received incoming seqno */
266  unsigned char oseqno; /*!< Next transmitted seqno */
267  unsigned char aseqno; /*!< Last acknowledge seqno */
268  AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets; /*!< Packets to be retransmitted */
269  struct packetlist lasttrans; /*!< Last transmitted / ACK'd packet */
270  struct dundi_request *parent; /*!< Parent request (if there is one) */
271  AST_LIST_ENTRY(dundi_transaction) parentlist; /*!< Next with respect to the parent */
272  AST_LIST_ENTRY(dundi_transaction) all; /*!< Next with respect to all DUNDi transactions */
273 };
274 
276  char dcontext[AST_MAX_EXTENSION];
278  dundi_eid query_eid;
279  dundi_eid root_eid;
280  struct dundi_result *dr;
281  struct dundi_entity_info *dei;
282  struct dundi_hint_metadata *hmd;
283  int maxcount;
284  int respcount;
285  int expiration;
286  int cbypass;
287  int pfds[2];
288  uint32_t crc32; /*!< CRC-32 of all but root EID's in avoid list */
289  AST_LIST_HEAD_NOLOCK(, dundi_transaction) trans; /*!< Transactions */
290  AST_LIST_ENTRY(dundi_request) list;
291 };
292 
294  char dcontext[AST_MAX_EXTENSION];
295  char lcontext[AST_MAX_EXTENSION];
296  int _weight;
297  char *weightstr;
298  int options;
299  int tech;
300  int dead;
301  char dest[512];
302  AST_LIST_ENTRY(dundi_mapping) list;
303 };
304 
305 struct dundi_peer {
306  dundi_eid eid;
307  struct ast_sockaddr addr; /*!< Address of DUNDi peer */
309  struct permissionlist include;
310  dundi_eid us_eid;
311  char inkey[80];
312  char outkey[80];
313  int dead;
314  int registerid;
315  int qualifyid;
316  int sentfullkey;
317  int order;
318  unsigned char txenckey[256]; /*!< Transmitted encrypted key + sig */
319  unsigned char rxenckey[256]; /*!< Cache received encrypted key + sig */
320  uint32_t us_keycrc32; /*!< CRC-32 of our key */
321  ast_aes_encrypt_key us_ecx; /*!< Cached AES 128 Encryption context */
322  ast_aes_decrypt_key us_dcx; /*!< Cached AES 128 Decryption context */
323  uint32_t them_keycrc32; /*!< CRC-32 of our key */
324  ast_aes_encrypt_key them_ecx; /*!< Cached AES 128 Encryption context */
325  ast_aes_decrypt_key them_dcx; /*!< Cached AES 128 Decryption context */
326  time_t keyexpire; /*!< When to expire/recreate key */
327  int registerexpire;
328  int lookuptimes[DUNDI_TIMING_HISTORY];
329  char *lookups[DUNDI_TIMING_HISTORY];
330  int avgms;
331  struct dundi_transaction *regtrans; /*!< Registration transaction */
332  struct dundi_transaction *qualtrans; /*!< Qualify transaction */
333  int model; /*!< Pull model */
334  int pcmodel; /*!< Push/precache model */
335  /*! Dynamic peers register with us */
336  unsigned int dynamic:1;
337  int lastms; /*!< Last measured latency */
338  int maxms; /*!< Max permissible latency */
339  struct timeval qualtx; /*!< Time of transmit */
341 };
342 
346 static AST_LIST_HEAD_NOLOCK_STATIC(requests, dundi_request);
348 
349 /*!
350  * \brief Wildcard peer
351  *
352  * This peer is created if the [*] entry is specified in dundi.conf
353  */
354 static struct dundi_peer *any_peer;
355 
356 static int dundi_xmit(struct dundi_packet *pack);
357 
358 static void dundi_debug_output(const char *data)
359 {
360  if (dundidebug)
361  ast_verbose("%s", data);
362 }
363 
364 static void dundi_error_output(const char *data)
365 {
366  ast_log(LOG_WARNING, "%s", data);
367 }
368 
369 static int has_permission(struct permissionlist *permlist, char *cont)
370 {
371  struct permission *perm;
372  int res = 0;
373 
374  AST_LIST_TRAVERSE(permlist, perm, list) {
375  if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont))
376  res = perm->allow;
377  }
378 
379  return res;
380 }
381 
382 static char *tech2str(int tech)
383 {
384  switch(tech) {
385  case DUNDI_PROTO_NONE:
386  return "None";
387  case DUNDI_PROTO_IAX:
388  return "IAX2";
389  case DUNDI_PROTO_SIP:
390  return "SIP";
391  case DUNDI_PROTO_H323:
392  return "H323";
393  case DUNDI_PROTO_PJSIP:
394  return "PJSIP";
395  default:
396  return "Unknown";
397  }
398 }
399 
400 static int str2tech(const char *str)
401 {
402  if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
403  return DUNDI_PROTO_IAX;
404  else if (!strcasecmp(str, "SIP"))
405  return DUNDI_PROTO_SIP;
406  else if (!strcasecmp(str, "H323"))
407  return DUNDI_PROTO_H323;
408  else if (!strcasecmp(str, "PJSIP"))
409  return DUNDI_PROTO_PJSIP;
410  else
411  return -1;
412 }
413 
414 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[]);
415 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
416 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
417 
418 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct ast_sockaddr *sin)
419 {
420  struct dundi_transaction *trans;
421 
422  /* Look for an exact match first */
423  AST_LIST_TRAVERSE(&alltrans, trans, all) {
424  if (!ast_sockaddr_cmp(&trans->addr, sin) &&
425  ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
426  ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
427  if (hdr->strans) {
428  trans->dtrans = ntohs(hdr->strans) & 32767;
429  }
430  return trans;
431  }
432  }
433 
434  switch(hdr->cmdresp & 0x7f) {
439  case DUNDI_COMMAND_NULL:
441  if (!hdr->strans)
442  break;
443  /* Create new transaction */
444  if (!(trans = create_transaction(NULL)))
445  break;
446  ast_sockaddr_copy(&trans->addr, sin);
447  trans->dtrans = ntohs(hdr->strans) & 32767;
448  default:
449  break;
450  }
451 
452  return trans;
453 }
454 
455 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
456 
457 static int dundi_ack(struct dundi_transaction *trans, int final)
458 {
459  return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
460 }
461 static void dundi_reject(struct dundi_hdr *h, struct ast_sockaddr *sin)
462 {
463  struct {
464  struct dundi_packet pack;
465  struct dundi_hdr hdr;
466  } tmp;
467  struct dundi_transaction trans;
468  /* Never respond to an INVALID with another INVALID */
469  if (h->cmdresp == DUNDI_COMMAND_INVALID)
470  return;
471  memset(&tmp, 0, sizeof(tmp));
472  memset(&trans, 0, sizeof(trans));
473  ast_sockaddr_copy(&trans.addr, sin);
474  tmp.hdr.strans = h->dtrans;
475  tmp.hdr.dtrans = h->strans;
476  tmp.hdr.iseqno = h->oseqno;
477  tmp.hdr.oseqno = h->iseqno;
478  tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
479  tmp.hdr.cmdflags = 0;
480  tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
481  tmp.pack.datalen = sizeof(struct dundi_hdr);
482  tmp.pack.parent = &trans;
483  dundi_xmit(&tmp.pack);
484 }
485 
486 static int get_trans_id(void)
487 {
488  struct dundi_transaction *t;
489  int stid = (ast_random() % 32766) + 1;
490  int tid = stid;
491 
492  do {
493  AST_LIST_TRAVERSE(&alltrans, t, all) {
494  if (t->strans == tid)
495  break;
496  }
497  if (!t)
498  return tid;
499  tid = (tid % 32766) + 1;
500  } while (tid != stid);
501 
502  return 0;
503 }
504 
505 static int reset_transaction(struct dundi_transaction *trans)
506 {
507  int tid;
508  tid = get_trans_id();
509  if (tid < 1)
510  return -1;
511  trans->strans = tid;
512  trans->dtrans = 0;
513  trans->iseqno = 0;
514  trans->oiseqno = 0;
515  trans->oseqno = 0;
516  trans->aseqno = 0;
517  ast_clear_flag(trans, FLAG_FINAL);
518  return 0;
519 }
520 
521 static struct dundi_peer *find_peer(dundi_eid *eid)
522 {
523  struct dundi_peer *cur = NULL;
524 
525  if (!eid)
526  eid = &empty_eid;
527 
528  AST_LIST_TRAVERSE(&peers, cur, list) {
529  if (!ast_eid_cmp(&cur->eid,eid))
530  break;
531  }
532 
533  if (!cur && any_peer)
534  cur = any_peer;
535 
536  return cur;
537 }
538 
539 static void build_iv(unsigned char *iv)
540 {
541  /* XXX Would be nice to be more random XXX */
542  unsigned int *fluffy;
543  int x;
544  fluffy = (unsigned int *)(iv);
545  for (x=0;x<4;x++)
546  fluffy[x] = ast_random();
547 }
548 
550  dundi_eid *eids[DUNDI_MAX_STACK + 1];
551  int directs[DUNDI_MAX_STACK + 1];
552  dundi_eid reqeid;
553  char called_context[AST_MAX_EXTENSION];
554  char called_number[AST_MAX_EXTENSION];
555  struct dundi_mapping *maps;
556  int nummaps;
557  int nocache;
558  struct dundi_transaction *trans;
559  void *chal;
560  int challen;
561  int ttl;
562  char fluffy[0];
563 };
564 
565 static int get_mapping_weight(struct dundi_mapping *map, struct varshead *headp)
566 {
567  char buf[32];
568 
569  buf[0] = 0;
570  if (map->weightstr) {
571  if (headp) {
572  pbx_substitute_variables_varshead(headp, map->weightstr, buf, sizeof(buf) - 1);
573  } else {
574  pbx_substitute_variables_helper(NULL, map->weightstr, buf, sizeof(buf) - 1);
575  }
576 
577  if (sscanf(buf, "%30d", &map->_weight) != 1)
578  map->_weight = MAX_WEIGHT;
579  }
580 
581  return map->_weight;
582 }
583 
584 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)
585 {
586  struct ast_flags flags = {0};
587  int x;
588  if (!ast_strlen_zero(map->lcontext)) {
589  if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
590  ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
591  if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
592  ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
593  if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
594  ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
595  if (ast_ignore_pattern(map->lcontext, called_number))
596  ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
597 
598  /* Clearly we can't say 'don't ask' anymore if we found anything... */
599  if (ast_test_flag(&flags, AST_FLAGS_ALL))
600  ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
601 
602  if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
603  /* Skip partial answers */
604  ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
605  }
606  if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
607  struct varshead headp;
608  struct ast_var_t *newvariable;
609  ast_set_flag(&flags, map->options & 0xffff);
610  ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
611  dr[anscnt].techint = map->tech;
612  dr[anscnt].expiration = dundi_cache_time;
613  ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
614  dr[anscnt].eid = *us_eid;
615  ast_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
616  if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
618  if ((newvariable = ast_var_assign("NUMBER", called_number))) {
619  AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
620  }
621  if ((newvariable = ast_var_assign("EID", dr[anscnt].eid_str))) {
622  AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
623  }
624  if ((newvariable = ast_var_assign("SECRET", cursecret))) {
625  AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
626  }
627  if ((newvariable = ast_var_assign("IPADDR", ipaddr))) {
628  AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
629  }
630  pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
631  dr[anscnt].weight = get_mapping_weight(map, &headp);
632  while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
633  ast_var_delete(newvariable);
634  } else {
635  dr[anscnt].dest[0] = '\0';
636  dr[anscnt].weight = get_mapping_weight(map, NULL);
637  }
638  anscnt++;
639  } else {
640  /* No answers... Find the fewest number of digits from the
641  number for which we have no answer. */
642  char tmp[AST_MAX_EXTENSION + 1] = "";
643  for (x = 0; x < (sizeof(tmp) - 1); x++) {
644  tmp[x] = called_number[x];
645  if (!tmp[x])
646  break;
647  if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
648  /* Oops found something we can't match. If this is longer
649  than the running hint, we have to consider it */
650  if (strlen(tmp) > strlen(hmd->exten)) {
651  ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
652  }
653  break;
654  }
655  }
656  }
657  }
658  return anscnt;
659 }
660 
661 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
662 
663 static void *dundi_lookup_thread(void *data)
664 {
665  struct dundi_query_state *st = data;
666  struct dundi_result dr[MAX_RESULTS];
667  struct dundi_ie_data ied;
668  struct dundi_hint_metadata hmd;
669  char eid_str[20];
670  int res, x;
671  int ouranswers=0;
672  int max = 999999;
673  int expiration = dundi_cache_time;
674 
675  ast_debug(1, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
676  st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
677  memset(&ied, 0, sizeof(ied));
678  memset(&dr, 0, sizeof(dr));
679  memset(&hmd, 0, sizeof(hmd));
680  /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
682  for (x=0;x<st->nummaps;x++)
683  ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
684  if (ouranswers < 0)
685  ouranswers = 0;
686  for (x=0;x<ouranswers;x++) {
687  if (dr[x].weight < max)
688  max = dr[x].weight;
689  }
690 
691  if (max) {
692  /* If we do not have a canonical result, keep looking */
693  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);
694  if (res > 0) {
695  /* Append answer in result */
696  ouranswers += res;
697  } else {
698  if ((res < -1) && (!ouranswers))
699  dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
700  }
701  }
703  /* Truncate if "don't ask" isn't present */
704  if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
705  hmd.exten[0] = '\0';
706  if (ast_test_flag(st->trans, FLAG_DEAD)) {
707  ast_debug(1, "Our transaction went away!\n");
708  st->trans->thread = 0;
709  destroy_trans(st->trans, 0);
710  } else {
711  for (x=0;x<ouranswers;x++) {
712  /* Add answers */
713  if (dr[x].expiration && (expiration > dr[x].expiration))
714  expiration = dr[x].expiration;
715  dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
716  }
717  dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
718  dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
719  dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
720  st->trans->thread = 0;
721  }
723  ast_free(st);
724  return NULL;
725 }
726 
727 static void *dundi_precache_thread(void *data)
728 {
729  struct dundi_query_state *st = data;
730  struct dundi_ie_data ied;
731  struct dundi_hint_metadata hmd = {0};
732  char eid_str[20];
733 
734  ast_debug(1, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
735  st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
736  memset(&ied, 0, sizeof(ied));
737 
738  /* Now produce precache */
739  dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
740 
742  /* Truncate if "don't ask" isn't present */
743  if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
744  hmd.exten[0] = '\0';
745  if (ast_test_flag(st->trans, FLAG_DEAD)) {
746  ast_debug(1, "Our transaction went away!\n");
747  st->trans->thread = 0;
748  destroy_trans(st->trans, 0);
749  } else {
750  dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
751  st->trans->thread = 0;
752  }
754  ast_free(st);
755  return NULL;
756 }
757 
758 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[]);
759 
760 static void *dundi_query_thread(void *data)
761 {
762  struct dundi_query_state *st = data;
763  struct dundi_entity_info dei;
764  struct dundi_ie_data ied;
765  struct dundi_hint_metadata hmd;
766  char eid_str[20];
767  int res;
768 
769  ast_debug(1, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
770  st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
771  memset(&ied, 0, sizeof(ied));
772  memset(&dei, 0, sizeof(dei));
773  memset(&hmd, 0, sizeof(hmd));
774  if (!ast_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
775  /* Ooh, it's us! */
776  ast_debug(1, "Neat, someone look for us!\n");
777  ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
778  ast_copy_string(dei.org, org, sizeof(dei.org));
779  ast_copy_string(dei.locality, locality, sizeof(dei.locality));
780  ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
781  ast_copy_string(dei.country, country, sizeof(dei.country));
782  ast_copy_string(dei.email, email, sizeof(dei.email));
783  ast_copy_string(dei.phone, phone, sizeof(dei.phone));
784  res = 1;
785  } else {
786  /* If we do not have a canonical result, keep looking */
787  res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
788  }
790  if (ast_test_flag(st->trans, FLAG_DEAD)) {
791  ast_debug(1, "Our transaction went away!\n");
792  st->trans->thread = 0;
793  destroy_trans(st->trans, 0);
794  } else {
795  if (res) {
796  dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
797  dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
798  dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
799  dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
800  dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
801  dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
802  dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
803  if (!ast_strlen_zero(dei.ipaddr))
804  dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
805  }
806  dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
807  dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
808  st->trans->thread = 0;
809  }
811  ast_free(st);
812  return NULL;
813 }
814 
815 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
816 {
817  struct dundi_query_state *st;
818  int totallen;
819  int x;
820  int skipfirst=0;
821  char eid_str[20];
822  char *s;
823  pthread_t lookupthread;
824 
825  if (ies->eidcount > 1) {
826  /* Since it is a requirement that the first EID is the authenticating host
827  and the last EID is the root, it is permissible that the first and last EID
828  could be the same. In that case, we should go ahead copy only the "root" section
829  since we will not need it for authentication. */
830  if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
831  skipfirst = 1;
832  }
833  totallen = sizeof(struct dundi_query_state);
834  totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
835  st = ast_calloc(1, totallen);
836  if (st) {
837  ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
838  memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
839  st->trans = trans;
840  st->ttl = ies->ttl - 1;
841  if (st->ttl < 0)
842  st->ttl = 0;
843  s = st->fluffy;
844  for (x=skipfirst;ies->eids[x];x++) {
845  st->eids[x-skipfirst] = (dundi_eid *)s;
846  *st->eids[x-skipfirst] = *ies->eids[x];
847  s += sizeof(dundi_eid);
848  }
849  ast_debug(1, "Answering EID query for '%s@%s'!\n", ast_eid_to_str(eid_str, sizeof(eid_str), ies->reqeid), ies->called_context);
850 
851  trans->thread = 1;
852  if (ast_pthread_create_detached(&lookupthread, NULL, dundi_query_thread, st)) {
853  struct dundi_ie_data ied = { 0, };
854  trans->thread = 0;
855  ast_log(LOG_WARNING, "Unable to create thread!\n");
856  ast_free(st);
857  dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
858  dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
859  return -1;
860  }
861  } else {
862  struct dundi_ie_data ied = { 0, };
863  dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
864  dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
865  return -1;
866  }
867  return 0;
868 }
869 
870 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
871 {
872  int unaffected;
873  char key1[256];
874  char key2[256];
875  char eidpeer_str[20];
876  char eidroot_str[20];
877  char data[80];
878  time_t timeout;
879 
880  if (expiration < 0)
881  expiration = dundi_cache_time;
882 
883  /* Only cache hint if "don't ask" is there... */
884  if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))
885  return 0;
886 
887  unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
888 
889  dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
890  dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
891  snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08x", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
892  snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
893 
894  time(&timeout);
895  timeout += expiration;
896  snprintf(data, sizeof(data), "%ld|", (long)(timeout));
897 
898  ast_db_put("dundi/cache", key1, data);
899  ast_debug(1, "Caching hint at '%s'\n", key1);
900  ast_db_put("dundi/cache", key2, data);
901  ast_debug(1, "Caching hint at '%s'\n", key2);
902  return 0;
903 }
904 
905 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
906 {
907  int x;
908  char key1[256];
909  char key2[256];
910  char data[1024];
911  char eidpeer_str[20];
912  char eidroot_str[20];
913  time_t timeout;
914 
915  if (expiration < 1)
916  expiration = dundi_cache_time;
917 
918  /* Keep pushes a little longer, cut pulls a little short */
919  if (push)
920  expiration += 10;
921  else
922  expiration -= 10;
923  if (expiration < 1)
924  expiration = 1;
925  dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
926  dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
927  snprintf(key1, sizeof(key1), "%s/%s/%s/e%08x", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
928  snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
929  /* Build request string */
930  time(&timeout);
931  timeout += expiration;
932  snprintf(data, sizeof(data), "%ld|", (long)(timeout));
933  for (x=start;x<req->respcount;x++) {
934  /* Skip anything with an illegal pipe in it */
935  if (strchr(req->dr[x].dest, '|'))
936  continue;
937  snprintf(data + strlen(data), sizeof(data) - strlen(data), "%u/%d/%d/%s/%s|",
938  req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
939  dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
940  }
941  ast_db_put("dundi/cache", key1, data);
942  ast_db_put("dundi/cache", key2, data);
943  return 0;
944 }
945 
946 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
947 {
948  struct dundi_query_state *st;
949  int totallen;
950  int x,z;
951  struct dundi_ie_data ied;
952  char *s;
953  struct dundi_result dr2[MAX_RESULTS];
954  struct dundi_request dr;
955  struct dundi_hint_metadata hmd;
956 
957  struct dundi_mapping *cur;
958  int mapcount;
959  int skipfirst = 0;
960 
961  pthread_t lookupthread;
962 
963  memset(&dr2, 0, sizeof(dr2));
964  memset(&dr, 0, sizeof(dr));
965  memset(&hmd, 0, sizeof(hmd));
966 
967  /* Forge request structure to hold answers for cache */
969  dr.dr = dr2;
970  dr.maxcount = MAX_RESULTS;
971  dr.expiration = dundi_cache_time;
972  dr.hmd = &hmd;
973  dr.pfds[0] = dr.pfds[1] = -1;
974  trans->parent = &dr;
975  ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
976  ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
977 
978  for (x=0;x<ies->anscount;x++) {
979  if (trans->parent->respcount < trans->parent->maxcount) {
980  /* Make sure it's not already there */
981  for (z=0;z<trans->parent->respcount;z++) {
982  if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
983  !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data))
984  break;
985  }
986  if (z == trans->parent->respcount) {
987  /* Copy into parent responses */
988  trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
989  trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
990  trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
991  trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
992  if (ies->expiration > 0)
993  trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
994  else
995  trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
996  ast_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
997  sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
998  &ies->answers[x]->eid);
999  ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
1000  sizeof(trans->parent->dr[trans->parent->respcount].dest));
1001  ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
1002  sizeof(trans->parent->dr[trans->parent->respcount].tech));
1003  trans->parent->respcount++;
1004  ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1005  } else if (trans->parent->dr[z].weight > ntohs(ies->answers[x]->weight)) {
1006  /* Update weight if appropriate */
1007  trans->parent->dr[z].weight = ntohs(ies->answers[x]->weight);
1008  }
1009  } else
1010  ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
1011  trans->parent->number, trans->parent->dcontext);
1012 
1013  }
1014  /* Save all the results (if any) we had. Even if no results, still cache lookup. */
1015  cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
1016  if (ies->hint)
1017  cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
1018 
1019  totallen = sizeof(struct dundi_query_state);
1020  /* Count matching map entries */
1021  mapcount = 0;
1022  AST_LIST_TRAVERSE(&mappings, cur, list) {
1023  if (!strcasecmp(cur->dcontext, ccontext))
1024  mapcount++;
1025  }
1026 
1027  /* If no maps, return -1 immediately */
1028  if (!mapcount)
1029  return -1;
1030 
1031  if (ies->eidcount > 1) {
1032  /* Since it is a requirement that the first EID is the authenticating host
1033  and the last EID is the root, it is permissible that the first and last EID
1034  could be the same. In that case, we should go ahead copy only the "root" section
1035  since we will not need it for authentication. */
1036  if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1037  skipfirst = 1;
1038  }
1039 
1040  /* Prepare to run a query and then propagate that as necessary */
1041  totallen += mapcount * sizeof(struct dundi_mapping);
1042  totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1043  st = ast_calloc(1, totallen);
1044  if (st) {
1045  ast_copy_string(st->called_context, dr.dcontext, sizeof(st->called_context));
1046  ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1047  st->trans = trans;
1048  st->ttl = ies->ttl - 1;
1049  st->nocache = ies->cbypass;
1050  if (st->ttl < 0)
1051  st->ttl = 0;
1052  s = st->fluffy;
1053  for (x=skipfirst;ies->eids[x];x++) {
1054  st->eids[x-skipfirst] = (dundi_eid *)s;
1055  *st->eids[x-skipfirst] = *ies->eids[x];
1056  st->directs[x-skipfirst] = ies->eid_direct[x];
1057  s += sizeof(dundi_eid);
1058  }
1059  /* Append mappings */
1060  x = 0;
1061  st->maps = (struct dundi_mapping *)s;
1062  AST_LIST_TRAVERSE(&mappings, cur, list) {
1063  if (!strcasecmp(cur->dcontext, ccontext)) {
1064  if (x < mapcount) {
1065  st->maps[x] = *cur;
1066  st->maps[x].list.next = NULL;
1067  x++;
1068  }
1069  }
1070  }
1071  st->nummaps = mapcount;
1072  ast_debug(1, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
1073  trans->thread = 1;
1074  if (ast_pthread_create_detached(&lookupthread, NULL, dundi_precache_thread, st)) {
1075  trans->thread = 0;
1076  ast_log(LOG_WARNING, "Unable to create thread!\n");
1077  ast_free(st);
1078  memset(&ied, 0, sizeof(ied));
1079  dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1080  dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1081  return -1;
1082  }
1083  } else {
1084  ast_log(LOG_WARNING, "Out of memory!\n");
1085  memset(&ied, 0, sizeof(ied));
1086  dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1087  dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1088  return -1;
1089  }
1090  return 0;
1091 }
1092 
1093 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
1094 {
1095  struct dundi_query_state *st;
1096  int totallen;
1097  int x;
1098  struct dundi_ie_data ied;
1099  char *s;
1100  struct dundi_mapping *cur;
1101  int mapcount = 0;
1102  int skipfirst = 0;
1103 
1104  pthread_t lookupthread;
1105  totallen = sizeof(struct dundi_query_state);
1106  /* Count matching map entries */
1107  AST_LIST_TRAVERSE(&mappings, cur, list) {
1108  if (!strcasecmp(cur->dcontext, ccontext))
1109  mapcount++;
1110  }
1111  /* If no maps, return -1 immediately */
1112  if (!mapcount)
1113  return -1;
1114 
1115  if (ies->eidcount > 1) {
1116  /* Since it is a requirement that the first EID is the authenticating host
1117  and the last EID is the root, it is permissible that the first and last EID
1118  could be the same. In that case, we should go ahead copy only the "root" section
1119  since we will not need it for authentication. */
1120  if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1121  skipfirst = 1;
1122  }
1123 
1124  totallen += mapcount * sizeof(struct dundi_mapping);
1125  totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1126  st = ast_calloc(1, totallen);
1127  if (st) {
1128  ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
1129  ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1130  st->trans = trans;
1131  st->ttl = ies->ttl - 1;
1132  st->nocache = ies->cbypass;
1133  if (st->ttl < 0)
1134  st->ttl = 0;
1135  s = st->fluffy;
1136  for (x=skipfirst;ies->eids[x];x++) {
1137  st->eids[x-skipfirst] = (dundi_eid *)s;
1138  *st->eids[x-skipfirst] = *ies->eids[x];
1139  st->directs[x-skipfirst] = ies->eid_direct[x];
1140  s += sizeof(dundi_eid);
1141  }
1142  /* Append mappings */
1143  x = 0;
1144  st->maps = (struct dundi_mapping *)s;
1145  AST_LIST_TRAVERSE(&mappings, cur, list) {
1146  if (!strcasecmp(cur->dcontext, ccontext)) {
1147  if (x < mapcount) {
1148  st->maps[x] = *cur;
1149  st->maps[x].list.next = NULL;
1150  x++;
1151  }
1152  }
1153  }
1154  st->nummaps = mapcount;
1155  ast_debug(1, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
1156  trans->thread = 1;
1157  if (ast_pthread_create_detached(&lookupthread, NULL, dundi_lookup_thread, st)) {
1158  trans->thread = 0;
1159  ast_log(LOG_WARNING, "Unable to create thread!\n");
1160  ast_free(st);
1161  memset(&ied, 0, sizeof(ied));
1162  dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1163  dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1164  return -1;
1165  }
1166  } else {
1167  ast_log(LOG_WARNING, "Out of memory!\n");
1168  memset(&ied, 0, sizeof(ied));
1169  dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1170  dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1171  return -1;
1172  }
1173  return 0;
1174 }
1175 
1176 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
1177 {
1178  char data[1024];
1179  char *ptr, *term, *src;
1180  int tech;
1181  struct ast_flags flags;
1182  int weight;
1183  int length;
1184  int z;
1185  char fs[256];
1186 
1187  /* Build request string */
1188  if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
1189  time_t timeout;
1190  ptr = data;
1191  if (!ast_get_time_t(ptr, &timeout, 0, &length)) {
1192  int expiration = timeout - now;
1193  if (expiration > 0) {
1194  ast_debug(1, "Found cache expiring in %d seconds!\n", expiration);
1195  ptr += length + 1;
1196  while((sscanf(ptr, "%30d/%30d/%30d/%n", (int *)&(flags.flags), &weight, &tech, &length) == 3)) {
1197  ptr += length;
1198  term = strchr(ptr, '|');
1199  if (term) {
1200  *term = '\0';
1201  src = strrchr(ptr, '/');
1202  if (src) {
1203  *src = '\0';
1204  src++;
1205  } else
1206  src = "";
1207  ast_debug(1, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
1208  tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
1209  /* Make sure it's not already there */
1210  for (z=0;z<req->respcount;z++) {
1211  if ((req->dr[z].techint == tech) &&
1212  !strcmp(req->dr[z].dest, ptr))
1213  break;
1214  }
1215  if (z == req->respcount) {
1216  /* Copy into parent responses */
1217  ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
1218  req->dr[req->respcount].weight = weight;
1219  req->dr[req->respcount].techint = tech;
1220  req->dr[req->respcount].expiration = expiration;
1221  dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
1222  ast_eid_to_str(req->dr[req->respcount].eid_str,
1223  sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
1224  ast_copy_string(req->dr[req->respcount].dest, ptr,
1225  sizeof(req->dr[req->respcount].dest));
1226  ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
1227  sizeof(req->dr[req->respcount].tech));
1228  req->respcount++;
1229  ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK);
1230  } else if (req->dr[z].weight > weight)
1231  req->dr[z].weight = weight;
1232  ptr = term + 1;
1233  }
1234  }
1235  /* We found *something* cached */
1236  if (expiration < *lowexpiration)
1237  *lowexpiration = expiration;
1238  return 1;
1239  } else
1240  ast_db_del("dundi/cache", key);
1241  } else
1242  ast_db_del("dundi/cache", key);
1243  }
1244 
1245  return 0;
1246 }
1247 
1248 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, uint32_t crc, int *lowexpiration)
1249 {
1250  char eid_str[20];
1251  char eidroot_str[20];
1252  time_t now;
1253  int res=0;
1254  int res2=0;
1255  char eid_str_full[20];
1256  char tmp[256]="";
1257  /* Enough space for largest value that can be stored in key. */
1258  char key[sizeof(eid_str) + sizeof(tmp) + sizeof(req->dcontext) + sizeof(eidroot_str) + sizeof("hint////r")];
1259  int x;
1260 
1261  time(&now);
1262  dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
1263  dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
1264  ast_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
1265  snprintf(key, sizeof(key), "%s/%s/%s/e%08x", eid_str, req->number, req->dcontext, crc);
1266  res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1267  snprintf(key, sizeof(key), "%s/%s/%s/e%08x", eid_str, req->number, req->dcontext, (unsigned)0);
1268  res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1269  snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1270  res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1271  x = 0;
1272  if (!req->respcount) {
1273  while(!res2) {
1274  /* Look and see if we have a hint that would preclude us from looking at this
1275  peer for this number. */
1276  if (!(tmp[x] = req->number[x]))
1277  break;
1278  x++;
1279  /* Check for hints */
1280  snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08x", eid_str, tmp, req->dcontext, crc);
1281  res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1282  snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08x", eid_str, tmp, req->dcontext, (unsigned)0);
1283  res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1284  snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1285  res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1286  if (res2) {
1287  if (strlen(tmp) > strlen(req->hmd->exten)) {
1288  /* Update meta data if appropriate */
1289  ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
1290  }
1291  }
1292  }
1293  res |= res2;
1294  }
1295 
1296  return res;
1297 }
1298 
1299 static void qualify_peer(struct dundi_peer *peer, int schedonly);
1300 
1301 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
1302 {
1303  if (ast_sockaddr_isnull(&trans->addr)) {
1304  ast_sockaddr_copy(&trans->addr, &p->addr);
1305  }
1306  trans->us_eid = p->us_eid;
1307  trans->them_eid = p->eid;
1308  /* Enable encryption if appropriate */
1309  if (!ast_strlen_zero(p->inkey))
1310  ast_set_flag(trans, FLAG_ENCRYPT);
1311  if (p->maxms) {
1312  trans->autokilltimeout = p->maxms;
1313  trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1314  if (p->lastms > 1) {
1315  trans->retranstimer = p->lastms * 2;
1316  /* Keep it from being silly */
1317  if (trans->retranstimer < 150)
1318  trans->retranstimer = 150;
1319  }
1320  if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
1321  trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1322  } else
1323  trans->autokilltimeout = global_autokilltimeout;
1324 }
1325 
1326 /*! \note Called with the peers list already locked */
1327 static int do_register_expire(const void *data)
1328 {
1329  struct dundi_peer *peer = (struct dundi_peer *)data;
1330  char eid_str[20];
1331 
1332  ast_debug(1, "Register expired for '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1333  ast_db_del("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid));
1334  peer->registerexpire = -1;
1335  peer->lastms = 0;
1336  ast_sockaddr_setnull(&peer->addr);
1337  return 0;
1338 }
1339 
1340 static int update_key(struct dundi_peer *peer)
1341 {
1342  unsigned char key[16];
1343  struct ast_key *ekey, *skey;
1344  char eid_str[20];
1345  int res;
1346  if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1347  build_iv(key);
1348  ast_aes_set_encrypt_key(key, &peer->us_ecx);
1349  ast_aes_set_decrypt_key(key, &peer->us_dcx);
1350  ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1351  if (!ekey) {
1352  ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1353  peer->inkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1354  return -1;
1355  }
1356  skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1357  if (!skey) {
1358  ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1359  peer->outkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1360  return -1;
1361  }
1362  if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1363  ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1364  return -1;
1365  }
1366  if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
1367  ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1368  return -1;
1369  }
1370  peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1371  peer->sentfullkey = 0;
1372  /* Looks good */
1373  time(&peer->keyexpire);
1374  peer->keyexpire += dundi_key_ttl;
1375  }
1376  return 0;
1377 }
1378 
1379 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_encrypt_key *ecx)
1380 {
1381  unsigned char curblock[16];
1382  int x;
1383  memcpy(curblock, iv, sizeof(curblock));
1384  while(len > 0) {
1385  for (x=0;x<16;x++)
1386  curblock[x] ^= src[x];
1387  ast_aes_encrypt(curblock, dst, ecx);
1388  memcpy(curblock, dst, sizeof(curblock));
1389  dst += 16;
1390  src += 16;
1391  len -= 16;
1392  }
1393  return 0;
1394 }
1395 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_decrypt_key *dcx)
1396 {
1397  unsigned char lastblock[16];
1398  int x;
1399  memcpy(lastblock, iv, sizeof(lastblock));
1400  while(len > 0) {
1401  ast_aes_decrypt(src, dst, dcx);
1402  for (x=0;x<16;x++)
1403  dst[x] ^= lastblock[x];
1404  memcpy(lastblock, src, sizeof(lastblock));
1405  dst += 16;
1406  src += 16;
1407  len -= 16;
1408  }
1409  return 0;
1410 }
1411 
1412 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)
1413 {
1414  int space = *dstlen;
1415  unsigned long bytes;
1416  struct dundi_hdr *h;
1417  unsigned char *decrypt_space;
1418  decrypt_space = ast_alloca(srclen);
1419  decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1420  /* Setup header */
1421  h = (struct dundi_hdr *)dst;
1422  *h = *ohdr;
1423  bytes = space - 6;
1424  if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1425  ast_debug(1, "Ouch, uncompress failed :(\n");
1426  return NULL;
1427  }
1428  /* Update length */
1429  *dstlen = bytes + 6;
1430  /* Return new header */
1431  return h;
1432 }
1433 
1434 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1435 {
1436  unsigned char *compress_space;
1437  int len;
1438  int res;
1439  unsigned long bytes;
1440  struct dundi_ie_data ied;
1441  struct dundi_peer *peer;
1442  unsigned char iv[16];
1443  len = pack->datalen + pack->datalen / 100 + 42;
1444  compress_space = ast_alloca(len);
1445  memset(compress_space, 0, len);
1446  /* We care about everthing save the first 6 bytes of header */
1447  bytes = len;
1448  res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1449  if (res != Z_OK) {
1450  ast_debug(1, "Ouch, compression failed!\n");
1451  return -1;
1452  }
1453  memset(&ied, 0, sizeof(ied));
1454  /* Say who we are */
1455  if (!pack->h->iseqno && !pack->h->oseqno) {
1456  /* Need the key in the first copy */
1457  if (!(peer = find_peer(&trans->them_eid)))
1458  return -1;
1459  if (update_key(peer))
1460  return -1;
1461  if (!peer->sentfullkey)
1462  ast_set_flag(trans, FLAG_SENDFULLKEY);
1463  /* Append key data */
1464  dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1465  if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
1466  dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1467  dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1468  } else {
1469  dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
1470  }
1471  /* Setup contexts */
1472  trans->ecx = peer->us_ecx;
1473  trans->dcx = peer->us_dcx;
1474 
1475  /* We've sent the full key */
1476  peer->sentfullkey = 1;
1477  }
1478  /* Build initialization vector */
1479  build_iv(iv);
1480  /* Add the field, rounded up to 16 bytes */
1481  dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1482  /* Copy the data */
1483  if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1484  ast_log(LOG_NOTICE, "Final packet too large!\n");
1485  return -1;
1486  }
1487  encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1488  ied.pos += ((bytes + 15) / 16) * 16;
1489  /* Reconstruct header */
1490  pack->datalen = sizeof(struct dundi_hdr);
1491  pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
1492  pack->h->cmdflags = 0;
1493  memcpy(pack->h->ies, ied.buf, ied.pos);
1494  pack->datalen += ied.pos;
1495  return 0;
1496 }
1497 
1498 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, uint32_t keycrc32)
1499 {
1500  unsigned char dst[128];
1501  int res;
1502  struct ast_key *key, *skey;
1503  char eid_str[20];
1504  ast_debug(1, "Expected '%08x' got '%08x'\n", peer->them_keycrc32, keycrc32);
1505  if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1506  /* A match */
1507  return 1;
1508  } else if (!newkey || !newsig)
1509  return 0;
1510  if (!memcmp(peer->rxenckey, newkey, 128) &&
1511  !memcmp(peer->rxenckey + 128, newsig, 128)) {
1512  /* By definition, a match */
1513  return 1;
1514  }
1515  /* Decrypt key */
1516  key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1517  if (!key) {
1518  ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1519  peer->outkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1520  return -1;
1521  }
1522 
1523  skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1524  if (!skey) {
1525  ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1526  peer->inkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1527  return -1;
1528  }
1529 
1530  /* First check signature */
1531  res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
1532  if (res)
1533  return 0;
1534 
1535  res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1536  if (res != 16) {
1537  if (res >= 0)
1538  ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1539  return 0;
1540  }
1541  /* Decrypted, passes signature */
1542  ast_debug(1, "Wow, new key combo passed signature and decrypt!\n");
1543  memcpy(peer->rxenckey, newkey, 128);
1544  memcpy(peer->rxenckey + 128, newsig, 128);
1545  peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1546  ast_aes_set_decrypt_key(dst, &peer->them_dcx);
1547  ast_aes_set_encrypt_key(dst, &peer->them_ecx);
1548  return 1;
1549 }
1550 
1551 static void deep_copy_peer(struct dundi_peer *peer_dst, const struct dundi_peer *peer_src)
1552 {
1553  struct permission *cur, *perm;
1554 
1555  *peer_dst = *peer_src;
1556  AST_LIST_NEXT(peer_dst, list) = NULL;
1557 
1558  /* Scheduled items cannot go with the copy */
1559  peer_dst->registerid = -1;
1560  peer_dst->qualifyid = -1;
1561  peer_dst->registerexpire = -1;
1562 
1563  /* Transactions and lookup history cannot go with the copy either */
1564  peer_dst->regtrans = NULL;
1565  peer_dst->qualtrans = NULL;
1566  memset(&peer_dst->lookups, 0, sizeof(peer_dst->lookups));
1567 
1568  memset(&peer_dst->permit, 0, sizeof(peer_dst->permit));
1569  memset(&peer_dst->include, 0, sizeof(peer_dst->permit));
1570 
1571  AST_LIST_TRAVERSE(&peer_src->permit, cur, list) {
1572  if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1573  continue;
1574 
1575  perm->allow = cur->allow;
1576  strcpy(perm->name, cur->name);
1577 
1578  AST_LIST_INSERT_HEAD(&peer_dst->permit, perm, list);
1579  }
1580 
1581  AST_LIST_TRAVERSE(&peer_src->include, cur, list) {
1582  if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1583  continue;
1584 
1585  perm->allow = cur->allow;
1586  strcpy(perm->name, cur->name);
1587 
1588  AST_LIST_INSERT_HEAD(&peer_dst->include, perm, list);
1589  }
1590 }
1591 
1592 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1593 {
1594  /* Handle canonical command / response */
1595  int final = hdr->cmdresp & 0x80;
1596  int cmd = hdr->cmdresp & 0x7f;
1597  int x,y,z;
1598  int resp;
1599  int res;
1600  int authpass=0;
1601  unsigned char *bufcpy;
1602 #ifdef LOW_MEMORY
1603  struct dundi_ie_data *ied = ast_calloc(1, sizeof(*ied));
1604 #else
1605  struct dundi_ie_data _ied = {
1606  .pos = 0,
1607  };
1608  struct dundi_ie_data *ied = &_ied;
1609 #endif
1610  struct dundi_ies ies = {
1611  .eidcount = 0,
1612  };
1613  struct dundi_peer *peer = NULL;
1614  char eid_str[20];
1615  char eid_str2[20];
1616  int retval = -1;
1617 
1618  if (!ied) {
1619  return -1;
1620  }
1621 
1622  if (datalen) {
1623  bufcpy = ast_alloca(datalen);
1624  /* Make a copy for parsing */
1625  memcpy(bufcpy, hdr->ies, datalen);
1626  ast_debug(1, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1627  if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1628  ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1629  goto return_cleanup;
1630  }
1631  }
1632  switch(cmd) {
1636  if (cmd == DUNDI_COMMAND_EIDQUERY)
1638  else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1639  resp = DUNDI_COMMAND_PRECACHERP;
1640  else
1641  resp = DUNDI_COMMAND_DPRESPONSE;
1642  /* A dialplan or entity discover -- qualify by highest level entity */
1643  peer = find_peer(ies.eids[0]);
1644  if (!peer) {
1645  dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1646  dundi_send(trans, resp, 0, 1, ied);
1647  } else {
1648  int hasauth = 0;
1649  trans->us_eid = peer->us_eid;
1650  if (strlen(peer->inkey)) {
1651  hasauth = encrypted;
1652  } else
1653  hasauth = 1;
1654  if (hasauth) {
1655  /* Okay we're authentiated and all, now we check if they're authorized */
1656  if (!ies.called_context)
1657  ies.called_context = "e164";
1658  if (cmd == DUNDI_COMMAND_EIDQUERY) {
1659  res = dundi_answer_entity(trans, &ies, ies.called_context);
1660  } else {
1661  if (ast_strlen_zero(ies.called_number)) {
1662  /* They're not permitted to access that context */
1663  dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1664  dundi_send(trans, resp, 0, 1, ied);
1665  } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
1666  (peer->model & DUNDI_MODEL_INBOUND) &&
1667  has_permission(&peer->permit, ies.called_context)) {
1668  res = dundi_answer_query(trans, &ies, ies.called_context);
1669  if (res < 0) {
1670  /* There is no such dundi context */
1671  dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1672  dundi_send(trans, resp, 0, 1, ied);
1673  }
1674  } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
1675  (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
1676  has_permission(&peer->include, ies.called_context)) {
1677  res = dundi_prop_precache(trans, &ies, ies.called_context);
1678  if (res < 0) {
1679  /* There is no such dundi context */
1680  dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1681  dundi_send(trans, resp, 0, 1, ied);
1682  }
1683  } else {
1684  /* They're not permitted to access that context */
1685  dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1686  dundi_send(trans, resp, 0, 1, ied);
1687  }
1688  }
1689  } else {
1690  /* They're not permitted to access that context */
1691  dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1692  dundi_send(trans, resp, 0, 1, ied);
1693  }
1694  }
1695  break;
1696  case DUNDI_COMMAND_REGREQ:
1697  /* A register request -- should only have one entity */
1698  peer = find_peer(ies.eids[0]);
1699 
1700  /* if the peer is not found and we have a valid 'any_peer' setting */
1701  if (any_peer && peer == any_peer) {
1702  /* copy any_peer into a new peer object */
1703  peer = ast_calloc(1, sizeof(*peer));
1704  if (peer) {
1705  deep_copy_peer(peer, any_peer);
1706 
1707  /* set EID to remote EID */
1708  peer->eid = *ies.eids[0];
1709 
1710  AST_LIST_LOCK(&peers);
1711  AST_LIST_INSERT_HEAD(&peers, peer, list);
1713  }
1714  }
1715 
1716  if (!peer || !peer->dynamic) {
1717  dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1718  dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
1719  } else {
1720  int hasauth = 0;
1721  trans->us_eid = peer->us_eid;
1722  if (!ast_strlen_zero(peer->inkey)) {
1723  hasauth = encrypted;
1724  } else
1725  hasauth = 1;
1726  if (hasauth) {
1727  int expire = default_expiration;
1728  char data[256];
1729  int needqual = 0;
1730  AST_SCHED_DEL(sched, peer->registerexpire);
1731  peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1732  snprintf(data, sizeof(data), "%s:%d", ast_sockaddr_stringify(&trans->addr), expire);
1733  ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1734  if (ast_sockaddr_cmp(&peer->addr, &trans->addr)) {
1735  ast_verb(3, "Registered DUNDi peer '%s' at '%s'\n",
1736  ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
1737  ast_sockaddr_stringify(&trans->addr));
1738  needqual = 1;
1739  }
1740 
1741  ast_sockaddr_copy(&peer->addr, &trans->addr);
1742  dundi_ie_append_short(ied, DUNDI_IE_EXPIRATION, default_expiration);
1743  dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
1744  if (needqual)
1745  qualify_peer(peer, 1);
1746  }
1747  }
1748  break;
1750  /* A dialplan response, lets see what we got... */
1751  if (ies.cause < 1) {
1752  /* Success of some sort */
1753  ast_debug(1, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1754  if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1755  authpass = encrypted;
1756  } else
1757  authpass = 1;
1758  if (authpass) {
1759  /* Pass back up answers */
1760  if (trans->parent && trans->parent->dr) {
1761  y = trans->parent->respcount;
1762  for (x=0;x<ies.anscount;x++) {
1763  if (trans->parent->respcount < trans->parent->maxcount) {
1764  /* Make sure it's not already there */
1765  for (z=0;z<trans->parent->respcount;z++) {
1766  if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1767  !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
1768  break;
1769  }
1770  if (z == trans->parent->respcount) {
1771  /* Copy into parent responses */
1772  trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1773  trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1774  trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1775  trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1776  if (ies.expiration > 0)
1777  trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1778  else
1779  trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
1780  ast_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
1781  sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1782  &ies.answers[x]->eid);
1783  ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1784  sizeof(trans->parent->dr[trans->parent->respcount].dest));
1785  ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1786  sizeof(trans->parent->dr[trans->parent->respcount].tech));
1787  trans->parent->respcount++;
1788  ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1789  } else if (trans->parent->dr[z].weight > ntohs(ies.answers[x]->weight)) {
1790  /* Update weight if appropriate */
1791  trans->parent->dr[z].weight = ntohs(ies.answers[x]->weight);
1792  }
1793  } else
1794  ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1795  trans->parent->number, trans->parent->dcontext);
1796  }
1797  /* Save all the results (if any) we had. Even if no results, still cache lookup. Let
1798  the cache know if this request was unaffected by our entity list. */
1799  cache_save(&trans->them_eid, trans->parent, y,
1800  ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1801  if (ies.hint) {
1802  cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1803  if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1804  ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1805  if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
1806  if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1807  ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
1808  sizeof(trans->parent->hmd->exten));
1809  }
1810  } else {
1811  ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1812  }
1813  }
1814  if (ies.expiration > 0) {
1815  if (trans->parent->expiration > ies.expiration) {
1816  trans->parent->expiration = ies.expiration;
1817  }
1818  }
1819  }
1820  /* Close connection if not final */
1821  if (!final)
1822  dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1823  }
1824 
1825  } else {
1826  /* Auth failure, check for data */
1827  if (!final) {
1828  /* Cancel if they didn't already */
1829  dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1830  }
1831  }
1832  break;
1834  /* A dialplan response, lets see what we got... */
1835  if (ies.cause < 1) {
1836  /* Success of some sort */
1837  ast_debug(1, "Looks like success of some sort (%d)\n", ies.cause);
1838  if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1839  authpass = encrypted;
1840  } else
1841  authpass = 1;
1842  if (authpass) {
1843  /* Pass back up answers */
1844  if (trans->parent && trans->parent->dei && ies.q_org) {
1845  if (!trans->parent->respcount) {
1846  trans->parent->respcount++;
1847  if (ies.q_dept)
1848  ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1849  if (ies.q_org)
1850  ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1851  if (ies.q_locality)
1852  ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1853  if (ies.q_stateprov)
1854  ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1855  if (ies.q_country)
1856  ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1857  if (ies.q_email)
1858  ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1859  if (ies.q_phone)
1860  ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1861  if (ies.q_ipaddr)
1862  ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1863  if (!ast_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1864  /* If it's them, update our address */
1865  ast_copy_string(trans->parent->dei->ipaddr, ast_sockaddr_stringify_addr(&trans->addr), sizeof(trans->parent->dei->ipaddr));
1866  }
1867  }
1868  if (ies.hint) {
1869  if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1870  ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1871  }
1872  }
1873  /* Close connection if not final */
1874  if (!final)
1875  dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1876  }
1877 
1878  } else {
1879  /* Auth failure, check for data */
1880  if (!final) {
1881  /* Cancel if they didn't already */
1882  dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1883  }
1884  }
1885  break;
1887  /* A dialplan response, lets see what we got... */
1888  if (ies.cause < 1) {
1889  int hasauth;
1890  /* Success of some sort */
1891  if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1892  hasauth = encrypted;
1893  } else
1894  hasauth = 1;
1895 
1896  if (!hasauth) {
1897  ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1898  if (!final) {
1899  dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1900  dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, ied);
1901  }
1902  } else {
1903  ast_debug(1, "Yay, we've registered as '%s' to '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &trans->us_eid),
1904  ast_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1905  /* Close connection if not final */
1906  if (!final)
1907  dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1908  }
1909  } else {
1910  /* Auth failure, cancel if they didn't for some reason */
1911  if (!final) {
1912  dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1913  }
1914  }
1915  break;
1916  case DUNDI_COMMAND_INVALID:
1917  case DUNDI_COMMAND_NULL:
1919  /* Do nothing special */
1920  if (!final)
1921  dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1922  break;
1923  case DUNDI_COMMAND_ENCREJ:
1924  if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
1925  /* No really, it's over at this point */
1926  if (!final)
1927  dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1928  } else {
1929  /* Send with full key */
1930  ast_set_flag(trans, FLAG_SENDFULLKEY);
1931  if (final) {
1932  /* Ooops, we got a final message, start by sending ACK... */
1933  dundi_ack(trans, hdr->cmdresp & 0x80);
1934  trans->aseqno = trans->iseqno;
1935  /* Now, we gotta create a new transaction */
1936  if (!reset_transaction(trans)) {
1937  /* Make sure handle_frame doesn't destroy us */
1938  hdr->cmdresp &= 0x7f;
1939  /* Parse the message we transmitted */
1940  memset(&ies, 0, sizeof(ies));
1941  dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
1942  /* Reconstruct outgoing encrypted packet */
1943  memset(ied, 0, sizeof(*ied));
1944  dundi_ie_append_eid(ied, DUNDI_IE_EID, &trans->us_eid);
1945  dundi_ie_append_raw(ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1946  dundi_ie_append_raw(ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1947  if (ies.encblock)
1948  dundi_ie_append_encdata(ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
1949  dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, ied);
1950  peer->sentfullkey = 1;
1951  }
1952  }
1953  }
1954  break;
1955  case DUNDI_COMMAND_ENCRYPT:
1956  if (!encrypted) {
1957  /* No nested encryption! */
1958  if ((trans->iseqno == 1) && !trans->oseqno) {
1959  if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
1960  ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1961  (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1962  if (!final) {
1963  dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1964  }
1965  break;
1966  }
1967  apply_peer(trans, peer);
1968  /* Key passed, use new contexts for this session */
1969  trans->ecx = peer->them_ecx;
1970  trans->dcx = peer->them_dcx;
1971  }
1972  if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1973  struct dundi_hdr *dhdr;
1974  unsigned char decoded[MAX_PACKET_SIZE];
1975  int ddatalen;
1976  ddatalen = sizeof(decoded);
1977  dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1978  if (dhdr) {
1979  /* Handle decrypted response */
1980  if (dundidebug)
1981  dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1982  handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1983  /* Carry back final flag */
1984  hdr->cmdresp |= dhdr->cmdresp & 0x80;
1985  break;
1986  } else {
1987  ast_debug(1, "Ouch, decrypt failed :(\n");
1988  }
1989  }
1990  }
1991  if (!final) {
1992  /* Turn off encryption */
1993  ast_clear_flag(trans, FLAG_ENCRYPT);
1994  dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1995  }
1996  break;
1997  default:
1998  /* Send unknown command if we don't know it, with final flag IFF it's the
1999  first command in the dialog and only if we haven't received final notification */
2000  if (!final) {
2001  dundi_ie_append_byte(ied, DUNDI_IE_UNKNOWN, cmd);
2002  dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, ied);
2003  }
2004  }
2005 
2006  retval = 0;
2007 
2008 return_cleanup:
2009 #ifdef LOW_MEMORY
2010  ast_free(ied);
2011 #endif
2012  return retval;
2013 }
2014 
2015 static void destroy_packet(struct dundi_packet *pack, int needfree);
2016 static void destroy_packets(struct packetlist *p)
2017 {
2018  struct dundi_packet *pack;
2019 
2020  while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
2021  AST_SCHED_DEL(sched, pack->retransid);
2022  ast_free(pack);
2023  }
2024 }
2025 
2026 
2027 static int ack_trans(struct dundi_transaction *trans, int iseqno)
2028 {
2029  struct dundi_packet *pack;
2030 
2031  /* Ack transmitted packet corresponding to iseqno */
2032  AST_LIST_TRAVERSE(&trans->packets, pack, list) {
2033  if ((pack->h->oseqno + 1) % 255 == iseqno) {
2034  destroy_packet(pack, 0);
2035  if (!AST_LIST_EMPTY(&trans->lasttrans)) {
2036  ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
2037  destroy_packets(&trans->lasttrans);
2038  }
2039  AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
2040  AST_SCHED_DEL(sched, trans->autokillid);
2041  return 1;
2042  }
2043  }
2044 
2045  return 0;
2046 }
2047 
2048 static int handle_frame(struct dundi_hdr *h, struct ast_sockaddr *sin, int datalen)
2049 {
2050  struct dundi_transaction *trans;
2051  trans = find_transaction(h, sin);
2052  if (!trans) {
2053  dundi_reject(h, sin);
2054  return 0;
2055  }
2056  /* Got a transaction, see where this header fits in */
2057  if (h->oseqno == trans->iseqno) {
2058  /* Just what we were looking for... Anything but ack increments iseqno */
2059  if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
2060  /* If final, we're done */
2061  destroy_trans(trans, 0);
2062  return 0;
2063  }
2064  if (h->cmdresp != DUNDI_COMMAND_ACK) {
2065  trans->oiseqno = trans->iseqno;
2066  trans->iseqno++;
2067  handle_command_response(trans, h, datalen, 0);
2068  }
2069  if (trans->aseqno != trans->iseqno) {
2070  dundi_ack(trans, h->cmdresp & 0x80);
2071  trans->aseqno = trans->iseqno;
2072  }
2073  /* Delete any saved last transmissions */
2074  destroy_packets(&trans->lasttrans);
2075  if (h->cmdresp & 0x80) {
2076  /* Final -- destroy now */
2077  destroy_trans(trans, 0);
2078  }
2079  } else if (h->oseqno == trans->oiseqno) {
2080  /* Last incoming sequence number -- send ACK without processing */
2081  dundi_ack(trans, 0);
2082  } else {
2083  /* Out of window -- simply drop */
2084  ast_debug(1, "Dropping packet out of window!\n");
2085  }
2086  return 0;
2087 }
2088 
2089 static int socket_read(int *id, int fd, short events, void *sock)
2090 {
2091  struct ast_sockaddr sin;
2092  int res;
2093  struct dundi_hdr *h;
2094  char buf[MAX_PACKET_SIZE];
2095 
2096  res = ast_recvfrom(*((int *)sock), buf, sizeof(buf), 0, &sin);
2097  if (res < 0) {
2098  if (errno != ECONNREFUSED)
2099  ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
2100  return 1;
2101  }
2102  if (res < sizeof(struct dundi_hdr)) {
2103  ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
2104  return 1;
2105  }
2106  buf[res] = '\0';
2107  h = (struct dundi_hdr *) buf;
2108  if (dundidebug)
2109  dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
2110  AST_LIST_LOCK(&peers);
2111  handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
2113  return 1;
2114 }
2115 
2116 static void build_secret(char *secret, int seclen)
2117 {
2118  unsigned char tmp[16];
2119  char *s;
2120  build_iv(tmp);
2121  secret[0] = '\0';
2122  ast_base64encode(secret, tmp, sizeof(tmp), seclen);
2123  /* Eliminate potential bad characters */
2124  while((s = strchr(secret, ';'))) *s = '+';
2125  while((s = strchr(secret, '/'))) *s = '+';
2126  while((s = strchr(secret, ':'))) *s = '+';
2127  while((s = strchr(secret, '@'))) *s = '+';
2128 }
2129 
2130 
2131 static void save_secret(const char *newkey, const char *oldkey)
2132 {
2133  char tmp[350];
2134  if (oldkey)
2135  snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2136  else
2137  snprintf(tmp, sizeof(tmp), "%s", newkey);
2138  rotatetime = time(NULL) + DUNDI_SECRET_TIME;
2139  ast_db_put(secretpath, "secret", tmp);
2140  snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
2141  ast_db_put(secretpath, "secretexpiry", tmp);
2142 }
2143 
2144 static void load_password(void)
2145 {
2146  char *current=NULL;
2147  char *last=NULL;
2148  char tmp[256];
2149  time_t expired;
2150 
2151  ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2152  if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
2153  ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2154  current = strchr(tmp, ';');
2155  if (!current)
2156  current = tmp;
2157  else {
2158  *current = '\0';
2159  current++;
2160  };
2161  if ((time(NULL) - expired) < 0) {
2162  if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2163  expired = time(NULL) + DUNDI_SECRET_TIME;
2164  } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2165  last = current;
2166  current = NULL;
2167  } else {
2168  last = NULL;
2169  current = NULL;
2170  }
2171  }
2172  if (current) {
2173  /* Current key is still valid, just setup rotation properly */
2174  ast_copy_string(cursecret, current, sizeof(cursecret));
2175  rotatetime = expired;
2176  } else {
2177  /* Current key is out of date, rotate or eliminate all together */
2178  build_secret(cursecret, sizeof(cursecret));
2179  save_secret(cursecret, last);
2180  }
2181 }
2182 
2183 static void check_password(void)
2184 {
2185  char oldsecret[80];
2186  time_t now;
2187 
2188  time(&now);
2189 #if 0
2190  printf("%ld/%ld\n", now, rotatetime);
2191 #endif
2192  if ((now - rotatetime) >= 0) {
2193  /* Time to rotate keys */
2194  ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
2195  build_secret(cursecret, sizeof(cursecret));
2196  save_secret(cursecret, oldsecret);
2197  }
2198 }
2199 
2200 static void *network_thread(void *ignore)
2201 {
2202  /* Our job is simple: Send queued messages, retrying if necessary. Read frames
2203  from the network, and queue them for delivery to the channels */
2204  int res;
2205  /* Establish I/O callback for socket read */
2206  int *socket_read_id = ast_io_add(io, netsocket, socket_read, AST_IO_IN, &netsocket);
2207  int *socket_read_id2 = NULL;
2208  if (netsocket2 >= 0) {
2209  socket_read_id2 = ast_io_add(io, netsocket2, socket_read, AST_IO_IN, &netsocket2);
2210  }
2211 
2212  while (!dundi_shutdown) {
2213  res = ast_sched_wait(sched);
2214  if ((res > 1000) || (res < 0))
2215  res = 1000;
2216  res = ast_io_wait(io, res);
2217  if (res >= 0) {
2218  AST_LIST_LOCK(&peers);
2219  ast_sched_runq(sched);
2221  }
2222  check_password();
2223  }
2224 
2225  ast_io_remove(io, socket_read_id);
2226 
2227  if (socket_read_id2) {
2228  ast_io_remove(io, socket_read_id2);
2229  }
2230 
2231  return NULL;
2232 }
2233 
2234 static void *process_clearcache(void *ignore)
2235 {
2236  struct ast_db_entry *db_entry, *db_tree;
2237  int striplen = sizeof("/dundi/cache");
2238  time_t now;
2239 
2240  while (!dundi_shutdown) {
2241  pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
2242 
2243  time(&now);
2244 
2245  db_entry = db_tree = ast_db_gettree("dundi/cache", NULL);
2246  for (; db_entry; db_entry = db_entry->next) {
2247  time_t expiry;
2248 
2249  if (!ast_get_time_t(db_entry->data, &expiry, 0, NULL)) {
2250  if (expiry < now) {
2251  ast_debug(1, "clearing expired DUNDI cache entry: %s\n", db_entry->key);
2252  ast_db_del("dundi/cache", db_entry->key + striplen);
2253  }
2254  }
2255  }
2256  ast_db_freetree(db_tree);
2257 
2258  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
2259  pthread_testcancel();
2260  sleep(60);
2261  pthread_testcancel();
2262  }
2263 
2264  return NULL;
2265 }
2266 
2267 static void *process_precache(void *ign)
2268 {
2269  struct dundi_precache_queue *qe;
2270  time_t now;
2271  char context[256];
2272  char number[256];
2273  int run;
2274 
2275  while (!dundi_shutdown) {
2276  time(&now);
2277  run = 0;
2278  AST_LIST_LOCK(&pcq);
2279  if ((qe = AST_LIST_FIRST(&pcq))) {
2280  if (!qe->expiration) {
2281  /* Gone... Remove... */
2282  AST_LIST_REMOVE_HEAD(&pcq, list);
2283  ast_free(qe);
2284  } else if (qe->expiration < now) {
2285  /* Process this entry */
2286  qe->expiration = 0;
2287  ast_copy_string(context, qe->context, sizeof(context));
2288  ast_copy_string(number, qe->number, sizeof(number));
2289  run = 1;
2290  }
2291  }
2292  AST_LIST_UNLOCK(&pcq);
2293  if (run) {
2294  dundi_precache(context, number);
2295  } else
2296  sleep(1);
2297  }
2298 
2299  return NULL;
2300 }
2301 
2302 static int start_network_thread(void)
2303 {
2304  ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
2305  ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL);
2306  ast_pthread_create_background(&clearcachethreadid, NULL, process_clearcache, NULL);
2307  return 0;
2308 }
2309 
2310 static char *dundi_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2311 {
2312  switch (cmd) {
2313  case CLI_INIT:
2314  e->command = "dundi set debug {on|off}";
2315  e->usage =
2316  "Usage: dundi set debug {on|off}\n"
2317  " Enables/Disables dumping of DUNDi packets for debugging purposes\n";
2318  return NULL;
2319  case CLI_GENERATE:
2320  return NULL;
2321  }
2322 
2323  if (a->argc != e->args) {
2324  return CLI_SHOWUSAGE;
2325  }
2326  if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
2327  dundidebug = 1;
2328  ast_cli(a->fd, "DUNDi Debugging Enabled\n");
2329  } else {
2330  dundidebug = 0;
2331  ast_cli(a->fd, "DUNDi Debugging Disabled\n");
2332  }
2333  return CLI_SUCCESS;
2334 }
2335 
2336 static char *dundi_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2337 {
2338  switch (cmd) {
2339  case CLI_INIT:
2340  e->command = "dundi store history {on|off}";
2341  e->usage =
2342  "Usage: dundi store history {on|off}\n"
2343  " Enables/Disables storing of DUNDi requests and times for debugging\n"
2344  "purposes\n";
2345  return NULL;
2346  case CLI_GENERATE:
2347  return NULL;
2348  }
2349 
2350  if (a->argc != e->args) {
2351  return CLI_SHOWUSAGE;
2352  }
2353  if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
2354  global_storehistory = 1;
2355  ast_cli(a->fd, "DUNDi History Storage Enabled\n");
2356  } else {
2357  global_storehistory = 0;
2358  ast_cli(a->fd, "DUNDi History Storage Disabled\n");
2359  }
2360  return CLI_SUCCESS;
2361 }
2362 
2363 static char *dundi_flush(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2364 {
2365  int stats = 0;
2366  switch (cmd) {
2367  case CLI_INIT:
2368  e->command = "dundi flush [stats]";
2369  e->usage =
2370  "Usage: dundi flush [stats]\n"
2371  " Flushes DUNDi answer cache, used primarily for debug. If\n"
2372  "'stats' is present, clears timer statistics instead of normal\n"
2373  "operation.\n";
2374  return NULL;
2375  case CLI_GENERATE:
2376  return NULL;
2377  }
2378  if ((a->argc < 2) || (a->argc > 3)) {
2379  return CLI_SHOWUSAGE;
2380  }
2381  if (a->argc > 2) {
2382  if (!strcasecmp(a->argv[2], "stats")) {
2383  stats = 1;
2384  } else {
2385  return CLI_SHOWUSAGE;
2386  }
2387  }
2388  if (stats) {
2389  /* Flush statistics */
2390  struct dundi_peer *p;
2391  int x;
2392  AST_LIST_LOCK(&peers);
2393  AST_LIST_TRAVERSE(&peers, p, list) {
2394  for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2395  ast_free(p->lookups[x]);
2396  p->lookups[x] = NULL;
2397  p->lookuptimes[x] = 0;
2398  }
2399  p->avgms = 0;
2400  }
2402  } else {
2403  ast_db_deltree("dundi/cache", NULL);
2404  ast_cli(a->fd, "DUNDi Cache Flushed\n");
2405  }
2406  return CLI_SUCCESS;
2407 }
2408 
2409 static char *model2str(int model)
2410 {
2411  switch(model) {
2412  case DUNDI_MODEL_INBOUND:
2413  return "Inbound";
2414  case DUNDI_MODEL_OUTBOUND:
2415  return "Outbound";
2416  case DUNDI_MODEL_SYMMETRIC:
2417  return "Symmetric";
2418  default:
2419  return "Unknown";
2420  }
2421 }
2422 
2423 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
2424 {
2425  int which=0, len;
2426  char *ret = NULL;
2427  struct dundi_peer *p;
2428  char eid_str[20];
2429 
2430  if (pos != rpos)
2431  return NULL;
2432  AST_LIST_LOCK(&peers);
2433  len = strlen(word);
2434  AST_LIST_TRAVERSE(&peers, p, list) {
2435  const char *s = ast_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
2436  if (!strncasecmp(word, s, len) && ++which > state) {
2437  ret = ast_strdup(s);
2438  break;
2439  }
2440  }
2442  return ret;
2443 }
2444 
2445 static int rescomp(const void *a, const void *b)
2446 {
2447  const struct dundi_result *resa, *resb;
2448  resa = a;
2449  resb = b;
2450  if (resa->weight < resb->weight)
2451  return -1;
2452  if (resa->weight > resb->weight)
2453  return 1;
2454  return 0;
2455 }
2456 
2457 static void sort_results(struct dundi_result *results, int count)
2458 {
2459  qsort(results, count, sizeof(results[0]), rescomp);
2460 }
2461 
2462 static char *dundi_do_lookup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2463 {
2464  int res;
2465  char tmp[256];
2466  char fs[80] = "";
2467  char *context;
2468  int x;
2469  int bypass = 0;
2470  struct dundi_result dr[MAX_RESULTS];
2471  struct timeval start;
2472  switch (cmd) {
2473  case CLI_INIT:
2474  e->command = "dundi lookup";
2475  e->usage =
2476  "Usage: dundi lookup <number>[@context] [bypass]\n"
2477  " Lookup the given number within the given DUNDi context\n"
2478  "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
2479  "keyword is specified.\n";
2480  return NULL;
2481  case CLI_GENERATE:
2482  return NULL;
2483  }
2484 
2485  if ((a->argc < 3) || (a->argc > 4)) {
2486  return CLI_SHOWUSAGE;
2487  }
2488  if (a->argc > 3) {
2489  if (!strcasecmp(a->argv[3], "bypass")) {
2490  bypass=1;
2491  } else {
2492  return CLI_SHOWUSAGE;
2493  }
2494  }
2495  ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2496  context = strchr(tmp, '@');
2497  if (context) {
2498  *context = '\0';
2499  context++;
2500  }
2501  start = ast_tvnow();
2502  res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2503 
2504  if (res < 0)
2505  ast_cli(a->fd, "DUNDi lookup returned error.\n");
2506  else if (!res)
2507  ast_cli(a->fd, "DUNDi lookup returned no results.\n");
2508  else
2509  sort_results(dr, res);
2510  for (x=0;x<res;x++) {
2511  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));
2512  ast_cli(a->fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2513  }
2514  ast_cli(a->fd, "DUNDi lookup completed in %" PRIi64 " ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2515  return CLI_SUCCESS;
2516 }
2517 
2518 static char *dundi_do_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2519 {
2520  int res;
2521  char tmp[256];
2522  char *context;
2523  struct timeval start;
2524  switch (cmd) {
2525  case CLI_INIT:
2526  e->command = "dundi precache";
2527  e->usage =
2528  "Usage: dundi precache <number>[@context]\n"
2529  " Lookup the given number within the given DUNDi context\n"
2530  "(or e164 if none is specified) and precaches the results to any\n"
2531  "upstream DUNDi push servers.\n";
2532  return NULL;
2533  case CLI_GENERATE:
2534  return NULL;
2535  }
2536  if ((a->argc < 3) || (a->argc > 3)) {
2537  return CLI_SHOWUSAGE;
2538  }
2539  ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2540  context = strchr(tmp, '@');
2541  if (context) {
2542  *context = '\0';
2543  context++;
2544  }
2545  start = ast_tvnow();
2546  res = dundi_precache(context, tmp);
2547 
2548  if (res < 0)
2549  ast_cli(a->fd, "DUNDi precache returned error.\n");
2550  else if (!res)
2551  ast_cli(a->fd, "DUNDi precache returned no error.\n");
2552  ast_cli(a->fd, "DUNDi lookup completed in %" PRIi64 " ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2553  return CLI_SUCCESS;
2554 }
2555 
2556 static char *dundi_do_query(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2557 {
2558  int res;
2559  char tmp[256];
2560  char *context;
2561  dundi_eid eid;
2562  struct dundi_entity_info dei;
2563  switch (cmd) {
2564  case CLI_INIT:
2565  e->command = "dundi query";
2566  e->usage =
2567  "Usage: dundi query <entity>[@context]\n"
2568  " Attempts to retrieve contact information for a specific\n"
2569  "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2570  "e164 if none is specified).\n";
2571  return NULL;
2572  case CLI_GENERATE:
2573  return NULL;
2574  }
2575  if ((a->argc < 3) || (a->argc > 3)) {
2576  return CLI_SHOWUSAGE;
2577  }
2578  if (ast_str_to_eid(&eid, a->argv[2])) {
2579  ast_cli(a->fd, "'%s' is not a valid EID!\n", a->argv[2]);
2580  return CLI_SHOWUSAGE;
2581  }
2582  ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2583  context = strchr(tmp, '@');
2584  if (context) {
2585  *context = '\0';
2586  context++;
2587  }
2588  res = dundi_query_eid(&dei, context, eid);
2589  if (res < 0)
2590  ast_cli(a->fd, "DUNDi Query EID returned error.\n");
2591  else if (!res)
2592  ast_cli(a->fd, "DUNDi Query EID returned no results.\n");
2593  else {
2594  ast_cli(a->fd, "DUNDi Query EID succeeded:\n");
2595  ast_cli(a->fd, "Department: %s\n", dei.orgunit);
2596  ast_cli(a->fd, "Organization: %s\n", dei.org);
2597  ast_cli(a->fd, "City/Locality: %s\n", dei.locality);
2598  ast_cli(a->fd, "State/Province: %s\n", dei.stateprov);
2599  ast_cli(a->fd, "Country: %s\n", dei.country);
2600  ast_cli(a->fd, "E-mail: %s\n", dei.email);
2601  ast_cli(a->fd, "Phone: %s\n", dei.phone);
2602  ast_cli(a->fd, "IP Address: %s\n", dei.ipaddr);
2603  }
2604  return CLI_SUCCESS;
2605 }
2606 
2607 static char *dundi_sockaddr_stringify_host(const struct ast_sockaddr *addr)
2608 {
2609  if (ast_sockaddr_isnull(addr)) {
2610  return "(Unspecified)";
2611  }
2612  return ast_sockaddr_stringify_host(addr);
2613 }
2614 
2615 static uint16_t dundi_sockaddr_port(const struct ast_sockaddr *addr)
2616 {
2617  /*
2618  * Test to avoid a debug message complaining about addr
2619  * not being an IPv4 or IPv6 address.
2620  */
2621  if (ast_sockaddr_isnull(addr)) {
2622  return 0;
2623  }
2624  return ast_sockaddr_port(addr);
2625 }
2626 
2627 static char *dundi_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2628 {
2629  struct dundi_peer *peer;
2630  struct permission *p;
2631  char *order;
2632  char eid_str[20];
2633  int x, cnt;
2634  switch (cmd) {
2635  case CLI_INIT:
2636  e->command = "dundi show peer";
2637  e->usage =
2638  "Usage: dundi show peer [peer]\n"
2639  " Provide a detailed description of a specifid DUNDi peer.\n";
2640  return NULL;
2641  case CLI_GENERATE:
2642  return complete_peer_helper(a->line, a->word, a->pos, a->n, 3);
2643  }
2644  if (a->argc != 4) {
2645  return CLI_SHOWUSAGE;
2646  }
2647  AST_LIST_LOCK(&peers);
2648  AST_LIST_TRAVERSE(&peers, peer, list) {
2649  if (!strcasecmp(ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), a->argv[3]))
2650  break;
2651  }
2652  if (peer) {
2653  switch(peer->order) {
2654  case 0:
2655  order = "Primary";
2656  break;
2657  case 1:
2658  order = "Secondary";
2659  break;
2660  case 2:
2661  order = "Tertiary";
2662  break;
2663  case 3:
2664  order = "Quartiary";
2665  break;
2666  default:
2667  order = "Unknown";
2668  }
2669  ast_cli(a->fd, "Peer: %s\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2670  ast_cli(a->fd, "Model: %s\n", model2str(peer->model));
2671  ast_cli(a->fd, "Order: %s\n", order);
2672  ast_cli(a->fd, "Host: %s\n", ast_sockaddr_isnull(&peer->addr) ? "<Unspecified>" : ast_sockaddr_stringify_host(&peer->addr));
2673  ast_cli(a->fd, "Port: %d\n", dundi_sockaddr_port(&peer->addr));
2674  ast_cli(a->fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2675  ast_cli(a->fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
2676  ast_cli(a->fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2677  ast_cli(a->fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2678  if (!AST_LIST_EMPTY(&peer->include))
2679  ast_cli(a->fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2680  AST_LIST_TRAVERSE(&peer->include, p, list)
2681  ast_cli(a->fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2682  if (!AST_LIST_EMPTY(&peer->permit))
2683  ast_cli(a->fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2684  AST_LIST_TRAVERSE(&peer->permit, p, list)
2685  ast_cli(a->fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2686  cnt = 0;
2687  for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2688  if (peer->lookups[x]) {
2689  if (!cnt)
2690  ast_cli(a->fd, "Last few query times:\n");
2691  ast_cli(a->fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2692  cnt++;
2693  }
2694  }
2695  if (cnt)
2696  ast_cli(a->fd, "Average query time: %d ms\n", peer->avgms);
2697  } else
2698  ast_cli(a->fd, "No such peer '%s'\n", a->argv[3]);
2700  return CLI_SUCCESS;
2701 }
2702 
2703 static char *dundi_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2704 {
2705 #define FORMAT2 "%-20.20s %-41s %-6.6s %-10.10s %-8.8s %-15.15s\n"
2706 #define FORMAT "%-20.20s %-41s %s %-6d %-10.10s %-8.8s %-15.15s\n"
2707  struct dundi_peer *peer;
2708  int registeredonly=0;
2709  char avgms[20];
2710  char eid_str[20];
2711  int online_peers = 0;
2712  int offline_peers = 0;
2713  int unmonitored_peers = 0;
2714  int total_peers = 0;
2715  switch (cmd) {
2716  case CLI_INIT:
2717  e->command = "dundi show peers [registered|include|exclude|begin]";
2718  e->usage =
2719  "Usage: dundi show peers [registered|include|exclude|begin]\n"
2720  " Lists all known DUNDi peers.\n"
2721  " If 'registered' is present, only registered peers are shown.\n";
2722  return NULL;
2723  case CLI_GENERATE:
2724  return NULL;
2725  }
2726 
2727  if ((a->argc != 3) && (a->argc != 4) && (a->argc != 5)) {
2728  return CLI_SHOWUSAGE;
2729  }
2730  if ((a->argc == 4)) {
2731  if (!strcasecmp(a->argv[3], "registered")) {
2732  registeredonly = 1;
2733  } else {
2734  return CLI_SHOWUSAGE;
2735  }
2736  }
2737  AST_LIST_LOCK(&peers);
2738  ast_cli(a->fd, FORMAT2, "EID", "Host", "Port", "Model", "AvgTime", "Status");
2739  AST_LIST_TRAVERSE(&peers, peer, list) {
2740  char status[64];
2741  int print_line = -1;
2742  char srch[2000];
2743 
2744  total_peers++;
2745  if (registeredonly && ast_sockaddr_isnull(&peer->addr)) {
2746  continue;
2747  }
2748  if (peer->maxms) {
2749  if (peer->lastms < 0) {
2750  strcpy(status, "UNREACHABLE");
2751  offline_peers++;
2752  }
2753  else if (peer->lastms > peer->maxms) {
2754  snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2755  offline_peers++;
2756  }
2757  else if (peer->lastms) {
2758  snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2759  online_peers++;
2760  }
2761  else {
2762  strcpy(status, "UNKNOWN");
2763  offline_peers++;
2764  }
2765  } else {
2766  strcpy(status, "Unmonitored");
2767  unmonitored_peers++;
2768  }
2769  if (peer->avgms)
2770  snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2771  else
2772  strcpy(avgms, "Unavail");
2773  snprintf(srch, sizeof(srch), FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str),
2774  &peer->eid), dundi_sockaddr_stringify_host(&peer->addr),
2775  peer->dynamic ? "(D)" : "(S)", dundi_sockaddr_port(&peer->addr), model2str(peer->model), avgms, status);
2776 
2777  if (a->argc == 5) {
2778  if (!strcasecmp(a->argv[3],"include") && strstr(srch,a->argv[4])) {
2779  print_line = -1;
2780  } else if (!strcasecmp(a->argv[3],"exclude") && !strstr(srch,a->argv[4])) {
2781  print_line = 1;
2782  } else if (!strcasecmp(a->argv[3],"begin") && !strncasecmp(srch,a->argv[4],strlen(a->argv[4]))) {
2783  print_line = -1;
2784  } else {
2785  print_line = 0;
2786  }
2787  }
2788 
2789  if (print_line) {
2790  ast_cli(a->fd, FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2791  dundi_sockaddr_stringify_host(&peer->addr),
2792  peer->dynamic ? "(D)" : "(S)", dundi_sockaddr_port(&peer->addr), model2str(peer->model), avgms, status);
2793  }
2794  }
2795  ast_cli(a->fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2797  return CLI_SUCCESS;
2798 #undef FORMAT
2799 #undef FORMAT2
2800 }
2801 
2802 static char *dundi_show_trans(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2803 {
2804 #define FORMAT2 "%-47s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2805 #define FORMAT "%-41s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2806  struct dundi_transaction *trans;
2807  switch (cmd) {
2808  case CLI_INIT:
2809  e->command = "dundi show trans";
2810  e->usage =
2811  "Usage: dundi show trans\n"
2812  " Lists all known DUNDi transactions.\n";
2813  return NULL;
2814  case CLI_GENERATE:
2815  return NULL;
2816  }
2817  if (a->argc != 3) {
2818  return CLI_SHOWUSAGE;
2819  }
2820  AST_LIST_LOCK(&peers);
2821  ast_cli(a->fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2822  AST_LIST_TRAVERSE(&alltrans, trans, all) {
2823  ast_cli(a->fd, FORMAT, ast_sockaddr_stringify_host(&trans->addr),
2824  ast_sockaddr_port(&trans->addr), trans->strans, trans->dtrans,
2825  trans->oseqno, trans->iseqno, trans->aseqno);
2826  }
2828  return CLI_SUCCESS;
2829 #undef FORMAT
2830 #undef FORMAT2
2831 }
2832 
2833 static char *dundi_show_entityid(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2834 {
2835  char eid_str[20];
2836  switch (cmd) {
2837  case CLI_INIT:
2838  e->command = "dundi show entityid";
2839  e->usage =
2840  "Usage: dundi show entityid\n"
2841  " Displays the global entityid for this host.\n";
2842  return NULL;
2843  case CLI_GENERATE:
2844  return NULL;
2845  }
2846  if (a->argc != 3) {
2847  return CLI_SHOWUSAGE;
2848  }
2849  AST_LIST_LOCK(&peers);
2850  ast_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2852  ast_cli(a->fd, "Global EID for this system is '%s'\n", eid_str);
2853  return CLI_SUCCESS;
2854 }
2855 
2856 static char *dundi_show_requests(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2857 {
2858 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2859 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2860  struct dundi_request *req;
2861  char eidstr[20];
2862  switch (cmd) {
2863  case CLI_INIT:
2864  e->command = "dundi show requests";
2865  e->usage =
2866  "Usage: dundi show requests\n"
2867  " Lists all known pending DUNDi requests.\n";
2868  return NULL;
2869  case CLI_GENERATE:
2870  return NULL;
2871  }
2872  if (a->argc != 3) {
2873  return CLI_SHOWUSAGE;
2874  }
2875  AST_LIST_LOCK(&peers);
2876  ast_cli(a->fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2877  AST_LIST_TRAVERSE(&requests, req, list) {
2878  ast_cli(a->fd, FORMAT, req->number, req->dcontext,
2879  dundi_eid_zero(&req->root_eid) ? "<unspecified>" : ast_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2880  }
2882  return CLI_SUCCESS;
2883 #undef FORMAT
2884 #undef FORMAT2
2885 }
2886 
2887 /* Grok-a-dial DUNDi */
2888 
2889 static char *dundi_show_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2890 {
2891 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2892 #define FORMAT "%-12.12s %-7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2893  struct dundi_mapping *map;
2894  char fs[256];
2895  char weight[8];
2896  switch (cmd) {
2897  case CLI_INIT:
2898  e->command = "dundi show mappings";
2899  e->usage =
2900  "Usage: dundi show mappings\n"
2901  " Lists all known DUNDi mappings.\n";
2902  return NULL;
2903  case CLI_GENERATE:
2904  return NULL;
2905  }
2906  if (a->argc != 3) {
2907  return CLI_SHOWUSAGE;
2908  }
2909  AST_LIST_LOCK(&peers);
2910  ast_cli(a->fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2911  AST_LIST_TRAVERSE(&mappings, map, list) {
2912  snprintf(weight, sizeof(weight), "%d", get_mapping_weight(map, NULL));
2913  ast_cli(a->fd, FORMAT, map->dcontext, weight,
2914  ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2915  dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2916  }
2918  return CLI_SUCCESS;
2919 #undef FORMAT
2920 #undef FORMAT2
2921 }
2922 
2923 static char *dundi_show_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2924 {
2925 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2926 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2927  struct dundi_precache_queue *qe;
2928  int h,m,s;
2929  time_t now;
2930  switch (cmd) {
2931  case CLI_INIT:
2932  e->command = "dundi show precache";
2933  e->usage =
2934  "Usage: dundi show precache\n"
2935  " Lists all known DUNDi scheduled precache updates.\n";
2936  return NULL;
2937  case CLI_GENERATE:
2938  return NULL;
2939  }
2940  if (a->argc != 3) {
2941  return CLI_SHOWUSAGE;
2942  }
2943  time(&now);
2944  ast_cli(a->fd, FORMAT2, "Number", "Context", "Expiration");
2945  AST_LIST_LOCK(&pcq);
2946  AST_LIST_TRAVERSE(&pcq, qe, list) {
2947  s = qe->expiration - now;
2948  h = s / 3600;
2949  s = s % 3600;
2950  m = s / 60;
2951  s = s % 60;
2952  ast_cli(a->fd, FORMAT, qe->number, qe->context, h,m,s);
2953  }
2954  AST_LIST_UNLOCK(&pcq);
2955 
2956  return CLI_SUCCESS;
2957 #undef FORMAT
2958 #undef FORMAT2
2959 }
2960 
2961 static char *dundi_show_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2962 {
2963 #define FORMAT2 "%-12.12s %-16.16s %-10.10s %-18s %-7s %s\n"
2964 #define FORMAT "%-12.12s %-16.16s %6d sec %-18s %-7d %s/%s (%s)\n"
2965  struct ast_db_entry *db_tree, *db_entry;
2966  int cnt = 0;
2967  time_t ts, now;
2968  dundi_eid src_eid;
2969  char src_eid_str[20];
2970  int expiry, tech, weight;
2971  struct ast_flags flags;
2972  char fs[256];
2973  int length;
2974  char *ptr, *term, *src, *number, *context, *dst;
2975 
2976  switch (cmd) {
2977  case CLI_INIT:
2978  e->command = "dundi show cache";
2979  e->usage =
2980  "Usage: dundi show cache\n"
2981  " Lists all DUNDi cache entries.\n";
2982  return NULL;
2983  case CLI_GENERATE:
2984  return NULL;
2985  }
2986 
2987  if (a->argc != 3) {
2988  return CLI_SHOWUSAGE;
2989  }
2990 
2991  time(&now);
2992  db_tree = ast_db_gettree("dundi/cache", NULL);
2993  ast_cli(a->fd, FORMAT2, "Number", "Context", "Expiration", "From", "Weight", "Destination (Flags)");
2994  for (db_entry = db_tree; db_entry; db_entry = db_entry->next) {
2995  char *rest;
2996 
2997  if ((strncmp(db_entry->key, "/dundi/cache/hint/", 18) == 0) || ast_get_time_t(db_entry->data, &ts, 0, &length)) {
2998  continue;
2999  }
3000 
3001  expiry = ts - now;
3002 
3003  if (expiry <= 0) {
3004  continue;
3005  }
3006 
3007  ptr = db_entry->key + sizeof("/dundi/cache");
3008  strtok_r(ptr, "/", &rest);
3009  number = strtok_r(NULL, "/", &rest);
3010  context = strtok_r(NULL, "/", &rest);
3011  ptr = strtok_r(NULL, "/", &rest);
3012 
3013  if (*ptr != 'e') {
3014  continue;
3015  }
3016 
3017  ptr = db_entry->data + length + 1;
3018 
3019  if ((sscanf(ptr, "%30u/%30d/%30d/%n", &(flags.flags), &weight, &tech, &length) != 3)) {
3020  continue;
3021  }
3022 
3023  ptr += length;
3024  dst = ptr;
3025  term = strchr(ptr, '|');
3026 
3027  if (!term) {
3028  continue;
3029  }
3030 
3031  /* Ok, at this point we know we aren't going to skp the entry, so we go ahead and increment the count. */
3032  cnt++;
3033 
3034  *term = '\0';
3035  src = strrchr(ptr, '/');
3036  dundi_eid_zero(&src_eid);
3037 
3038  if (src) {
3039  *src = '\0';
3040  src++;
3041  dundi_str_short_to_eid(&src_eid, src);
3042  ast_eid_to_str(src_eid_str, sizeof(src_eid_str), &src_eid);
3043  }
3044 
3045  ast_cli(a->fd, FORMAT, number, context, expiry, src_eid_str, weight, tech2str(tech), dst, dundi_flags2str(fs, sizeof(fs), flags.flags));
3046  }
3047 
3048  ast_cli(a->fd, "Number of entries: %d\n", cnt);
3049  ast_db_freetree(db_tree);
3050 
3051  return CLI_SUCCESS;
3052 #undef FORMAT
3053 #undef FORMAT2
3054 }
3055 
3056 static char *dundi_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3057 {
3058 #define FORMAT2 "%-12.12s %-16.16s %-10.10s %-18s\n"
3059 #define FORMAT "%-12.12s %-16.16s %6d sec %-18s\n"
3060  struct ast_db_entry *db_tree, *db_entry;
3061  int cnt = 0;
3062  time_t ts, now;
3063  dundi_eid src_eid;
3064  char src_eid_str[20];
3065  int expiry;
3066  int length;
3067  char *ptr, *src, *number, *context;
3068 
3069  switch (cmd) {
3070  case CLI_INIT:
3071  e->command = "dundi show hints";
3072  e->usage =
3073  "Usage: dundi show hints\n"
3074  " Lists all DUNDi 'DONTASK' hints in the cache.\n";
3075  return NULL;
3076  case CLI_GENERATE:
3077  return NULL;
3078  }
3079 
3080  if (a->argc != 3) {
3081  return CLI_SHOWUSAGE;
3082  }
3083 
3084  time(&now);
3085  db_tree = ast_db_gettree("dundi/cache/hint", NULL);
3086  ast_cli(a->fd, FORMAT2, "Prefix", "Context", "Expiration", "From");
3087 
3088  for (db_entry = db_tree; db_entry; db_entry = db_entry->next) {
3089  char *rest = NULL;
3090 
3091  if (ast_get_time_t(db_entry->data, &ts, 0, &length)) {
3092  continue;
3093  }
3094 
3095  expiry = ts - now;
3096 
3097  if (expiry <= 0) {
3098  continue;
3099  }
3100 
3101  ptr = db_entry->key + sizeof("/dundi/cache/hint");
3102  src = strtok_r(ptr, "/", &rest);
3103  number = strtok_r(NULL, "/", &rest);
3104  context = strtok_r(NULL, "/", &rest);
3105  ptr = strtok_r(NULL, "/", &rest);
3106 
3107  if (*ptr != 'e') {
3108  continue;
3109  }
3110 
3111  cnt++;
3112  dundi_str_short_to_eid(&src_eid, src);
3113  ast_eid_to_str(src_eid_str, sizeof(src_eid_str), &src_eid);
3114  ast_cli(a->fd, FORMAT, number, context, expiry, src_eid_str);
3115  }
3116 
3117  ast_cli(a->fd, "Number of entries: %d\n", cnt);
3118  ast_db_freetree(db_tree);
3119 
3120  return CLI_SUCCESS;
3121 #undef FORMAT
3122 #undef FORMAT2
3123 }
3124 
3125 static struct ast_cli_entry cli_dundi[] = {
3126  AST_CLI_DEFINE(dundi_set_debug, "Enable/Disable DUNDi debugging"),
3127  AST_CLI_DEFINE(dundi_store_history, "Enable/Disable DUNDi historic records"),
3128  AST_CLI_DEFINE(dundi_flush, "Flush DUNDi cache"),
3129  AST_CLI_DEFINE(dundi_show_peers, "Show defined DUNDi peers"),
3130  AST_CLI_DEFINE(dundi_show_trans, "Show active DUNDi transactions"),
3131  AST_CLI_DEFINE(dundi_show_entityid, "Display Global Entity ID"),
3132  AST_CLI_DEFINE(dundi_show_mappings, "Show DUNDi mappings"),
3133  AST_CLI_DEFINE(dundi_show_precache, "Show DUNDi precache"),
3134  AST_CLI_DEFINE(dundi_show_requests, "Show DUNDi requests"),
3135  AST_CLI_DEFINE(dundi_show_peer, "Show info on a specific DUNDi peer"),
3136  AST_CLI_DEFINE(dundi_show_cache, "Show DUNDi cache"),
3137  AST_CLI_DEFINE(dundi_show_hints, "Show DUNDi hints in the cache"),
3138  AST_CLI_DEFINE(dundi_do_precache, "Precache a number in DUNDi"),
3139  AST_CLI_DEFINE(dundi_do_lookup, "Lookup a number in DUNDi"),
3140  AST_CLI_DEFINE(dundi_do_query, "Query a DUNDi EID"),
3141 };
3142 
3143 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
3144 {
3145  struct dundi_transaction *trans;
3146  int tid;
3147 
3148  /* Don't allow creation of transactions to non-registered peers */
3149  if (p && ast_sockaddr_isnull(&p->addr)) {
3150  return NULL;
3151  }
3152  tid = get_trans_id();
3153  if (tid < 1)
3154  return NULL;
3155  if (!(trans = ast_calloc(1, sizeof(*trans))))
3156  return NULL;
3157 
3158  if (global_storehistory) {
3159  trans->start = ast_tvnow();
3160  ast_set_flag(trans, FLAG_STOREHIST);
3161  }
3162  trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
3163  trans->autokillid = -1;
3164  if (p) {
3165  apply_peer(trans, p);
3166  if (!p->sentfullkey)
3167  ast_set_flag(trans, FLAG_SENDFULLKEY);
3168  }
3169  trans->strans = tid;
3170  AST_LIST_INSERT_HEAD(&alltrans, trans, all);
3171 
3172  return trans;
3173 }
3174 
3175 static int dundi_xmit(struct dundi_packet *pack)
3176 {
3177  int res;
3178  if (dundidebug)
3179  dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
3180 
3181  if (netsocket2 < 0) {
3182  res = ast_sendto(netsocket, pack->data, pack->datalen, 0, &pack->parent->addr);
3183  } else {
3184  if (ast_sockaddr_is_ipv4(&pack->parent->addr)) {
3185  res = ast_sendto(netsocket, pack->data, pack->datalen, 0, &pack->parent->addr);
3186  } else {
3187  res = ast_sendto(netsocket2, pack->data, pack->datalen, 0, &pack->parent->addr);
3188  }
3189  }
3190 
3191  if (res < 0) {
3192  ast_log(LOG_WARNING, "Failed to transmit to '%s': %s\n",
3193  ast_sockaddr_stringify(&pack->parent->addr), strerror(errno));
3194  }
3195  if (res > 0)
3196  res = 0;
3197  return res;
3198 }
3199 
3200 static void destroy_packet(struct dundi_packet *pack, int needfree)
3201 {
3202  if (pack->parent)
3203  AST_LIST_REMOVE(&pack->parent->packets, pack, list);
3204  AST_SCHED_DEL(sched, pack->retransid);
3205  if (needfree)
3206  ast_free(pack);
3207 }
3208 
3209 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
3210 {
3211  struct dundi_peer *peer;
3212  int ms;
3213  int x;
3214  int cnt;
3215  char eid_str[20];
3216  if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
3217  AST_LIST_TRAVERSE(&peers, peer, list) {
3218  if (peer->regtrans == trans)
3219  peer->regtrans = NULL;
3220  if (peer->qualtrans == trans) {
3221  if (fromtimeout) {
3222  if (peer->lastms > -1)
3223  ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
3224  peer->lastms = -1;
3225  } else {
3226  ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
3227  if (ms < 1)
3228  ms = 1;
3229  if (ms < peer->maxms) {
3230  if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
3231  ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
3232  } else if (peer->lastms < peer->maxms) {
3233  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);
3234  }
3235  peer->lastms = ms;
3236  }
3237  peer->qualtrans = NULL;
3238  }
3239  if (ast_test_flag(trans, FLAG_STOREHIST)) {
3240  if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
3241  if (!ast_eid_cmp(&trans->them_eid, &peer->eid)) {
3242  peer->avgms = 0;
3243  cnt = 0;
3244  ast_free(peer->lookups[DUNDI_TIMING_HISTORY - 1]);
3245  for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
3246  peer->lookuptimes[x] = peer->lookuptimes[x-1];
3247  peer->lookups[x] = peer->lookups[x-1];
3248  if (peer->lookups[x]) {
3249  peer->avgms += peer->lookuptimes[x];
3250  cnt++;
3251  }
3252  }
3253  peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
3254  peer->lookups[0] = ast_malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
3255  if (peer->lookups[0]) {
3256  sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
3257  peer->avgms += peer->lookuptimes[0];
3258  cnt++;
3259  }
3260  if (cnt)
3261  peer->avgms /= cnt;
3262  }
3263  }
3264  }
3265  }
3266  }
3267  if (trans->parent) {
3268  /* Unlink from parent if appropriate */
3269  AST_LIST_REMOVE(&trans->parent->trans, trans, parentlist);
3270  if (AST_LIST_EMPTY(&trans->parent->trans)) {
3271  /* Wake up sleeper */
3272  if (trans->parent->pfds[1] > -1) {
3273  if (write(trans->parent->pfds[1], "killa!", 6) < 0) {
3274  ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
3275  }
3276  }
3277  }
3278  }
3279  /* Unlink from all trans */
3280  AST_LIST_REMOVE(&alltrans, trans, all);
3281  destroy_packets(&trans->packets);
3282  destroy_packets(&trans->lasttrans);
3283  AST_SCHED_DEL(sched, trans->autokillid);
3284  if (trans->thread) {
3285  /* If used by a thread, mark as dead and be done */
3286  ast_set_flag(trans, FLAG_DEAD);
3287  } else
3288  ast_free(trans);
3289 }
3290 
3291 static int dundi_rexmit(const void *data)
3292 {
3293  struct dundi_packet *pack = (struct dundi_packet *)data;
3294  int res;
3295  AST_LIST_LOCK(&peers);
3296  if (pack->retrans < 1) {
3297  pack->retransid = -1;
3298  if (!ast_test_flag(pack->parent, FLAG_ISQUAL)) {
3299  ast_log(LOG_NOTICE, "Max retries exceeded to host '%s' msg %d on call %d\n",
3300  ast_sockaddr_stringify(&pack->parent->addr), pack->h->oseqno, ntohs(pack->h->strans));
3301  }
3302  destroy_trans(pack->parent, 1);
3303  res = 0;
3304  } else {
3305  /* Decrement retransmission, try again */
3306  pack->retrans--;
3307  dundi_xmit(pack);
3308  res = 1;
3309  }
3311  return res;
3312 }
3313 
3314 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
3315 {
3316  struct dundi_packet *pack;
3317  int res;
3318  int len;
3319  char eid_str[20];
3320  len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
3321  /* Reserve enough space for encryption */
3322  if (ast_test_flag(trans, FLAG_ENCRYPT))
3323  len += 384;
3324  pack = ast_calloc(1, len);
3325  if (pack) {
3326  pack->h = (struct dundi_hdr *)(pack->data);
3327  pack->retransid = -1;
3328  if (cmdresp != DUNDI_COMMAND_ACK) {
3329  pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
3330  pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
3331  AST_LIST_INSERT_HEAD(&trans->packets, pack, list);
3332  }
3333  pack->parent = trans;
3334  pack->h->strans = htons(trans->strans);
3335  pack->h->dtrans = htons(trans->dtrans);
3336  pack->h->iseqno = trans->iseqno;
3337  pack->h->oseqno = trans->oseqno;
3338  pack->h->cmdresp = cmdresp;
3339  pack->datalen = sizeof(struct dundi_hdr);
3340  if (ied) {
3341  memcpy(pack->h->ies, ied->buf, ied->pos);
3342  pack->datalen += ied->pos;
3343  }
3344  if (final) {
3345  pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
3346  ast_set_flag(trans, FLAG_FINAL);
3347  }
3348  pack->h->cmdflags = flags;
3349  if (cmdresp != DUNDI_COMMAND_ACK) {
3350  trans->oseqno++;
3351  trans->oseqno = trans->oseqno % 256;
3352  }
3353  trans->aseqno = trans->iseqno;
3354  /* If we have their public key, encrypt */
3355  if (ast_test_flag(trans, FLAG_ENCRYPT)) {
3356  switch(cmdresp) {
3357  case DUNDI_COMMAND_REGREQ:
3365  if (dundidebug)
3366  dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
3367  res = dundi_encrypt(trans, pack);
3368  break;
3369  default:
3370  res = 0;
3371  }
3372  } else
3373  res = 0;
3374  if (!res)
3375  res = dundi_xmit(pack);
3376  if (res)
3377  ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3378 
3379  if (cmdresp == DUNDI_COMMAND_ACK)
3380  ast_free(pack);
3381  return res;
3382  }
3383  return -1;
3384 }
3385 
3386 static int do_autokill(const void *data)
3387 {
3388  struct dundi_transaction *trans = (struct dundi_transaction *)data;
3389  char eid_str[20];
3390  ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n",
3391  ast_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3392  trans->autokillid = -1;
3393  destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
3394  return 0;
3395 }
3396 
3397 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
3398 {
3399  struct dundi_peer *p;
3400  if (!ast_eid_cmp(eid, us)) {
3401  dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3402  return;
3403  }
3404  AST_LIST_LOCK(&peers);
3405  AST_LIST_TRAVERSE(&peers, p, list) {
3406  if (!ast_eid_cmp(&p->eid, eid)) {
3407  if (has_permission(&p->include, context))
3408  dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3409  else
3410  dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3411  break;
3412  }
3413  }
3414  if (!p)
3415  dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3417 }
3418 
3419 static int dundi_discover(struct dundi_transaction *trans)
3420 {
3421  struct dundi_ie_data ied;
3422  int x;
3423  if (!trans->parent) {
3424  ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3425  return -1;
3426  }
3427  memset(&ied, 0, sizeof(ied));
3428  dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3429  if (!dundi_eid_zero(&trans->us_eid))
3430  dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
3431  for (x=0;x<trans->eidcount;x++)
3432  dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
3433  dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3434  dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3435  dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3436  if (trans->parent->cbypass)
3437  dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
3438  if (trans->autokilltimeout)
3439  trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3440  return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
3441 }
3442 
3443 static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
3444 {
3445  struct dundi_ie_data ied;
3446  int x, res;
3447  int max = 999999;
3448  int expiration = dundi_cache_time;
3449  int ouranswers=0;
3450  dundi_eid *avoid[1] = { NULL, };
3451  int direct[1] = { 0, };
3452  struct dundi_result dr[MAX_RESULTS];
3453  struct dundi_hint_metadata hmd;
3454  if (!trans->parent) {
3455  ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3456  return -1;
3457  }
3458  memset(&hmd, 0, sizeof(hmd));
3459  memset(&dr, 0, sizeof(dr));
3460  /* Look up the answers we're going to include */
3461  for (x=0;x<mapcount;x++)
3462  ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
3463  if (ouranswers < 0)
3464  ouranswers = 0;
3465  for (x=0;x<ouranswers;x++) {
3466  if (dr[x].weight < max)
3467  max = dr[x].weight;
3468  }
3469  if (max) {
3470  /* If we do not have a canonical result, keep looking */
3471  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);
3472  if (res > 0) {
3473  /* Append answer in result */
3474  ouranswers += res;
3475  }
3476  }
3477 
3478  if (ouranswers > 0) {
3479  *foundanswers += ouranswers;
3480  memset(&ied, 0, sizeof(ied));
3481  dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3482  if (!dundi_eid_zero(&trans->us_eid))
3483  dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3484  for (x=0;x<trans->eidcount;x++)
3485  dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3486  dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
3487  dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3488  dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3489  for (x=0;x<ouranswers;x++) {
3490  /* Add answers */
3491  if (dr[x].expiration && (expiration > dr[x].expiration))
3492  expiration = dr[x].expiration;
3493  dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
3494  }
3495  dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
3496  dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
3497  if (trans->autokilltimeout)
3498  trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3499  if (expiration < *minexp)
3500  *minexp = expiration;
3501  return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
3502  } else {
3503  /* Oops, nothing to send... */
3504  destroy_trans(trans, 0);
3505  return 0;
3506  }
3507 }
3508 
3509 static int dundi_query(struct dundi_transaction *trans)
3510 {
3511  struct dundi_ie_data ied;
3512  int x;
3513  if (!trans->parent) {
3514  ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
3515  return -1;
3516  }
3517  memset(&ied, 0, sizeof(ied));
3518  dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3519  if (!dundi_eid_zero(&trans->us_eid))
3520  dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3521  for (x=0;x<trans->eidcount;x++)
3522  dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
3523  dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
3524  dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
3525  dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
3526  if (trans->autokilltimeout)
3527  trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
3528  return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
3529 }
3530 
3531 static int discover_transactions(struct dundi_request *dr)
3532 {
3533  struct dundi_transaction *trans;
3534  AST_LIST_LOCK(&peers);
3535  AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3536  dundi_discover(trans);
3537  }
3539  return 0;
3540 }
3541 
3542 static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
3543 {
3544  struct dundi_transaction *trans;
3545 
3546  /* Mark all as "in thread" so they don't disappear */
3547  AST_LIST_LOCK(&peers);
3548  AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3549  if (trans->thread)
3550  ast_log(LOG_WARNING, "This shouldn't happen, really...\n");
3551  trans->thread = 1;
3552  }
3554 
3555  AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3556  if (!ast_test_flag(trans, FLAG_DEAD))
3557  precache_trans(trans, maps, mapcount, expiration, foundanswers);
3558  }
3559 
3560  /* Cleanup any that got destroyed in the mean time */
3561  AST_LIST_LOCK(&peers);
3562  AST_LIST_TRAVERSE_SAFE_BEGIN(&dr->trans, trans, parentlist) {
3563  trans->thread = 0;
3564  if (ast_test_flag(trans, FLAG_DEAD)) {
3565  ast_debug(1, "Our transaction went away!\n");
3566  /* This is going to remove the transaction from the dundi_request's list, as well
3567  * as the global transactions list */
3568  destroy_trans(trans, 0);
3569  }
3570  }
3573 
3574  return 0;
3575 }
3576 
3577 static int query_transactions(struct dundi_request *dr)
3578 {
3579  struct dundi_transaction *trans;
3580 
3581  AST_LIST_LOCK(&peers);
3582  AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3583  dundi_query(trans);
3584  }
3586 
3587  return 0;
3588 }
3589 
3590 static int optimize_transactions(struct dundi_request *dr, int order)
3591 {
3592  /* Minimize the message propagation through DUNDi by
3593  alerting the network to hops which should be not be considered */
3594  struct dundi_transaction *trans;
3595  struct dundi_peer *peer;
3596  dundi_eid tmp;
3597  int x;
3598  int needpush;
3599 
3600  AST_LIST_LOCK(&peers);
3601  AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
3602  /* Pop off the true root */
3603  if (trans->eidcount) {
3604  tmp = trans->eids[--trans->eidcount];
3605  needpush = 1;
3606  } else {
3607  tmp = trans->us_eid;
3608  needpush = 0;
3609  }
3610 
3611  AST_LIST_TRAVERSE(&peers, peer, list) {
3612  if (ast_eid_cmp(&peer->eid, &empty_eid) && /* peer's eid is not empty (in case of dynamic peers) */
3613  (peer->lastms > -1) && /* peer is reachable */
3614  has_permission(&peer->include, dr->dcontext) && /* peer has destination context */
3615  ast_eid_cmp(&peer->eid, &trans->them_eid) && /* peer is not transaction endpoint */
3616  (peer->order <= order)) {
3617  /* For each other transaction, make sure we don't
3618  ask this EID about the others if they're not
3619  already in the list */
3620  if (!ast_eid_cmp(&tmp, &peer->eid))
3621  x = -1;
3622  else {
3623  for (x=0;x<trans->eidcount;x++) {
3624  if (!ast_eid_cmp(&trans->eids[x], &peer->eid))
3625  break;
3626  }
3627  }
3628  if (x == trans->eidcount) {
3629  /* Nope not in the list, if needed, add us at the end since we're the source */
3630  if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
3631  trans->eids[trans->eidcount++] = peer->eid;
3632  /* Need to insert the real root (or us) at the bottom now as
3633  a requirement now. */
3634  needpush = 1;
3635  }
3636  }
3637  }
3638  }
3639  /* If necessary, push the true root back on the end */
3640  if (needpush)
3641  trans->eids[trans->eidcount++] = tmp;
3642  }
3644 
3645  return 0;
3646 }
3647 
3648 static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
3649 {
3650  struct dundi_transaction *trans;
3651  int x;
3652  char eid_str[20];
3653  char eid_str2[20];
3654 
3655  /* Ignore if not registered */
3656  if (ast_sockaddr_isnull(&p->addr)) {
3657  return 0;
3658  }
3659  if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
3660  return 0;
3661 
3662  if (ast_strlen_zero(dr->number))
3663  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);
3664  else
3665  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);
3666 
3667  trans = create_transaction(p);
3668  if (!trans)
3669  return -1;
3670  trans->parent = dr;
3671  trans->ttl = ttl;
3672  for (x = 0; avoid[x] && (x < DUNDI_MAX_STACK); x++)
3673  trans->eids[x] = *avoid[x];
3674  trans->eidcount = x;
3675  AST_LIST_INSERT_HEAD(&dr->trans, trans, parentlist);
3676 
3677  return 0;
3678 }
3679 
3680 static void cancel_request(struct dundi_request *dr)
3681 {
3682  struct dundi_transaction *trans;
3683 
3684  AST_LIST_LOCK(&peers);
3685  while ((trans = AST_LIST_REMOVE_HEAD(&dr->trans, parentlist))) {
3686  /* Orphan transaction from request */
3687  trans->parent = NULL;
3688  /* Send final cancel */
3689  dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3690  }
3692 }
3693 
3694 static void abort_request(struct dundi_request *dr)
3695 {
3696  struct dundi_transaction *trans;
3697 
3698  AST_LIST_LOCK(&peers);
3699  while ((trans = AST_LIST_FIRST(&dr->trans))) {
3700  /* This will remove the transaction from the list */
3701  destroy_trans(trans, 0);
3702  }
3704 }
3705 
3706 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[])
3707 {
3708  struct dundi_peer *p;
3709  int x;
3710  int res;
3711  int pass;
3712  int allowconnect;
3713  char eid_str[20];
3714  AST_LIST_LOCK(&peers);
3715  AST_LIST_TRAVERSE(&peers, p, list) {
3716  if (modeselect == 1) {
3717  /* Send the precache to push upstreams only! */
3718  pass = has_permission(&p->permit, dr->dcontext) && (p->pcmodel & DUNDI_MODEL_OUTBOUND);
3719  allowconnect = 1;
3720  } else {
3721  /* Normal lookup / EID query */
3722  pass = has_permission(&p->include, dr->dcontext);
3723  allowconnect = p->model & DUNDI_MODEL_OUTBOUND;
3724  }
3725  if (skip) {
3726  if (!ast_eid_cmp(skip, &p->eid))
3727  pass = 0;
3728  }
3729  if (pass) {
3730  if (p->order <= order) {
3731  /* Check order first, then check cache, regardless of
3732  omissions, this gets us more likely to not have an
3733  affected answer. */
3734  if((nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration)))) {
3735  res = 0;
3736  /* Make sure we haven't already seen it and that it won't
3737  affect our answer */
3738  for (x=0;avoid[x];x++) {
3739  if (!ast_eid_cmp(avoid[x], &p->eid) || !ast_eid_cmp(avoid[x], &p->us_eid)) {
3740  /* If not a direct connection, it affects our answer */
3741  if (directs && !directs[x])
3742  ast_clear_flag_nonstd(dr->hmd, DUNDI_HINT_UNAFFECTED);
3743  break;
3744  }
3745  }
3746  /* Make sure we can ask */
3747  if (allowconnect) {
3748  if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) {
3749  /* Check for a matching or 0 cache entry */
3750  append_transaction(dr, p, ttl, avoid);
3751  } else {
3752  ast_debug(1, "Avoiding '%s' in transaction\n", ast_eid_to_str(eid_str, sizeof(eid_str), avoid[x]));
3753  }
3754  }
3755  }
3756  *foundcache |= res;
3757  } else if (!*skipped || (p->order < *skipped))
3758  *skipped = p->order;
3759  }
3760  }
3762 }
3763 
3764 static int register_request(struct dundi_request *dr, struct dundi_request **pending)
3765 {
3766  struct dundi_request *cur;
3767  int res=0;
3768  char eid_str[20];
3769  AST_LIST_LOCK(&peers);
3770  AST_LIST_TRAVERSE(&requests, cur, list) {
3771  ast_debug(1, "Checking '%s@%s' vs '%s@%s'\n", cur->dcontext, cur->number,
3772  dr->dcontext, dr->number);
3773  if (!strcasecmp(cur->dcontext, dr->dcontext) &&
3774  !strcasecmp(cur->number, dr->number) &&
3775  (!ast_eid_cmp(&cur->root_eid, &dr->root_eid) || (cur->crc32 == dr->crc32))) {
3776  ast_debug(1, "Found existing query for '%s@%s' for '%s' crc '%08x'\n",
3777  cur->dcontext, cur->number, ast_eid_to_str(eid_str, sizeof(eid_str), &cur->root_eid), cur->crc32);
3778  *pending = cur;
3779  res = 1;
3780  break;
3781  }
3782  }
3783  if (!res) {
3784  ast_debug(1, "Registering request for '%s@%s' on behalf of '%s' crc '%08x'\n",
3785  dr->number, dr->dcontext, ast_eid_to_str(eid_str, sizeof(eid_str), &dr->root_eid), dr->crc32);
3786  /* Go ahead and link us in since nobody else is searching for this */
3787  AST_LIST_INSERT_HEAD(&requests, dr, list);
3788  *pending = NULL;
3789  }
3791  return res;
3792 }
3793 
3794 static void unregister_request(struct dundi_request *dr)
3795 {
3796  AST_LIST_LOCK(&peers);
3797  AST_LIST_REMOVE(&requests, dr, list);
3799 }
3800 
3801 static int check_request(struct dundi_request *dr)
3802 {
3803  struct dundi_request *cur;
3804 
3805  AST_LIST_LOCK(&peers);
3806  AST_LIST_TRAVERSE(&requests, cur, list) {
3807  if (cur == dr)
3808  break;
3809  }
3811 
3812  return cur ? 1 : 0;
3813 }
3814 
3815 static unsigned long avoid_crc32(dundi_eid *avoid[])
3816 {
3817  /* Idea is that we're calculating a checksum which is independent of
3818  the order that the EID's are listed in */
3819  uint32_t acrc32 = 0;
3820  int x;
3821  for (x=0;avoid[x];x++) {
3822  /* Order doesn't matter */
3823  if (avoid[x+1]) {
3824  acrc32 ^= crc32(0L, (unsigned char *)avoid[x], sizeof(dundi_eid));
3825  }
3826  }
3827  return acrc32;
3828 }
3829 
3830 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[])
3831 {
3832  int res;
3833  struct dundi_request dr, *pending;
3834  dundi_eid *rooteid=NULL;
3835  int x;
3836  int ttlms;
3837  int ms;
3838  int foundcache;
3839  int skipped=0;
3840  int order=0;
3841  char eid_str[20];
3842  struct timeval start;
3843 
3844  /* Don't do anthing for a hungup channel */
3845  if (chan && ast_check_hangup(chan))
3846  return 0;
3847 
3848  ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
3849 
3850  for (x=0;avoid[x];x++)
3851  rooteid = avoid[x];
3852  /* Now perform real check */
3853  memset(&dr, 0, sizeof(dr));
3854  if (pipe(dr.pfds)) {
3855  ast_log(LOG_WARNING, "pipe failed: %s\n" , strerror(errno));
3856  return -1;
3857  }
3858  dr.dr = result;
3859  dr.hmd = hmd;
3860  dr.maxcount = maxret;
3861  dr.expiration = *expiration;
3862  dr.cbypass = cbypass;
3863  dr.crc32 = avoid_crc32(avoid);
3864  ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
3865  ast_copy_string(dr.number, number, sizeof(dr.number));
3866  if (rooteid)
3867  dr.root_eid = *rooteid;
3868  res = register_request(&dr, &pending);
3869  if (res) {
3870  /* Already a request */
3871  if (rooteid && !ast_eid_cmp(&dr.root_eid, &pending->root_eid)) {
3872  /* This is on behalf of someone else. Go ahead and close this out since
3873  they'll get their answer anyway. */
3874  ast_debug(1, "Oooh, duplicate request for '%s@%s' for '%s'\n",
3875  dr.number,dr.dcontext,ast_eid_to_str(eid_str, sizeof(eid_str), &dr.root_eid));
3876  close(dr.pfds[0]);
3877  close(dr.pfds[1]);
3878  return -2;
3879  } else {
3880  /* Wait for the cache to populate */
3881  ast_debug(1, "Waiting for similar request for '%s@%s' for '%s'\n",
3882  dr.number,dr.dcontext,ast_eid_to_str(eid_str, sizeof(eid_str), &pending->root_eid));
3883  start = ast_tvnow();
3884  while(check_request(pending) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !ast_check_hangup(chan))) {
3885  /* XXX Would be nice to have a way to poll/select here XXX */
3886  /* XXX this is a busy wait loop!!! */
3887  usleep(1);
3888  }
3889  /* Continue on as normal, our cache should kick in */
3890  }
3891  }
3892  /* Create transactions */
3893  do {
3894  order = skipped;
3895  skipped = 0;
3896  foundcache = 0;
3897  build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, modeselect, skip, avoid, direct);
3898  } while (skipped && !foundcache && AST_LIST_EMPTY(&dr.trans));
3899  /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
3900  do this earlier because we didn't know if we were going to have transactions
3901  or not. */
3902  if (!ttl) {
3903  ast_set_flag_nonstd(hmd, DUNDI_HINT_TTL_EXPIRED);
3904  abort_request(&dr);
3905  unregister_request(&dr);
3906  close(dr.pfds[0]);
3907  close(dr.pfds[1]);
3908  return 0;
3909  }
3910 
3911  /* Optimize transactions */
3912  optimize_transactions(&dr, order);
3913  /* Actually perform transactions */
3914  discover_transactions(&dr);
3915  /* Wait for transaction to come back */
3916  start = ast_tvnow();
3917  while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !ast_check_hangup(chan))) {
3918  ms = 100;
3919  ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
3920  }
3921  if (chan && ast_check_hangup(chan))
3922  ast_debug(1, "Hrm, '%s' hungup before their query for %s@%s finished\n", ast_channel_name(chan), dr.number, dr.dcontext);
3923  cancel_request(&dr);
3924  unregister_request(&dr);
3925  res = dr.respcount;
3926  *expiration = dr.expiration;
3927  close(dr.pfds[0]);
3928  close(dr.pfds[1]);
3929  return res;
3930 }
3931 
3932 int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass)
3933 {
3934  struct dundi_hint_metadata hmd;
3935  dundi_eid *avoid[1] = { NULL, };
3936  int direct[1] = { 0, };
3937  int expiration = dundi_cache_time;
3938  memset(&hmd, 0, sizeof(hmd));
3940  return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, 0, NULL, avoid, direct);
3941 }
3942 
3943 static void reschedule_precache(const char *number, const char *context, int expiration)
3944 {
3945  struct dundi_precache_queue *qe, *prev;
3946 
3947  AST_LIST_LOCK(&pcq);
3948  AST_LIST_TRAVERSE_SAFE_BEGIN(&pcq, qe, list) {
3949  if (!strcmp(number, qe->number) && !strcasecmp(context, qe->context)) {
3951  break;
3952  }
3953  }
3955  if (!qe) {
3956  int len = sizeof(*qe);
3957  int num_len = strlen(number) + 1;
3958  int context_len = strlen(context) + 1;
3959  if (!(qe = ast_calloc(1, len + num_len + context_len))) {
3960  AST_LIST_UNLOCK(&pcq);
3961  return;
3962  }
3963  strcpy(qe->number, number);
3964  qe->context = qe->number + num_len + 1;
3965  ast_copy_string(qe->context, context, context_len);
3966  }
3967  time(&qe->expiration);
3968  qe->expiration += expiration;
3969  if ((prev = AST_LIST_FIRST(&pcq))) {
3970  while (AST_LIST_NEXT(prev, list) && ((AST_LIST_NEXT(prev, list))->expiration <= qe->expiration))
3971  prev = AST_LIST_NEXT(prev, list);
3972  AST_LIST_INSERT_AFTER(&pcq, prev, qe, list);
3973  } else
3974  AST_LIST_INSERT_HEAD(&pcq, qe, list);
3975  AST_LIST_UNLOCK(&pcq);
3976 }
3977 
3978 static void dundi_precache_full(void)
3979 {
3980  struct dundi_mapping *cur;
3981  struct ast_context *con;
3982  struct ast_exten *e;
3983 
3984  AST_LIST_TRAVERSE(&mappings, cur, list) {
3985  ast_log(LOG_NOTICE, "Should precache context '%s'\n", cur->dcontext);
3987  con = NULL;
3988  while ((con = ast_walk_contexts(con))) {
3989  if (strcasecmp(cur->lcontext, ast_get_context_name(con)))
3990  continue;
3991  /* Found the match, now queue them all up */
3992  ast_rdlock_context(con);
3993  e = NULL;
3994  while ((e = ast_walk_context_extensions(con, e)))
3995  reschedule_precache(ast_get_extension_name(e), cur->dcontext, 0);
3996  ast_unlock_context(con);
3997  }
3999  }
4000 }
4001 
4002 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[])
4003 {
4004  struct dundi_request dr;
4005  struct dundi_hint_metadata hmd;
4006  struct dundi_result dr2[MAX_RESULTS];
4007  struct timeval start;
4008  struct dundi_mapping *maps = NULL, *cur;
4009  int nummaps = 0;
4010  int foundanswers;
4011  int foundcache, skipped, ttlms, ms;
4012  if (!context)
4013  context = "e164";
4014  ast_debug(1, "Precache internal (%s@%s)!\n", number, context);
4015 
4016  AST_LIST_LOCK(&peers);
4017  AST_LIST_TRAVERSE(&mappings, cur, list) {
4018  if (!strcasecmp(cur->dcontext, context))
4019  nummaps++;
4020  }
4021  if (nummaps) {
4022  maps = ast_alloca(nummaps * sizeof(*maps));
4023  nummaps = 0;
4024  AST_LIST_TRAVERSE(&mappings, cur, list) {
4025  if (!strcasecmp(cur->dcontext, context))
4026  maps[nummaps++] = *cur;
4027  }
4028  }
4030  if (!nummaps) {
4031  return -1;
4032  }
4033  ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
4034  memset(&dr2, 0, sizeof(dr2));
4035  memset(&dr, 0, sizeof(dr));
4036  memset(&hmd, 0, sizeof(hmd));
4037  dr.dr = dr2;
4038  ast_copy_string(dr.number, number, sizeof(dr.number));
4039  ast_copy_string(dr.dcontext, context ? context : "e164", sizeof(dr.dcontext));
4040  dr.maxcount = MAX_RESULTS;
4041  dr.expiration = dundi_cache_time;
4042  dr.hmd = &hmd;
4043  dr.pfds[0] = dr.pfds[1] = -1;
4044  if (pipe(dr.pfds) < 0) {
4045  ast_log(LOG_WARNING, "pipe() failed: %s\n", strerror(errno));
4046  return -1;
4047  }
4048  build_transactions(&dr, ttl, 0, &foundcache, &skipped, 0, 1, 1, NULL, avoids, NULL);
4049  optimize_transactions(&dr, 0);
4050  foundanswers = 0;
4051  precache_transactions(&dr, maps, nummaps, &dr.expiration, &foundanswers);
4052  if (foundanswers) {
4053  if (dr.expiration > 0)
4054  reschedule_precache(dr.number, dr.dcontext, dr.expiration);
4055  else
4056  ast_log(LOG_NOTICE, "Weird, expiration = %d, but need to precache for %s@%s?!\n", dr.expiration, dr.number, dr.dcontext);
4057  }
4058  start = ast_tvnow();
4059  while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms)) {
4060  if (dr.pfds[0] > -1) {
4061  ms = 100;
4062  ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
4063  } else
4064  usleep(1);
4065  }
4066  cancel_request(&dr);
4067  if (dr.pfds[0] > -1) {
4068  close(dr.pfds[0]);
4069  close(dr.pfds[1]);
4070  }
4071  return 0;
4072 }
4073 
4074 int dundi_precache(const char *context, const char *number)
4075 {
4076  dundi_eid *avoid[1] = { NULL, };
4077  return dundi_precache_internal(context, number, dundi_ttl, avoid);
4078 }
4079 
4080 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[])
4081 {
4082  int res;
4083  struct dundi_request dr;
4084  dundi_eid *rooteid=NULL;
4085  int x;
4086  int ttlms;
4087  int skipped=0;
4088  int foundcache=0;
4089  struct timeval start;
4090 
4091  ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
4092 
4093  for (x=0;avoid[x];x++)
4094  rooteid = avoid[x];
4095  /* Now perform real check */
4096  memset(&dr, 0, sizeof(dr));
4097  dr.hmd = hmd;
4098  dr.dei = dei;
4099  dr.pfds[0] = dr.pfds[1] = -1;
4100  ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
4101  memcpy(&dr.query_eid, eid, sizeof(dr.query_eid));
4102  if (rooteid)
4103  dr.root_eid = *rooteid;
4104  /* Create transactions */
4105  build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, 0, 0, NULL, avoid, NULL);
4106 
4107  /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't
4108  do this earlier because we didn't know if we were going to have transactions
4109  or not. */
4110  if (!ttl) {
4111  ast_set_flag_nonstd(hmd, DUNDI_HINT_TTL_EXPIRED);
4112  return 0;
4113  }
4114 
4115  /* Optimize transactions */
4116  optimize_transactions(&dr, 9999);
4117  /* Actually perform transactions */
4118  query_transactions(&dr);
4119  /* Wait for transaction to come back */
4120  start = ast_tvnow();
4121  while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms))
4122  usleep(1);
4123  res = dr.respcount;
4124  return res;
4125 }
4126 
4127 int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid)
4128 {
4129  dundi_eid *avoid[1] = { NULL, };
4130  struct dundi_hint_metadata hmd;
4131  memset(&hmd, 0, sizeof(hmd));
4132  return dundi_query_eid_internal(dei, dcontext, &eid, &hmd, dundi_ttl, 0, avoid);
4133 }
4134 
4135 enum {
4136  OPT_BYPASS_CACHE = (1 << 0),
4137 };
4138 
4139 AST_APP_OPTIONS(dundi_query_opts, BEGIN_OPTIONS
4140  AST_APP_OPTION('b', OPT_BYPASS_CACHE),
4141 END_OPTIONS );
4142 
4143 static int dundifunc_read(struct ast_channel *chan, const char *cmd, char *num, char *buf, size_t len)
4144 {
4145  int results;
4146  int x;
4147  struct dundi_result dr[MAX_RESULTS];
4148  AST_DECLARE_APP_ARGS(args,
4149  AST_APP_ARG(number);
4150  AST_APP_ARG(context);
4151  AST_APP_ARG(options);
4152  );
4153  char *parse;
4154  struct ast_flags opts = { 0, };
4155 
4156  buf[0] = '\0';
4157 
4158  if (ast_strlen_zero(num)) {
4159  ast_log(LOG_WARNING, "DUNDILOOKUP requires an argument (number)\n");
4160  return -1;
4161  }
4162 
4163  parse = ast_strdupa(num);
4164 
4165  AST_STANDARD_APP_ARGS(args, parse);
4166 
4167  if (!ast_strlen_zero(args.options)) {
4168  ast_app_parse_options(dundi_query_opts, &opts, NULL, args.options);
4169  }
4170  if (ast_strlen_zero(args.context)) {
4171  args.context = "e164";
4172  }
4173 
4174  results = dundi_lookup(dr, MAX_RESULTS, NULL, args.context, args.number, ast_test_flag(&opts, OPT_BYPASS_CACHE));
4175  if (results > 0) {
4176  sort_results(dr, results);
4177  for (x = 0; x < results; x++) {
4178  if (ast_test_flag(dr + x, DUNDI_FLAG_EXISTS)) {
4179  snprintf(buf, len, "%s/%s", dr[x].tech, dr[x].dest);
4180  break;
4181  }
4182  }
4183  }
4184 
4185  return 0;
4186 }
4187 
4188 /*! DUNDILOOKUP
4189  * \ingroup functions
4190 */
4191 
4193  .name = "DUNDILOOKUP",
4194  .read = dundifunc_read,
4195 };
4196 
4197 static unsigned int dundi_result_id;
4198 
4200  struct dundi_result results[MAX_RESULTS];
4201  int num_results;
4202  unsigned int id;
4203 };
4204 
4205 static void drds_destroy(struct dundi_result_datastore *drds)
4206 {
4207  ast_free(drds);
4208 }
4209 
4210 static void drds_destroy_cb(void *data)
4211 {
4212  struct dundi_result_datastore *drds = data;
4213  drds_destroy(drds);
4214 }
4215 
4216 static const struct ast_datastore_info dundi_result_datastore_info = {
4217  .type = "DUNDIQUERY",
4218  .destroy = drds_destroy_cb,
4219 };
4220 
4221 static int dundi_query_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4222 {
4223  AST_DECLARE_APP_ARGS(args,
4224  AST_APP_ARG(number);
4225  AST_APP_ARG(context);
4226  AST_APP_ARG(options);
4227  );
4228  struct ast_flags opts = { 0, };
4229  char *parse;
4230  struct dundi_result_datastore *drds;
4231  struct ast_datastore *datastore;
4232 
4233  if (ast_strlen_zero(data)) {
4234  ast_log(LOG_WARNING, "DUNDIQUERY requires an argument (number)\n");
4235  return -1;
4236  }
4237 
4238  if (!chan) {
4239  ast_log(LOG_ERROR, "DUNDIQUERY can not be used without a channel!\n");
4240  return -1;
4241  }
4242 
4243  parse = ast_strdupa(data);
4244 
4245  AST_STANDARD_APP_ARGS(args, parse);
4246 
4247  if (!ast_strlen_zero(args.options))
4248  ast_app_parse_options(dundi_query_opts, &opts, NULL, args.options);
4249 
4250  if (ast_strlen_zero(args.context))
4251  args.context = "e164";
4252 
4253  if (!(drds = ast_calloc(1, sizeof(*drds)))) {
4254  return -1;
4255  }
4256 
4257  drds->id = ast_atomic_fetchadd_int((int *) &dundi_result_id, 1);
4258  snprintf(buf, len, "%u", drds->id);
4259 
4260  if (!(datastore = ast_datastore_alloc(&dundi_result_datastore_info, buf))) {
4261  drds_destroy(drds);
4262  return -1;
4263  }
4264 
4265  datastore->data = drds;
4266 
4267  drds->num_results = dundi_lookup(drds->results, ARRAY_LEN(drds->results), NULL, args.context,
4268  args.number, ast_test_flag(&opts, OPT_BYPASS_CACHE));
4269 
4270  if (drds->num_results > 0)
4271  sort_results(drds->results, drds->num_results);
4272 
4273  ast_channel_lock(chan);
4274  ast_channel_datastore_add(chan, datastore);
4275  ast_channel_unlock(chan);
4276 
4277  return 0;
4278 }
4279 
4280 static struct ast_custom_function dundi_query_function = {
4281  .name = "DUNDIQUERY",
4282  .read = dundi_query_read,
4283 };
4284 
4285 static int dundi_result_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
4286 {
4287  AST_DECLARE_APP_ARGS(args,
4288  AST_APP_ARG(id);
4289  AST_APP_ARG(resultnum);
4290  );
4291  char *parse;
4292  unsigned int num;
4293  struct dundi_result_datastore *drds;
4294  struct ast_datastore *datastore;
4295  int res = -1;
4296 
4297  if (ast_strlen_zero(data)) {
4298  ast_log(LOG_WARNING, "DUNDIRESULT requires an argument (id and resultnum)\n");
4299  goto finish;
4300  }
4301 
4302  if (!chan) {
4303  ast_log(LOG_ERROR, "DUNDRESULT can not be used without a channel!\n");
4304  goto finish;
4305  }
4306 
4307  parse = ast_strdupa(data);
4308 
4309  AST_STANDARD_APP_ARGS(args, parse);
4310 
4311  if (ast_strlen_zero(args.id)) {
4312  ast_log(LOG_ERROR, "A result ID must be provided to DUNDIRESULT\n");
4313  goto finish;
4314  }
4315 
4316  if (ast_strlen_zero(args.resultnum)) {
4317  ast_log(LOG_ERROR, "A result number must be given to DUNDIRESULT!\n");
4318  goto finish;
4319  }
4320 
4321  ast_channel_lock(chan);
4322  datastore = ast_channel_datastore_find(chan, &dundi_result_datastore_info, args.id);
4323  ast_channel_unlock(chan);
4324 
4325  if (!datastore) {
4326  ast_log(LOG_WARNING, "No DUNDi results found for query ID '%s'\n", args.id);
4327  goto finish;
4328  }
4329 
4330  drds = datastore->data;
4331 
4332  if (!strcasecmp(args.resultnum, "getnum")) {
4333  snprintf(buf, len, "%d", drds->num_results < 0 ? 0 : drds->num_results);
4334  res = 0;
4335  goto finish;
4336  }
4337 
4338  if (sscanf(args.resultnum, "%30u", &num) != 1) {
4339  ast_log(LOG_ERROR, "Invalid value '%s' for resultnum to DUNDIRESULT!\n",
4340  args.resultnum);
4341  goto finish;
4342  }
4343 
4344  if (num && drds->num_results > 0 && num <= drds->num_results) {
4345  snprintf(buf, len, "%s/%s", drds->results[num - 1].tech, drds->results[num - 1].dest);
4346  res = 0;
4347  } else
4348  ast_log(LOG_WARNING, "Result number %u is not valid for DUNDi query results for ID %s!\n", num, args.id);
4349 
4350 finish:
4351  return res;
4352 }
4353 
4354 static struct ast_custom_function dundi_result_function = {
4355  .name = "DUNDIRESULT",
4356  .read = dundi_result_read,
4357 };
4358 
4359 static void mark_peers(void)
4360 {
4361  struct dundi_peer *peer;
4362  AST_LIST_LOCK(&peers);
4363  AST_LIST_TRAVERSE(&peers, peer, list) {
4364  peer->dead = 1;
4365  }
4367 }
4368 
4369 static void mark_mappings(void)
4370 {
4371  struct dundi_mapping *map;
4372 
4373  AST_LIST_LOCK(&peers);
4374  AST_LIST_TRAVERSE(&mappings, map, list) {
4375  map->dead = 1;
4376  }
4378 }
4379 
4380 static void destroy_permissions(struct permissionlist *permlist)
4381 {
4382  struct permission *perm;
4383 
4384  while ((perm = AST_LIST_REMOVE_HEAD(permlist, list)))
4385  ast_free(perm);
4386 }
4387 
4388 static void destroy_peer(struct dundi_peer *peer)
4389 {
4390  int idx;
4391 
4392  AST_SCHED_DEL(sched, peer->registerexpire);
4393  AST_SCHED_DEL(sched, peer->registerid);
4394  if (peer->regtrans) {
4395  destroy_trans(peer->regtrans, 0);
4396  }
4397  AST_SCHED_DEL(sched, peer->qualifyid);
4398  if (peer->qualtrans) {
4399  destroy_trans(peer->qualtrans, 0);
4400  }
4401  destroy_permissions(&peer->permit);
4402  destroy_permissions(&peer->include);
4403 
4404  /* Release lookup history */
4405  for (idx = 0; idx < ARRAY_LEN(peer->lookups); ++idx) {
4406  ast_free(peer->lookups[idx]);
4407  }
4408 
4409  ast_free(peer);
4410 }
4411 
4412 static void destroy_map(struct dundi_mapping *map)
4413 {
4414  ast_free(map->weightstr);
4415  ast_free(map);
4416 }
4417 
4418 static void prune_peers(void)
4419 {
4420  struct dundi_peer *peer;
4421 
4422  AST_LIST_LOCK(&peers);
4423  AST_LIST_TRAVERSE_SAFE_BEGIN(&peers, peer, list) {
4424  if (peer->dead) {
4426  destroy_peer(peer);
4427  }
4428  }
4431 }
4432 
4433 static void prune_mappings(void)
4434 {
4435  struct dundi_mapping *map;
4436 
4437  AST_LIST_LOCK(&peers);
4438  AST_LIST_TRAVERSE_SAFE_BEGIN(&mappings, map, list) {
4439  if (map->dead) {
4441  destroy_map(map);
4442  }
4443  }
4446 }
4447 
4448 static void append_permission(struct permissionlist *permlist, const char *s, int allow)
4449 {
4450  struct permission *perm;
4451 
4452  if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(s) + 1)))
4453  return;
4454 
4455  strcpy(perm->name, s);
4456  perm->allow = allow;
4457 
4458  AST_LIST_INSERT_TAIL(permlist, perm, list);
4459 }
4460 
4461 #define MAX_OPTS 128
4462 
4463 static void build_mapping(const char *name, const char *value)
4464 {
4465  char *t, *fields[MAX_OPTS];
4466  struct dundi_mapping *map;
4467  int x;
4468  int y;
4469 
4470  t = ast_strdupa(value);
4471 
4472  AST_LIST_TRAVERSE(&mappings, map, list) {
4473  /* Find a double match */
4474  if (!strcasecmp(map->dcontext, name) &&
4475  (!strncasecmp(map->lcontext, value, strlen(map->lcontext)) &&
4476  (!value[strlen(map->lcontext)] ||
4477  (value[strlen(map->lcontext)] == ','))))
4478  break;
4479  }
4480  if (!map) {
4481  if (!(map = ast_calloc(1, sizeof(*map))))
4482  return;
4483  AST_LIST_INSERT_HEAD(&mappings, map, list);
4484  map->dead = 1;
4485  }
4486  map->options = 0;
4487  memset(fields, 0, sizeof(fields));
4488  x = 0;
4489  while (t && x < MAX_OPTS) {
4490  fields[x++] = t;
4491  t = strchr(t, ',');
4492  if (t) {
4493  *t = '\0';
4494  t++;
4495  }
4496  } /* Russell was here, arrrr! */
4497  if ((x == 1) && ast_strlen_zero(fields[0])) {
4498  /* Placeholder mapping */
4499  ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
4500  map->dead = 0;
4501  } else if (x >= 4) {
4502  ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
4503  ast_copy_string(map->lcontext, fields[0], sizeof(map->lcontext));
4504  if ((sscanf(fields[1], "%30d", &map->_weight) == 1) && (map->_weight >= 0) && (map->_weight <= MAX_WEIGHT)) {
4505  ast_copy_string(map->dest, fields[3], sizeof(map->dest));
4506  if ((map->tech = str2tech(fields[2])))
4507  map->dead = 0;
4508  } else if (!strncmp(fields[1], "${", 2) && fields[1][strlen(fields[1]) - 1] == '}') {
4509  map->weightstr = ast_strdup(fields[1]);
4510  ast_copy_string(map->dest, fields[3], sizeof(map->dest));
4511  if ((map->tech = str2tech(fields[2])))
4512  map->dead = 0;
4513  } else {
4514  ast_log(LOG_WARNING, "Invalid weight '%s' specified, deleting entry '%s/%s'\n", fields[1], map->dcontext, map->lcontext);
4515  }
4516  for (y = 4;y < x; y++) {
4517  if (!strcasecmp(fields[y], "nounsolicited"))
4518  map->options |= DUNDI_FLAG_NOUNSOLICITED;
4519  else if (!strcasecmp(fields[y], "nocomunsolicit"))
4520  map->options |= DUNDI_FLAG_NOCOMUNSOLICIT;
4521  else if (!strcasecmp(fields[y], "residential"))
4522  map->options |= DUNDI_FLAG_RESIDENTIAL;
4523  else if (!strcasecmp(fields[y], "commercial"))
4524  map->options |= DUNDI_FLAG_COMMERCIAL;
4525  else if (!strcasecmp(fields[y], "mobile"))
4526  map->options |= DUNDI_FLAG_MOBILE;
4527  else if (!strcasecmp(fields[y], "nopartial"))
4528  map->options |= DUNDI_FLAG_INTERNAL_NOPARTIAL;
4529  else
4530  ast_log(LOG_WARNING, "Don't know anything about option '%s'\n", fields[y]);
4531  }
4532  } else
4533  ast_log(LOG_WARNING, "Expected at least %d arguments in map, but got only %d\n", 4, x);
4534 }
4535 
4536 /*! \note Called with the peers list already locked */
4537 static int do_register(const void *data)
4538 {
4539  struct dundi_ie_data ied;
4540  struct dundi_peer *peer = (struct dundi_peer *)data;
4541  char eid_str[20];
4542  char eid_str2[20];
4543  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));
4544  peer->registerid = ast_sched_add(sched, default_expiration * 1000, do_register, data);
4545  /* Destroy old transaction if there is one */
4546  if (peer->regtrans)
4547  destroy_trans(peer->regtrans, 0);
4548  peer->regtrans = create_transaction(peer);
4549  if (peer->regtrans) {
4550  ast_set_flag(peer->regtrans, FLAG_ISREG);
4551  memset(&ied, 0, sizeof(ied));
4552  dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
4553  dundi_ie_append_eid(&ied, DUNDI_IE_EID, &peer->regtrans->us_eid);
4554  dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
4555  dundi_send(peer->regtrans, DUNDI_COMMAND_REGREQ, 0, 0, &ied);
4556 
4557  } else
4558  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));
4559 
4560  return 0;
4561 }
4562 
4563 static int do_qualify(const void *data)
4564 {
4565  struct dundi_peer *peer = (struct dundi_peer *)data;
4566  peer->qualifyid = -1;
4567  qualify_peer(peer, 0);
4568  return 0;
4569 }
4570 
4571 static void qualify_peer(struct dundi_peer *peer, int schedonly)
4572 {
4573  int when;
4574  AST_SCHED_DEL(sched, peer->qualifyid);
4575  if (peer->qualtrans)
4576  destroy_trans(peer->qualtrans, 0);
4577  peer->qualtrans = NULL;
4578  if (peer->maxms > 0) {
4579  when = 60000;
4580  if (peer->lastms < 0)
4581  when = 10000;
4582  if (schedonly)
4583  when = 5000;
4584  peer->qualifyid = ast_sched_add(sched, when, do_qualify, peer);
4585  if (!schedonly)
4586  peer->qualtrans = create_transaction(peer);
4587  if (peer->qualtrans) {
4588  peer->qualtx = ast_tvnow();
4589  ast_set_flag(peer->qualtrans, FLAG_ISQUAL);
4590  dundi_send(peer->qualtrans, DUNDI_COMMAND_NULL, 0, 1, NULL);
4591  }
4592  }
4593 }
4594 static void populate_addr(struct dundi_peer *peer, dundi_eid *eid)
4595 {
4596  char data[256];
4597  char *c;
4598  int port, expire;
4599  char eid_str[20];
4600  ast_eid_to_str(eid_str, sizeof(eid_str), eid);
4601  if (!ast_db_get("dundi/dpeers", eid_str, data, sizeof(data))) {
4602  /*
4603  * data is in the form:
4604  * IPv6 address: [ffff:ffff::ffff:ffff]:port:expire
4605  * IPv4 address: a.b.c.d:port:expire
4606  */
4607  c = data;
4608  if (*c == '[') {
4609  /* Need to skip over the IPv6 address. */
4610  c = strchr(c, ']');
4611  }
4612  if (c) {
4613  c = strchr(c, ':');
4614  }
4615  if (c) {
4616  *c = '\0';
4617  c++;
4618  if (sscanf(c, "%5d:%30d", &port, &expire) == 2) {
4619  /* Got it! */
4620  struct ast_sockaddr *addrs;
4621 
4622  if (ast_sockaddr_resolve(&addrs, data, PARSE_PORT_FORBID, AF_UNSPEC) > 0){
4623  ast_sockaddr_copy(&peer->addr, &addrs[0]);
4624  ast_free(addrs);
4625  }
4626  ast_sockaddr_set_port(&peer->addr, port);
4627  peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
4628  }
4629  }
4630  }
4631 }
4632 
4633 
4634 static void build_peer(dundi_eid *eid, struct ast_variable *v, int *globalpcmode)
4635 {
4636  struct dundi_peer *peer;
4637  dundi_eid testeid;
4638  int needregister=0;
4639  char eid_str[20];
4640  int port = 0;
4641 
4642  AST_LIST_LOCK(&peers);
4643  AST_LIST_TRAVERSE(&peers, peer, list) {
4644  if (!ast_eid_cmp(&peer->eid, eid)) {
4645  break;
4646  }
4647  }
4648  if (!peer) {
4649  /* Add us into the list */
4650  if (!(peer = ast_calloc(1, sizeof(*peer)))) {
4652  return;
4653  }
4654  peer->registerid = -1;
4655  peer->registerexpire = -1;
4656  peer->qualifyid = -1;
4657  populate_addr(peer, eid);
4658  AST_LIST_INSERT_HEAD(&peers, peer, list);
4659  }
4660  peer->dead = 0;
4661  peer->eid = *eid;
4662  peer->us_eid = global_eid;
4663  destroy_permissions(&peer->permit);
4664  destroy_permissions(&peer->include);
4665  AST_SCHED_DEL(sched, peer->registerid);
4666  for (; v; v = v->next) {
4667  if (!strcasecmp(v->name, "inkey")) {
4668  ast_copy_string(peer->inkey, v->value, sizeof(peer->inkey));
4669  } else if (!strcasecmp(v->name, "outkey")) {
4670  ast_copy_string(peer->outkey, v->value, sizeof(peer->outkey));
4671  } else if (!strcasecmp(v->name, "port")) {
4672  port = atoi(v->value);
4673  } else if (!strcasecmp(v->name, "host")) {
4674  if (!strcasecmp(v->value, "dynamic")) {
4675  peer->dynamic = 1;
4676  } else {
4677  struct ast_sockaddr *addrs;
4678 
4679  if (ast_sockaddr_resolve(&addrs, v->value, PARSE_PORT_FORBID, AF_UNSPEC) > 0) {
4680  ast_sockaddr_copy(&peer->addr, &addrs[0]);
4681  peer->dynamic = 0;
4682  ast_free(addrs);
4683  } else {
4684  ast_log(LOG_WARNING, "Unable to find host '%s' at line %d\n", v->value, v->lineno);
4685  peer->dead = 1;
4686  }
4687  }
4688  } else if (!strcasecmp(v->name, "ustothem")) {
4689  if (!ast_str_to_eid(&testeid, v->value))
4690  peer->us_eid = testeid;
4691  else
4692  ast_log(LOG_WARNING, "'%s' is not a valid DUNDi Entity Identifier at line %d\n", v->value, v->lineno);
4693  } else if (!strcasecmp(v->name, "include")) {
4694  append_permission(&peer->include, v->value, 1);
4695  } else if (!strcasecmp(v->name, "permit")) {
4696  append_permission(&peer->permit, v->value, 1);
4697  } else if (!strcasecmp(v->name, "noinclude")) {
4698  append_permission(&peer->include, v->value, 0);
4699  } else if (!strcasecmp(v->name, "deny")) {
4700  append_permission(&peer->permit, v->value, 0);
4701  } else if (!strcasecmp(v->name, "register")) {
4702  needregister = ast_true(v->value);
4703  } else if (!strcasecmp(v->name, "order")) {
4704  if (!strcasecmp(v->value, "primary"))
4705  peer->order = 0;
4706  else if (!strcasecmp(v->value, "secondary"))
4707  peer->order = 1;
4708  else if (!strcasecmp(v->value, "tertiary"))
4709  peer->order = 2;
4710  else if (!strcasecmp(v->value, "quartiary"))
4711  peer->order = 3;
4712  else {
4713  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);
4714  }
4715  } else if (!strcasecmp(v->name, "qualify")) {
4716  if (!strcasecmp(v->value, "no")) {
4717  peer->maxms = 0;
4718  } else if (!strcasecmp(v->value, "yes")) {
4719  peer->maxms = DEFAULT_MAXMS;
4720  } else if (sscanf(v->value, "%30d", &peer->maxms) != 1) {
4721  ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of dundi.conf\n",
4722  ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), v->lineno);
4723  peer->maxms = 0;
4724  }
4725  } else if (!strcasecmp(v->name, "model")) {
4726  if (!strcasecmp(v->value, "inbound"))
4727  peer->model = DUNDI_MODEL_INBOUND;
4728  else if (!strcasecmp(v->value, "outbound"))
4729  peer->model = DUNDI_MODEL_OUTBOUND;
4730  else if (!strcasecmp(v->value, "symmetric"))
4731  peer->model = DUNDI_MODEL_SYMMETRIC;
4732  else if (!strcasecmp(v->value, "none"))
4733  peer->model = 0;
4734  else {
4735  ast_log(LOG_WARNING, "Unknown model '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
4736  v->value, v->lineno);
4737  }
4738  } else if (!strcasecmp(v->name, "precache")) {
4739  if (!strcasecmp(v->value, "inbound"))
4740  peer->pcmodel = DUNDI_MODEL_INBOUND;
4741  else if (!strcasecmp(v->value, "outbound"))
4742  peer->pcmodel = DUNDI_MODEL_OUTBOUND;
4743  else if (!strcasecmp(v->value, "symmetric"))
4744  peer->pcmodel = DUNDI_MODEL_SYMMETRIC;
4745  else if (!strcasecmp(v->value, "none"))
4746  peer->pcmodel = 0;
4747  else {
4748  ast_log(LOG_WARNING, "Unknown pcmodel '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
4749  v->value, v->lineno);
4750  }
4751  }
4752  }
4753 
4754  if (!ast_sockaddr_isnull(&peer->addr)) {
4755  ast_sockaddr_set_port(&peer->addr, (0 < port) ? port : DUNDI_PORT);
4756  }
4757 
4758  (*globalpcmode) |= peer->pcmodel;
4759  if (!peer->model && !peer->pcmodel) {
4760  ast_log(LOG_WARNING, "Peer '%s' lacks a model or pcmodel, discarding!\n",
4761  ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4762  peer->dead = 1;
4763  } else if ((peer->model & DUNDI_MODEL_INBOUND) && (peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
4764  ast_log(LOG_WARNING, "Peer '%s' may not be both inbound/symmetric model and outbound/symmetric precache model, discarding!\n",
4765  ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4766  peer->dead = 1;
4767  } else if ((peer->model & DUNDI_MODEL_OUTBOUND) && (peer->pcmodel & DUNDI_MODEL_INBOUND)) {
4768  ast_log(LOG_WARNING, "Peer '%s' may not be both outbound/symmetric model and inbound/symmetric precache model, discarding!\n",
4769  ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4770  peer->dead = 1;
4771  } else if (!AST_LIST_EMPTY(&peer->include) && !(peer->model & DUNDI_MODEL_OUTBOUND) && !(peer->pcmodel & DUNDI_MODEL_INBOUND)) {
4772  ast_log(LOG_WARNING, "Peer '%s' is supposed to be included in outbound searches but isn't an outbound peer or inbound precache!\n",
4773  ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4774  } else if (!AST_LIST_EMPTY(&peer->permit) && !(peer->model & DUNDI_MODEL_INBOUND) && !(peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
4775  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",
4776  ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4777  } else {
4778  if (ast_eid_cmp(&peer->eid, &empty_eid)) {
4779  /* Schedule any items for explicitly configured peers. */
4780  if (needregister) {
4781  peer->registerid = ast_sched_add(sched, 2000, do_register, peer);
4782  }
4783  qualify_peer(peer, 1);
4784  }
4785  }
4787 }
4788 
4789 static int dundi_helper(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *data, int flag)
4790 {
4791  struct dundi_result results[MAX_RESULTS];
4792  int res;
4793  int x;
4794  int found = 0;
4795  if (ast_strlen_zero(data))
4796  data = context;
4797 
4798  res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
4799  for (x=0;x<res;x++) {
4800  if (ast_test_flag(results + x, flag))
4801  found++;
4802  }
4803  if (found >= priority)
4804  return 1;
4805  return 0;
4806 }
4807 
4808 static int dundi_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4809 {
4810  return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_EXISTS);
4811 }
4812 
4813 static int dundi_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4814 {
4815  return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_CANMATCH);
4816 }
4817 
4818 static int dundi_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4819 {
4820  struct dundi_result results[MAX_RESULTS];
4821  int res;
4822  int x=0;
4823  char req[1024];
4824  const char *dundiargs;
4825 
4826  if (ast_strlen_zero(data))
4827  data = context;
4828 
4829  res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
4830  if (res > 0) {
4831  sort_results(results, res);
4832  for (x=0;x<res;x++) {
4833  if (ast_test_flag(results + x, DUNDI_FLAG_EXISTS)) {
4834  if (!--priority)
4835  break;
4836  }
4837  }
4838  }
4839  if (x < res) {
4840  /* Got a hit! */
4841  dundiargs = pbx_builtin_getvar_helper(chan, "DUNDIDIALARGS");
4842  /* Backwards compatibility with lookups using chan_sip even if we don't have it anymore:
4843  * At a protocol level, "SIP" will always be specified, but depending on our configuration,
4844  * we will use the user-specified channel driver (from dundi.conf) to complete the call.
4845  */
4846  if (!strcasecmp(results[x].tech, "SIP") || !strcasecmp(results[x].tech, "PJSIP")) {
4847  /* Only "SIP" is a valid technology for a DUNDi peer to communicate.
4848  * But if they tell use to use "PJSIP" instead, just interpret it as if they said "SIP" instead. */
4849  if (strcasecmp(results[x].tech, "SIP")) {
4850  ast_log(LOG_WARNING, "%s cannot be specified by DUNDi peers (peer should use SIP for DUNDi lookups instead)\n", results[x].tech);
4851  }
4852  /* Use whatever we're configured to use for SIP protocol calls. */
4853  results[x].techint = outgoing_sip_tech;
4854  ast_copy_string(results[x].tech, tech2str(outgoing_sip_tech), sizeof(results[x].tech));
4855  }
4856  /* PJSIP requires an endpoint to be specified explicitly. */
4857  if (outgoing_sip_tech == DUNDI_PROTO_PJSIP) {
4858  char *number, *ip = ast_strdupa(results[x].dest);
4859  if (ast_strlen_zero(pjsip_outgoing_endpoint)) {
4860  ast_log(LOG_WARNING, "PJSIP calls require an endpoint to be specified explicitly (use the pjsip_outgoing_endpoint option in dundi.conf)\n");
4861  return -1;
4862  }
4863  /* Take IP/number and turn it into sip:number@IP */
4864  if (ast_strlen_zero(ip)) {
4865  ast_log(LOG_WARNING, "PJSIP destination is empty?\n");
4866  return -1;
4867  }
4868  number = strsep(&ip, "/");
4869  snprintf(req, sizeof(req), "%s/%s/sip:%s@%s,,%s", results[x].tech, pjsip_outgoing_endpoint, S_OR(number, ""), ip, S_OR(dundiargs, ""));
4870  ast_debug(1, "Finalized PJSIP Dial: %s\n", req);
4871  } else { /* SIP, or something else. */
4872  snprintf(req, sizeof(req), "%s/%s,,%s", results[x].tech, results[x].dest, S_OR(dundiargs, ""));
4873  }
4874  res = ast_pbx_exec_application(chan, "Dial", req);
4875  } else {
4876  res = -1;
4877  }
4878  return res;
4879 }
4880 
4881 static int dundi_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
4882 {
4883  return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_MATCHMORE);
4884 }
4885 
4886 static struct ast_switch dundi_switch = {
4887  .name = "DUNDi",
4888  .description = "DUNDi Discovered Dialplan Switch",
4889  .exists = dundi_exists,
4890  .canmatch = dundi_canmatch,
4891  .exec = dundi_exec,
4892  .matchmore = dundi_matchmore,
4893 };
4894 
4895 static int get_ipaddress(char *ip, size_t size, const char *str, int family)
4896 {
4897  struct ast_sockaddr *addrs;
4898 
4899  if (!ast_sockaddr_resolve(&addrs, str, 0, family)) {
4900  return -1;
4901  }
4902 
4903  ast_copy_string(ip, ast_sockaddr_stringify_host(&addrs[0]), size);
4904  ast_free(addrs);
4905 
4906  return 0;
4907 }
4908 
4909 static void set_host_ipaddr(struct ast_sockaddr *sin)
4910 {
4911  char hn[MAXHOSTNAMELEN];
4912  struct addrinfo hints;
4913  int family;
4914 
4915  memset(&hints, 0, sizeof(hints));
4916 
4917  if (ast_sockaddr_is_ipv6(sin)) {
4918  family = AF_INET6;
4919  } else {
4920  family = AF_INET;
4921  }
4922 
4923  if (gethostname(hn, sizeof(hn) - 1) < 0) {
4924  ast_log(LOG_WARNING, "Unable to get host name!\n");
4925  return;
4926  }
4927 
4928  get_ipaddress(ipaddr, sizeof(ipaddr), hn, family);
4929 }
4930 
4931 static int set_config(char *config_file, struct ast_sockaddr *sin, int reload, struct ast_sockaddr *sin2)
4932 {
4933  struct ast_config *cfg;
4934  struct ast_variable *v;
4935  char *cat;
4936  int x;
4937  struct ast_flags config_flags = { 0 };
4938  static int last_port = 0;
4939  int port = 0;
4940  int globalpcmodel = 0;
4941  dundi_eid testeid;
4942  char bind_addr[80]={0,};
4943  char bind_addr2[80]={0,};
4944 
4945  if (!(cfg = ast_config_load(config_file, config_flags)) || cfg == CONFIG_STATUS_FILEINVALID) {
4946  ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
4947  return -1;
4948  }
4949 
4950  dundi_ttl = DUNDI_DEFAULT_TTL;
4951  dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
4952  any_peer = NULL;
4953  outgoing_sip_tech = DUNDI_PROTO_PJSIP; /* Default for new versions */
4954 
4955  AST_LIST_LOCK(&peers);
4956 
4958  ast_log(LOG_WARNING, "Entity ID is not set.\n");
4959  }
4960  memcpy(&global_eid, &ast_eid_default, sizeof(global_eid));
4961 
4962  global_storehistory = 0;
4963  ast_copy_string(secretpath, "dundi", sizeof(secretpath));
4964  v = ast_variable_browse(cfg, "general");
4965  while(v) {
4966  if (!strcasecmp(v->name, "port")){
4967  port = atoi(v->value);
4968  } else if (!strcasecmp(v->name, "bindaddr")) {
4969  if (get_ipaddress(bind_addr, sizeof(bind_addr), v->value, AF_UNSPEC) == 0) {
4970  if (!ast_sockaddr_parse(sin, bind_addr, 0)) {
4971  ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
4972  }
4973  }
4974  } else if (!strcasecmp(v->name, "bindaddr2")) {
4975  if (get_ipaddress(bind_addr2, sizeof(bind_addr2), v->value, AF_UNSPEC) == 0) {
4976  if (!ast_sockaddr_parse(sin2, bind_addr2, 0)) {
4977  ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
4978  }
4979  }
4980  } else if (!strcasecmp(v->name, "authdebug")) {
4981  authdebug = ast_true(v->value);
4982  } else if (!strcasecmp(v->name, "ttl")) {
4983  if ((sscanf(v->value, "%30d", &x) == 1) && (x > 0) && (x < DUNDI_DEFAULT_TTL)) {
4984  dundi_ttl = x;
4985  } else {
4986  ast_log(LOG_WARNING, "'%s' is not a valid TTL at line %d, must be number from 1 to %d\n",
4987  v->value, v->lineno, DUNDI_DEFAULT_TTL);
4988  }
4989  } else if (!strcasecmp(v->name, "autokill")) {
4990  if (sscanf(v->value, "%30d", &x) == 1) {
4991  if (x >= 0)
4992  global_autokilltimeout = x;
4993  else
4994  ast_log(LOG_NOTICE, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno);
4995  } else if (ast_true(v->value)) {
4996  global_autokilltimeout = DEFAULT_MAXMS;
4997  } else {
4998  global_autokilltimeout = 0;
4999  }
5000  } else if (!strcasecmp(v->name, "entityid")) {
5001  if (!ast_str_to_eid(&testeid, v->value))
5002  global_eid = testeid;
5003  else
5004  ast_log(LOG_WARNING, "Invalid global endpoint identifier '%s' at line %d\n", v->value, v->lineno);
5005  } else if (!strcasecmp(v->name, "tos")) {
5006  if (ast_str2tos(v->value, &tos))
5007  ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
5008  } else if (!strcasecmp(v->name, "department")) {
5009  ast_copy_string(dept, v->value, sizeof(dept));
5010  } else if (!strcasecmp(v->name, "organization")) {
5011  ast_copy_string(org, v->value, sizeof(org));
5012  } else if (!strcasecmp(v->name, "locality")) {
5013  ast_copy_string(locality, v->value, sizeof(locality));
5014  } else if (!strcasecmp(v->name, "stateprov")) {
5015  ast_copy_string(stateprov, v->value, sizeof(stateprov));
5016  } else if (!strcasecmp(v->name, "country")) {
5017  ast_copy_string(country, v->value, sizeof(country));
5018  } else if (!strcasecmp(v->name, "email")) {
5019  ast_copy_string(email, v->value, sizeof(email));
5020  } else if (!strcasecmp(v->name, "phone")) {
5021  ast_copy_string(phone, v->value, sizeof(phone));
5022  } else if (!strcasecmp(v->name, "storehistory")) {
5023  global_storehistory = ast_true(v->value);
5024  } else if (!strcasecmp(v->name, "outgoing_sip_tech")) {
5025  int outgoing_tech = str2tech(v->value);
5026  if (outgoing_tech != DUNDI_PROTO_SIP && outgoing_tech != DUNDI_PROTO_PJSIP) {
5027  ast_log(LOG_WARNING, "outgoing_sip_tech must be SIP or PJSIP\n");
5028  } else {
5029  outgoing_sip_tech = outgoing_tech;
5030  }
5031  } else if (!strcasecmp(v->name, "pjsip_outgoing_endpoint")) {
5032  ast_copy_string(pjsip_outgoing_endpoint, v->value, sizeof(pjsip_outgoing_endpoint));
5033  } else if (!strcasecmp(v->name, "cachetime")) {
5034  if ((sscanf(v->value, "%30d", &x) == 1)) {
5035  dundi_cache_time = x;
5036  } else {
5037  ast_log(LOG_WARNING, "'%s' is not a valid cache time at line %d. Using default value '%d'.\n",
5038  v->value, v->lineno, DUNDI_DEFAULT_CACHE_TIME);
5039  }
5040  }
5041  v = v->next;
5042  }
5043 
5044  if (port == 0) {
5045  port = DUNDI_PORT;
5046  }
5047 
5048  if (ast_sockaddr_isnull(sin)) {
5049  sprintf(bind_addr, "0.0.0.0:%d", port);
5050  ast_sockaddr_parse(sin, bind_addr, 0);
5051  } else {
5052  ast_sockaddr_set_port(sin, port);
5053  }
5054 
5055  if (last_port == 0) {
5056  last_port = port;
5057  } else if (last_port != port) {
5058  ast_log(LOG_WARNING, "change to port ignored until next asterisk re-start\n");
5059  }
5060 
5061  set_host_ipaddr(sin);
5062 
5063  if (!ast_sockaddr_isnull(sin2)) {
5064  ast_sockaddr_set_port(sin2, port);
5065  }
5066 
5068 
5069  mark_mappings();
5070  v = ast_variable_browse(cfg, "mappings");
5071  while (v) {
5072  AST_LIST_LOCK(&peers);
5073  build_mapping(v->name, v->value);
5075  v = v->next;
5076  }
5077  prune_mappings();
5078 
5079  mark_peers();
5080  cat = ast_category_browse(cfg, NULL);
5081  while(cat) {
5082  if (strcasecmp(cat, "general") && strcasecmp(cat, "mappings")) {
5083  /* Entries */
5084  if (!ast_str_to_eid(&testeid, cat))
5085  build_peer(&testeid, ast_variable_browse(cfg, cat), &globalpcmodel);
5086  else if (!strcasecmp(cat, "*")) {
5087  build_peer(&empty_eid, ast_variable_browse(cfg, cat), &globalpcmodel);
5088  any_peer = find_peer(NULL);
5089  } else
5090  ast_log(LOG_NOTICE, "Ignoring invalid EID entry '%s'\n", cat);
5091  }
5092  cat = ast_category_browse(cfg, cat);
5093  }
5094  prune_peers();
5095 
5096  ast_config_destroy(cfg);
5097  load_password();
5098  if (globalpcmodel & DUNDI_MODEL_OUTBOUND)
5099  dundi_precache_full();
5100  return 0;
5101 }
5102 
5103 static int unload_module(void)
5104 {
5105  ast_cli_unregister_multiple(cli_dundi, ARRAY_LEN(cli_dundi));
5106  ast_unregister_switch(&dundi_switch);
5107  ast_custom_function_unregister(&dundi_function);
5108  ast_custom_function_unregister(&dundi_query_function);
5109  ast_custom_function_unregister(&dundi_result_function);
5110 
5111  /* Stop all currently running threads */
5112  dundi_shutdown = 1;
5113  if (netthreadid != AST_PTHREADT_NULL) {
5114  pthread_kill(netthreadid, SIGURG);
5115  pthread_join(netthreadid, NULL);
5116  netthreadid = AST_PTHREADT_NULL;
5117  }
5118  if (precachethreadid != AST_PTHREADT_NULL) {
5119  pthread_kill(precachethreadid, SIGURG);
5120  pthread_join(precachethreadid, NULL);
5121  precachethreadid = AST_PTHREADT_NULL;
5122  }
5123  if (clearcachethreadid != AST_PTHREADT_NULL) {
5124  pthread_cancel(clearcachethreadid);
5125  pthread_join(clearcachethreadid, NULL);
5126  clearcachethreadid = AST_PTHREADT_NULL;
5127  }
5128 
5129  if (netsocket >= 0) {
5130  close(netsocket);
5131  }
5132 
5133  if (netsocket2 >= 0) {
5134  close(netsocket2);
5135  }
5136 
5137  mark_mappings();
5138  prune_mappings();
5139  mark_peers();
5140  prune_peers();
5141 
5142  if (-1 < netsocket) {
5143  close(netsocket);
5144  netsocket = -1;
5145  }
5146  if (io) {
5147  io_context_destroy(io);
5148  io = NULL;
5149  }
5150 
5151  if (sched) {
5153  sched = NULL;
5154  }
5155 
5156  return 0;
5157 }
5158 
5159 static int reload(void)
5160 {
5161  struct ast_sockaddr sin;
5162  struct ast_sockaddr sin2;
5163 
5164  ast_sockaddr_setnull(&sin);
5165  ast_sockaddr_setnull(&sin2);
5166 
5167  if (set_config("dundi.conf", &sin, 1, &sin2))
5168  return AST_MODULE_LOAD_FAILURE;
5169 
5170  return AST_MODULE_LOAD_SUCCESS;
5171 }
5172 
5173 static int load_module(void)
5174 {
5175  struct ast_sockaddr sin;
5176  struct ast_sockaddr sin2;
5177 
5178  dundi_set_output(dundi_debug_output);
5179  dundi_set_error(dundi_error_output);
5180 
5181  /* Make a UDP socket */
5182  io = io_context_create();
5183  sched = ast_sched_context_create();
5184 
5185  if (!io || !sched) {
5186  goto declined;
5187  }
5188 
5189  ast_sockaddr_setnull(&sin);
5190  ast_sockaddr_setnull(&sin2);
5191 
5192  if (set_config("dundi.conf", &sin, 0, &sin2)) {
5193  goto declined;
5194  }
5195 
5196  if (!ast_sockaddr_isnull(&sin2)) {
5197  if ((ast_sockaddr_is_ipv4(&sin) == ast_sockaddr_is_ipv4(&sin2)) || (ast_sockaddr_is_ipv6(&sin) == ast_sockaddr_is_ipv6(&sin2))) {
5198  ast_log(LOG_ERROR, "bindaddr & bindaddr2 should be different IP protocols.\n");
5199  goto declined;
5200  }
5201 
5202  /*bind netsocket to ipv4, netsocket2 to ipv6 */
5203 
5204  netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
5205  netsocket2 = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
5206  if (netsocket < 0 || netsocket2 < 0) {
5207  ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
5208  goto declined;
5209  }
5210  if (ast_sockaddr_is_ipv4(&sin)) {
5211  if (ast_bind(netsocket, &sin)) {
5212  ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
5213  ast_sockaddr_stringify(&sin), strerror(errno));
5214  goto declined;
5215  }
5216  if (ast_bind(netsocket2, &sin2)) {
5217  ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
5218  ast_sockaddr_stringify(&sin2), strerror(errno));
5219  goto declined;
5220  }
5221  } else {
5222  if (ast_bind(netsocket, &sin2)) {
5223  ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
5224  ast_sockaddr_stringify(&sin2), strerror(errno));
5225  goto declined;
5226  }
5227  if (ast_bind(netsocket2, &sin)) {
5228  ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
5229  ast_sockaddr_stringify(&sin), strerror(errno));
5230  goto declined;
5231  }
5232  }
5233  ast_set_qos(netsocket, tos, 0, "DUNDi");
5234  ast_set_qos(netsocket2, tos, 0, "DUNDi");
5235  } else {
5236  if (ast_sockaddr_is_ipv6(&sin)) {
5237  netsocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
5238  } else {
5239  netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
5240  }
5241  if (netsocket < 0) {
5242  ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
5243  goto declined;
5244  }
5245  if (ast_bind(netsocket, &sin)) {
5246  ast_log(LOG_ERROR, "Unable to bind to %s : %s\n",
5247  ast_sockaddr_stringify(&sin), strerror(errno));
5248  goto declined;
5249  }
5250  ast_set_qos(netsocket, tos, 0, "DUNDi");
5251  }
5252 
5253  if (start_network_thread()) {
5254  ast_log(LOG_ERROR, "Unable to start network thread\n");
5255  goto declined;
5256  }
5257 
5258  ast_cli_register_multiple(cli_dundi, ARRAY_LEN(cli_dundi));
5259  if (ast_register_switch(&dundi_switch))
5260  ast_log(LOG_ERROR, "Unable to register DUNDi switch\n");
5261  ast_custom_function_register(&dundi_function);
5262  ast_custom_function_register(&dundi_query_function);
5263  ast_custom_function_register(&dundi_result_function);
5264 
5265  ast_verb(2, "DUNDi Ready and Listening on %s\n", ast_sockaddr_stringify(&sin));
5266  if (!ast_sockaddr_isnull(&sin2))
5267  ast_verb(2, "DUNDi Ready and Listening on %s\n", ast_sockaddr_stringify(&sin2));
5268 
5269  return AST_MODULE_LOAD_SUCCESS;
5270 
5271 declined:
5272  unload_module();
5273  return AST_MODULE_LOAD_DECLINE;
5274 }
5275 
5276 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Distributed Universal Number Discovery (DUNDi)",
5277  .support_level = AST_MODULE_SUPPORT_EXTENDED,
5278  .load = load_module,
5279  .unload = unload_module,
5280  .reload = reload,
5281  .optional_modules = "res_crypto",
5282 );
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:286
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
struct ast_variable * next
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:4127
int ast_unlock_context(struct ast_context *con)
Definition: pbx.c:8491
#define DUNDI_IE_EXPIRATION
Definition: dundi.h:191
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:4195
Main Channel structure associated with a channel.
Definition: pbx_dundi.c:344
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:3932
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:213
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
#define DUNDI_IE_EID_DIRECT
Definition: dundi.h:187
Asterisk locking-related definitions:
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's own link...
Definition: pbx.c:237
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:421
static struct iax2_peer * find_peer(const char *name, int realtime)
Definition: chan_iax2.c:2056
Distributed Universal Number Discovery (DUNDi) See also.
unsigned char aseqno
Definition: pbx_dundi.c:267
ast_aes_encrypt_key ecx
Definition: pbx_dundi.c:254
struct dundi_request * parent
Definition: pbx_dundi.c:270
#define DUNDI_IE_IPADDR
Definition: dundi.h:208
#define DUNDI_IE_CACHEBYPASS
Definition: dundi.h:209
int ast_sched_runq(struct ast_sched_context *con)
Runs the queue.
Definition: sched.c:786
int ast_sockaddr_parse(struct ast_sockaddr *addr, const char *str, int flags)
Parse an IPv4 or IPv6 address string.
Definition: netsock2.c:230
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define DUNDI_COMMAND_ENCREJ
Definition: dundi.h:175
char * ast_eid_to_str(char *s, int maxlen, struct ast_eid *eid)
Convert an EID to a string.
Definition: utils.c:2839
static int do_register_expire(const void *data)
Definition: pbx_dundi.c:1327
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:167
unsigned char encdata[0]
Definition: dundi.h:101
#define DUNDI_TIMING_HISTORY
Definition: pbx_dundi.c:164
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
descriptor for a cli entry.
Definition: cli.h:171
#define DUNDI_DEFAULT_CACHE_TIME
Definition: dundi.h:220
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
#define DUNDI_IE_SHAREDKEY
Definition: dundi.h:196
void ast_db_freetree(struct ast_db_entry *entry)
Free structure created by ast_db_gettree()
Definition: main/db.c:677
Provide cryptographic signature routines.
unsigned int flags
Definition: pbx_dundi.c:256
Structure for variables, used for configurations and for channel variables.
unsigned char rxenckey[256]
Definition: pbx_dundi.c:319
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:439
#define DUNDI_IE_CAUSE
Definition: dundi.h:193
#define AST_IO_IN
Definition: io.h:34
Definition: sched.c:76
int ast_ignore_pattern(const char *context, const char *pattern)
Checks to see if a number should be ignored.
Definition: pbx.c:6879
Structure for a data store type.
Definition: datastore.h:31
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
#define DUNDI_IE_EMAIL
Definition: dundi.h:206
#define DUNDI_IE_REQEID
Definition: dundi.h:194
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
int ast_rdlock_contexts(void)
Read locks the context list.
Definition: pbx.c:8468
#define DUNDI_COMMAND_ENCRYPT
Definition: dundi.h:174
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:450
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:107
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
Structure for a data store object.
Definition: datastore.h:64
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:2399
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:966
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
struct packetlist lasttrans
Definition: pbx_dundi.c:269
int ast_sockaddr_cmp(const struct ast_sockaddr *a, const struct ast_sockaddr *b)
Compares two ast_sockaddr structures.
Definition: netsock2.c:388
dundi_eid us_eid
Definition: pbx_dundi.c:252
I/O Management (derived from Cheops-NG)
#define DUNDI_IE_VERSION
Definition: dundi.h:190
#define AST_LIST_REMOVE(head, elm, field)
Removes a specific entry from a list.
Definition: linkedlists.h:856
unsigned char oseqno
Definition: dundi.h:38
#define DUNDI_IE_DEPARTMENT
Definition: dundi.h:201
static int do_register(const void *data)
Definition: pbx_dundi.c:4537
dundi_eid them_eid
Definition: pbx_dundi.c:253
#define DUNDI_COMMAND_ACK
Definition: dundi.h:161
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
Socket address structure.
Definition: netsock2.h:97
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:813
ast_aes_decrypt_key them_dcx
Definition: pbx_dundi.c:325
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:757
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:4190
struct dundi_request::@421 trans
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
int ast_eid_cmp(const struct ast_eid *eid1, const struct ast_eid *eid2)
Compare two EIDs.
Definition: utils.c:3094
int lastms
Definition: pbx_dundi.c:337
Utility functions.
#define AST_LIST_INSERT_AFTER(head, listelm, elm, field)
Inserts a list entry after a given entry.
Definition: linkedlists.h:695
static void ast_sockaddr_setnull(struct ast_sockaddr *addr)
Sets address addr to null.
Definition: netsock2.h:138
int args
This gets set in ast_cli_register()
Definition: cli.h:185
const char * name
Definition: pbx.h:162
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
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:517
unsigned char data[0]
Definition: dundi.h:114
time_t keyexpire
Definition: pbx_dundi.c:326
Configuration File Parser.
static const char config_file[]
int ast_get_time_t(const char *src, time_t *dst, time_t _default, int *consumed)
Parse a time (integer) string.
Definition: utils.c:2446
static struct iax2_peer * build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly)
Create peer structure based on configuration.
Definition: chan_iax2.c:12964
uint32_t us_keycrc32
Definition: pbx_dundi.c:320
#define DUNDI_COMMAND_DPRESPONSE
Definition: dundi.h:163
#define DUNDI_IE_LOCALITY
Definition: dundi.h:203
#define DUNDI_IE_UNKNOWN
Definition: dundi.h:192
unsigned short flags
Definition: dundi.h:107
struct timeval qualtx
Definition: pbx_dundi.c:339
#define ast_config_load(filename, flags)
Load a config file.
#define DUNDI_IE_KEYCRC32
Definition: dundi.h:198
#define DUNDI_IE_SIGNATURE
Definition: dundi.h:197
int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception)
Waits for input on an fd.
Definition: channel.c:2980
unsigned char protocol
Definition: dundi.h:106
static struct dundi_peer * any_peer
Wildcard peer.
Definition: pbx_dundi.c:354
General Asterisk PBX channel definitions.
#define AST_SCHED_DEL(sched, id)
Remove a scheduler entry.
Definition: sched.h:46
#define DUNDI_DEFAULT_TTL
Definition: dundi.h:218
void io_context_destroy(struct io_context *ioc)
Destroys a context.
Definition: io.c:107
ast_aes_decrypt_key us_dcx
Definition: pbx_dundi.c:322
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
unsigned char cmdflags
Definition: dundi.h:40
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
#define AST_MAX_EXTENSION
Definition: channel.h:134
Scheduler Routines (derived from cheops)
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
Asterisk internal frame definitions.
#define DUNDI_IE_PHONE
Definition: dundi.h:207
int ast_str_to_eid(struct ast_eid *eid, const char *s)
Convert a string into an EID.
Definition: utils.c:3077
unsigned char oiseqno
Definition: pbx_dundi.c:265
static int check_password(struct ast_vm_user *vmu, char *password)
Check that password meets minimum required length.
static struct stasis_rest_handlers events
REST handler for /api-docs/events.json.
#define DUNDI_IE_EID
Definition: dundi.h:184
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
struct dundi_transaction::@419 parentlist
struct dundi_transaction::packetlist packets
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:4175
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:3066
static struct ast_custom_function dundi_function
Definition: pbx_dundi.c:4192
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:324
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:291
struct ast_sched_context * ast_sched_context_create(void)
Create a scheduler context.
Definition: sched.c:238
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
Definition: linkedlists.h:225
#define DUNDI_TTL_TIME
Definition: dundi.h:214
unsigned int dynamic
Definition: pbx_dundi.c:336
int ast_pbx_exec_application(struct ast_channel *chan, const char *app_name, const char *app_args)
Execute an application.
Definition: pbx_app.c:501
unsigned short strans
Definition: pbx_dundi.c:262
#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:346
#define DUNDI_COMMAND_UNKNOWN
Definition: dundi.h:169
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
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: utils.c:2199
int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max)
Encode data in base64.
Definition: utils.c:406
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:610
#define ast_sockaddr_set_port(addr, port)
Sets the port number of a socket address.
Definition: netsock2.h:532
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:549
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:8473
struct dundi_transaction * qualtrans
Definition: pbx_dundi.c:332
#define DUNDI_IE_CALLED_CONTEXT
Definition: dundi.h:185
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:256
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:202
unsigned char oseqno
Definition: pbx_dundi.c:266
#define DUNDI_IE_HINT
Definition: dundi.h:199
#define DUNDI_DEFAULT_KEY_EXPIRE
Definition: dundi.h:221
int dundi_precache(const char *context, const char *number)
Pre-cache to push upstream peers.
Definition: pbx_dundi.c:4074
uint32_t crc32
Definition: pbx_dundi.c:288
Definition: astdb.h:31
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
unsigned short weight
Definition: dundi.h:108
unsigned short dtrans
Definition: pbx_dundi.c:263
char * command
Definition: cli.h:186
unsigned short strans
Definition: dundi.h:35
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
uint32_t them_keycrc32
Definition: pbx_dundi.c:323
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:390
int ast_register_switch(struct ast_switch *sw)
Register an alternative dialplan switch.
Definition: pbx_switch.c:58
Module could not be loaded properly.
Definition: module.h:102
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:634
Support for logging to various files, console and syslog Configuration in file logger.conf.
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
Definition: crypto.h:39
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:427
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:567
Structure used to handle boolean flags.
Definition: utils.h:199
int ast_io_remove(struct io_context *ioc, int *id)
Removes an IO context.
Definition: io.c:245
#define DUNDI_IE_CALLED_NUMBER
Definition: dundi.h:186
#define DUNDI_COMMAND_PRECACHERQ
Definition: dundi.h:166
const char * usage
Definition: cli.h:177
int pcmodel
Definition: pbx_dundi.c:334
#define DUNDI_COMMAND_PRECACHERP
Definition: dundi.h:167
struct ast_eid ast_eid_default
Global EID.
Definition: options.c:93
unsigned char iv[16]
Definition: dundi.h:100
static void handle_frame(struct ast_dial *dial, struct ast_dial_channel *channel, struct ast_frame *fr, struct ast_channel *chan)
Helper function that handles frames.
Definition: dial.c:580
ast_aes_encrypt_key us_ecx
Definition: pbx_dundi.c:321
#define DUNDI_COMMAND_REGREQ
Definition: dundi.h:171
#define DUNDI_COMMAND_FINAL
Definition: dundi.h:159
void * data
Definition: datastore.h:66
#define AST_LIST_HEAD_INIT_NOLOCK(head)
Initializes a list head structure.
Definition: linkedlists.h:681
unsigned char txenckey[256]
Definition: pbx_dundi.c:318
#define DUNDI_COMMAND_EIDRESPONSE
Definition: dundi.h:165
Standard Command Line Interface.
#define DUNDI_IE_STATE_PROV
Definition: dundi.h:204
int ast_db_del(const char *family, const char *key)
Delete entry in astdb.
Definition: main/db.c:476
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:80
struct dundi_transaction::@420 all
#define DUNDI_COMMAND_EIDQUERY
Definition: dundi.h:164
struct ast_sockaddr addr
Definition: pbx_dundi.c:307
int ast_sockaddr_is_ipv4(const struct ast_sockaddr *addr)
Determine if the address is an IPv4 address.
Definition: netsock2.c:497
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:433
dundi_eid eid
Definition: dundi.h:105
#define DUNDI_COMMAND_INVALID
Definition: dundi.h:168
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:149
struct dundi_transaction * regtrans
Definition: pbx_dundi.c:331
int ast_db_put(const char *family, const char *key, const char *value)
Store value addressed by family/key.
Definition: main/db.c:342
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
#define DUNDI_COMMAND_DPDISCOVER
Definition: dundi.h:162
struct timeval start
Definition: pbx_dundi.c:249
#define DUNDI_IE_COUNTRY
Definition: dundi.h:205
int ast_eid_is_empty(const struct ast_eid *eid)
Check if EID is empty.
Definition: utils.c:3099
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
unsigned short dtrans
Definition: dundi.h:36
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
unsigned char iseqno
Definition: pbx_dundi.c:264
struct ast_sockaddr addr
Definition: pbx_dundi.c:248
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
MD5 digest functions.
#define DUNDI_IE_ENCDATA
Definition: dundi.h:195
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2385
Persistent data storage (akin to *doze registry)
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
ast_context: An extension context
Definition: pbx.c:284
ast_aes_decrypt_key dcx
Definition: pbx_dundi.c:255
void ast_sched_context_destroy(struct ast_sched_context *c)
destroys a schedule context
Definition: sched.c:271
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
#define DUNDI_COMMAND_NULL
Definition: dundi.h:170
int ast_sockaddr_is_ipv6(const struct ast_sockaddr *addr)
Determine if this is an IPv6 address.
Definition: netsock2.c:524
int ast_db_deltree(const char *family, const char *keytree)
Delete one or more entries in astdb.
Definition: main/db.c:536
#define DUNDI_IE_ANSWER
Definition: dundi.h:188
#define DUNDI_COMMAND_REGRESPONSE
Definition: dundi.h:172
unsigned char data[0]
Definition: dundi.h:109
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:327
int ast_rdlock_context(struct ast_context *con)
Read locks a given context.
Definition: pbx.c:8486
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
#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:472
#define DUNDI_COMMAND_CANCEL
Definition: dundi.h:173
unsigned char iseqno
Definition: dundi.h:37
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:189