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