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