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