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