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