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