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