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