Wed Jan 8 2020 09:49:49

Asterisk developer's documentation


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