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