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