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