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