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