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