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