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