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