pbx_dundi: Added IPv6 support for dundi
[asterisk/asterisk.git] / pbx / pbx_dundi.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Distributed Universal Number Discovery (DUNDi)
22  */
23
24 /*! \li \ref pbx_dundi.c uses configuration file \ref dundi.conf
25  * \addtogroup configuration_file Configuration Files
26  */
27
28 /*!
29  * \page dundi.conf dundi.conf
30  * \verbinclude dundi.conf.sample
31  */
32
33 /*** MODULEINFO
34         <depend>zlib</depend>
35         <use type="module">res_crypto</use>
36         <use type="external">crypto</use>
37         <support_level>extended</support_level>
38  ***/
39
40 #include "asterisk.h"
41
42 #include "asterisk/network.h"
43 #include <sys/ioctl.h>
44 #include <zlib.h>
45 #include <signal.h>
46 #include <pthread.h>
47 #include <net/if.h>
48
49 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
50 #include <net/if_dl.h>
51 #include <ifaddrs.h>
52 #include <signal.h>
53 #endif
54
55 #include "asterisk/file.h"
56 #include "asterisk/logger.h"
57 #include "asterisk/channel.h"
58 #include "asterisk/config.h"
59 #include "asterisk/pbx.h"
60 #include "asterisk/module.h"
61 #include "asterisk/frame.h"
62 #include "asterisk/cli.h"
63 #include "asterisk/lock.h"
64 #include "asterisk/md5.h"
65 #include "asterisk/dundi.h"
66 #include "asterisk/sched.h"
67 #include "asterisk/io.h"
68 #include "asterisk/utils.h"
69 #include "asterisk/netsock2.h"
70 #include "asterisk/crypto.h"
71 #include "asterisk/astdb.h"
72 #include "asterisk/acl.h"
73 #include "asterisk/app.h"
74
75 #include "dundi-parser.h"
76
77 /*** DOCUMENTATION
78         <function name="DUNDILOOKUP" language="en_US">
79                 <synopsis>
80                         Do a DUNDi lookup of a phone number.
81                 </synopsis>
82                 <syntax>
83                         <parameter name="number" required="true"/>
84                         <parameter name="context">
85                                 <para>If not specified the default will be <literal>e164</literal>.</para>
86                         </parameter>
87                         <parameter name="options">
88                                 <optionlist>
89                                         <option name="b">
90                                                 <para>Bypass the internal DUNDi cache</para>
91                                         </option>
92                                 </optionlist>
93                         </parameter>
94                 </syntax>
95                 <description>
96                         <para>This will do a DUNDi lookup of the given phone number.</para>
97                         <para>This function will return the Technology/Resource found in the first result
98                         in the DUNDi lookup. If no results were found, the result will be blank.</para>
99                 </description>
100         </function>
101
102
103         <function name="DUNDIQUERY" language="en_US">
104                 <synopsis>
105                         Initiate a DUNDi query.
106                 </synopsis>
107                 <syntax>
108                         <parameter name="number" required="true"/>
109                         <parameter name="context">
110                                 <para>If not specified the default will be <literal>e164</literal>.</para>
111                         </parameter>
112                         <parameter name="options">
113                                 <optionlist>
114                                         <option name="b">
115                                                 <para>Bypass the internal DUNDi cache</para>
116                                         </option>
117                                 </optionlist>
118                         </parameter>
119                 </syntax>
120                 <description>
121                         <para>This will do a DUNDi lookup of the given phone number.</para>
122                         <para>The result of this function will be a numeric ID that can be used to retrieve
123                         the results with the <literal>DUNDIRESULT</literal> function.</para>
124                 </description>
125         </function>
126
127         <function name="DUNDIRESULT" language="en_US">
128                 <synopsis>
129                         Retrieve results from a DUNDIQUERY.
130                 </synopsis>
131                 <syntax>
132                         <parameter name="id" required="true">
133                                 <para>The identifier returned by the <literal>DUNDIQUERY</literal> function.</para>
134                         </parameter>
135                         <parameter name="resultnum">
136                                 <optionlist>
137                                         <option name="number">
138                                                 <para>The number of the result that you want to retrieve, this starts at <literal>1</literal></para>
139                                         </option>
140                                         <option name="getnum">
141                                                 <para>The total number of results that are available.</para>
142                                         </option>
143                                 </optionlist>
144                         </parameter>
145                 </syntax>
146                 <description>
147                         <para>This function will retrieve results from a previous use\n"
148                         of the <literal>DUNDIQUERY</literal> function.</para>
149                 </description>
150         </function>
151  ***/
152
153 #define MAX_RESULTS     64
154
155 #define MAX_PACKET_SIZE 8192
156
157 #define MAX_WEIGHT 59999
158
159 #define DUNDI_MODEL_INBOUND             (1 << 0)
160 #define DUNDI_MODEL_OUTBOUND    (1 << 1)
161 #define DUNDI_MODEL_SYMMETRIC   (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
162
163 /*! Keep times of last 10 lookups */
164 #define DUNDI_TIMING_HISTORY    10
165
166 enum {
167         FLAG_ISREG =       (1 << 0),  /*!< Transaction is register request */
168         FLAG_DEAD =        (1 << 1),  /*!< Transaction is dead */
169         FLAG_FINAL =       (1 << 2),  /*!< Transaction has final message sent */
170         FLAG_ISQUAL =      (1 << 3),  /*!< Transaction is a qualification */
171         FLAG_ENCRYPT =     (1 << 4),  /*!< Transaction is encrypted wiht ECX/DCX */
172         FLAG_SENDFULLKEY = (1 << 5),  /*!< Send full key on transaction */
173         FLAG_STOREHIST =   (1 << 6),  /*!< Record historic performance */
174 };
175
176 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
177
178 #if 0
179 #define DUNDI_SECRET_TIME 15    /* Testing only */
180 #else
181 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
182 #endif
183
184 static struct io_context *io;
185 static struct ast_sched_context *sched;
186 static int netsocket = -1;
187 static pthread_t netthreadid = AST_PTHREADT_NULL;
188 static pthread_t precachethreadid = AST_PTHREADT_NULL;
189 static pthread_t clearcachethreadid = AST_PTHREADT_NULL;
190 static unsigned int tos = 0;
191 static int dundidebug = 0;
192 static int authdebug = 0;
193 static int dundi_ttl = DUNDI_DEFAULT_TTL;
194 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
195 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
196 static int global_autokilltimeout = 0;
197 static dundi_eid global_eid;
198 static int default_expiration = 60;
199 static int global_storehistory = 0;
200 static char dept[80];
201 static char org[80];
202 static char locality[80];
203 static char stateprov[80];
204 static char country[80];
205 static char email[80];
206 static char phone[80];
207 static char secretpath[80];
208 static char cursecret[80];
209 static char ipaddr[80];
210 static time_t rotatetime;
211 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
212 static int dundi_shutdown = 0;
213
214 struct permission {
215         AST_LIST_ENTRY(permission) list;
216         int allow;
217         char name[0];
218 };
219
220 struct dundi_packet {
221         AST_LIST_ENTRY(dundi_packet) list;
222         struct dundi_hdr *h;
223         int datalen;
224         struct dundi_transaction *parent;
225         int retransid;
226         int retrans;
227         unsigned char data[0];
228 };
229
230 struct dundi_hint_metadata {
231         unsigned short flags;
232         char exten[AST_MAX_EXTENSION];
233 };
234
235 struct dundi_precache_queue {
236         AST_LIST_ENTRY(dundi_precache_queue) list;
237         char *context;
238         time_t expiration;
239         char number[0];
240 };
241
242 struct dundi_request;
243
244 struct dundi_transaction {
245         struct ast_sockaddr addr;                      /*!< Other end of transaction */
246         struct timeval start;                          /*!< When this transaction was created */
247         dundi_eid eids[DUNDI_MAX_STACK + 1];
248         int eidcount;                                  /*!< Number of eids in eids */
249         dundi_eid us_eid;                              /*!< Our EID, to them */
250         dundi_eid them_eid;                            /*!< Their EID, to us */
251         ast_aes_encrypt_key ecx;                       /*!< AES 128 Encryption context */
252         ast_aes_decrypt_key dcx;                       /*!< AES 128 Decryption context */
253         unsigned int flags;                            /*!< Has final packet been sent */
254         int ttl;                                       /*!< Remaining TTL for queries on this one */
255         int thread;                                    /*!< We have a calling thread */
256         int retranstimer;                              /*!< How long to wait before retransmissions */
257         int autokillid;                                /*!< ID to kill connection if answer doesn't come back fast enough */
258         int autokilltimeout;                           /*!< Recommended timeout for autokill */
259         unsigned short strans;                         /*!< Our transaction identifier */
260         unsigned short dtrans;                         /*!< Their transaction identifer */
261         unsigned char iseqno;                          /*!< Next expected received seqno */
262         unsigned char oiseqno;                         /*!< Last received incoming seqno */
263         unsigned char oseqno;                          /*!< Next transmitted seqno */
264         unsigned char aseqno;                          /*!< Last acknowledge seqno */
265         AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets;  /*!< Packets to be retransmitted */
266         struct packetlist lasttrans;                   /*!< Last transmitted / ACK'd packet */
267         struct dundi_request *parent;                  /*!< Parent request (if there is one) */
268         AST_LIST_ENTRY(dundi_transaction) parentlist;  /*!< Next with respect to the parent */
269         AST_LIST_ENTRY(dundi_transaction) all;         /*!< Next with respect to all DUNDi transactions */
270 };
271
272 struct dundi_request {
273         char dcontext[AST_MAX_EXTENSION];
274         char number[AST_MAX_EXTENSION];
275         dundi_eid query_eid;
276         dundi_eid root_eid;
277         struct dundi_result *dr;
278         struct dundi_entity_info *dei;
279         struct dundi_hint_metadata *hmd;
280         int maxcount;
281         int respcount;
282         int expiration;
283         int cbypass;
284         int pfds[2];
285         uint32_t crc32;                              /*!< CRC-32 of all but root EID's in avoid list */
286         AST_LIST_HEAD_NOLOCK(, dundi_transaction) trans;  /*!< Transactions */
287         AST_LIST_ENTRY(dundi_request) list;
288 };
289
290 struct dundi_mapping {
291         char dcontext[AST_MAX_EXTENSION];
292         char lcontext[AST_MAX_EXTENSION];
293         int _weight;
294         char *weightstr;
295         int options;
296         int tech;
297         int dead;
298         char dest[512];
299         AST_LIST_ENTRY(dundi_mapping) list;
300 };
301
302 struct dundi_peer {
303         dundi_eid eid;
304         struct ast_sockaddr addr;              /*!< Address of DUNDi peer */
305         AST_LIST_HEAD_NOLOCK(permissionlist, permission) permit;
306         struct permissionlist include;
307         dundi_eid us_eid;
308         char inkey[80];
309         char outkey[80];
310         int dead;
311         int registerid;
312         int qualifyid;
313         int sentfullkey;
314         int order;
315         unsigned char txenckey[256];           /*!< Transmitted encrypted key + sig */
316         unsigned char rxenckey[256];           /*!< Cache received encrypted key + sig */
317         uint32_t us_keycrc32;                  /*!< CRC-32 of our key */
318         ast_aes_encrypt_key us_ecx;            /*!< Cached AES 128 Encryption context */
319         ast_aes_decrypt_key us_dcx;            /*!< Cached AES 128 Decryption context */
320         uint32_t them_keycrc32;                /*!< CRC-32 of our key */
321         ast_aes_encrypt_key them_ecx;          /*!< Cached AES 128 Encryption context */
322         ast_aes_decrypt_key them_dcx;          /*!< Cached AES 128 Decryption context */
323         time_t keyexpire;                      /*!< When to expire/recreate key */
324         int registerexpire;
325         int lookuptimes[DUNDI_TIMING_HISTORY];
326         char *lookups[DUNDI_TIMING_HISTORY];
327         int avgms;
328         struct dundi_transaction *regtrans;    /*!< Registration transaction */
329         struct dundi_transaction *qualtrans;   /*!< Qualify transaction */
330         int model;                             /*!< Pull model */
331         int pcmodel;                           /*!< Push/precache model */
332         /*! Dynamic peers register with us */
333         unsigned int dynamic:1;
334         int lastms;                            /*!< Last measured latency */
335         int maxms;                             /*!< Max permissible latency */
336         struct timeval qualtx;                 /*!< Time of transmit */
337         AST_LIST_ENTRY(dundi_peer) list;
338 };
339
340 static AST_LIST_HEAD_STATIC(peers, dundi_peer);
341 static AST_LIST_HEAD_STATIC(pcq, dundi_precache_queue);
342 static AST_LIST_HEAD_NOLOCK_STATIC(mappings, dundi_mapping);
343 static AST_LIST_HEAD_NOLOCK_STATIC(requests, dundi_request);
344 static AST_LIST_HEAD_NOLOCK_STATIC(alltrans, dundi_transaction);
345
346 /*!
347  * \brief Wildcard peer
348  *
349  * This peer is created if the [*] entry is specified in dundi.conf
350  */
351 static struct dundi_peer *any_peer;
352
353 static int dundi_xmit(struct dundi_packet *pack);
354
355 static void dundi_debug_output(const char *data)
356 {
357         if (dundidebug)
358                 ast_verbose("%s", data);
359 }
360
361 static void dundi_error_output(const char *data)
362 {
363         ast_log(LOG_WARNING, "%s", data);
364 }
365
366 static int has_permission(struct permissionlist *permlist, char *cont)
367 {
368         struct permission *perm;
369         int res = 0;
370
371         AST_LIST_TRAVERSE(permlist, perm, list) {
372                 if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont))
373                         res = perm->allow;
374         }
375
376         return res;
377 }
378
379 static char *tech2str(int tech)
380 {
381         switch(tech) {
382         case DUNDI_PROTO_NONE:
383                 return "None";
384         case DUNDI_PROTO_IAX:
385                 return "IAX2";
386         case DUNDI_PROTO_SIP:
387                 return "SIP";
388         case DUNDI_PROTO_H323:
389                 return "H323";
390         default:
391                 return "Unknown";
392         }
393 }
394
395 static int str2tech(char *str)
396 {
397         if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
398                 return DUNDI_PROTO_IAX;
399         else if (!strcasecmp(str, "SIP"))
400                 return DUNDI_PROTO_SIP;
401         else if (!strcasecmp(str, "H323"))
402                 return DUNDI_PROTO_H323;
403         else
404                 return -1;
405 }
406
407 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[]);
408 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
409 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
410
411 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct ast_sockaddr *sin)
412 {
413         struct dundi_transaction *trans;
414
415         /* Look for an exact match first */
416         AST_LIST_TRAVERSE(&alltrans, trans, all) {
417                 if (!ast_sockaddr_cmp(&trans->addr, sin) &&
418                         ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
419                                 ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
420                         if (hdr->strans) {
421                                 trans->dtrans = ntohs(hdr->strans) & 32767;
422                         }
423                         return trans;
424                 }
425         }
426
427         switch(hdr->cmdresp & 0x7f) {
428         case DUNDI_COMMAND_DPDISCOVER:
429         case DUNDI_COMMAND_EIDQUERY:
430         case DUNDI_COMMAND_PRECACHERQ:
431         case DUNDI_COMMAND_REGREQ:
432         case DUNDI_COMMAND_NULL:
433         case DUNDI_COMMAND_ENCRYPT:
434                 if (!hdr->strans)
435                         break;
436                 /* Create new transaction */
437                 if (!(trans = create_transaction(NULL)))
438                         break;
439                 ast_sockaddr_copy(&trans->addr, sin);
440                 trans->dtrans = ntohs(hdr->strans) & 32767;
441         default:
442                 break;
443         }
444
445         return trans;
446 }
447
448 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
449
450 static int dundi_ack(struct dundi_transaction *trans, int final)
451 {
452         return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
453 }
454 static void dundi_reject(struct dundi_hdr *h, struct ast_sockaddr *sin)
455 {
456         struct {
457                 struct dundi_packet pack;
458                 struct dundi_hdr hdr;
459         } tmp;
460         struct dundi_transaction trans;
461         /* Never respond to an INVALID with another INVALID */
462         if (h->cmdresp == DUNDI_COMMAND_INVALID)
463                 return;
464         memset(&tmp, 0, sizeof(tmp));
465         memset(&trans, 0, sizeof(trans));
466         ast_sockaddr_copy(&trans.addr, sin);
467         tmp.hdr.strans = h->dtrans;
468         tmp.hdr.dtrans = h->strans;
469         tmp.hdr.iseqno = h->oseqno;
470         tmp.hdr.oseqno = h->iseqno;
471         tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
472         tmp.hdr.cmdflags = 0;
473         tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
474         tmp.pack.datalen = sizeof(struct dundi_hdr);
475         tmp.pack.parent = &trans;
476         dundi_xmit(&tmp.pack);
477 }
478
479 static int get_trans_id(void)
480 {
481         struct dundi_transaction *t;
482         int stid = (ast_random() % 32766) + 1;
483         int tid = stid;
484
485         do {
486                 AST_LIST_TRAVERSE(&alltrans, t, all) {
487                         if (t->strans == tid)
488                                 break;
489                 }
490                 if (!t)
491                         return tid;
492                 tid = (tid % 32766) + 1;
493         } while (tid != stid);
494
495         return 0;
496 }
497
498 static int reset_transaction(struct dundi_transaction *trans)
499 {
500         int tid;
501         tid = get_trans_id();
502         if (tid < 1)
503                 return -1;
504         trans->strans = tid;
505         trans->dtrans = 0;
506         trans->iseqno = 0;
507         trans->oiseqno = 0;
508         trans->oseqno = 0;
509         trans->aseqno = 0;
510         ast_clear_flag(trans, FLAG_FINAL);
511         return 0;
512 }
513
514 static struct dundi_peer *find_peer(dundi_eid *eid)
515 {
516         struct dundi_peer *cur = NULL;
517
518         if (!eid)
519                 eid = &empty_eid;
520
521         AST_LIST_TRAVERSE(&peers, cur, list) {
522                 if (!ast_eid_cmp(&cur->eid,eid))
523                         break;
524         }
525
526         if (!cur && any_peer)
527                 cur = any_peer;
528
529         return cur;
530 }
531
532 static void build_iv(unsigned char *iv)
533 {
534         /* XXX Would be nice to be more random XXX */
535         unsigned int *fluffy;
536         int x;
537         fluffy = (unsigned int *)(iv);
538         for (x=0;x<4;x++)
539                 fluffy[x] = ast_random();
540 }
541
542 struct dundi_query_state {
543         dundi_eid *eids[DUNDI_MAX_STACK + 1];
544         int directs[DUNDI_MAX_STACK + 1];
545         dundi_eid reqeid;
546         char called_context[AST_MAX_EXTENSION];
547         char called_number[AST_MAX_EXTENSION];
548         struct dundi_mapping *maps;
549         int nummaps;
550         int nocache;
551         struct dundi_transaction *trans;
552         void *chal;
553         int challen;
554         int ttl;
555         char fluffy[0];
556 };
557
558 static int get_mapping_weight(struct dundi_mapping *map, struct varshead *headp)
559 {
560         char buf[32];
561
562         buf[0] = 0;
563         if (map->weightstr) {
564                 if (headp) {
565                         pbx_substitute_variables_varshead(headp, map->weightstr, buf, sizeof(buf) - 1);
566                 } else {
567                         pbx_substitute_variables_helper(NULL, map->weightstr, buf, sizeof(buf) - 1);
568                 }
569
570                 if (sscanf(buf, "%30d", &map->_weight) != 1)
571                         map->_weight = MAX_WEIGHT;
572         }
573
574         return map->_weight;
575 }
576
577 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)
578 {
579         struct ast_flags flags = {0};
580         int x;
581         if (!ast_strlen_zero(map->lcontext)) {
582                 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
583                         ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
584                 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
585                         ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
586                 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
587                         ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
588                 if (ast_ignore_pattern(map->lcontext, called_number))
589                         ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
590
591                 /* Clearly we can't say 'don't ask' anymore if we found anything... */
592                 if (ast_test_flag(&flags, AST_FLAGS_ALL))
593                         ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
594
595                 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
596                         /* Skip partial answers */
597                         ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
598                 }
599                 if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
600                         struct varshead headp;
601                         struct ast_var_t *newvariable;
602                         ast_set_flag(&flags, map->options & 0xffff);
603                         ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
604                         dr[anscnt].techint = map->tech;
605                         dr[anscnt].expiration = dundi_cache_time;
606                         ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
607                         dr[anscnt].eid = *us_eid;
608                         ast_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
609                         if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
610                                 AST_LIST_HEAD_INIT_NOLOCK(&headp);
611                                 if ((newvariable = ast_var_assign("NUMBER", called_number))) {
612                                         AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
613                                 }
614                                 if ((newvariable = ast_var_assign("EID", dr[anscnt].eid_str))) {
615                                         AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
616                                 }
617                                 if ((newvariable = ast_var_assign("SECRET", cursecret))) {
618                                         AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
619                                 }
620                                 if ((newvariable = ast_var_assign("IPADDR", ipaddr))) {
621                                         AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
622                                 }
623                                 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
624                                 dr[anscnt].weight = get_mapping_weight(map, &headp);
625                                 while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
626                                         ast_var_delete(newvariable);
627                         } else {
628                                 dr[anscnt].dest[0] = '\0';
629                                 dr[anscnt].weight = get_mapping_weight(map, NULL);
630                         }
631                         anscnt++;
632                 } else {
633                         /* No answers...  Find the fewest number of digits from the
634                            number for which we have no answer. */
635                         char tmp[AST_MAX_EXTENSION + 1] = "";
636                         for (x = 0; x < (sizeof(tmp) - 1); x++) {
637                                 tmp[x] = called_number[x];
638                                 if (!tmp[x])
639                                         break;
640                                 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
641                                         /* Oops found something we can't match.  If this is longer
642                                            than the running hint, we have to consider it */
643                                         if (strlen(tmp) > strlen(hmd->exten)) {
644                                                 ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
645                                         }
646                                         break;
647                                 }
648                         }
649                 }
650         }
651         return anscnt;
652 }
653
654 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
655
656 static void *dundi_lookup_thread(void *data)
657 {
658         struct dundi_query_state *st = data;
659         struct dundi_result dr[MAX_RESULTS];
660         struct dundi_ie_data ied;
661         struct dundi_hint_metadata hmd;
662         char eid_str[20];
663         int res, x;
664         int ouranswers=0;
665         int max = 999999;
666         int expiration = dundi_cache_time;
667
668         ast_debug(1, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
669                         st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
670         memset(&ied, 0, sizeof(ied));
671         memset(&dr, 0, sizeof(dr));
672         memset(&hmd, 0, sizeof(hmd));
673         /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
674         hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
675         for (x=0;x<st->nummaps;x++)
676                 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
677         if (ouranswers < 0)
678                 ouranswers = 0;
679         for (x=0;x<ouranswers;x++) {
680                 if (dr[x].weight < max)
681                         max = dr[x].weight;
682         }
683
684         if (max) {
685                 /* If we do not have a canonical result, keep looking */
686                 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);
687                 if (res > 0) {
688                         /* Append answer in result */
689                         ouranswers += res;
690                 } else {
691                         if ((res < -1) && (!ouranswers))
692                                 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
693                 }
694         }
695         AST_LIST_LOCK(&peers);
696         /* Truncate if "don't ask" isn't present */
697         if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
698                 hmd.exten[0] = '\0';
699         if (ast_test_flag(st->trans, FLAG_DEAD)) {
700                 ast_debug(1, "Our transaction went away!\n");
701                 st->trans->thread = 0;
702                 destroy_trans(st->trans, 0);
703         } else {
704                 for (x=0;x<ouranswers;x++) {
705                         /* Add answers */
706                         if (dr[x].expiration && (expiration > dr[x].expiration))
707                                 expiration = dr[x].expiration;
708                         dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
709                 }
710                 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
711                 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
712                 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
713                 st->trans->thread = 0;
714         }
715         AST_LIST_UNLOCK(&peers);
716         ast_free(st);
717         return NULL;
718 }
719
720 static void *dundi_precache_thread(void *data)
721 {
722         struct dundi_query_state *st = data;
723         struct dundi_ie_data ied;
724         struct dundi_hint_metadata hmd;
725         char eid_str[20];
726
727         ast_debug(1, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
728                 st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
729         memset(&ied, 0, sizeof(ied));
730
731         /* Now produce precache */
732         dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
733
734         AST_LIST_LOCK(&peers);
735         /* Truncate if "don't ask" isn't present */
736         if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
737                 hmd.exten[0] = '\0';
738         if (ast_test_flag(st->trans, FLAG_DEAD)) {
739                 ast_debug(1, "Our transaction went away!\n");
740                 st->trans->thread = 0;
741                 destroy_trans(st->trans, 0);
742         } else {
743                 dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
744                 st->trans->thread = 0;
745         }
746         AST_LIST_UNLOCK(&peers);
747         ast_free(st);
748         return NULL;
749 }
750
751 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[]);
752
753 static void *dundi_query_thread(void *data)
754 {
755         struct dundi_query_state *st = data;
756         struct dundi_entity_info dei;
757         struct dundi_ie_data ied;
758         struct dundi_hint_metadata hmd;
759         char eid_str[20];
760         int res;
761
762         ast_debug(1, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
763                 st->eids[0] ? ast_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
764         memset(&ied, 0, sizeof(ied));
765         memset(&dei, 0, sizeof(dei));
766         memset(&hmd, 0, sizeof(hmd));
767         if (!ast_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
768                 /* Ooh, it's us! */
769                 ast_debug(1, "Neat, someone look for us!\n");
770                 ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
771                 ast_copy_string(dei.org, org, sizeof(dei.org));
772                 ast_copy_string(dei.locality, locality, sizeof(dei.locality));
773                 ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
774                 ast_copy_string(dei.country, country, sizeof(dei.country));
775                 ast_copy_string(dei.email, email, sizeof(dei.email));
776                 ast_copy_string(dei.phone, phone, sizeof(dei.phone));
777                 res = 1;
778         } else {
779                 /* If we do not have a canonical result, keep looking */
780                 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
781         }
782         AST_LIST_LOCK(&peers);
783         if (ast_test_flag(st->trans, FLAG_DEAD)) {
784                 ast_debug(1, "Our transaction went away!\n");
785                 st->trans->thread = 0;
786                 destroy_trans(st->trans, 0);
787         } else {
788                 if (res) {
789                         dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
790                         dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
791                         dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
792                         dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
793                         dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
794                         dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
795                         dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
796                         if (!ast_strlen_zero(dei.ipaddr))
797                                 dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
798                 }
799                 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
800                 dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
801                 st->trans->thread = 0;
802         }
803         AST_LIST_UNLOCK(&peers);
804         ast_free(st);
805         return NULL;
806 }
807
808 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
809 {
810         struct dundi_query_state *st;
811         int totallen;
812         int x;
813         int skipfirst=0;
814         char eid_str[20];
815         char *s;
816         pthread_t lookupthread;
817
818         if (ies->eidcount > 1) {
819                 /* Since it is a requirement that the first EID is the authenticating host
820                    and the last EID is the root, it is permissible that the first and last EID
821                    could be the same.  In that case, we should go ahead copy only the "root" section
822                    since we will not need it for authentication. */
823                 if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
824                         skipfirst = 1;
825         }
826         totallen = sizeof(struct dundi_query_state);
827         totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
828         st = ast_calloc(1, totallen);
829         if (st) {
830                 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
831                 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
832                 st->trans = trans;
833                 st->ttl = ies->ttl - 1;
834                 if (st->ttl < 0)
835                         st->ttl = 0;
836                 s = st->fluffy;
837                 for (x=skipfirst;ies->eids[x];x++) {
838                         st->eids[x-skipfirst] = (dundi_eid *)s;
839                         *st->eids[x-skipfirst] = *ies->eids[x];
840                         s += sizeof(dundi_eid);
841                 }
842                 ast_debug(1, "Answering EID query for '%s@%s'!\n", ast_eid_to_str(eid_str, sizeof(eid_str), ies->reqeid), ies->called_context);
843
844                 trans->thread = 1;
845                 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_query_thread, st)) {
846                         struct dundi_ie_data ied = { 0, };
847                         trans->thread = 0;
848                         ast_log(LOG_WARNING, "Unable to create thread!\n");
849                         ast_free(st);
850                         dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
851                         dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
852                         return -1;
853                 }
854         } else {
855                 struct dundi_ie_data ied = { 0, };
856                 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
857                 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
858                 return -1;
859         }
860         return 0;
861 }
862
863 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
864 {
865         int unaffected;
866         char key1[256];
867         char key2[256];
868         char eidpeer_str[20];
869         char eidroot_str[20];
870         char data[80];
871         time_t timeout;
872
873         if (expiration < 0)
874                 expiration = dundi_cache_time;
875
876         /* Only cache hint if "don't ask" is there... */
877         if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))
878                 return 0;
879
880         unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
881
882         dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
883         dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
884         snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08x", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
885         snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
886
887         time(&timeout);
888         timeout += expiration;
889         snprintf(data, sizeof(data), "%ld|", (long)(timeout));
890
891         ast_db_put("dundi/cache", key1, data);
892         ast_debug(1, "Caching hint at '%s'\n", key1);
893         ast_db_put("dundi/cache", key2, data);
894         ast_debug(1, "Caching hint at '%s'\n", key2);
895         return 0;
896 }
897
898 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
899 {
900         int x;
901         char key1[256];
902         char key2[256];
903         char data[1024];
904         char eidpeer_str[20];
905         char eidroot_str[20];
906         time_t timeout;
907
908         if (expiration < 1)
909                 expiration = dundi_cache_time;
910
911         /* Keep pushes a little longer, cut pulls a little short */
912         if (push)
913                 expiration += 10;
914         else
915                 expiration -= 10;
916         if (expiration < 1)
917                 expiration = 1;
918         dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
919         dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
920         snprintf(key1, sizeof(key1), "%s/%s/%s/e%08x", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
921         snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
922         /* Build request string */
923         time(&timeout);
924         timeout += expiration;
925         snprintf(data, sizeof(data), "%ld|", (long)(timeout));
926         for (x=start;x<req->respcount;x++) {
927                 /* Skip anything with an illegal pipe in it */
928                 if (strchr(req->dr[x].dest, '|'))
929                         continue;
930                 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%u/%d/%d/%s/%s|",
931                         req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
932                         dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
933         }
934         ast_db_put("dundi/cache", key1, data);
935         ast_db_put("dundi/cache", key2, data);
936         return 0;
937 }
938
939 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
940 {
941         struct dundi_query_state *st;
942         int totallen;
943         int x,z;
944         struct dundi_ie_data ied;
945         char *s;
946         struct dundi_result dr2[MAX_RESULTS];
947         struct dundi_request dr;
948         struct dundi_hint_metadata hmd;
949
950         struct dundi_mapping *cur;
951         int mapcount;
952         int skipfirst = 0;
953
954         pthread_t lookupthread;
955
956         memset(&dr2, 0, sizeof(dr2));
957         memset(&dr, 0, sizeof(dr));
958         memset(&hmd, 0, sizeof(hmd));
959
960         /* Forge request structure to hold answers for cache */
961         hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
962         dr.dr = dr2;
963         dr.maxcount = MAX_RESULTS;
964         dr.expiration = dundi_cache_time;
965         dr.hmd = &hmd;
966         dr.pfds[0] = dr.pfds[1] = -1;
967         trans->parent = &dr;
968         ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
969         ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
970
971         for (x=0;x<ies->anscount;x++) {
972                 if (trans->parent->respcount < trans->parent->maxcount) {
973                         /* Make sure it's not already there */
974                         for (z=0;z<trans->parent->respcount;z++) {
975                                 if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
976                                     !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data))
977                                                 break;
978                         }
979                         if (z == trans->parent->respcount) {
980                                 /* Copy into parent responses */
981                                 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
982                                 trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
983                                 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
984                                 trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
985                                 if (ies->expiration > 0)
986                                         trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
987                                 else
988                                         trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
989                                 ast_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
990                                         sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
991                                         &ies->answers[x]->eid);
992                                 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
993                                         sizeof(trans->parent->dr[trans->parent->respcount].dest));
994                                         ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
995                                         sizeof(trans->parent->dr[trans->parent->respcount].tech));
996                                 trans->parent->respcount++;
997                                 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
998                         } else if (trans->parent->dr[z].weight > ntohs(ies->answers[x]->weight)) {
999                                 /* Update weight if appropriate */
1000                                 trans->parent->dr[z].weight = ntohs(ies->answers[x]->weight);
1001                         }
1002                 } else
1003                         ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
1004                                 trans->parent->number, trans->parent->dcontext);
1005
1006         }
1007         /* Save all the results (if any) we had.  Even if no results, still cache lookup. */
1008         cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
1009         if (ies->hint)
1010                 cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
1011
1012         totallen = sizeof(struct dundi_query_state);
1013         /* Count matching map entries */
1014         mapcount = 0;
1015         AST_LIST_TRAVERSE(&mappings, cur, list) {
1016                 if (!strcasecmp(cur->dcontext, ccontext))
1017                         mapcount++;
1018         }
1019
1020         /* If no maps, return -1 immediately */
1021         if (!mapcount)
1022                 return -1;
1023
1024         if (ies->eidcount > 1) {
1025                 /* Since it is a requirement that the first EID is the authenticating host
1026                    and the last EID is the root, it is permissible that the first and last EID
1027                    could be the same.  In that case, we should go ahead copy only the "root" section
1028                    since we will not need it for authentication. */
1029                 if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1030                         skipfirst = 1;
1031         }
1032
1033         /* Prepare to run a query and then propagate that as necessary */
1034         totallen += mapcount * sizeof(struct dundi_mapping);
1035         totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1036         st = ast_calloc(1, totallen);
1037         if (st) {
1038                 ast_copy_string(st->called_context, dr.dcontext, sizeof(st->called_context));
1039                 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1040                 st->trans = trans;
1041                 st->ttl = ies->ttl - 1;
1042                 st->nocache = ies->cbypass;
1043                 if (st->ttl < 0)
1044                         st->ttl = 0;
1045                 s = st->fluffy;
1046                 for (x=skipfirst;ies->eids[x];x++) {
1047                         st->eids[x-skipfirst] = (dundi_eid *)s;
1048                         *st->eids[x-skipfirst] = *ies->eids[x];
1049                         st->directs[x-skipfirst] = ies->eid_direct[x];
1050                         s += sizeof(dundi_eid);
1051                 }
1052                 /* Append mappings */
1053                 x = 0;
1054                 st->maps = (struct dundi_mapping *)s;
1055                 AST_LIST_TRAVERSE(&mappings, cur, list) {
1056                         if (!strcasecmp(cur->dcontext, ccontext)) {
1057                                 if (x < mapcount) {
1058                                         st->maps[x] = *cur;
1059                                         st->maps[x].list.next = NULL;
1060                                         x++;
1061                                 }
1062                         }
1063                 }
1064                 st->nummaps = mapcount;
1065                 ast_debug(1, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
1066                 trans->thread = 1;
1067                 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_precache_thread, st)) {
1068                         trans->thread = 0;
1069                         ast_log(LOG_WARNING, "Unable to create thread!\n");
1070                         ast_free(st);
1071                         memset(&ied, 0, sizeof(ied));
1072                         dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1073                         dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1074                         return -1;
1075                 }
1076         } else {
1077                 ast_log(LOG_WARNING, "Out of memory!\n");
1078                 memset(&ied, 0, sizeof(ied));
1079                 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1080                 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
1081                 return -1;
1082         }
1083         return 0;
1084 }
1085
1086 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
1087 {
1088         struct dundi_query_state *st;
1089         int totallen;
1090         int x;
1091         struct dundi_ie_data ied;
1092         char *s;
1093         struct dundi_mapping *cur;
1094         int mapcount = 0;
1095         int skipfirst = 0;
1096
1097         pthread_t lookupthread;
1098         totallen = sizeof(struct dundi_query_state);
1099         /* Count matching map entries */
1100         AST_LIST_TRAVERSE(&mappings, cur, list) {
1101                 if (!strcasecmp(cur->dcontext, ccontext))
1102                         mapcount++;
1103         }
1104         /* If no maps, return -1 immediately */
1105         if (!mapcount)
1106                 return -1;
1107
1108         if (ies->eidcount > 1) {
1109                 /* Since it is a requirement that the first EID is the authenticating host
1110                    and the last EID is the root, it is permissible that the first and last EID
1111                    could be the same.  In that case, we should go ahead copy only the "root" section
1112                    since we will not need it for authentication. */
1113                 if (!ast_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
1114                         skipfirst = 1;
1115         }
1116
1117         totallen += mapcount * sizeof(struct dundi_mapping);
1118         totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
1119         st = ast_calloc(1, totallen);
1120         if (st) {
1121                 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
1122                 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
1123                 st->trans = trans;
1124                 st->ttl = ies->ttl - 1;
1125                 st->nocache = ies->cbypass;
1126                 if (st->ttl < 0)
1127                         st->ttl = 0;
1128                 s = st->fluffy;
1129                 for (x=skipfirst;ies->eids[x];x++) {
1130                         st->eids[x-skipfirst] = (dundi_eid *)s;
1131                         *st->eids[x-skipfirst] = *ies->eids[x];
1132                         st->directs[x-skipfirst] = ies->eid_direct[x];
1133                         s += sizeof(dundi_eid);
1134                 }
1135                 /* Append mappings */
1136                 x = 0;
1137                 st->maps = (struct dundi_mapping *)s;
1138                 AST_LIST_TRAVERSE(&mappings, cur, list) {
1139                         if (!strcasecmp(cur->dcontext, ccontext)) {
1140                                 if (x < mapcount) {
1141                                         st->maps[x] = *cur;
1142                                         st->maps[x].list.next = NULL;
1143                                         x++;
1144                                 }
1145                         }
1146                 }
1147                 st->nummaps = mapcount;
1148                 ast_debug(1, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
1149                 trans->thread = 1;
1150                 if (ast_pthread_create_detached(&lookupthread, NULL, dundi_lookup_thread, st)) {
1151                         trans->thread = 0;
1152                         ast_log(LOG_WARNING, "Unable to create thread!\n");
1153                         ast_free(st);
1154                         memset(&ied, 0, sizeof(ied));
1155                         dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
1156                         dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1157                         return -1;
1158                 }
1159         } else {
1160                 ast_log(LOG_WARNING, "Out of memory!\n");
1161                 memset(&ied, 0, sizeof(ied));
1162                 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
1163                 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
1164                 return -1;
1165         }
1166         return 0;
1167 }
1168
1169 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
1170 {
1171         char data[1024];
1172         char *ptr, *term, *src;
1173         int tech;
1174         struct ast_flags flags;
1175         int weight;
1176         int length;
1177         int z;
1178         char fs[256];
1179
1180         /* Build request string */
1181         if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
1182                 time_t timeout;
1183                 ptr = data;
1184                 if (!ast_get_time_t(ptr, &timeout, 0, &length)) {
1185                         int expiration = timeout - now;
1186                         if (expiration > 0) {
1187                                 ast_debug(1, "Found cache expiring in %d seconds!\n", expiration);
1188                                 ptr += length + 1;
1189                                 while((sscanf(ptr, "%30d/%30d/%30d/%n", (int *)&(flags.flags), &weight, &tech, &length) == 3)) {
1190                                         ptr += length;
1191                                         term = strchr(ptr, '|');
1192                                         if (term) {
1193                                                 *term = '\0';
1194                                                 src = strrchr(ptr, '/');
1195                                                 if (src) {
1196                                                         *src = '\0';
1197                                                         src++;
1198                                                 } else
1199                                                         src = "";
1200                                                 ast_debug(1, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
1201                                                         tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
1202                                                 /* Make sure it's not already there */
1203                                                 for (z=0;z<req->respcount;z++) {
1204                                                         if ((req->dr[z].techint == tech) &&
1205                                                             !strcmp(req->dr[z].dest, ptr))
1206                                                                         break;
1207                                                 }
1208                                                 if (z == req->respcount) {
1209                                                         /* Copy into parent responses */
1210                                                         ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
1211                                                         req->dr[req->respcount].weight = weight;
1212                                                         req->dr[req->respcount].techint = tech;
1213                                                         req->dr[req->respcount].expiration = expiration;
1214                                                         dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
1215                                                         ast_eid_to_str(req->dr[req->respcount].eid_str,
1216                                                                 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
1217                                                         ast_copy_string(req->dr[req->respcount].dest, ptr,
1218                                                                 sizeof(req->dr[req->respcount].dest));
1219                                                         ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
1220                                                                 sizeof(req->dr[req->respcount].tech));
1221                                                         req->respcount++;
1222                                                         ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK);
1223                                                 } else if (req->dr[z].weight > weight)
1224                                                         req->dr[z].weight = weight;
1225                                                 ptr = term + 1;
1226                                         }
1227                                 }
1228                                 /* We found *something* cached */
1229                                 if (expiration < *lowexpiration)
1230                                         *lowexpiration = expiration;
1231                                 return 1;
1232                         } else
1233                                 ast_db_del("dundi/cache", key);
1234                 } else
1235                         ast_db_del("dundi/cache", key);
1236         }
1237
1238         return 0;
1239 }
1240
1241 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, uint32_t crc, int *lowexpiration)
1242 {
1243         char eid_str[20];
1244         char eidroot_str[20];
1245         time_t now;
1246         int res=0;
1247         int res2=0;
1248         char eid_str_full[20];
1249         char tmp[256]="";
1250         /* Enough space for largest value that can be stored in key. */
1251         char key[sizeof(eid_str) + sizeof(tmp) + sizeof(req->dcontext) + sizeof(eidroot_str) + sizeof("hint////r")];
1252         int x;
1253
1254         time(&now);
1255         dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
1256         dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
1257         ast_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
1258         snprintf(key, sizeof(key), "%s/%s/%s/e%08x", eid_str, req->number, req->dcontext, crc);
1259         res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1260         snprintf(key, sizeof(key), "%s/%s/%s/e%08x", eid_str, req->number, req->dcontext, (unsigned)0);
1261         res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1262         snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
1263         res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1264         x = 0;
1265         if (!req->respcount) {
1266                 while(!res2) {
1267                         /* Look and see if we have a hint that would preclude us from looking at this
1268                            peer for this number. */
1269                         if (!(tmp[x] = req->number[x]))
1270                                 break;
1271                         x++;
1272                         /* Check for hints */
1273                         snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08x", eid_str, tmp, req->dcontext, crc);
1274                         res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1275                         snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08x", eid_str, tmp, req->dcontext, (unsigned)0);
1276                         res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1277                         snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
1278                         res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
1279                         if (res2) {
1280                                 if (strlen(tmp) > strlen(req->hmd->exten)) {
1281                                         /* Update meta data if appropriate */
1282                                         ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
1283                                 }
1284                         }
1285                 }
1286                 res |= res2;
1287         }
1288
1289         return res;
1290 }
1291
1292 static void qualify_peer(struct dundi_peer *peer, int schedonly);
1293
1294 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
1295 {
1296         if (ast_sockaddr_isnull(&trans->addr)) {
1297                 ast_sockaddr_copy(&trans->addr, &p->addr);
1298         }
1299         trans->us_eid = p->us_eid;
1300         trans->them_eid = p->eid;
1301         /* Enable encryption if appropriate */
1302         if (!ast_strlen_zero(p->inkey))
1303                 ast_set_flag(trans, FLAG_ENCRYPT);
1304         if (p->maxms) {
1305                 trans->autokilltimeout = p->maxms;
1306                 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1307                 if (p->lastms > 1) {
1308                         trans->retranstimer = p->lastms * 2;
1309                         /* Keep it from being silly */
1310                         if (trans->retranstimer < 150)
1311                                 trans->retranstimer = 150;
1312                 }
1313                 if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
1314                         trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
1315         } else
1316                 trans->autokilltimeout = global_autokilltimeout;
1317 }
1318
1319 /*! \note Called with the peers list already locked */
1320 static int do_register_expire(const void *data)
1321 {
1322         struct dundi_peer *peer = (struct dundi_peer *)data;
1323         char eid_str[20];
1324         ast_debug(1, "Register expired for '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1325         peer->registerexpire = -1;
1326         peer->lastms = 0;
1327         ast_sockaddr_setnull(&peer->addr);
1328         return 0;
1329 }
1330
1331 static int update_key(struct dundi_peer *peer)
1332 {
1333         unsigned char key[16];
1334         struct ast_key *ekey, *skey;
1335         char eid_str[20];
1336         int res;
1337         if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1338                 build_iv(key);
1339                 ast_aes_set_encrypt_key(key, &peer->us_ecx);
1340                 ast_aes_set_decrypt_key(key, &peer->us_dcx);
1341                 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1342                 if (!ekey) {
1343                         ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1344                                 peer->inkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1345                         return -1;
1346                 }
1347                 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1348                 if (!skey) {
1349                         ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1350                                 peer->outkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1351                         return -1;
1352                 }
1353                 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1354                         ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1355                         return -1;
1356                 }
1357                 if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
1358                         ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1359                         return -1;
1360                 }
1361                 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1362                 peer->sentfullkey = 0;
1363                 /* Looks good */
1364                 time(&peer->keyexpire);
1365                 peer->keyexpire += dundi_key_ttl;
1366         }
1367         return 0;
1368 }
1369
1370 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_encrypt_key *ecx)
1371 {
1372         unsigned char curblock[16];
1373         int x;
1374         memcpy(curblock, iv, sizeof(curblock));
1375         while(len > 0) {
1376                 for (x=0;x<16;x++)
1377                         curblock[x] ^= src[x];
1378                 ast_aes_encrypt(curblock, dst, ecx);
1379                 memcpy(curblock, dst, sizeof(curblock));
1380                 dst += 16;
1381                 src += 16;
1382                 len -= 16;
1383         }
1384         return 0;
1385 }
1386 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_decrypt_key *dcx)
1387 {
1388         unsigned char lastblock[16];
1389         int x;
1390         memcpy(lastblock, iv, sizeof(lastblock));
1391         while(len > 0) {
1392                 ast_aes_decrypt(src, dst, dcx);
1393                 for (x=0;x<16;x++)
1394                         dst[x] ^= lastblock[x];
1395                 memcpy(lastblock, src, sizeof(lastblock));
1396                 dst += 16;
1397                 src += 16;
1398                 len -= 16;
1399         }
1400         return 0;
1401 }
1402
1403 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)
1404 {
1405         int space = *dstlen;
1406         unsigned long bytes;
1407         struct dundi_hdr *h;
1408         unsigned char *decrypt_space;
1409         decrypt_space = ast_alloca(srclen);
1410         decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1411         /* Setup header */
1412         h = (struct dundi_hdr *)dst;
1413         *h = *ohdr;
1414         bytes = space - 6;
1415         if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1416                 ast_debug(1, "Ouch, uncompress failed :(\n");
1417                 return NULL;
1418         }
1419         /* Update length */
1420         *dstlen = bytes + 6;
1421         /* Return new header */
1422         return h;
1423 }
1424
1425 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1426 {
1427         unsigned char *compress_space;
1428         int len;
1429         int res;
1430         unsigned long bytes;
1431         struct dundi_ie_data ied;
1432         struct dundi_peer *peer;
1433         unsigned char iv[16];
1434         len = pack->datalen + pack->datalen / 100 + 42;
1435         compress_space = ast_alloca(len);
1436         memset(compress_space, 0, len);
1437         /* We care about everthing save the first 6 bytes of header */
1438         bytes = len;
1439         res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1440         if (res != Z_OK) {
1441                 ast_debug(1, "Ouch, compression failed!\n");
1442                 return -1;
1443         }
1444         memset(&ied, 0, sizeof(ied));
1445         /* Say who we are */
1446         if (!pack->h->iseqno && !pack->h->oseqno) {
1447                 /* Need the key in the first copy */
1448                 if (!(peer = find_peer(&trans->them_eid)))
1449                         return -1;
1450                 if (update_key(peer))
1451                         return -1;
1452                 if (!peer->sentfullkey)
1453                         ast_set_flag(trans, FLAG_SENDFULLKEY);
1454                 /* Append key data */
1455                 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1456                 if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
1457                         dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1458                         dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1459                 } else {
1460                         dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
1461                 }
1462                 /* Setup contexts */
1463                 trans->ecx = peer->us_ecx;
1464                 trans->dcx = peer->us_dcx;
1465
1466                 /* We've sent the full key */
1467                 peer->sentfullkey = 1;
1468         }
1469         /* Build initialization vector */
1470         build_iv(iv);
1471         /* Add the field, rounded up to 16 bytes */
1472         dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1473         /* Copy the data */
1474         if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1475                 ast_log(LOG_NOTICE, "Final packet too large!\n");
1476                 return -1;
1477         }
1478         encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1479         ied.pos += ((bytes + 15) / 16) * 16;
1480         /* Reconstruct header */
1481         pack->datalen = sizeof(struct dundi_hdr);
1482         pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
1483         pack->h->cmdflags = 0;
1484         memcpy(pack->h->ies, ied.buf, ied.pos);
1485         pack->datalen += ied.pos;
1486         return 0;
1487 }
1488
1489 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, uint32_t keycrc32)
1490 {
1491         unsigned char dst[128];
1492         int res;
1493         struct ast_key *key, *skey;
1494         char eid_str[20];
1495         ast_debug(1, "Expected '%08x' got '%08x'\n", peer->them_keycrc32, keycrc32);
1496         if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1497                 /* A match */
1498                 return 1;
1499         } else if (!newkey || !newsig)
1500                 return 0;
1501         if (!memcmp(peer->rxenckey, newkey, 128) &&
1502             !memcmp(peer->rxenckey + 128, newsig, 128)) {
1503                 /* By definition, a match */
1504                 return 1;
1505         }
1506         /* Decrypt key */
1507         key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1508         if (!key) {
1509                 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1510                         peer->outkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1511                 return -1;
1512         }
1513
1514         skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1515         if (!skey) {
1516                 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1517                         peer->inkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1518                 return -1;
1519         }
1520
1521         /* First check signature */
1522         res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
1523         if (res)
1524                 return 0;
1525
1526         res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1527         if (res != 16) {
1528                 if (res >= 0)
1529                         ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1530                 return 0;
1531         }
1532         /* Decrypted, passes signature */
1533         ast_debug(1, "Wow, new key combo passed signature and decrypt!\n");
1534         memcpy(peer->rxenckey, newkey, 128);
1535         memcpy(peer->rxenckey + 128, newsig, 128);
1536         peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1537         ast_aes_set_decrypt_key(dst, &peer->them_dcx);
1538         ast_aes_set_encrypt_key(dst, &peer->them_ecx);
1539         return 1;
1540 }
1541
1542 static void deep_copy_peer(struct dundi_peer *peer_dst, const struct dundi_peer *peer_src)
1543 {
1544         struct permission *cur, *perm;
1545
1546         memcpy(peer_dst, peer_src, sizeof(*peer_dst));
1547
1548         memset(&peer_dst->permit, 0, sizeof(peer_dst->permit));
1549         memset(&peer_dst->include, 0, sizeof(peer_dst->permit));
1550
1551         AST_LIST_TRAVERSE(&peer_src->permit, cur, list) {
1552                 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1553                         continue;
1554
1555                 perm->allow = cur->allow;
1556                 strcpy(perm->name, cur->name);
1557
1558                 AST_LIST_INSERT_HEAD(&peer_dst->permit, perm, list);
1559         }
1560
1561         AST_LIST_TRAVERSE(&peer_src->include, cur, list) {
1562                 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1563                         continue;
1564
1565                 perm->allow = cur->allow;
1566                 strcpy(perm->name, cur->name);
1567
1568                 AST_LIST_INSERT_HEAD(&peer_dst->include, perm, list);
1569         }
1570 }
1571
1572 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1573 {
1574         /* Handle canonical command / response */
1575         int final = hdr->cmdresp & 0x80;
1576         int cmd = hdr->cmdresp & 0x7f;
1577         int x,y,z;
1578         int resp;
1579         int res;
1580         int authpass=0;
1581         unsigned char *bufcpy;
1582 #ifdef LOW_MEMORY
1583         struct dundi_ie_data *ied = ast_calloc(1, sizeof(*ied));
1584 #else
1585         struct dundi_ie_data _ied = {
1586                 .pos = 0,
1587         };
1588         struct dundi_ie_data *ied = &_ied;
1589 #endif
1590         struct dundi_ies ies = {
1591                 .eidcount = 0,
1592         };
1593         struct dundi_peer *peer = NULL;
1594         char eid_str[20];
1595         char eid_str2[20];
1596         int retval = -1;
1597
1598         if (!ied) {
1599                 return -1;
1600         }
1601
1602         if (datalen) {
1603                 bufcpy = ast_alloca(datalen);
1604                 /* Make a copy for parsing */
1605                 memcpy(bufcpy, hdr->ies, datalen);
1606                 ast_debug(1, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1607                 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1608                         ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1609                         goto return_cleanup;
1610                 }
1611         }
1612         switch(cmd) {
1613         case DUNDI_COMMAND_DPDISCOVER:
1614         case DUNDI_COMMAND_EIDQUERY:
1615         case DUNDI_COMMAND_PRECACHERQ:
1616                 if (cmd == DUNDI_COMMAND_EIDQUERY)
1617                         resp = DUNDI_COMMAND_EIDRESPONSE;
1618                 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1619                         resp = DUNDI_COMMAND_PRECACHERP;
1620                 else
1621                         resp = DUNDI_COMMAND_DPRESPONSE;
1622                 /* A dialplan or entity discover -- qualify by highest level entity */
1623                 peer = find_peer(ies.eids[0]);
1624                 if (!peer) {
1625                         dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1626                         dundi_send(trans, resp, 0, 1, ied);
1627                 } else {
1628                         int hasauth = 0;
1629                         trans->us_eid = peer->us_eid;
1630                         if (strlen(peer->inkey)) {
1631                                 hasauth = encrypted;
1632                         } else
1633                                 hasauth = 1;
1634                         if (hasauth) {
1635                                 /* Okay we're authentiated and all, now we check if they're authorized */
1636                                 if (!ies.called_context)
1637                                         ies.called_context = "e164";
1638                                 if (cmd == DUNDI_COMMAND_EIDQUERY) {
1639                                         res = dundi_answer_entity(trans, &ies, ies.called_context);
1640                                 } else {
1641                                         if (ast_strlen_zero(ies.called_number)) {
1642                                                 /* They're not permitted to access that context */
1643                                                 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1644                                                 dundi_send(trans, resp, 0, 1, ied);
1645                                         } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
1646                                                    (peer->model & DUNDI_MODEL_INBOUND) &&
1647                                                            has_permission(&peer->permit, ies.called_context)) {
1648                                                 res = dundi_answer_query(trans, &ies, ies.called_context);
1649                                                 if (res < 0) {
1650                                                         /* There is no such dundi context */
1651                                                         dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1652                                                         dundi_send(trans, resp, 0, 1, ied);
1653                                                 }
1654                                         } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
1655                                                    (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
1656                                                            has_permission(&peer->include, ies.called_context)) {
1657                                                 res = dundi_prop_precache(trans, &ies, ies.called_context);
1658                                                 if (res < 0) {
1659                                                         /* There is no such dundi context */
1660                                                         dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1661                                                         dundi_send(trans, resp, 0, 1, ied);
1662                                                 }
1663                                         } else {
1664                                                 /* They're not permitted to access that context */
1665                                                 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1666                                                 dundi_send(trans, resp, 0, 1, ied);
1667                                         }
1668                                 }
1669                         } else {
1670                                 /* They're not permitted to access that context */
1671                                 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1672                                 dundi_send(trans, resp, 0, 1, ied);
1673                         }
1674                 }
1675                 break;
1676         case DUNDI_COMMAND_REGREQ:
1677                 /* A register request -- should only have one entity */
1678                 peer = find_peer(ies.eids[0]);
1679
1680                 /* if the peer is not found and we have a valid 'any_peer' setting */
1681                 if (any_peer && peer == any_peer) {
1682                         /* copy any_peer into a new peer object */
1683                         peer = ast_calloc(1, sizeof(*peer));
1684                         if (peer) {
1685                                 deep_copy_peer(peer, any_peer);
1686
1687                                 /* set EID to remote EID */
1688                                 peer->eid = *ies.eids[0];
1689
1690                                 AST_LIST_LOCK(&peers);
1691                                 AST_LIST_INSERT_HEAD(&peers, peer, list);
1692                                 AST_LIST_UNLOCK(&peers);
1693                         }
1694                 }
1695
1696                 if (!peer || !peer->dynamic) {
1697                         dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1698                         dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
1699                 } else {
1700                         int hasauth = 0;
1701                         trans->us_eid = peer->us_eid;
1702                         if (!ast_strlen_zero(peer->inkey)) {
1703                                 hasauth = encrypted;
1704                         } else
1705                                 hasauth = 1;
1706                         if (hasauth) {
1707                                 int expire = default_expiration;
1708                                 char data[256];
1709                                 int needqual = 0;
1710                                 AST_SCHED_DEL(sched, peer->registerexpire);
1711                                 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1712                                 snprintf(data, sizeof(data), "%s:%d", ast_sockaddr_stringify(&trans->addr), expire);
1713                                 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1714                                 if (ast_sockaddr_cmp(&peer->addr, &trans->addr)) {
1715                                         ast_verb(3, "Registered DUNDi peer '%s' at '%s'\n",
1716                                                 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
1717                                                 ast_sockaddr_stringify(&trans->addr));
1718                                         needqual = 1;
1719                                 }
1720
1721                                 ast_sockaddr_copy(&peer->addr, &trans->addr);
1722                                 dundi_ie_append_short(ied, DUNDI_IE_EXPIRATION, default_expiration);
1723                                 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
1724                                 if (needqual)
1725                                         qualify_peer(peer, 1);
1726                         }
1727                 }
1728                 break;
1729         case DUNDI_COMMAND_DPRESPONSE:
1730                 /* A dialplan response, lets see what we got... */
1731                 if (ies.cause < 1) {
1732                         /* Success of some sort */
1733                         ast_debug(1, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1734                         if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1735                                 authpass = encrypted;
1736                         } else
1737                                 authpass = 1;
1738                         if (authpass) {
1739                                 /* Pass back up answers */
1740                                 if (trans->parent && trans->parent->dr) {
1741                                         y = trans->parent->respcount;
1742                                         for (x=0;x<ies.anscount;x++) {
1743                                                 if (trans->parent->respcount < trans->parent->maxcount) {
1744                                                         /* Make sure it's not already there */
1745                                                         for (z=0;z<trans->parent->respcount;z++) {
1746                                                                 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1747                                                                     !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
1748                                                                                 break;
1749                                                         }
1750                                                         if (z == trans->parent->respcount) {
1751                                                                 /* Copy into parent responses */
1752                                                                 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1753                                                                 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1754                                                                 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1755                                                                 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1756                                                                 if (ies.expiration > 0)
1757                                                                         trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1758                                                                 else
1759                                                                         trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
1760                                                                 ast_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
1761                                                                         sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1762                                                                         &ies.answers[x]->eid);
1763                                                                 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1764                                                                         sizeof(trans->parent->dr[trans->parent->respcount].dest));
1765                                                                 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1766                                                                         sizeof(trans->parent->dr[trans->parent->respcount].tech));
1767                                                                 trans->parent->respcount++;
1768                                                                 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1769                                                         } else if (trans->parent->dr[z].weight > ntohs(ies.answers[x]->weight)) {
1770                                                                 /* Update weight if appropriate */
1771                                                                 trans->parent->dr[z].weight = ntohs(ies.answers[x]->weight);
1772                                                         }
1773                                                 } else
1774                                                         ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1775                                                                 trans->parent->number, trans->parent->dcontext);
1776                                         }
1777                                         /* Save all the results (if any) we had.  Even if no results, still cache lookup.  Let
1778                                            the cache know if this request was unaffected by our entity list. */
1779                                         cache_save(&trans->them_eid, trans->parent, y,
1780                                                         ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1781                                         if (ies.hint) {
1782                                                 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1783                                                 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1784                                                         ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1785                                                 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
1786                                                         if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1787                                                                 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
1788                                                                         sizeof(trans->parent->hmd->exten));
1789                                                         }
1790                                                 } else {
1791                                                         ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1792                                                 }
1793                                         }
1794                                         if (ies.expiration > 0) {
1795                                                 if (trans->parent->expiration > ies.expiration) {
1796                                                         trans->parent->expiration = ies.expiration;
1797                                                 }
1798                                         }
1799                                 }
1800                                 /* Close connection if not final */
1801                                 if (!final)
1802                                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1803                         }
1804
1805                 } else {
1806                         /* Auth failure, check for data */
1807                         if (!final) {
1808                                 /* Cancel if they didn't already */
1809                                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1810                         }
1811                 }
1812                 break;
1813         case DUNDI_COMMAND_EIDRESPONSE:
1814                 /* A dialplan response, lets see what we got... */
1815                 if (ies.cause < 1) {
1816                         /* Success of some sort */
1817                         ast_debug(1, "Looks like success of some sort (%d)\n", ies.cause);
1818                         if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1819                                 authpass = encrypted;
1820                         } else
1821                                 authpass = 1;
1822                         if (authpass) {
1823                                 /* Pass back up answers */
1824                                 if (trans->parent && trans->parent->dei && ies.q_org) {
1825                                         if (!trans->parent->respcount) {
1826                                                 trans->parent->respcount++;
1827                                                 if (ies.q_dept)
1828                                                         ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1829                                                 if (ies.q_org)
1830                                                         ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1831                                                 if (ies.q_locality)
1832                                                         ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1833                                                 if (ies.q_stateprov)
1834                                                         ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1835                                                 if (ies.q_country)
1836                                                         ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1837                                                 if (ies.q_email)
1838                                                         ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1839                                                 if (ies.q_phone)
1840                                                         ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1841                                                 if (ies.q_ipaddr)
1842                                                         ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1843                                                 if (!ast_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1844                                                         /* If it's them, update our address */
1845                                                         ast_copy_string(trans->parent->dei->ipaddr, ast_sockaddr_stringify_addr(&trans->addr), sizeof(trans->parent->dei->ipaddr));
1846                                                 }
1847                                         }
1848                                         if (ies.hint) {
1849                                                 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1850                                                         ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1851                                         }
1852                                 }
1853                                 /* Close connection if not final */
1854                                 if (!final)
1855                                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1856                         }
1857
1858                 } else {
1859                         /* Auth failure, check for data */
1860                         if (!final) {
1861                                 /* Cancel if they didn't already */
1862                                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1863                         }
1864                 }
1865                 break;
1866         case DUNDI_COMMAND_REGRESPONSE:
1867                 /* A dialplan response, lets see what we got... */
1868                 if (ies.cause < 1) {
1869                         int hasauth;
1870                         /* Success of some sort */
1871                         if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1872                                 hasauth = encrypted;
1873                         } else
1874                                 hasauth = 1;
1875
1876                         if (!hasauth) {
1877                                 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1878                                 if (!final) {
1879                                         dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1880                                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, ied);
1881                                 }
1882                         } else {
1883                                 ast_debug(1, "Yay, we've registered as '%s' to '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &trans->us_eid),
1884                                                 ast_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1885                                 /* Close connection if not final */
1886                                 if (!final)
1887                                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1888                         }
1889                 } else {
1890                         /* Auth failure, cancel if they didn't for some reason */
1891                         if (!final) {
1892                                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1893                         }
1894                 }
1895                 break;
1896         case DUNDI_COMMAND_INVALID:
1897         case DUNDI_COMMAND_NULL:
1898         case DUNDI_COMMAND_PRECACHERP:
1899                 /* Do nothing special */
1900                 if (!final)
1901                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1902                 break;
1903         case DUNDI_COMMAND_ENCREJ:
1904                 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
1905                         /* No really, it's over at this point */
1906                         if (!final)
1907                                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1908                 } else {
1909                         /* Send with full key */
1910                         ast_set_flag(trans, FLAG_SENDFULLKEY);
1911                         if (final) {
1912                                 /* Ooops, we got a final message, start by sending ACK... */
1913                                 dundi_ack(trans, hdr->cmdresp & 0x80);
1914                                 trans->aseqno = trans->iseqno;
1915                                 /* Now, we gotta create a new transaction */
1916                                 if (!reset_transaction(trans)) {
1917                                         /* Make sure handle_frame doesn't destroy us */
1918                                         hdr->cmdresp &= 0x7f;
1919                                         /* Parse the message we transmitted */
1920                                         memset(&ies, 0, sizeof(ies));
1921                                         dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
1922                                         /* Reconstruct outgoing encrypted packet */
1923                                         memset(ied, 0, sizeof(*ied));
1924                                         dundi_ie_append_eid(ied, DUNDI_IE_EID, &trans->us_eid);
1925                                         dundi_ie_append_raw(ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1926                                         dundi_ie_append_raw(ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1927                                         if (ies.encblock)
1928                                                 dundi_ie_append_encdata(ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
1929                                         dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, ied);
1930                                         peer->sentfullkey = 1;
1931                                 }
1932                         }
1933                 }
1934                 break;
1935         case DUNDI_COMMAND_ENCRYPT:
1936                 if (!encrypted) {
1937                         /* No nested encryption! */
1938                         if ((trans->iseqno == 1) && !trans->oseqno) {
1939                                 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
1940                                         ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1941                                         (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1942                                         if (!final) {
1943                                                 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1944                                         }
1945                                         break;
1946                                 }
1947                                 apply_peer(trans, peer);
1948                                 /* Key passed, use new contexts for this session */
1949                                 trans->ecx = peer->them_ecx;
1950                                 trans->dcx = peer->them_dcx;
1951                         }
1952                         if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1953                                 struct dundi_hdr *dhdr;
1954                                 unsigned char decoded[MAX_PACKET_SIZE];
1955                                 int ddatalen;
1956                                 ddatalen = sizeof(decoded);
1957                                 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1958                                 if (dhdr) {
1959                                         /* Handle decrypted response */
1960                                         if (dundidebug)
1961                                                 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1962                                         handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1963                                         /* Carry back final flag */
1964                                         hdr->cmdresp |= dhdr->cmdresp & 0x80;
1965                                         break;
1966                                 } else {
1967                                         ast_debug(1, "Ouch, decrypt failed :(\n");
1968                                 }
1969                         }
1970                 }
1971                 if (!final) {
1972                         /* Turn off encryption */
1973                         ast_clear_flag(trans, FLAG_ENCRYPT);
1974                         dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1975                 }
1976                 break;
1977         default:
1978                 /* Send unknown command if we don't know it, with final flag IFF it's the
1979                    first command in the dialog and only if we haven't received final notification */
1980                 if (!final) {
1981                         dundi_ie_append_byte(ied, DUNDI_IE_UNKNOWN, cmd);
1982                         dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, ied);
1983                 }
1984         }
1985
1986         retval = 0;
1987
1988 return_cleanup:
1989 #ifdef LOW_MEMORY
1990         ast_free(ied);
1991 #endif
1992         return retval;
1993 }
1994
1995 static void destroy_packet(struct dundi_packet *pack, int needfree);
1996 static void destroy_packets(struct packetlist *p)
1997 {
1998         struct dundi_packet *pack;
1999
2000         while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
2001                 AST_SCHED_DEL(sched, pack->retransid);
2002                 ast_free(pack);
2003         }
2004 }
2005
2006
2007 static int ack_trans(struct dundi_transaction *trans, int iseqno)
2008 {
2009         struct dundi_packet *pack;
2010
2011         /* Ack transmitted packet corresponding to iseqno */
2012         AST_LIST_TRAVERSE(&trans->packets, pack, list) {
2013                 if ((pack->h->oseqno + 1) % 255 == iseqno) {
2014                         destroy_packet(pack, 0);
2015                         if (!AST_LIST_EMPTY(&trans->lasttrans)) {
2016                                 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
2017                                 destroy_packets(&trans->lasttrans);
2018                         }
2019                         AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
2020                         AST_SCHED_DEL(sched, trans->autokillid);
2021                         return 1;
2022                 }
2023         }
2024
2025         return 0;
2026 }
2027
2028 static int handle_frame(struct dundi_hdr *h, struct ast_sockaddr *sin, int datalen)
2029 {
2030         struct dundi_transaction *trans;
2031         trans = find_transaction(h, sin);
2032         if (!trans) {
2033                 dundi_reject(h, sin);
2034                 return 0;
2035         }
2036         /* Got a transaction, see where this header fits in */
2037         if (h->oseqno == trans->iseqno) {
2038                 /* Just what we were looking for...  Anything but ack increments iseqno */
2039                 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
2040                         /* If final, we're done */
2041                         destroy_trans(trans, 0);
2042                         return 0;
2043                 }
2044                 if (h->cmdresp != DUNDI_COMMAND_ACK) {
2045                         trans->oiseqno = trans->iseqno;
2046                         trans->iseqno++;
2047                         handle_command_response(trans, h, datalen, 0);
2048                 }
2049                 if (trans->aseqno != trans->iseqno) {
2050                         dundi_ack(trans, h->cmdresp & 0x80);
2051                         trans->aseqno = trans->iseqno;
2052                 }
2053                 /* Delete any saved last transmissions */
2054                 destroy_packets(&trans->lasttrans);
2055                 if (h->cmdresp & 0x80) {
2056                         /* Final -- destroy now */
2057                         destroy_trans(trans, 0);
2058                 }
2059         } else if (h->oseqno == trans->oiseqno) {
2060                 /* Last incoming sequence number -- send ACK without processing */
2061                 dundi_ack(trans, 0);
2062         } else {
2063                 /* Out of window -- simply drop */
2064                 ast_debug(1, "Dropping packet out of window!\n");
2065         }
2066         return 0;
2067 }
2068
2069 static int socket_read(int *id, int fd, short events, void *cbdata)
2070 {
2071         struct ast_sockaddr sin;
2072         int res;
2073         struct dundi_hdr *h;
2074         char buf[MAX_PACKET_SIZE];
2075
2076         res = ast_recvfrom(netsocket, buf, sizeof(buf), 0, &sin);
2077         if (res < 0) {
2078                 if (errno != ECONNREFUSED)
2079                         ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
2080                 return 1;
2081         }
2082         if (res < sizeof(struct dundi_hdr)) {
2083                 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
2084                 return 1;
2085         }
2086         buf[res] = '\0';
2087         h = (struct dundi_hdr *) buf;
2088         if (dundidebug)
2089                 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
2090         AST_LIST_LOCK(&peers);
2091         handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
2092         AST_LIST_UNLOCK(&peers);
2093         return 1;
2094 }
2095
2096 static void build_secret(char *secret, int seclen)
2097 {
2098         unsigned char tmp[16];
2099         char *s;
2100         build_iv(tmp);
2101         secret[0] = '\0';
2102         ast_base64encode(secret, tmp, sizeof(tmp), seclen);
2103         /* Eliminate potential bad characters */
2104         while((s = strchr(secret, ';'))) *s = '+';
2105         while((s = strchr(secret, '/'))) *s = '+';
2106         while((s = strchr(secret, ':'))) *s = '+';
2107         while((s = strchr(secret, '@'))) *s = '+';
2108 }
2109
2110
2111 static void save_secret(const char *newkey, const char *oldkey)
2112 {
2113         char tmp[256];
2114         if (oldkey)
2115                 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2116         else
2117                 snprintf(tmp, sizeof(tmp), "%s", newkey);
2118         rotatetime = time(NULL) + DUNDI_SECRET_TIME;
2119         ast_db_put(secretpath, "secret", tmp);
2120         snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
2121         ast_db_put(secretpath, "secretexpiry", tmp);
2122 }
2123
2124 static void load_password(void)
2125 {
2126         char *current=NULL;
2127         char *last=NULL;
2128         char tmp[256];
2129         time_t expired;
2130
2131         ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2132         if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
2133                 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2134                 current = strchr(tmp, ';');
2135                 if (!current)
2136                         current = tmp;
2137                 else {
2138                         *current = '\0';
2139                         current++;
2140                 };
2141                 if ((time(NULL) - expired) < 0) {
2142                         if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2143                                 expired = time(NULL) + DUNDI_SECRET_TIME;
2144                 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2145                         last = current;
2146                         current = NULL;
2147                 } else {
2148                         last = NULL;
2149                         current = NULL;
2150                 }
2151         }
2152         if (current) {
2153                 /* Current key is still valid, just setup rotatation properly */
2154                 ast_copy_string(cursecret, current, sizeof(cursecret));
2155                 rotatetime = expired;
2156         } else {
2157                 /* Current key is out of date, rotate or eliminate all together */
2158                 build_secret(cursecret, sizeof(cursecret));
2159                 save_secret(cursecret, last);
2160         }
2161 }
2162
2163 static void check_password(void)
2164 {
2165         char oldsecret[80];
2166         time_t now;
2167
2168         time(&now);
2169 #if 0
2170         printf("%ld/%ld\n", now, rotatetime);
2171 #endif
2172         if ((now - rotatetime) >= 0) {
2173                 /* Time to rotate keys */
2174                 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
2175                 build_secret(cursecret, sizeof(cursecret));
2176                 save_secret(cursecret, oldsecret);
2177         }
2178 }
2179
2180 static void *network_thread(void *ignore)
2181 {
2182         /* Our job is simple: Send queued messages, retrying if necessary.  Read frames
2183            from the network, and queue them for delivery to the channels */
2184         int res;
2185         /* Establish I/O callback for socket read */
2186         int *socket_read_id = ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
2187
2188         while (!dundi_shutdown) {
2189                 res = ast_sched_wait(sched);
2190                 if ((res > 1000) || (res < 0))
2191                         res = 1000;
2192                 res = ast_io_wait(io, res);
2193                 if (res >= 0) {
2194                         AST_LIST_LOCK(&peers);
2195                         ast_sched_runq(sched);
2196                         AST_LIST_UNLOCK(&peers);
2197                 }
2198                 check_password();
2199         }
2200
2201         ast_io_remove(io, socket_read_id);
2202         netthreadid = AST_PTHREADT_NULL;
2203
2204         return NULL;
2205 }
2206
2207 static void *process_clearcache(void *ignore)
2208 {
2209         struct ast_db_entry *db_entry, *db_tree;
2210         int striplen = sizeof("/dundi/cache");
2211         time_t now;
2212
2213         while (!dundi_shutdown) {
2214                 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
2215
2216                 time(&now);
2217
2218                 db_entry = db_tree = ast_db_gettree("dundi/cache", NULL);
2219                 for (; db_entry; db_entry = db_entry->next) {
2220                         time_t expiry;
2221
2222                         if (!ast_get_time_t(db_entry->data, &expiry, 0, NULL)) {
2223                                 if (expiry < now) {
2224                                         ast_debug(1, "clearing expired DUNDI cache entry: %s\n", db_entry->key);
2225                                         ast_db_del("dundi/cache", db_entry->key + striplen);
2226                                 }
2227                         }
2228                 }
2229                 ast_db_freetree(db_tree);
2230
2231                 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
2232                 pthread_testcancel();
2233                 sleep(60);
2234                 pthread_testcancel();
2235         }
2236
2237         clearcachethreadid = AST_PTHREADT_NULL;
2238         return NULL;
2239 }
2240
2241 static void *process_precache(void *ign)
2242 {
2243         struct dundi_precache_queue *qe;
2244         time_t now;
2245         char context[256];
2246         char number[256];
2247         int run;
2248
2249         while (!dundi_shutdown) {
2250                 time(&now);
2251                 run = 0;
2252                 AST_LIST_LOCK(&pcq);
2253                 if ((qe = AST_LIST_FIRST(&pcq))) {
2254                         if (!qe->expiration) {
2255                                 /* Gone...  Remove... */
2256                                 AST_LIST_REMOVE_HEAD(&pcq, list);
2257                                 ast_free(qe);
2258                         } else if (qe->expiration < now) {
2259                                 /* Process this entry */
2260                                 qe->expiration = 0;
2261                                 ast_copy_string(context, qe->context, sizeof(context));
2262                                 ast_copy_string(number, qe->number, sizeof(number));
2263                                 run = 1;
2264                         }
2265                 }
2266                 AST_LIST_UNLOCK(&pcq);
2267                 if (run) {
2268                         dundi_precache(context, number);
2269                 } else
2270                         sleep(1);
2271         }
2272
2273         precachethreadid = AST_PTHREADT_NULL;
2274
2275         return NULL;
2276 }
2277
2278 static int start_network_thread(void)
2279 {
2280         ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
2281         ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL);
2282         ast_pthread_create_background(&clearcachethreadid, NULL, process_clearcache, NULL);
2283         return 0;
2284 }
2285
2286 static char *dundi_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2287 {
2288         switch (cmd) {
2289         case CLI_INIT:
2290                 e->command = "dundi set debug {on|off}";
2291                 e->usage =
2292                         "Usage: dundi set debug {on|off}\n"
2293                         "       Enables/Disables dumping of DUNDi packets for debugging purposes\n";
2294                 return NULL;
2295         case CLI_GENERATE:
2296                 return NULL;
2297         }
2298
2299         if (a->argc != e->args) {
2300                 return CLI_SHOWUSAGE;
2301         }
2302         if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
2303                 dundidebug = 1;
2304                 ast_cli(a->fd, "DUNDi Debugging Enabled\n");
2305         } else {
2306                 dundidebug = 0;
2307                 ast_cli(a->fd, "DUNDi Debugging Disabled\n");
2308         }
2309         return CLI_SUCCESS;
2310 }
2311
2312 static char *dundi_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2313 {
2314         switch (cmd) {
2315         case CLI_INIT:
2316                 e->command = "dundi store history {on|off}";
2317                 e->usage =
2318                         "Usage: dundi store history {on|off}\n"
2319                         "       Enables/Disables storing of DUNDi requests and times for debugging\n"
2320                         "purposes\n";
2321                 return NULL;
2322         case CLI_GENERATE:
2323                 return NULL;
2324         }
2325
2326         if (a->argc != e->args) {
2327                 return CLI_SHOWUSAGE;
2328         }
2329         if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
2330                 global_storehistory = 1;
2331                 ast_cli(a->fd, "DUNDi History Storage Enabled\n");
2332         } else {
2333                 global_storehistory = 0;
2334                 ast_cli(a->fd, "DUNDi History Storage Disabled\n");
2335         }
2336         return CLI_SUCCESS;
2337 }
2338
2339 static char *dundi_flush(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2340 {
2341         int stats = 0;
2342         switch (cmd) {
2343         case CLI_INIT:
2344                 e->command = "dundi flush [stats]";
2345                 e->usage =
2346                         "Usage: dundi flush [stats]\n"
2347                         "       Flushes DUNDi answer cache, used primarily for debug.  If\n"
2348                         "'stats' is present, clears timer statistics instead of normal\n"
2349                         "operation.\n";
2350                 return NULL;
2351         case CLI_GENERATE:
2352                 return NULL;
2353         }
2354         if ((a->argc < 2) || (a->argc > 3)) {
2355                 return CLI_SHOWUSAGE;
2356         }
2357         if (a->argc > 2) {
2358                 if (!strcasecmp(a->argv[2], "stats")) {
2359                         stats = 1;
2360                 } else {
2361                         return CLI_SHOWUSAGE;
2362                 }
2363         }
2364         if (stats) {
2365                 /* Flush statistics */
2366                 struct dundi_peer *p;
2367                 int x;
2368                 AST_LIST_LOCK(&peers);
2369                 AST_LIST_TRAVERSE(&peers, p, list) {
2370                         for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2371                                 if (p->lookups[x])
2372                                         ast_free(p->lookups[x]);
2373                                 p->lookups[x] = NULL;
2374                                 p->lookuptimes[x] = 0;
2375                         }
2376                         p->avgms = 0;
2377                 }
2378                 AST_LIST_UNLOCK(&peers);
2379         } else {
2380                 ast_db_deltree("dundi/cache", NULL);
2381                 ast_cli(a->fd, "DUNDi Cache Flushed\n");
2382         }
2383         return CLI_SUCCESS;
2384 }
2385
2386 static char *model2str(int model)
2387 {
2388         switch(model) {
2389         case DUNDI_MODEL_INBOUND:
2390                 return "Inbound";
2391         case DUNDI_MODEL_OUTBOUND:
2392                 return "Outbound";
2393         case DUNDI_MODEL_SYMMETRIC:
2394                 return "Symmetric";
2395         default:
2396                 return "Unknown";
2397         }
2398 }
2399
2400 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
2401 {
2402         int which=0, len;
2403         char *ret = NULL;
2404         struct dundi_peer *p;
2405         char eid_str[20];
2406
2407         if (pos != rpos)
2408                 return NULL;
2409         AST_LIST_LOCK(&peers);
2410         len = strlen(word);
2411         AST_LIST_TRAVERSE(&peers, p, list) {
2412                 const char *s = ast_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
2413                 if (!strncasecmp(word, s, len) && ++which > state) {
2414                         ret = ast_strdup(s);
2415                         break;
2416                 }
2417         }
2418         AST_LIST_UNLOCK(&peers);
2419         return ret;
2420 }
2421
2422 static int rescomp(const void *a, const void *b)
2423 {
2424         const struct dundi_result *resa, *resb;
2425         resa = a;
2426         resb = b;
2427         if (resa->weight < resb->weight)
2428                 return -1;
2429         if (resa->weight > resb->weight)
2430                 return 1;
2431         return 0;
2432 }
2433
2434 static void sort_results(struct dundi_result *results, int count)
2435 {
2436         qsort(results, count, sizeof(results[0]), rescomp);
2437 }
2438
2439 static char *dundi_do_lookup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2440 {
2441         int res;
2442         char tmp[256];
2443         char fs[80] = "";
2444         char *context;
2445         int x;
2446         int bypass = 0;
2447         struct dundi_result dr[MAX_RESULTS];
2448         struct timeval start;
2449         switch (cmd) {
2450         case CLI_INIT:
2451                 e->command = "dundi lookup";
2452                 e->usage =
2453                         "Usage: dundi lookup <number>[@context] [bypass]\n"
2454                         "       Lookup the given number within the given DUNDi context\n"
2455                         "(or e164 if none is specified).  Bypasses cache if 'bypass'\n"
2456                         "keyword is specified.\n";
2457                 return NULL;
2458         case CLI_GENERATE:
2459                 return NULL;
2460         }
2461
2462         if ((a->argc < 3) || (a->argc > 4)) {
2463                 return CLI_SHOWUSAGE;
2464         }
2465         if (a->argc > 3) {
2466                 if (!strcasecmp(a->argv[3], "bypass")) {
2467                         bypass=1;
2468                 } else {
2469                         return CLI_SHOWUSAGE;
2470                 }
2471         }
2472         ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2473         context = strchr(tmp, '@');
2474         if (context) {
2475                 *context = '\0';
2476                 context++;
2477         }
2478         start = ast_tvnow();
2479         res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2480
2481         if (res < 0)
2482                 ast_cli(a->fd, "DUNDi lookup returned error.\n");
2483         else if (!res)
2484                 ast_cli(a->fd, "DUNDi lookup returned no results.\n");
2485         else
2486                 sort_results(dr, res);
2487         for (x=0;x<res;x++) {
2488                 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));
2489                 ast_cli(a->fd, "     from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2490         }
2491         ast_cli(a->fd, "DUNDi lookup completed in %" PRIi64 " ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2492         return CLI_SUCCESS;
2493 }
2494
2495 static char *dundi_do_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2496 {
2497         int res;
2498         char tmp[256];
2499         char *context;
2500         struct timeval start;
2501         switch (cmd) {
2502         case CLI_INIT:
2503                 e->command = "dundi precache";
2504                 e->usage =
2505                         "Usage: dundi precache <number>[@context]\n"
2506                         "       Lookup the given number within the given DUNDi context\n"
2507                         "(or e164 if none is specified) and precaches the results to any\n"
2508                         "upstream DUNDi push servers.\n";
2509                 return NULL;
2510         case CLI_GENERATE:
2511                 return NULL;
2512         }
2513         if ((a->argc < 3) || (a->argc > 3)) {
2514                 return CLI_SHOWUSAGE;
2515         }
2516         ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2517         context = strchr(tmp, '@');
2518         if (context) {
2519                 *context = '\0';
2520                 context++;
2521         }
2522         start = ast_tvnow();
2523         res = dundi_precache(context, tmp);
2524
2525         if (res < 0)
2526                 ast_cli(a->fd, "DUNDi precache returned error.\n");
2527         else if (!res)
2528                 ast_cli(a->fd, "DUNDi precache returned no error.\n");
2529         ast_cli(a->fd, "DUNDi lookup completed in %" PRIi64 " ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2530         return CLI_SUCCESS;
2531 }
2532
2533 static char *dundi_do_query(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2534 {
2535         int res;
2536         char tmp[256];
2537         char *context;
2538         dundi_eid eid;
2539         struct dundi_entity_info dei;
2540         switch (cmd) {
2541         case CLI_INIT:
2542                 e->command = "dundi query";
2543                 e->usage =
2544                         "Usage: dundi query <entity>[@context]\n"
2545                         "       Attempts to retrieve contact information for a specific\n"
2546                         "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2547                         "e164 if none is specified).\n";
2548                 return NULL;
2549         case CLI_GENERATE:
2550                 return NULL;
2551         }
2552         if ((a->argc < 3) || (a->argc > 3)) {
2553                 return CLI_SHOWUSAGE;
2554         }
2555         if (ast_str_to_eid(&eid, a->argv[2])) {
2556                 ast_cli(a->fd, "'%s' is not a valid EID!\n", a->argv[2]);
2557                 return CLI_SHOWUSAGE;
2558         }
2559         ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2560         context = strchr(tmp, '@');
2561         if (context) {
2562                 *context = '\0';
2563                 context++;
2564         }
2565         res = dundi_query_eid(&dei, context, eid);
2566         if (res < 0)
2567                 ast_cli(a->fd, "DUNDi Query EID returned error.\n");
2568         else if (!res)
2569                 ast_cli(a->fd, "DUNDi Query EID returned no results.\n");
2570         else {
2571                 ast_cli(a->fd, "DUNDi Query EID succeeded:\n");
2572                 ast_cli(a->fd, "Department:      %s\n", dei.orgunit);
2573                 ast_cli(a->fd, "Organization:    %s\n", dei.org);
2574                 ast_cli(a->fd, "City/Locality:   %s\n", dei.locality);
2575                 ast_cli(a->fd, "State/Province:  %s\n", dei.stateprov);
2576                 ast_cli(a->fd, "Country:         %s\n", dei.country);
2577                 ast_cli(a->fd, "E-mail:          %s\n", dei.email);
2578                 ast_cli(a->fd, "Phone:           %s\n", dei.phone);
2579                 ast_cli(a->fd, "IP Address:      %s\n", dei.ipaddr);
2580         }
2581         return CLI_SUCCESS;
2582 }
2583
2584 static char *dundi_sockaddr_stringify_host(const struct ast_sockaddr *addr)
2585 {
2586         if (ast_sockaddr_isnull(addr)) {
2587                 return "(Unspecified)";
2588         }
2589         return ast_sockaddr_stringify_host(addr);
2590 }
2591
2592 static uint16_t dundi_sockaddr_port(const struct ast_sockaddr *addr)
2593 {
2594         /*
2595          * Test to avoid a debug message complaining about addr
2596          * not being an IPv4 or IPv6 address.
2597          */
2598         if (ast_sockaddr_isnull(addr)) {
2599                 return 0;
2600         }
2601         return ast_sockaddr_port(addr);
2602 }
2603
2604 static char *dundi_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2605 {
2606         struct dundi_peer *peer;
2607         struct permission *p;
2608         char *order;
2609         char eid_str[20];
2610         int x, cnt;
2611         switch (cmd) {
2612         case CLI_INIT:
2613                 e->command = "dundi show peer";
2614                 e->usage =
2615                         "Usage: dundi show peer [peer]\n"
2616                         "       Provide a detailed description of a specifid DUNDi peer.\n";
2617                 return NULL;
2618         case CLI_GENERATE:
2619                 return complete_peer_helper(a->line, a->word, a->pos, a->n, 3);
2620         }
2621         if (a->argc != 4) {
2622                 return CLI_SHOWUSAGE;
2623         }
2624         AST_LIST_LOCK(&peers);
2625         AST_LIST_TRAVERSE(&peers, peer, list) {
2626                 if (!strcasecmp(ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), a->argv[3]))
2627                         break;
2628         }
2629         if (peer) {
2630                 switch(peer->order) {
2631                 case 0:
2632                         order = "Primary";
2633                         break;
2634                 case 1:
2635                         order = "Secondary";
2636                         break;
2637                 case 2:
2638                         order = "Tertiary";
2639                         break;
2640                 case 3:
2641                         order = "Quartiary";
2642                         break;
2643                 default:
2644                         order = "Unknown";
2645                 }
2646                 ast_cli(a->fd, "Peer:    %s\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2647                 ast_cli(a->fd, "Model:   %s\n", model2str(peer->model));
2648                 ast_cli(a->fd, "Order:   %s\n", order);
2649                 ast_cli(a->fd, "Host:    %s\n", ast_sockaddr_isnull(&peer->addr) ? "<Unspecified>" : ast_sockaddr_stringify_host(&peer->addr));
2650                 ast_cli(a->fd, "Port:    %d\n", dundi_sockaddr_port(&peer->addr));
2651                 ast_cli(a->fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2652                 ast_cli(a->fd, "Reg:     %s\n", peer->registerid < 0 ? "No" : "Yes");
2653                 ast_cli(a->fd, "In Key:  %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2654                 ast_cli(a->fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2655                 if (!AST_LIST_EMPTY(&peer->include))
2656                         ast_cli(a->fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2657                 AST_LIST_TRAVERSE(&peer->include, p, list)
2658                         ast_cli(a->fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2659                 if (!AST_LIST_EMPTY(&peer->permit))
2660                         ast_cli(a->fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2661                 AST_LIST_TRAVERSE(&peer->permit, p, list)
2662                         ast_cli(a->fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2663                 cnt = 0;
2664                 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2665                         if (peer->lookups[x]) {
2666                                 if (!cnt)
2667                                         ast_cli(a->fd, "Last few query times:\n");
2668                                 ast_cli(a->fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2669                                 cnt++;
2670                         }
2671                 }
2672                 if (cnt)
2673                         ast_cli(a->fd, "Average query time: %d ms\n", peer->avgms);
2674         } else
2675                 ast_cli(a->fd, "No such peer '%s'\n", a->argv[3]);
2676         AST_LIST_UNLOCK(&peers);
2677         return CLI_SUCCESS;
2678 }
2679
2680 static char *dundi_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2681 {
2682 #define FORMAT2 "%-20.20s %-41s     %-6.6s %-10.10s %-8.8s %-15.15s\n"
2683 #define FORMAT "%-20.20s %-41s %s %-6d %-10.10s %-8.8s %-15.15s\n"
2684         struct dundi_peer *peer;
2685         int registeredonly=0;
2686         char avgms[20];
2687         char eid_str[20];
2688         int online_peers = 0;
2689         int offline_peers = 0;
2690         int unmonitored_peers = 0;
2691         int total_peers = 0;
2692         switch (cmd) {
2693         case CLI_INIT:
2694                 e->command = "dundi show peers [registered|include|exclude|begin]";
2695                 e->usage =
2696                         "Usage: dundi show peers [registered|include|exclude|begin]\n"
2697                         "       Lists all known DUNDi peers.\n"
2698                         "       If 'registered' is present, only registered peers are shown.\n";
2699                 return NULL;
2700         case CLI_GENERATE:
2701                 return NULL;
2702         }
2703
2704         if ((a->argc != 3) && (a->argc != 4) && (a->argc != 5)) {
2705                 return CLI_SHOWUSAGE;
2706         }
2707         if ((a->argc == 4)) {
2708                 if (!strcasecmp(a->argv[3], "registered")) {
2709                         registeredonly = 1;
2710                 } else {
2711                         return CLI_SHOWUSAGE;
2712                 }
2713         }
2714         AST_LIST_LOCK(&peers);
2715         ast_cli(a->fd, FORMAT2, "EID", "Host", "Port", "Model", "AvgTime", "Status");
2716         AST_LIST_TRAVERSE(&peers, peer, list) {
2717                 char status[20];
2718                 int print_line = -1;
2719                 char srch[2000];
2720
2721                 total_peers++;
2722                 if (registeredonly && ast_sockaddr_isnull(&peer->addr)) {
2723                         continue;
2724                 }
2725                 if (peer->maxms) {
2726                         if (peer->lastms < 0) {
2727                                 strcpy(status, "UNREACHABLE");
2728                                 offline_peers++;
2729                         }
2730                         else if (peer->lastms > peer->maxms) {
2731                                 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2732                                 offline_peers++;
2733                         }
2734                         else if (peer->lastms) {
2735                                 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2736                                 online_peers++;
2737                         }
2738                         else {
2739                                 strcpy(status, "UNKNOWN");
2740                                 offline_peers++;
2741                         }
2742                 } else {
2743                         strcpy(status, "Unmonitored");
2744                         unmonitored_peers++;
2745                 }
2746                 if (peer->avgms)
2747                         snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2748                 else
2749                         strcpy(avgms, "Unavail");
2750                 snprintf(srch, sizeof(srch), FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str),
2751                         &peer->eid), dundi_sockaddr_stringify_host(&peer->addr),
2752                         peer->dynamic ? "(D)" : "(S)", dundi_sockaddr_port(&peer->addr), model2str(peer->model), avgms, status);
2753
2754                 if (a->argc == 5) {
2755                   if (!strcasecmp(a->argv[3],"include") && strstr(srch,a->argv[4])) {
2756                         print_line = -1;
2757                    } else if (!strcasecmp(a->argv[3],"exclude") && !strstr(srch,a->argv[4])) {
2758                         print_line = 1;
2759                    } else if (!strcasecmp(a->argv[3],"begin") && !strncasecmp(srch,a->argv[4],strlen(a->argv[4]))) {
2760                         print_line = -1;
2761                    } else {
2762                         print_line = 0;
2763                   }
2764                 }
2765
2766         if (print_line) {
2767                         ast_cli(a->fd, FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2768                                 dundi_sockaddr_stringify_host(&peer->addr),
2769                                 peer->dynamic ? "(D)" : "(S)", dundi_sockaddr_port(&peer->addr), model2str(peer->model), avgms, status);
2770                 }
2771         }
2772         ast_cli(a->fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2773         AST_LIST_UNLOCK(&peers);
2774         return CLI_SUCCESS;
2775 #undef FORMAT
2776 #undef FORMAT2
2777 }
2778
2779 static char *dundi_show_trans(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2780 {
2781 #define FORMAT2 "%-47s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2782 #define FORMAT "%-41s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2783         struct dundi_transaction *trans;
2784         switch (cmd) {
2785         case CLI_INIT:
2786                 e->command = "dundi show trans";
2787                 e->usage =
2788                         "Usage: dundi show trans\n"
2789                         "       Lists all known DUNDi transactions.\n";
2790                 return NULL;
2791         case CLI_GENERATE:
2792                 return NULL;
2793         }
2794         if (a->argc != 3) {
2795                 return CLI_SHOWUSAGE;
2796         }
2797         AST_LIST_LOCK(&peers);
2798         ast_cli(a->fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2799         AST_LIST_TRAVERSE(&alltrans, trans, all) {
2800                 ast_cli(a->fd, FORMAT, ast_sockaddr_stringify_host(&trans->addr),
2801                         ast_sockaddr_port(&trans->addr), trans->strans, trans->dtrans,
2802                         trans->oseqno, trans->iseqno, trans->aseqno);
2803         }
2804         AST_LIST_UNLOCK(&peers);
2805         return CLI_SUCCESS;
2806 #undef FORMAT
2807 #undef FORMAT2
2808 }
2809
2810 static char *dundi_show_entityid(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2811 {
2812         char eid_str[20];
2813         switch (cmd) {
2814         case CLI_INIT:
2815                 e->command = "dundi show entityid";
2816                 e->usage =
2817                         "Usage: dundi show entityid\n"
2818                         "       Displays the global entityid for this host.\n";
2819                 return NULL;
2820         case CLI_GENERATE:
2821                 return NULL;
2822         }
2823         if (a->argc != 3) {
2824                 return CLI_SHOWUSAGE;
2825         }
2826         AST_LIST_LOCK(&peers);
2827         ast_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2828         AST_LIST_UNLOCK(&peers);
2829         ast_cli(a->fd, "Global EID for this system is '%s'\n", eid_str);
2830         return CLI_SUCCESS;
2831 }
2832
2833 static char *dundi_show_requests(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2834 {
2835 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2836 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2837         struct dundi_request *req;
2838         char eidstr[20];
2839         switch (cmd) {
2840         case CLI_INIT:
2841                 e->command = "dundi show requests";
2842                 e->usage =
2843                         "Usage: dundi show requests\n"
2844                         "       Lists all known pending DUNDi requests.\n";
2845                 return NULL;
2846         case CLI_GENERATE:
2847                 return NULL;
2848         }
2849         if (a->argc != 3) {
2850                 return CLI_SHOWUSAGE;
2851         }
2852         AST_LIST_LOCK(&peers);
2853         ast_cli(a->fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2854         AST_LIST_TRAVERSE(&requests, req, list) {
2855                 ast_cli(a->fd, FORMAT, req->number, req->dcontext,
2856                         dundi_eid_zero(&req->root_eid) ? "<unspecified>" : ast_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2857         }
2858         AST_LIST_UNLOCK(&peers);
2859         return CLI_SUCCESS;
2860 #undef FORMAT
2861 #undef FORMAT2
2862 }
2863
2864 /* Grok-a-dial DUNDi */
2865
2866 static char *dundi_show_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2867 {
2868 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2869 #define FORMAT "%-12.12s %-7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2870         struct dundi_mapping *map;
2871         char fs[256];
2872         char weight[8];
2873         switch (cmd) {
2874         case CLI_INIT:
2875                 e->command = "dundi show mappings";
2876                 e->usage =
2877                         "Usage: dundi show mappings\n"
2878                         "       Lists all known DUNDi mappings.\n";
2879                 return NULL;
2880         case CLI_GENERATE:
2881                 return NULL;
2882         }
2883         if (a->argc != 3) {
2884                 return CLI_SHOWUSAGE;
2885         }
2886         AST_LIST_LOCK(&peers);
2887         ast_cli(a->fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2888         AST_LIST_TRAVERSE(&mappings, map, list) {
2889                 snprintf(weight, sizeof(weight), "%d", get_mapping_weight(map, NULL));
2890                 ast_cli(a->fd, FORMAT, map->dcontext, weight,
2891                         ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2892                         dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2893         }
2894         AST_LIST_UNLOCK(&peers);
2895         return CLI_SUCCESS;
2896 #undef FORMAT
2897 #undef FORMAT2
2898 }
2899
2900 static char *dundi_show_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2901 {
2902 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2903 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2904         struct dundi_precache_queue *qe;
2905         int h,m,s;
2906         time_t now;
2907         switch (cmd) {
2908         case CLI_INIT:
2909                 e->command = "dundi show precache";
2910                 e->usage =
2911                         "Usage: dundi show precache\n"
2912                         "       Lists all known DUNDi scheduled precache updates.\n";
2913                 return NULL;
2914         case CLI_GENERATE:
2915                 return NULL;
2916         }
2917         if (a->argc != 3) {
2918                 return CLI_SHOWUSAGE;
2919         }
2920         time(&now);
2921         ast_cli(a->fd, FORMAT2, "Number", "Context", "Expiration");
2922         AST_LIST_LOCK(&pcq);
2923         AST_LIST_TRAVERSE(&pcq, qe, list) {
2924                 s = qe->expiration - now;
2925                 h = s / 3600;
2926                 s = s % 3600;
2927                 m = s / 60;
2928                 s = s % 60;
2929                 ast_cli(a->fd, FORMAT, qe->number, qe->context, h,m,s);
2930         }
2931         AST_LIST_UNLOCK(&pcq);
2932
2933         return CLI_SUCCESS;
2934 #undef FORMAT
2935 #undef FORMAT2
2936 }
2937
2938 static char *dundi_show_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2939 {
2940 #define FORMAT2 "%-12.12s %-16.16s %-10.10s  %-18s %-7s %s\n"
2941 #define FORMAT "%-12.12s %-16.16s %6d sec  %-18s %-7d %s/%s (%s)\n"
2942         struct ast_db_entry *db_tree, *db_entry;
2943         int cnt = 0;
2944         time_t ts, now;
2945         dundi_eid src_eid;
2946         char src_eid_str[20];
2947         int expiry, tech, weight;
2948         struct ast_fl