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