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