Doxygen Updates - Title update
[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         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         netthreadid = AST_PTHREADT_NULL;
2197
2198         return NULL;
2199 }
2200
2201 static void *process_clearcache(void *ignore)
2202 {
2203         struct ast_db_entry *db_entry, *db_tree;
2204         int striplen = sizeof("/dundi/cache");
2205         time_t now;
2206
2207         while (!dundi_shutdown) {
2208                 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
2209
2210                 time(&now);
2211
2212                 db_entry = db_tree = ast_db_gettree("dundi/cache", NULL);
2213                 for (; db_entry; db_entry = db_entry->next) {
2214                         time_t expiry;
2215
2216                         if (!ast_get_time_t(db_entry->data, &expiry, 0, NULL)) {
2217                                 if (expiry < now) {
2218                                         ast_debug(1, "clearing expired DUNDI cache entry: %s\n", db_entry->key);
2219                                         ast_db_del("dundi/cache", db_entry->key + striplen);
2220                                 }
2221                         }
2222                 }
2223                 ast_db_freetree(db_tree);
2224
2225                 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
2226                 pthread_testcancel();
2227                 sleep(60);
2228                 pthread_testcancel();
2229         }
2230
2231         clearcachethreadid = AST_PTHREADT_NULL;
2232         return NULL;
2233 }
2234
2235 static void *process_precache(void *ign)
2236 {
2237         struct dundi_precache_queue *qe;
2238         time_t now;
2239         char context[256];
2240         char number[256];
2241         int run;
2242
2243         while (!dundi_shutdown) {
2244                 time(&now);
2245                 run = 0;
2246                 AST_LIST_LOCK(&pcq);
2247                 if ((qe = AST_LIST_FIRST(&pcq))) {
2248                         if (!qe->expiration) {
2249                                 /* Gone...  Remove... */
2250                                 AST_LIST_REMOVE_HEAD(&pcq, list);
2251                                 ast_free(qe);
2252                         } else if (qe->expiration < now) {
2253                                 /* Process this entry */
2254                                 qe->expiration = 0;
2255                                 ast_copy_string(context, qe->context, sizeof(context));
2256                                 ast_copy_string(number, qe->number, sizeof(number));
2257                                 run = 1;
2258                         }
2259                 }
2260                 AST_LIST_UNLOCK(&pcq);
2261                 if (run) {
2262                         dundi_precache(context, number);
2263                 } else
2264                         sleep(1);
2265         }
2266
2267         precachethreadid = AST_PTHREADT_NULL;
2268
2269         return NULL;
2270 }
2271
2272 static int start_network_thread(void)
2273 {
2274         ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
2275         ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL);
2276         ast_pthread_create_background(&clearcachethreadid, NULL, process_clearcache, NULL);
2277         return 0;
2278 }
2279
2280 static char *dundi_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2281 {
2282         switch (cmd) {
2283         case CLI_INIT:
2284                 e->command = "dundi set debug {on|off}";
2285                 e->usage =
2286                         "Usage: dundi set debug {on|off}\n"
2287                         "       Enables/Disables dumping of DUNDi packets for debugging purposes\n";
2288                 return NULL;
2289         case CLI_GENERATE:
2290                 return NULL;
2291         }
2292
2293         if (a->argc != e->args) {
2294                 return CLI_SHOWUSAGE;
2295         }
2296         if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
2297                 dundidebug = 1;
2298                 ast_cli(a->fd, "DUNDi Debugging Enabled\n");
2299         } else {
2300                 dundidebug = 0;
2301                 ast_cli(a->fd, "DUNDi Debugging Disabled\n");
2302         }
2303         return CLI_SUCCESS;
2304 }
2305
2306 static char *dundi_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2307 {
2308         switch (cmd) {
2309         case CLI_INIT:
2310                 e->command = "dundi store history {on|off}";
2311                 e->usage =
2312                         "Usage: dundi store history {on|off}\n"
2313                         "       Enables/Disables storing of DUNDi requests and times for debugging\n"
2314                         "purposes\n";
2315                 return NULL;
2316         case CLI_GENERATE:
2317                 return NULL;
2318         }
2319
2320         if (a->argc != e->args) {
2321                 return CLI_SHOWUSAGE;
2322         }
2323         if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
2324                 global_storehistory = 1;
2325                 ast_cli(a->fd, "DUNDi History Storage Enabled\n");
2326         } else {
2327                 global_storehistory = 0;
2328                 ast_cli(a->fd, "DUNDi History Storage Disabled\n");
2329         }
2330         return CLI_SUCCESS;
2331 }
2332
2333 static char *dundi_flush(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2334 {
2335         int stats = 0;
2336         switch (cmd) {
2337         case CLI_INIT:
2338                 e->command = "dundi flush [stats]";
2339                 e->usage =
2340                         "Usage: dundi flush [stats]\n"
2341                         "       Flushes DUNDi answer cache, used primarily for debug.  If\n"
2342                         "'stats' is present, clears timer statistics instead of normal\n"
2343                         "operation.\n";
2344                 return NULL;
2345         case CLI_GENERATE:
2346                 return NULL;
2347         }
2348         if ((a->argc < 2) || (a->argc > 3)) {
2349                 return CLI_SHOWUSAGE;
2350         }
2351         if (a->argc > 2) {
2352                 if (!strcasecmp(a->argv[2], "stats")) {
2353                         stats = 1;
2354                 } else {
2355                         return CLI_SHOWUSAGE;
2356                 }
2357         }
2358         if (stats) {
2359                 /* Flush statistics */
2360                 struct dundi_peer *p;
2361                 int x;
2362                 AST_LIST_LOCK(&peers);
2363                 AST_LIST_TRAVERSE(&peers, p, list) {
2364                         for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2365                                 if (p->lookups[x])
2366                                         ast_free(p->lookups[x]);
2367                                 p->lookups[x] = NULL;
2368                                 p->lookuptimes[x] = 0;
2369                         }
2370                         p->avgms = 0;
2371                 }
2372                 AST_LIST_UNLOCK(&peers);
2373         } else {
2374                 ast_db_deltree("dundi/cache", NULL);
2375                 ast_cli(a->fd, "DUNDi Cache Flushed\n");
2376         }
2377         return CLI_SUCCESS;
2378 }
2379
2380 static char *model2str(int model)
2381 {
2382         switch(model) {
2383         case DUNDI_MODEL_INBOUND:
2384                 return "Inbound";
2385         case DUNDI_MODEL_OUTBOUND:
2386                 return "Outbound";
2387         case DUNDI_MODEL_SYMMETRIC:
2388                 return "Symmetric";
2389         default:
2390                 return "Unknown";
2391         }
2392 }
2393
2394 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
2395 {
2396         int which=0, len;
2397         char *ret = NULL;
2398         struct dundi_peer *p;
2399         char eid_str[20];
2400
2401         if (pos != rpos)
2402                 return NULL;
2403         AST_LIST_LOCK(&peers);
2404         len = strlen(word);
2405         AST_LIST_TRAVERSE(&peers, p, list) {
2406                 const char *s = ast_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
2407                 if (!strncasecmp(word, s, len) && ++which > state) {
2408                         ret = ast_strdup(s);
2409                         break;
2410                 }
2411         }
2412         AST_LIST_UNLOCK(&peers);
2413         return ret;
2414 }
2415
2416 static int rescomp(const void *a, const void *b)
2417 {
2418         const struct dundi_result *resa, *resb;
2419         resa = a;
2420         resb = b;
2421         if (resa->weight < resb->weight)
2422                 return -1;
2423         if (resa->weight > resb->weight)
2424                 return 1;
2425         return 0;
2426 }
2427
2428 static void sort_results(struct dundi_result *results, int count)
2429 {
2430         qsort(results, count, sizeof(results[0]), rescomp);
2431 }
2432
2433 static char *dundi_do_lookup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2434 {
2435         int res;
2436         char tmp[256];
2437         char fs[80] = "";
2438         char *context;
2439         int x;
2440         int bypass = 0;
2441         struct dundi_result dr[MAX_RESULTS];
2442         struct timeval start;
2443         switch (cmd) {
2444         case CLI_INIT:
2445                 e->command = "dundi lookup";
2446                 e->usage =
2447                         "Usage: dundi lookup <number>[@context] [bypass]\n"
2448                         "       Lookup the given number within the given DUNDi context\n"
2449                         "(or e164 if none is specified).  Bypasses cache if 'bypass'\n"
2450                         "keyword is specified.\n";
2451                 return NULL;
2452         case CLI_GENERATE:
2453                 return NULL;
2454         }
2455
2456         if ((a->argc < 3) || (a->argc > 4)) {
2457                 return CLI_SHOWUSAGE;
2458         }
2459         if (a->argc > 3) {
2460                 if (!strcasecmp(a->argv[3], "bypass")) {
2461                         bypass=1;
2462                 } else {
2463                         return CLI_SHOWUSAGE;
2464                 }
2465         }
2466         ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2467         context = strchr(tmp, '@');
2468         if (context) {
2469                 *context = '\0';
2470                 context++;
2471         }
2472         start = ast_tvnow();
2473         res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2474
2475         if (res < 0)
2476                 ast_cli(a->fd, "DUNDi lookup returned error.\n");
2477         else if (!res)
2478                 ast_cli(a->fd, "DUNDi lookup returned no results.\n");
2479         else
2480                 sort_results(dr, res);
2481         for (x=0;x<res;x++) {
2482                 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));
2483                 ast_cli(a->fd, "     from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2484         }
2485         ast_cli(a->fd, "DUNDi lookup completed in %" PRIi64 " ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2486         return CLI_SUCCESS;
2487 }
2488
2489 static char *dundi_do_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2490 {
2491         int res;
2492         char tmp[256];
2493         char *context;
2494         struct timeval start;
2495         switch (cmd) {
2496         case CLI_INIT:
2497                 e->command = "dundi precache";
2498                 e->usage =
2499                         "Usage: dundi precache <number>[@context]\n"
2500                         "       Lookup the given number within the given DUNDi context\n"
2501                         "(or e164 if none is specified) and precaches the results to any\n"
2502                         "upstream DUNDi push servers.\n";
2503                 return NULL;
2504         case CLI_GENERATE:
2505                 return NULL;
2506         }
2507         if ((a->argc < 3) || (a->argc > 3)) {
2508                 return CLI_SHOWUSAGE;
2509         }
2510         ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2511         context = strchr(tmp, '@');
2512         if (context) {
2513                 *context = '\0';
2514                 context++;
2515         }
2516         start = ast_tvnow();
2517         res = dundi_precache(context, tmp);
2518
2519         if (res < 0)
2520                 ast_cli(a->fd, "DUNDi precache returned error.\n");
2521         else if (!res)
2522                 ast_cli(a->fd, "DUNDi precache returned no error.\n");
2523         ast_cli(a->fd, "DUNDi lookup completed in %" PRIi64 " ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2524         return CLI_SUCCESS;
2525 }
2526
2527 static char *dundi_do_query(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2528 {
2529         int res;
2530         char tmp[256];
2531         char *context;
2532         dundi_eid eid;
2533         struct dundi_entity_info dei;
2534         switch (cmd) {
2535         case CLI_INIT:
2536                 e->command = "dundi query";
2537                 e->usage =
2538                         "Usage: dundi query <entity>[@context]\n"
2539                         "       Attempts to retrieve contact information for a specific\n"
2540                         "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2541                         "e164 if none is specified).\n";
2542                 return NULL;
2543         case CLI_GENERATE:
2544                 return NULL;
2545         }
2546         if ((a->argc < 3) || (a->argc > 3)) {
2547                 return CLI_SHOWUSAGE;
2548         }
2549         if (ast_str_to_eid(&eid, a->argv[2])) {
2550                 ast_cli(a->fd, "'%s' is not a valid EID!\n", a->argv[2]);
2551                 return CLI_SHOWUSAGE;
2552         }
2553         ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2554         context = strchr(tmp, '@');
2555         if (context) {
2556                 *context = '\0';
2557                 context++;
2558         }
2559         res = dundi_query_eid(&dei, context, eid);
2560         if (res < 0)
2561                 ast_cli(a->fd, "DUNDi Query EID returned error.\n");
2562         else if (!res)
2563                 ast_cli(a->fd, "DUNDi Query EID returned no results.\n");
2564         else {
2565                 ast_cli(a->fd, "DUNDi Query EID succeeded:\n");
2566                 ast_cli(a->fd, "Department:      %s\n", dei.orgunit);
2567                 ast_cli(a->fd, "Organization:    %s\n", dei.org);
2568                 ast_cli(a->fd, "City/Locality:   %s\n", dei.locality);
2569                 ast_cli(a->fd, "State/Province:  %s\n", dei.stateprov);
2570                 ast_cli(a->fd, "Country:         %s\n", dei.country);
2571                 ast_cli(a->fd, "E-mail:          %s\n", dei.email);
2572                 ast_cli(a->fd, "Phone:           %s\n", dei.phone);
2573                 ast_cli(a->fd, "IP Address:      %s\n", dei.ipaddr);
2574         }
2575         return CLI_SUCCESS;
2576 }
2577
2578 static char *dundi_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2579 {
2580         struct dundi_peer *peer;
2581         struct permission *p;
2582         char *order;
2583         char eid_str[20];
2584         int x, cnt;
2585         switch (cmd) {
2586         case CLI_INIT:
2587                 e->command = "dundi show peer";
2588                 e->usage =
2589                         "Usage: dundi show peer [peer]\n"
2590                         "       Provide a detailed description of a specifid DUNDi peer.\n";
2591                 return NULL;
2592         case CLI_GENERATE:
2593                 return complete_peer_helper(a->line, a->word, a->pos, a->n, 3);
2594         }
2595         if (a->argc != 4) {
2596                 return CLI_SHOWUSAGE;
2597         }
2598         AST_LIST_LOCK(&peers);
2599         AST_LIST_TRAVERSE(&peers, peer, list) {
2600                 if (!strcasecmp(ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), a->argv[3]))
2601                         break;
2602         }
2603         if (peer) {
2604                 switch(peer->order) {
2605                 case 0:
2606                         order = "Primary";
2607                         break;
2608                 case 1:
2609                         order = "Secondary";
2610                         break;
2611                 case 2:
2612                         order = "Tertiary";
2613                         break;
2614                 case 3:
2615                         order = "Quartiary";
2616                         break;
2617                 default:
2618                         order = "Unknown";
2619                 }
2620                 ast_cli(a->fd, "Peer:    %s\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2621                 ast_cli(a->fd, "Model:   %s\n", model2str(peer->model));
2622                 ast_cli(a->fd, "Order:   %s\n", order);
2623                 ast_cli(a->fd, "Host:    %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "<Unspecified>");
2624                 ast_cli(a->fd, "Port:    %d\n", ntohs(peer->addr.sin_port));
2625                 ast_cli(a->fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2626                 ast_cli(a->fd, "Reg:     %s\n", peer->registerid < 0 ? "No" : "Yes");
2627                 ast_cli(a->fd, "In Key:  %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2628                 ast_cli(a->fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2629                 if (!AST_LIST_EMPTY(&peer->include))
2630                         ast_cli(a->fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2631                 AST_LIST_TRAVERSE(&peer->include, p, list)
2632                         ast_cli(a->fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2633                 if (!AST_LIST_EMPTY(&peer->permit))
2634                         ast_cli(a->fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2635                 AST_LIST_TRAVERSE(&peer->permit, p, list)
2636                         ast_cli(a->fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2637                 cnt = 0;
2638                 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2639                         if (peer->lookups[x]) {
2640                                 if (!cnt)
2641                                         ast_cli(a->fd, "Last few query times:\n");
2642                                 ast_cli(a->fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2643                                 cnt++;
2644                         }
2645                 }
2646                 if (cnt)
2647                         ast_cli(a->fd, "Average query time: %d ms\n", peer->avgms);
2648         } else
2649                 ast_cli(a->fd, "No such peer '%s'\n", a->argv[3]);
2650         AST_LIST_UNLOCK(&peers);
2651         return CLI_SUCCESS;
2652 }
2653
2654 static char *dundi_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2655 {
2656 #define FORMAT2 "%-20.20s %-15.15s     %-6.6s %-10.10s %-8.8s %-15.15s\n"
2657 #define FORMAT "%-20.20s %-15.15s %s %-6d %-10.10s %-8.8s %-15.15s\n"
2658         struct dundi_peer *peer;
2659         int registeredonly=0;
2660         char avgms[20];
2661         char eid_str[20];
2662         int online_peers = 0;
2663         int offline_peers = 0;
2664         int unmonitored_peers = 0;
2665         int total_peers = 0;
2666         switch (cmd) {
2667         case CLI_INIT:
2668                 e->command = "dundi show peers [registered|include|exclude|begin]";
2669                 e->usage =
2670                         "Usage: dundi show peers [registered|include|exclude|begin]\n"
2671                         "       Lists all known DUNDi peers.\n"
2672                         "       If 'registered' is present, only registered peers are shown.\n";
2673                 return NULL;
2674         case CLI_GENERATE:
2675                 return NULL;
2676         }
2677
2678         if ((a->argc != 3) && (a->argc != 4) && (a->argc != 5)) {
2679                 return CLI_SHOWUSAGE;
2680         }
2681         if ((a->argc == 4)) {
2682                 if (!strcasecmp(a->argv[3], "registered")) {
2683                         registeredonly = 1;
2684                 } else {
2685                         return CLI_SHOWUSAGE;
2686                 }
2687         }
2688         AST_LIST_LOCK(&peers);
2689         ast_cli(a->fd, FORMAT2, "EID", "Host", "Port", "Model", "AvgTime", "Status");
2690         AST_LIST_TRAVERSE(&peers, peer, list) {
2691                 char status[20];
2692                 int print_line = -1;
2693                 char srch[2000];
2694                 total_peers++;
2695                 if (registeredonly && !peer->addr.sin_addr.s_addr)
2696                         continue;
2697                 if (peer->maxms) {
2698                         if (peer->lastms < 0) {
2699                                 strcpy(status, "UNREACHABLE");
2700                                 offline_peers++;
2701                         }
2702                         else if (peer->lastms > peer->maxms) {
2703                                 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2704                                 offline_peers++;
2705                         }
2706                         else if (peer->lastms) {
2707                                 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2708                                 online_peers++;
2709                         }
2710                         else {
2711                                 strcpy(status, "UNKNOWN");
2712                                 offline_peers++;
2713                         }
2714                 } else {
2715                         strcpy(status, "Unmonitored");
2716                         unmonitored_peers++;
2717                 }
2718                 if (peer->avgms)
2719                         snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2720                 else
2721                         strcpy(avgms, "Unavail");
2722                 snprintf(srch, sizeof(srch), FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2723                                         peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
2724                                         peer->dynamic ? "(D)" : "(S)", ntohs(peer->addr.sin_port), model2str(peer->model), avgms, status);
2725
2726                 if (a->argc == 5) {
2727                   if (!strcasecmp(a->argv[3],"include") && strstr(srch,a->argv[4])) {
2728                         print_line = -1;
2729                    } else if (!strcasecmp(a->argv[3],"exclude") && !strstr(srch,a->argv[4])) {
2730                         print_line = 1;
2731                    } else if (!strcasecmp(a->argv[3],"begin") && !strncasecmp(srch,a->argv[4],strlen(a->argv[4]))) {
2732                         print_line = -1;
2733                    } else {
2734                         print_line = 0;
2735                   }
2736                 }
2737
2738         if (print_line) {
2739                         ast_cli(a->fd, FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2740                                         peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
2741                                         peer->dynamic ? "(D)" : "(S)", ntohs(peer->addr.sin_port), model2str(peer->model), avgms, status);
2742                 }
2743         }
2744         ast_cli(a->fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2745         AST_LIST_UNLOCK(&peers);
2746         return CLI_SUCCESS;
2747 #undef FORMAT
2748 #undef FORMAT2
2749 }
2750
2751 static char *dundi_show_trans(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2752 {
2753 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2754 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2755         struct dundi_transaction *trans;
2756         switch (cmd) {
2757         case CLI_INIT:
2758                 e->command = "dundi show trans";
2759                 e->usage =
2760                         "Usage: dundi show trans\n"
2761                         "       Lists all known DUNDi transactions.\n";
2762                 return NULL;
2763         case CLI_GENERATE:
2764                 return NULL;
2765         }
2766         if (a->argc != 3) {
2767                 return CLI_SHOWUSAGE;
2768         }
2769         AST_LIST_LOCK(&peers);
2770         ast_cli(a->fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2771         AST_LIST_TRAVERSE(&alltrans, trans, all) {
2772                 ast_cli(a->fd, FORMAT, ast_inet_ntoa(trans->addr.sin_addr),
2773                         ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2774         }
2775         AST_LIST_UNLOCK(&peers);
2776         return CLI_SUCCESS;
2777 #undef FORMAT
2778 #undef FORMAT2
2779 }
2780
2781 static char *dundi_show_entityid(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2782 {
2783         char eid_str[20];
2784         switch (cmd) {
2785         case CLI_INIT:
2786                 e->command = "dundi show entityid";
2787                 e->usage =
2788                         "Usage: dundi show entityid\n"
2789                         "       Displays the global entityid for this host.\n";
2790                 return NULL;
2791         case CLI_GENERATE:
2792                 return NULL;
2793         }
2794         if (a->argc != 3) {
2795                 return CLI_SHOWUSAGE;
2796         }
2797         AST_LIST_LOCK(&peers);
2798         ast_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2799         AST_LIST_UNLOCK(&peers);
2800         ast_cli(a->fd, "Global EID for this system is '%s'\n", eid_str);
2801         return CLI_SUCCESS;
2802 }
2803
2804 static char *dundi_show_requests(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2805 {
2806 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2807 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2808         struct dundi_request *req;
2809         char eidstr[20];
2810         switch (cmd) {
2811         case CLI_INIT:
2812                 e->command = "dundi show requests";
2813                 e->usage =
2814                         "Usage: dundi show requests\n"
2815                         "       Lists all known pending DUNDi requests.\n";
2816                 return NULL;
2817         case CLI_GENERATE:
2818                 return NULL;
2819         }
2820         if (a->argc != 3) {
2821                 return CLI_SHOWUSAGE;
2822         }
2823         AST_LIST_LOCK(&peers);
2824         ast_cli(a->fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2825         AST_LIST_TRAVERSE(&requests, req, list) {
2826                 ast_cli(a->fd, FORMAT, req->number, req->dcontext,
2827                         dundi_eid_zero(&req->root_eid) ? "<unspecified>" : ast_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2828         }
2829         AST_LIST_UNLOCK(&peers);
2830         return CLI_SUCCESS;
2831 #undef FORMAT
2832 #undef FORMAT2
2833 }
2834
2835 /* Grok-a-dial DUNDi */
2836
2837 static char *dundi_show_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2838 {
2839 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2840 #define FORMAT "%-12.12s %-7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2841         struct dundi_mapping *map;
2842         char fs[256];
2843         char weight[8];
2844         switch (cmd) {
2845         case CLI_INIT:
2846                 e->command = "dundi show mappings";
2847                 e->usage =
2848                         "Usage: dundi show mappings\n"
2849                         "       Lists all known DUNDi mappings.\n";
2850                 return NULL;
2851         case CLI_GENERATE:
2852                 return NULL;
2853         }
2854         if (a->argc != 3) {
2855                 return CLI_SHOWUSAGE;
2856         }
2857         AST_LIST_LOCK(&peers);
2858         ast_cli(a->fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2859         AST_LIST_TRAVERSE(&mappings, map, list) {
2860                 snprintf(weight, sizeof(weight), "%d", get_mapping_weight(map, NULL));
2861                 ast_cli(a->fd, FORMAT, map->dcontext, weight,
2862                         ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2863                         dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2864         }
2865         AST_LIST_UNLOCK(&peers);
2866         return CLI_SUCCESS;
2867 #undef FORMAT
2868 #undef FORMAT2
2869 }
2870
2871 static char *dundi_show_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2872 {
2873 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2874 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2875         struct dundi_precache_queue *qe;
2876         int h,m,s;
2877         time_t now;
2878         switch (cmd) {
2879         case CLI_INIT:
2880                 e->command = "dundi show precache";
2881                 e->usage =
2882                         "Usage: dundi show precache\n"
2883                         "       Lists all known DUNDi scheduled precache updates.\n";
2884                 return NULL;
2885         case CLI_GENERATE:
2886                 return NULL;
2887         }
2888         if (a->argc != 3) {
2889                 return CLI_SHOWUSAGE;
2890         }
2891         time(&now);
2892         ast_cli(a->fd, FORMAT2, "Number", "Context", "Expiration");
2893         AST_LIST_LOCK(&pcq);
2894         AST_LIST_TRAVERSE(&pcq, qe, list) {
2895                 s = qe->expiration - now;
2896                 h = s / 3600;
2897                 s = s % 3600;
2898                 m = s / 60;
2899                 s = s % 60;
2900                 ast_cli(a->fd, FORMAT, qe->number, qe->context, h,m,s);
2901         }
2902         AST_LIST_UNLOCK(&pcq);
2903
2904         return CLI_SUCCESS;
2905 #undef FORMAT
2906 #undef FORMAT2
2907 }
2908
2909 static char *dundi_show_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2910 {
2911 #define FORMAT2 "%-12.12s %-16.16s %-10.10s  %-18s %-7s %s\n"
2912 #define FORMAT "%-12.12s %-16.16s %6d sec  %-18s %-7d %s/%s (%s)\n"
2913         struct ast_db_entry *db_tree, *db_entry;
2914         int cnt = 0;
2915         time_t ts, now;
2916         dundi_eid src_eid;
2917         char src_eid_str[20];
2918         int expiry, tech, weight;
2919         struct ast_flags flags;
2920         char fs[256];
2921         int length;
2922         char *ptr, *term, *src, *number, *context, *dst;
2923
2924         switch (cmd) {
2925         case CLI_INIT:
2926                 e->command = "dundi show cache";
2927                 e->usage =
2928                 "Usage: dundi show cache\n"
2929                 "       Lists all DUNDi cache entries.\n";
2930                 return NULL;
2931         case CLI_GENERATE:
2932                 return NULL;
2933         }
2934
2935         if (a->argc != 3) {
2936                 return CLI_SHOWUSAGE;
2937         }
2938
2939         time(&now);
2940         db_tree = ast_db_gettree("dundi/cache", NULL);
2941         ast_cli(a->fd, FORMAT2, "Number", "Context", "Expiration", "From", "Weight", "Destination (Flags)");
2942         for (db_entry = db_tree; db_entry; db_entry = db_entry->next) {
2943                 if ((strncmp(db_entry->key, "/dundi/cache/hint/", 18) == 0) || ast_get_time_t(db_entry->data, &ts, 0, &length)) {
2944                         continue;
2945