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