a94d71cc42083f35fd5236788995ddca6ee65413
[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         ast_debug(1, "Register expired for '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1322         peer->registerexpire = -1;
1323         peer->lastms = 0;
1324         memset(&peer->addr, 0, sizeof(peer->addr));
1325         return 0;
1326 }
1327
1328 static int update_key(struct dundi_peer *peer)
1329 {
1330         unsigned char key[16];
1331         struct ast_key *ekey, *skey;
1332         char eid_str[20];
1333         int res;
1334         if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
1335                 build_iv(key);
1336                 ast_aes_set_encrypt_key(key, &peer->us_ecx);
1337                 ast_aes_set_decrypt_key(key, &peer->us_dcx);
1338                 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1339                 if (!ekey) {
1340                         ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
1341                                 peer->inkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1342                         return -1;
1343                 }
1344                 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1345                 if (!skey) {
1346                         ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
1347                                 peer->outkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1348                         return -1;
1349                 }
1350                 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
1351                         ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
1352                         return -1;
1353                 }
1354                 if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
1355                         ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
1356                         return -1;
1357                 }
1358                 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
1359                 peer->sentfullkey = 0;
1360                 /* Looks good */
1361                 time(&peer->keyexpire);
1362                 peer->keyexpire += dundi_key_ttl;
1363         }
1364         return 0;
1365 }
1366
1367 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_encrypt_key *ecx)
1368 {
1369         unsigned char curblock[16];
1370         int x;
1371         memcpy(curblock, iv, sizeof(curblock));
1372         while(len > 0) {
1373                 for (x=0;x<16;x++)
1374                         curblock[x] ^= src[x];
1375                 ast_aes_encrypt(curblock, dst, ecx);
1376                 memcpy(curblock, dst, sizeof(curblock));
1377                 dst += 16;
1378                 src += 16;
1379                 len -= 16;
1380         }
1381         return 0;
1382 }
1383 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, ast_aes_decrypt_key *dcx)
1384 {
1385         unsigned char lastblock[16];
1386         int x;
1387         memcpy(lastblock, iv, sizeof(lastblock));
1388         while(len > 0) {
1389                 ast_aes_decrypt(src, dst, dcx);
1390                 for (x=0;x<16;x++)
1391                         dst[x] ^= lastblock[x];
1392                 memcpy(lastblock, src, sizeof(lastblock));
1393                 dst += 16;
1394                 src += 16;
1395                 len -= 16;
1396         }
1397         return 0;
1398 }
1399
1400 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)
1401 {
1402         int space = *dstlen;
1403         unsigned long bytes;
1404         struct dundi_hdr *h;
1405         unsigned char *decrypt_space;
1406         decrypt_space = ast_alloca(srclen);
1407         decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
1408         /* Setup header */
1409         h = (struct dundi_hdr *)dst;
1410         *h = *ohdr;
1411         bytes = space - 6;
1412         if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
1413                 ast_debug(1, "Ouch, uncompress failed :(\n");
1414                 return NULL;
1415         }
1416         /* Update length */
1417         *dstlen = bytes + 6;
1418         /* Return new header */
1419         return h;
1420 }
1421
1422 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
1423 {
1424         unsigned char *compress_space;
1425         int len;
1426         int res;
1427         unsigned long bytes;
1428         struct dundi_ie_data ied;
1429         struct dundi_peer *peer;
1430         unsigned char iv[16];
1431         len = pack->datalen + pack->datalen / 100 + 42;
1432         compress_space = ast_alloca(len);
1433         memset(compress_space, 0, len);
1434         /* We care about everthing save the first 6 bytes of header */
1435         bytes = len;
1436         res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
1437         if (res != Z_OK) {
1438                 ast_debug(1, "Ouch, compression failed!\n");
1439                 return -1;
1440         }
1441         memset(&ied, 0, sizeof(ied));
1442         /* Say who we are */
1443         if (!pack->h->iseqno && !pack->h->oseqno) {
1444                 /* Need the key in the first copy */
1445                 if (!(peer = find_peer(&trans->them_eid)))
1446                         return -1;
1447                 if (update_key(peer))
1448                         return -1;
1449                 if (!peer->sentfullkey)
1450                         ast_set_flag(trans, FLAG_SENDFULLKEY);
1451                 /* Append key data */
1452                 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
1453                 if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
1454                         dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1455                         dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1456                 } else {
1457                         dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
1458                 }
1459                 /* Setup contexts */
1460                 trans->ecx = peer->us_ecx;
1461                 trans->dcx = peer->us_dcx;
1462
1463                 /* We've sent the full key */
1464                 peer->sentfullkey = 1;
1465         }
1466         /* Build initialization vector */
1467         build_iv(iv);
1468         /* Add the field, rounded up to 16 bytes */
1469         dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
1470         /* Copy the data */
1471         if ((ied.pos + bytes) >= sizeof(ied.buf)) {
1472                 ast_log(LOG_NOTICE, "Final packet too large!\n");
1473                 return -1;
1474         }
1475         encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
1476         ied.pos += ((bytes + 15) / 16) * 16;
1477         /* Reconstruct header */
1478         pack->datalen = sizeof(struct dundi_hdr);
1479         pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
1480         pack->h->cmdflags = 0;
1481         memcpy(pack->h->ies, ied.buf, ied.pos);
1482         pack->datalen += ied.pos;
1483         return 0;
1484 }
1485
1486 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, uint32_t keycrc32)
1487 {
1488         unsigned char dst[128];
1489         int res;
1490         struct ast_key *key, *skey;
1491         char eid_str[20];
1492         ast_debug(1, "Expected '%08x' got '%08x'\n", peer->them_keycrc32, keycrc32);
1493         if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
1494                 /* A match */
1495                 return 1;
1496         } else if (!newkey || !newsig)
1497                 return 0;
1498         if (!memcmp(peer->rxenckey, newkey, 128) &&
1499             !memcmp(peer->rxenckey + 128, newsig, 128)) {
1500                 /* By definition, a match */
1501                 return 1;
1502         }
1503         /* Decrypt key */
1504         key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
1505         if (!key) {
1506                 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
1507                         peer->outkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1508                 return -1;
1509         }
1510
1511         skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
1512         if (!skey) {
1513                 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
1514                         peer->inkey, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
1515                 return -1;
1516         }
1517
1518         /* First check signature */
1519         res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
1520         if (res)
1521                 return 0;
1522
1523         res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
1524         if (res != 16) {
1525                 if (res >= 0)
1526                         ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
1527                 return 0;
1528         }
1529         /* Decrypted, passes signature */
1530         ast_debug(1, "Wow, new key combo passed signature and decrypt!\n");
1531         memcpy(peer->rxenckey, newkey, 128);
1532         memcpy(peer->rxenckey + 128, newsig, 128);
1533         peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
1534         ast_aes_set_decrypt_key(dst, &peer->them_dcx);
1535         ast_aes_set_encrypt_key(dst, &peer->them_ecx);
1536         return 1;
1537 }
1538
1539 static void deep_copy_peer(struct dundi_peer *peer_dst, const struct dundi_peer *peer_src)
1540 {
1541         struct permission *cur, *perm;
1542
1543         memcpy(peer_dst, peer_src, sizeof(*peer_dst));
1544
1545         memset(&peer_dst->permit, 0, sizeof(peer_dst->permit));
1546         memset(&peer_dst->include, 0, sizeof(peer_dst->permit));
1547
1548         AST_LIST_TRAVERSE(&peer_src->permit, cur, list) {
1549                 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1550                         continue;
1551
1552                 perm->allow = cur->allow;
1553                 strcpy(perm->name, cur->name);
1554
1555                 AST_LIST_INSERT_HEAD(&peer_dst->permit, perm, list);
1556         }
1557
1558         AST_LIST_TRAVERSE(&peer_src->include, cur, list) {
1559                 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
1560                         continue;
1561
1562                 perm->allow = cur->allow;
1563                 strcpy(perm->name, cur->name);
1564
1565                 AST_LIST_INSERT_HEAD(&peer_dst->include, perm, list);
1566         }
1567 }
1568
1569 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
1570 {
1571         /* Handle canonical command / response */
1572         int final = hdr->cmdresp & 0x80;
1573         int cmd = hdr->cmdresp & 0x7f;
1574         int x,y,z;
1575         int resp;
1576         int res;
1577         int authpass=0;
1578         unsigned char *bufcpy;
1579 #ifdef LOW_MEMORY
1580         struct dundi_ie_data *ied = ast_calloc(1, sizeof(*ied));
1581 #else
1582         struct dundi_ie_data _ied = {
1583                 .pos = 0,
1584         };
1585         struct dundi_ie_data *ied = &_ied;
1586 #endif
1587         struct dundi_ies ies = {
1588                 .eidcount = 0,
1589         };
1590         struct dundi_peer *peer = NULL;
1591         char eid_str[20];
1592         char eid_str2[20];
1593         int retval = -1;
1594
1595         if (!ied) {
1596                 return -1;
1597         }
1598
1599         if (datalen) {
1600                 bufcpy = ast_alloca(datalen);
1601                 /* Make a copy for parsing */
1602                 memcpy(bufcpy, hdr->ies, datalen);
1603                 ast_debug(1, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
1604                 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
1605                         ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
1606                         goto return_cleanup;
1607                 }
1608         }
1609         switch(cmd) {
1610         case DUNDI_COMMAND_DPDISCOVER:
1611         case DUNDI_COMMAND_EIDQUERY:
1612         case DUNDI_COMMAND_PRECACHERQ:
1613                 if (cmd == DUNDI_COMMAND_EIDQUERY)
1614                         resp = DUNDI_COMMAND_EIDRESPONSE;
1615                 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
1616                         resp = DUNDI_COMMAND_PRECACHERP;
1617                 else
1618                         resp = DUNDI_COMMAND_DPRESPONSE;
1619                 /* A dialplan or entity discover -- qualify by highest level entity */
1620                 peer = find_peer(ies.eids[0]);
1621                 if (!peer) {
1622                         dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1623                         dundi_send(trans, resp, 0, 1, ied);
1624                 } else {
1625                         int hasauth = 0;
1626                         trans->us_eid = peer->us_eid;
1627                         if (strlen(peer->inkey)) {
1628                                 hasauth = encrypted;
1629                         } else
1630                                 hasauth = 1;
1631                         if (hasauth) {
1632                                 /* Okay we're authentiated and all, now we check if they're authorized */
1633                                 if (!ies.called_context)
1634                                         ies.called_context = "e164";
1635                                 if (cmd == DUNDI_COMMAND_EIDQUERY) {
1636                                         res = dundi_answer_entity(trans, &ies, ies.called_context);
1637                                 } else {
1638                                         if (ast_strlen_zero(ies.called_number)) {
1639                                                 /* They're not permitted to access that context */
1640                                                 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
1641                                                 dundi_send(trans, resp, 0, 1, ied);
1642                                         } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
1643                                                    (peer->model & DUNDI_MODEL_INBOUND) &&
1644                                                            has_permission(&peer->permit, ies.called_context)) {
1645                                                 res = dundi_answer_query(trans, &ies, ies.called_context);
1646                                                 if (res < 0) {
1647                                                         /* There is no such dundi context */
1648                                                         dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1649                                                         dundi_send(trans, resp, 0, 1, ied);
1650                                                 }
1651                                         } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
1652                                                    (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
1653                                                            has_permission(&peer->include, ies.called_context)) {
1654                                                 res = dundi_prop_precache(trans, &ies, ies.called_context);
1655                                                 if (res < 0) {
1656                                                         /* There is no such dundi context */
1657                                                         dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
1658                                                         dundi_send(trans, resp, 0, 1, ied);
1659                                                 }
1660                                         } else {
1661                                                 /* They're not permitted to access that context */
1662                                                 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
1663                                                 dundi_send(trans, resp, 0, 1, ied);
1664                                         }
1665                                 }
1666                         } else {
1667                                 /* They're not permitted to access that context */
1668                                 dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
1669                                 dundi_send(trans, resp, 0, 1, ied);
1670                         }
1671                 }
1672                 break;
1673         case DUNDI_COMMAND_REGREQ:
1674                 /* A register request -- should only have one entity */
1675                 peer = find_peer(ies.eids[0]);
1676
1677                 /* if the peer is not found and we have a valid 'any_peer' setting */
1678                 if (any_peer && peer == any_peer) {
1679                         /* copy any_peer into a new peer object */
1680                         peer = ast_calloc(1, sizeof(*peer));
1681                         if (peer) {
1682                                 deep_copy_peer(peer, any_peer);
1683
1684                                 /* set EID to remote EID */
1685                                 peer->eid = *ies.eids[0];
1686
1687                                 AST_LIST_LOCK(&peers);
1688                                 AST_LIST_INSERT_HEAD(&peers, peer, list);
1689                                 AST_LIST_UNLOCK(&peers);
1690                         }
1691                 }
1692
1693                 if (!peer || !peer->dynamic) {
1694                         dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
1695                         dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
1696                 } else {
1697                         int hasauth = 0;
1698                         trans->us_eid = peer->us_eid;
1699                         if (!ast_strlen_zero(peer->inkey)) {
1700                                 hasauth = encrypted;
1701                         } else
1702                                 hasauth = 1;
1703                         if (hasauth) {
1704                                 int expire = default_expiration;
1705                                 char data[256];
1706                                 int needqual = 0;
1707                                 AST_SCHED_DEL(sched, peer->registerexpire);
1708                                 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
1709                                 snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(trans->addr.sin_addr),
1710                                         ntohs(trans->addr.sin_port), expire);
1711                                 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
1712                                 if (inaddrcmp(&peer->addr, &trans->addr)) {
1713                                         ast_verb(3, "Registered DUNDi peer '%s' at '%s:%d'\n",
1714                                                         ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
1715                                                         ast_inet_ntoa(trans->addr.sin_addr), ntohs(trans->addr.sin_port));
1716                                         needqual = 1;
1717                                 }
1718
1719                                 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
1720                                 dundi_ie_append_short(ied, DUNDI_IE_EXPIRATION, default_expiration);
1721                                 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, ied);
1722                                 if (needqual)
1723                                         qualify_peer(peer, 1);
1724                         }
1725                 }
1726                 break;
1727         case DUNDI_COMMAND_DPRESPONSE:
1728                 /* A dialplan response, lets see what we got... */
1729                 if (ies.cause < 1) {
1730                         /* Success of some sort */
1731                         ast_debug(1, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
1732                         if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1733                                 authpass = encrypted;
1734                         } else
1735                                 authpass = 1;
1736                         if (authpass) {
1737                                 /* Pass back up answers */
1738                                 if (trans->parent && trans->parent->dr) {
1739                                         y = trans->parent->respcount;
1740                                         for (x=0;x<ies.anscount;x++) {
1741                                                 if (trans->parent->respcount < trans->parent->maxcount) {
1742                                                         /* Make sure it's not already there */
1743                                                         for (z=0;z<trans->parent->respcount;z++) {
1744                                                                 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
1745                                                                     !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
1746                                                                                 break;
1747                                                         }
1748                                                         if (z == trans->parent->respcount) {
1749                                                                 /* Copy into parent responses */
1750                                                                 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
1751                                                                 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
1752                                                                 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
1753                                                                 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
1754                                                                 if (ies.expiration > 0)
1755                                                                         trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
1756                                                                 else
1757                                                                         trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
1758                                                                 ast_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
1759                                                                         sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
1760                                                                         &ies.answers[x]->eid);
1761                                                                 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
1762                                                                         sizeof(trans->parent->dr[trans->parent->respcount].dest));
1763                                                                 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
1764                                                                         sizeof(trans->parent->dr[trans->parent->respcount].tech));
1765                                                                 trans->parent->respcount++;
1766                                                                 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1767                                                         } else if (trans->parent->dr[z].weight > ntohs(ies.answers[x]->weight)) {
1768                                                                 /* Update weight if appropriate */
1769                                                                 trans->parent->dr[z].weight = ntohs(ies.answers[x]->weight);
1770                                                         }
1771                                                 } else
1772                                                         ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
1773                                                                 trans->parent->number, trans->parent->dcontext);
1774                                         }
1775                                         /* Save all the results (if any) we had.  Even if no results, still cache lookup.  Let
1776                                            the cache know if this request was unaffected by our entity list. */
1777                                         cache_save(&trans->them_eid, trans->parent, y,
1778                                                         ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
1779                                         if (ies.hint) {
1780                                                 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
1781                                                 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1782                                                         ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1783                                                 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
1784                                                         if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
1785                                                                 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
1786                                                                         sizeof(trans->parent->hmd->exten));
1787                                                         }
1788                                                 } else {
1789                                                         ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
1790                                                 }
1791                                         }
1792                                         if (ies.expiration > 0) {
1793                                                 if (trans->parent->expiration > ies.expiration) {
1794                                                         trans->parent->expiration = ies.expiration;
1795                                                 }
1796                                         }
1797                                 }
1798                                 /* Close connection if not final */
1799                                 if (!final)
1800                                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1801                         }
1802
1803                 } else {
1804                         /* Auth failure, check for data */
1805                         if (!final) {
1806                                 /* Cancel if they didn't already */
1807                                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1808                         }
1809                 }
1810                 break;
1811         case DUNDI_COMMAND_EIDRESPONSE:
1812                 /* A dialplan response, lets see what we got... */
1813                 if (ies.cause < 1) {
1814                         /* Success of some sort */
1815                         ast_debug(1, "Looks like success of some sort (%d)\n", ies.cause);
1816                         if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1817                                 authpass = encrypted;
1818                         } else
1819                                 authpass = 1;
1820                         if (authpass) {
1821                                 /* Pass back up answers */
1822                                 if (trans->parent && trans->parent->dei && ies.q_org) {
1823                                         if (!trans->parent->respcount) {
1824                                                 trans->parent->respcount++;
1825                                                 if (ies.q_dept)
1826                                                         ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
1827                                                 if (ies.q_org)
1828                                                         ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
1829                                                 if (ies.q_locality)
1830                                                         ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
1831                                                 if (ies.q_stateprov)
1832                                                         ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
1833                                                 if (ies.q_country)
1834                                                         ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
1835                                                 if (ies.q_email)
1836                                                         ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
1837                                                 if (ies.q_phone)
1838                                                         ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
1839                                                 if (ies.q_ipaddr)
1840                                                         ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
1841                                                 if (!ast_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
1842                                                         /* If it's them, update our address */
1843                                                         ast_copy_string(trans->parent->dei->ipaddr, ast_inet_ntoa(trans->addr.sin_addr), sizeof(trans->parent->dei->ipaddr));
1844                                                 }
1845                                         }
1846                                         if (ies.hint) {
1847                                                 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
1848                                                         ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
1849                                         }
1850                                 }
1851                                 /* Close connection if not final */
1852                                 if (!final)
1853                                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1854                         }
1855
1856                 } else {
1857                         /* Auth failure, check for data */
1858                         if (!final) {
1859                                 /* Cancel if they didn't already */
1860                                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1861                         }
1862                 }
1863                 break;
1864         case DUNDI_COMMAND_REGRESPONSE:
1865                 /* A dialplan response, lets see what we got... */
1866                 if (ies.cause < 1) {
1867                         int hasauth;
1868                         /* Success of some sort */
1869                         if (ast_test_flag(trans, FLAG_ENCRYPT)) {
1870                                 hasauth = encrypted;
1871                         } else
1872                                 hasauth = 1;
1873
1874                         if (!hasauth) {
1875                                 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
1876                                 if (!final) {
1877                                         dundi_ie_append_cause(ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
1878                                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, ied);
1879                                 }
1880                         } else {
1881                                 ast_debug(1, "Yay, we've registered as '%s' to '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &trans->us_eid),
1882                                                 ast_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
1883                                 /* Close connection if not final */
1884                                 if (!final)
1885                                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1886                         }
1887                 } else {
1888                         /* Auth failure, cancel if they didn't for some reason */
1889                         if (!final) {
1890                                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1891                         }
1892                 }
1893                 break;
1894         case DUNDI_COMMAND_INVALID:
1895         case DUNDI_COMMAND_NULL:
1896         case DUNDI_COMMAND_PRECACHERP:
1897                 /* Do nothing special */
1898                 if (!final)
1899                         dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1900                 break;
1901         case DUNDI_COMMAND_ENCREJ:
1902                 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
1903                         /* No really, it's over at this point */
1904                         if (!final)
1905                                 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
1906                 } else {
1907                         /* Send with full key */
1908                         ast_set_flag(trans, FLAG_SENDFULLKEY);
1909                         if (final) {
1910                                 /* Ooops, we got a final message, start by sending ACK... */
1911                                 dundi_ack(trans, hdr->cmdresp & 0x80);
1912                                 trans->aseqno = trans->iseqno;
1913                                 /* Now, we gotta create a new transaction */
1914                                 if (!reset_transaction(trans)) {
1915                                         /* Make sure handle_frame doesn't destroy us */
1916                                         hdr->cmdresp &= 0x7f;
1917                                         /* Parse the message we transmitted */
1918                                         memset(&ies, 0, sizeof(ies));
1919                                         dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
1920                                         /* Reconstruct outgoing encrypted packet */
1921                                         memset(ied, 0, sizeof(*ied));
1922                                         dundi_ie_append_eid(ied, DUNDI_IE_EID, &trans->us_eid);
1923                                         dundi_ie_append_raw(ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
1924                                         dundi_ie_append_raw(ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
1925                                         if (ies.encblock)
1926                                                 dundi_ie_append_encdata(ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
1927                                         dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, ied);
1928                                         peer->sentfullkey = 1;
1929                                 }
1930                         }
1931                 }
1932                 break;
1933         case DUNDI_COMMAND_ENCRYPT:
1934                 if (!encrypted) {
1935                         /* No nested encryption! */
1936                         if ((trans->iseqno == 1) && !trans->oseqno) {
1937                                 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
1938                                         ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
1939                                         (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
1940                                         if (!final) {
1941                                                 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1942                                         }
1943                                         break;
1944                                 }
1945                                 apply_peer(trans, peer);
1946                                 /* Key passed, use new contexts for this session */
1947                                 trans->ecx = peer->them_ecx;
1948                                 trans->dcx = peer->them_dcx;
1949                         }
1950                         if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
1951                                 struct dundi_hdr *dhdr;
1952                                 unsigned char decoded[MAX_PACKET_SIZE];
1953                                 int ddatalen;
1954                                 ddatalen = sizeof(decoded);
1955                                 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
1956                                 if (dhdr) {
1957                                         /* Handle decrypted response */
1958                                         if (dundidebug)
1959                                                 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
1960                                         handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
1961                                         /* Carry back final flag */
1962                                         hdr->cmdresp |= dhdr->cmdresp & 0x80;
1963                                         break;
1964                                 } else {
1965                                         ast_debug(1, "Ouch, decrypt failed :(\n");
1966                                 }
1967                         }
1968                 }
1969                 if (!final) {
1970                         /* Turn off encryption */
1971                         ast_clear_flag(trans, FLAG_ENCRYPT);
1972                         dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
1973                 }
1974                 break;
1975         default:
1976                 /* Send unknown command if we don't know it, with final flag IFF it's the
1977                    first command in the dialog and only if we haven't received final notification */
1978                 if (!final) {
1979                         dundi_ie_append_byte(ied, DUNDI_IE_UNKNOWN, cmd);
1980                         dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, ied);
1981                 }
1982         }
1983
1984         retval = 0;
1985
1986 return_cleanup:
1987 #ifdef LOW_MEMORY
1988         ast_free(ied);
1989 #endif
1990         return retval;
1991 }
1992
1993 static void destroy_packet(struct dundi_packet *pack, int needfree);
1994 static void destroy_packets(struct packetlist *p)
1995 {
1996         struct dundi_packet *pack;
1997
1998         while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
1999                 AST_SCHED_DEL(sched, pack->retransid);
2000                 ast_free(pack);
2001         }
2002 }
2003
2004
2005 static int ack_trans(struct dundi_transaction *trans, int iseqno)
2006 {
2007         struct dundi_packet *pack;
2008
2009         /* Ack transmitted packet corresponding to iseqno */
2010         AST_LIST_TRAVERSE(&trans->packets, pack, list) {
2011                 if ((pack->h->oseqno + 1) % 255 == iseqno) {
2012                         destroy_packet(pack, 0);
2013                         if (!AST_LIST_EMPTY(&trans->lasttrans)) {
2014                                 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
2015                                 destroy_packets(&trans->lasttrans);
2016                         }
2017                         AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
2018                         AST_SCHED_DEL(sched, trans->autokillid);
2019                         return 1;
2020                 }
2021         }
2022
2023         return 0;
2024 }
2025
2026 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
2027 {
2028         struct dundi_transaction *trans;
2029         trans = find_transaction(h, sin);
2030         if (!trans) {
2031                 dundi_reject(h, sin);
2032                 return 0;
2033         }
2034         /* Got a transaction, see where this header fits in */
2035         if (h->oseqno == trans->iseqno) {
2036                 /* Just what we were looking for...  Anything but ack increments iseqno */
2037                 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
2038                         /* If final, we're done */
2039                         destroy_trans(trans, 0);
2040                         return 0;
2041                 }
2042                 if (h->cmdresp != DUNDI_COMMAND_ACK) {
2043                         trans->oiseqno = trans->iseqno;
2044                         trans->iseqno++;
2045                         handle_command_response(trans, h, datalen, 0);
2046                 }
2047                 if (trans->aseqno != trans->iseqno) {
2048                         dundi_ack(trans, h->cmdresp & 0x80);
2049                         trans->aseqno = trans->iseqno;
2050                 }
2051                 /* Delete any saved last transmissions */
2052                 destroy_packets(&trans->lasttrans);
2053                 if (h->cmdresp & 0x80) {
2054                         /* Final -- destroy now */
2055                         destroy_trans(trans, 0);
2056                 }
2057         } else if (h->oseqno == trans->oiseqno) {
2058                 /* Last incoming sequence number -- send ACK without processing */
2059                 dundi_ack(trans, 0);
2060         } else {
2061                 /* Out of window -- simply drop */
2062                 ast_debug(1, "Dropping packet out of window!\n");
2063         }
2064         return 0;
2065 }
2066
2067 static int socket_read(int *id, int fd, short events, void *cbdata)
2068 {
2069         struct sockaddr_in sin;
2070         int res;
2071         struct dundi_hdr *h;
2072         char buf[MAX_PACKET_SIZE];
2073         socklen_t len = sizeof(sin);
2074
2075         res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
2076         if (res < 0) {
2077                 if (errno != ECONNREFUSED)
2078                         ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
2079                 return 1;
2080         }
2081         if (res < sizeof(struct dundi_hdr)) {
2082                 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
2083                 return 1;
2084         }
2085         buf[res] = '\0';
2086         h = (struct dundi_hdr *) buf;
2087         if (dundidebug)
2088                 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
2089         AST_LIST_LOCK(&peers);
2090         handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
2091         AST_LIST_UNLOCK(&peers);
2092         return 1;
2093 }
2094
2095 static void build_secret(char *secret, int seclen)
2096 {
2097         unsigned char tmp[16];
2098         char *s;
2099         build_iv(tmp);
2100         secret[0] = '\0';
2101         ast_base64encode(secret, tmp, sizeof(tmp), seclen);
2102         /* Eliminate potential bad characters */
2103         while((s = strchr(secret, ';'))) *s = '+';
2104         while((s = strchr(secret, '/'))) *s = '+';
2105         while((s = strchr(secret, ':'))) *s = '+';
2106         while((s = strchr(secret, '@'))) *s = '+';
2107 }
2108
2109
2110 static void save_secret(const char *newkey, const char *oldkey)
2111 {
2112         char tmp[256];
2113         if (oldkey)
2114                 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
2115         else
2116                 snprintf(tmp, sizeof(tmp), "%s", newkey);
2117         rotatetime = time(NULL) + DUNDI_SECRET_TIME;
2118         ast_db_put(secretpath, "secret", tmp);
2119         snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
2120         ast_db_put(secretpath, "secretexpiry", tmp);
2121 }
2122
2123 static void load_password(void)
2124 {
2125         char *current=NULL;
2126         char *last=NULL;
2127         char tmp[256];
2128         time_t expired;
2129
2130         ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
2131         if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
2132                 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
2133                 current = strchr(tmp, ';');
2134                 if (!current)
2135                         current = tmp;
2136                 else {
2137                         *current = '\0';
2138                         current++;
2139                 };
2140                 if ((time(NULL) - expired) < 0) {
2141                         if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
2142                                 expired = time(NULL) + DUNDI_SECRET_TIME;
2143                 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
2144                         last = current;
2145                         current = NULL;
2146                 } else {
2147                         last = NULL;
2148                         current = NULL;
2149                 }
2150         }
2151         if (current) {
2152                 /* Current key is still valid, just setup rotatation properly */
2153                 ast_copy_string(cursecret, current, sizeof(cursecret));
2154                 rotatetime = expired;
2155         } else {
2156                 /* Current key is out of date, rotate or eliminate all together */
2157                 build_secret(cursecret, sizeof(cursecret));
2158                 save_secret(cursecret, last);
2159         }
2160 }
2161
2162 static void check_password(void)
2163 {
2164         char oldsecret[80];
2165         time_t now;
2166
2167         time(&now);
2168 #if 0
2169         printf("%ld/%ld\n", now, rotatetime);
2170 #endif
2171         if ((now - rotatetime) >= 0) {
2172                 /* Time to rotate keys */
2173                 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
2174                 build_secret(cursecret, sizeof(cursecret));
2175                 save_secret(cursecret, oldsecret);
2176         }
2177 }
2178
2179 static void *network_thread(void *ignore)
2180 {
2181         /* Our job is simple: Send queued messages, retrying if necessary.  Read frames
2182            from the network, and queue them for delivery to the channels */
2183         int res;
2184         /* Establish I/O callback for socket read */
2185         int *socket_read_id = ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
2186
2187         while (!dundi_shutdown) {
2188                 res = ast_sched_wait(sched);
2189                 if ((res > 1000) || (res < 0))
2190                         res = 1000;
2191                 res = ast_io_wait(io, res);
2192                 if (res >= 0) {
2193                         AST_LIST_LOCK(&peers);
2194                         ast_sched_runq(sched);
2195                         AST_LIST_UNLOCK(&peers);
2196                 }
2197                 check_password();
2198         }
2199
2200         ast_io_remove(io, socket_read_id);
2201         netthreadid = AST_PTHREADT_NULL;
2202
2203         return NULL;
2204 }
2205
2206 static void *process_clearcache(void *ignore)
2207 {
2208         struct ast_db_entry *db_entry, *db_tree;
2209         int striplen = sizeof("/dundi/cache");
2210         time_t now;
2211
2212         while (!dundi_shutdown) {
2213                 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
2214
2215                 time(&now);
2216
2217                 db_entry = db_tree = ast_db_gettree("dundi/cache", NULL);
2218                 for (; db_entry; db_entry = db_entry->next) {
2219                         time_t expiry;
2220
2221                         if (!ast_get_time_t(db_entry->data, &expiry, 0, NULL)) {
2222                                 if (expiry < now) {
2223                                         ast_debug(1, "clearing expired DUNDI cache entry: %s\n", db_entry->key);
2224                                         ast_db_del("dundi/cache", db_entry->key + striplen);
2225                                 }
2226                         }
2227                 }
2228                 ast_db_freetree(db_tree);
2229
2230                 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
2231                 pthread_testcancel();
2232                 sleep(60);
2233                 pthread_testcancel();
2234         }
2235
2236         clearcachethreadid = AST_PTHREADT_NULL;
2237         return NULL;
2238 }
2239
2240 static void *process_precache(void *ign)
2241 {
2242         struct dundi_precache_queue *qe;
2243         time_t now;
2244         char context[256];
2245         char number[256];
2246         int run;
2247
2248         while (!dundi_shutdown) {
2249                 time(&now);
2250                 run = 0;
2251                 AST_LIST_LOCK(&pcq);
2252                 if ((qe = AST_LIST_FIRST(&pcq))) {
2253                         if (!qe->expiration) {
2254                                 /* Gone...  Remove... */
2255                                 AST_LIST_REMOVE_HEAD(&pcq, list);
2256                                 ast_free(qe);
2257                         } else if (qe->expiration < now) {
2258                                 /* Process this entry */
2259                                 qe->expiration = 0;
2260                                 ast_copy_string(context, qe->context, sizeof(context));
2261                                 ast_copy_string(number, qe->number, sizeof(number));
2262                                 run = 1;
2263                         }
2264                 }
2265                 AST_LIST_UNLOCK(&pcq);
2266                 if (run) {
2267                         dundi_precache(context, number);
2268                 } else
2269                         sleep(1);
2270         }
2271
2272         precachethreadid = AST_PTHREADT_NULL;
2273
2274         return NULL;
2275 }
2276
2277 static int start_network_thread(void)
2278 {
2279         ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
2280         ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL);
2281         ast_pthread_create_background(&clearcachethreadid, NULL, process_clearcache, NULL);
2282         return 0;
2283 }
2284
2285 static char *dundi_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2286 {
2287         switch (cmd) {
2288         case CLI_INIT:
2289                 e->command = "dundi set debug {on|off}";
2290                 e->usage =
2291                         "Usage: dundi set debug {on|off}\n"
2292                         "       Enables/Disables dumping of DUNDi packets for debugging purposes\n";
2293                 return NULL;
2294         case CLI_GENERATE:
2295                 return NULL;
2296         }
2297
2298         if (a->argc != e->args) {
2299                 return CLI_SHOWUSAGE;
2300         }
2301         if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
2302                 dundidebug = 1;
2303                 ast_cli(a->fd, "DUNDi Debugging Enabled\n");
2304         } else {
2305                 dundidebug = 0;
2306                 ast_cli(a->fd, "DUNDi Debugging Disabled\n");
2307         }
2308         return CLI_SUCCESS;
2309 }
2310
2311 static char *dundi_store_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2312 {
2313         switch (cmd) {
2314         case CLI_INIT:
2315                 e->command = "dundi store history {on|off}";
2316                 e->usage =
2317                         "Usage: dundi store history {on|off}\n"
2318                         "       Enables/Disables storing of DUNDi requests and times for debugging\n"
2319                         "purposes\n";
2320                 return NULL;
2321         case CLI_GENERATE:
2322                 return NULL;
2323         }
2324
2325         if (a->argc != e->args) {
2326                 return CLI_SHOWUSAGE;
2327         }
2328         if (!strncasecmp(a->argv[e->args -1], "on", 2)) {
2329                 global_storehistory = 1;
2330                 ast_cli(a->fd, "DUNDi History Storage Enabled\n");
2331         } else {
2332                 global_storehistory = 0;
2333                 ast_cli(a->fd, "DUNDi History Storage Disabled\n");
2334         }
2335         return CLI_SUCCESS;
2336 }
2337
2338 static char *dundi_flush(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2339 {
2340         int stats = 0;
2341         switch (cmd) {
2342         case CLI_INIT:
2343                 e->command = "dundi flush [stats]";
2344                 e->usage =
2345                         "Usage: dundi flush [stats]\n"
2346                         "       Flushes DUNDi answer cache, used primarily for debug.  If\n"
2347                         "'stats' is present, clears timer statistics instead of normal\n"
2348                         "operation.\n";
2349                 return NULL;
2350         case CLI_GENERATE:
2351                 return NULL;
2352         }
2353         if ((a->argc < 2) || (a->argc > 3)) {
2354                 return CLI_SHOWUSAGE;
2355         }
2356         if (a->argc > 2) {
2357                 if (!strcasecmp(a->argv[2], "stats")) {
2358                         stats = 1;
2359                 } else {
2360                         return CLI_SHOWUSAGE;
2361                 }
2362         }
2363         if (stats) {
2364                 /* Flush statistics */
2365                 struct dundi_peer *p;
2366                 int x;
2367                 AST_LIST_LOCK(&peers);
2368                 AST_LIST_TRAVERSE(&peers, p, list) {
2369                         for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2370                                 if (p->lookups[x])
2371                                         ast_free(p->lookups[x]);
2372                                 p->lookups[x] = NULL;
2373                                 p->lookuptimes[x] = 0;
2374                         }
2375                         p->avgms = 0;
2376                 }
2377                 AST_LIST_UNLOCK(&peers);
2378         } else {
2379                 ast_db_deltree("dundi/cache", NULL);
2380                 ast_cli(a->fd, "DUNDi Cache Flushed\n");
2381         }
2382         return CLI_SUCCESS;
2383 }
2384
2385 static char *model2str(int model)
2386 {
2387         switch(model) {
2388         case DUNDI_MODEL_INBOUND:
2389                 return "Inbound";
2390         case DUNDI_MODEL_OUTBOUND:
2391                 return "Outbound";
2392         case DUNDI_MODEL_SYMMETRIC:
2393                 return "Symmetric";
2394         default:
2395                 return "Unknown";
2396         }
2397 }
2398
2399 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
2400 {
2401         int which=0, len;
2402         char *ret = NULL;
2403         struct dundi_peer *p;
2404         char eid_str[20];
2405
2406         if (pos != rpos)
2407                 return NULL;
2408         AST_LIST_LOCK(&peers);
2409         len = strlen(word);
2410         AST_LIST_TRAVERSE(&peers, p, list) {
2411                 const char *s = ast_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
2412                 if (!strncasecmp(word, s, len) && ++which > state) {
2413                         ret = ast_strdup(s);
2414                         break;
2415                 }
2416         }
2417         AST_LIST_UNLOCK(&peers);
2418         return ret;
2419 }
2420
2421 static int rescomp(const void *a, const void *b)
2422 {
2423         const struct dundi_result *resa, *resb;
2424         resa = a;
2425         resb = b;
2426         if (resa->weight < resb->weight)
2427                 return -1;
2428         if (resa->weight > resb->weight)
2429                 return 1;
2430         return 0;
2431 }
2432
2433 static void sort_results(struct dundi_result *results, int count)
2434 {
2435         qsort(results, count, sizeof(results[0]), rescomp);
2436 }
2437
2438 static char *dundi_do_lookup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2439 {
2440         int res;
2441         char tmp[256];
2442         char fs[80] = "";
2443         char *context;
2444         int x;
2445         int bypass = 0;
2446         struct dundi_result dr[MAX_RESULTS];
2447         struct timeval start;
2448         switch (cmd) {
2449         case CLI_INIT:
2450                 e->command = "dundi lookup";
2451                 e->usage =
2452                         "Usage: dundi lookup <number>[@context] [bypass]\n"
2453                         "       Lookup the given number within the given DUNDi context\n"
2454                         "(or e164 if none is specified).  Bypasses cache if 'bypass'\n"
2455                         "keyword is specified.\n";
2456                 return NULL;
2457         case CLI_GENERATE:
2458                 return NULL;
2459         }
2460
2461         if ((a->argc < 3) || (a->argc > 4)) {
2462                 return CLI_SHOWUSAGE;
2463         }
2464         if (a->argc > 3) {
2465                 if (!strcasecmp(a->argv[3], "bypass")) {
2466                         bypass=1;
2467                 } else {
2468                         return CLI_SHOWUSAGE;
2469                 }
2470         }
2471         ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2472         context = strchr(tmp, '@');
2473         if (context) {
2474                 *context = '\0';
2475                 context++;
2476         }
2477         start = ast_tvnow();
2478         res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
2479
2480         if (res < 0)
2481                 ast_cli(a->fd, "DUNDi lookup returned error.\n");
2482         else if (!res)
2483                 ast_cli(a->fd, "DUNDi lookup returned no results.\n");
2484         else
2485                 sort_results(dr, res);
2486         for (x=0;x<res;x++) {
2487                 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));
2488                 ast_cli(a->fd, "     from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
2489         }
2490         ast_cli(a->fd, "DUNDi lookup completed in %" PRIi64 " ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2491         return CLI_SUCCESS;
2492 }
2493
2494 static char *dundi_do_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2495 {
2496         int res;
2497         char tmp[256];
2498         char *context;
2499         struct timeval start;
2500         switch (cmd) {
2501         case CLI_INIT:
2502                 e->command = "dundi precache";
2503                 e->usage =
2504                         "Usage: dundi precache <number>[@context]\n"
2505                         "       Lookup the given number within the given DUNDi context\n"
2506                         "(or e164 if none is specified) and precaches the results to any\n"
2507                         "upstream DUNDi push servers.\n";
2508                 return NULL;
2509         case CLI_GENERATE:
2510                 return NULL;
2511         }
2512         if ((a->argc < 3) || (a->argc > 3)) {
2513                 return CLI_SHOWUSAGE;
2514         }
2515         ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2516         context = strchr(tmp, '@');
2517         if (context) {
2518                 *context = '\0';
2519                 context++;
2520         }
2521         start = ast_tvnow();
2522         res = dundi_precache(context, tmp);
2523
2524         if (res < 0)
2525                 ast_cli(a->fd, "DUNDi precache returned error.\n");
2526         else if (!res)
2527                 ast_cli(a->fd, "DUNDi precache returned no error.\n");
2528         ast_cli(a->fd, "DUNDi lookup completed in %" PRIi64 " ms\n", ast_tvdiff_ms(ast_tvnow(), start));
2529         return CLI_SUCCESS;
2530 }
2531
2532 static char *dundi_do_query(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2533 {
2534         int res;
2535         char tmp[256];
2536         char *context;
2537         dundi_eid eid;
2538         struct dundi_entity_info dei;
2539         switch (cmd) {
2540         case CLI_INIT:
2541                 e->command = "dundi query";
2542                 e->usage =
2543                         "Usage: dundi query <entity>[@context]\n"
2544                         "       Attempts to retrieve contact information for a specific\n"
2545                         "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
2546                         "e164 if none is specified).\n";
2547                 return NULL;
2548         case CLI_GENERATE:
2549                 return NULL;
2550         }
2551         if ((a->argc < 3) || (a->argc > 3)) {
2552                 return CLI_SHOWUSAGE;
2553         }
2554         if (ast_str_to_eid(&eid, a->argv[2])) {
2555                 ast_cli(a->fd, "'%s' is not a valid EID!\n", a->argv[2]);
2556                 return CLI_SHOWUSAGE;
2557         }
2558         ast_copy_string(tmp, a->argv[2], sizeof(tmp));
2559         context = strchr(tmp, '@');
2560         if (context) {
2561                 *context = '\0';
2562                 context++;
2563         }
2564         res = dundi_query_eid(&dei, context, eid);
2565         if (res < 0)
2566                 ast_cli(a->fd, "DUNDi Query EID returned error.\n");
2567         else if (!res)
2568                 ast_cli(a->fd, "DUNDi Query EID returned no results.\n");
2569         else {
2570                 ast_cli(a->fd, "DUNDi Query EID succeeded:\n");
2571                 ast_cli(a->fd, "Department:      %s\n", dei.orgunit);
2572                 ast_cli(a->fd, "Organization:    %s\n", dei.org);
2573                 ast_cli(a->fd, "City/Locality:   %s\n", dei.locality);
2574                 ast_cli(a->fd, "State/Province:  %s\n", dei.stateprov);
2575                 ast_cli(a->fd, "Country:         %s\n", dei.country);
2576                 ast_cli(a->fd, "E-mail:          %s\n", dei.email);
2577                 ast_cli(a->fd, "Phone:           %s\n", dei.phone);
2578                 ast_cli(a->fd, "IP Address:      %s\n", dei.ipaddr);
2579         }
2580         return CLI_SUCCESS;
2581 }
2582
2583 static char *dundi_show_peer(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2584 {
2585         struct dundi_peer *peer;
2586         struct permission *p;
2587         char *order;
2588         char eid_str[20];
2589         int x, cnt;
2590         switch (cmd) {
2591         case CLI_INIT:
2592                 e->command = "dundi show peer";
2593                 e->usage =
2594                         "Usage: dundi show peer [peer]\n"
2595                         "       Provide a detailed description of a specifid DUNDi peer.\n";
2596                 return NULL;
2597         case CLI_GENERATE:
2598                 return complete_peer_helper(a->line, a->word, a->pos, a->n, 3);
2599         }
2600         if (a->argc != 4) {
2601                 return CLI_SHOWUSAGE;
2602         }
2603         AST_LIST_LOCK(&peers);
2604         AST_LIST_TRAVERSE(&peers, peer, list) {
2605                 if (!strcasecmp(ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), a->argv[3]))
2606                         break;
2607         }
2608         if (peer) {
2609                 switch(peer->order) {
2610                 case 0:
2611                         order = "Primary";
2612                         break;
2613                 case 1:
2614                         order = "Secondary";
2615                         break;
2616                 case 2:
2617                         order = "Tertiary";
2618                         break;
2619                 case 3:
2620                         order = "Quartiary";
2621                         break;
2622                 default:
2623                         order = "Unknown";
2624                 }
2625                 ast_cli(a->fd, "Peer:    %s\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2626                 ast_cli(a->fd, "Model:   %s\n", model2str(peer->model));
2627                 ast_cli(a->fd, "Order:   %s\n", order);
2628                 ast_cli(a->fd, "Host:    %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "<Unspecified>");
2629                 ast_cli(a->fd, "Port:    %d\n", ntohs(peer->addr.sin_port));
2630                 ast_cli(a->fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
2631                 ast_cli(a->fd, "Reg:     %s\n", peer->registerid < 0 ? "No" : "Yes");
2632                 ast_cli(a->fd, "In Key:  %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
2633                 ast_cli(a->fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
2634                 if (!AST_LIST_EMPTY(&peer->include))
2635                         ast_cli(a->fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
2636                 AST_LIST_TRAVERSE(&peer->include, p, list)
2637                         ast_cli(a->fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
2638                 if (!AST_LIST_EMPTY(&peer->permit))
2639                         ast_cli(a->fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
2640                 AST_LIST_TRAVERSE(&peer->permit, p, list)
2641                         ast_cli(a->fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
2642                 cnt = 0;
2643                 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
2644                         if (peer->lookups[x]) {
2645                                 if (!cnt)
2646                                         ast_cli(a->fd, "Last few query times:\n");
2647                                 ast_cli(a->fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
2648                                 cnt++;
2649                         }
2650                 }
2651                 if (cnt)
2652                         ast_cli(a->fd, "Average query time: %d ms\n", peer->avgms);
2653         } else
2654                 ast_cli(a->fd, "No such peer '%s'\n", a->argv[3]);
2655         AST_LIST_UNLOCK(&peers);
2656         return CLI_SUCCESS;
2657 }
2658
2659 static char *dundi_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2660 {
2661 #define FORMAT2 "%-20.20s %-15.15s     %-6.6s %-10.10s %-8.8s %-15.15s\n"
2662 #define FORMAT "%-20.20s %-15.15s %s %-6d %-10.10s %-8.8s %-15.15s\n"
2663         struct dundi_peer *peer;
2664         int registeredonly=0;
2665         char avgms[20];
2666         char eid_str[20];
2667         int online_peers = 0;
2668         int offline_peers = 0;
2669         int unmonitored_peers = 0;
2670         int total_peers = 0;
2671         switch (cmd) {
2672         case CLI_INIT:
2673                 e->command = "dundi show peers [registered|include|exclude|begin]";
2674                 e->usage =
2675                         "Usage: dundi show peers [registered|include|exclude|begin]\n"
2676                         "       Lists all known DUNDi peers.\n"
2677                         "       If 'registered' is present, only registered peers are shown.\n";
2678                 return NULL;
2679         case CLI_GENERATE:
2680                 return NULL;
2681         }
2682
2683         if ((a->argc != 3) && (a->argc != 4) && (a->argc != 5)) {
2684                 return CLI_SHOWUSAGE;
2685         }
2686         if ((a->argc == 4)) {
2687                 if (!strcasecmp(a->argv[3], "registered")) {
2688                         registeredonly = 1;
2689                 } else {
2690                         return CLI_SHOWUSAGE;
2691                 }
2692         }
2693         AST_LIST_LOCK(&peers);
2694         ast_cli(a->fd, FORMAT2, "EID", "Host", "Port", "Model", "AvgTime", "Status");
2695         AST_LIST_TRAVERSE(&peers, peer, list) {
2696                 char status[20];
2697                 int print_line = -1;
2698                 char srch[2000];
2699                 total_peers++;
2700                 if (registeredonly && !peer->addr.sin_addr.s_addr)
2701                         continue;
2702                 if (peer->maxms) {
2703                         if (peer->lastms < 0) {
2704                                 strcpy(status, "UNREACHABLE");
2705                                 offline_peers++;
2706                         }
2707                         else if (peer->lastms > peer->maxms) {
2708                                 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
2709                                 offline_peers++;
2710                         }
2711                         else if (peer->lastms) {
2712                                 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
2713                                 online_peers++;
2714                         }
2715                         else {
2716                                 strcpy(status, "UNKNOWN");
2717                                 offline_peers++;
2718                         }
2719                 } else {
2720                         strcpy(status, "Unmonitored");
2721                         unmonitored_peers++;
2722                 }
2723                 if (peer->avgms)
2724                         snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
2725                 else
2726                         strcpy(avgms, "Unavail");
2727                 snprintf(srch, sizeof(srch), FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2728                                         peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
2729                                         peer->dynamic ? "(D)" : "(S)", ntohs(peer->addr.sin_port), model2str(peer->model), avgms, status);
2730
2731                 if (a->argc == 5) {
2732                   if (!strcasecmp(a->argv[3],"include") && strstr(srch,a->argv[4])) {
2733                         print_line = -1;
2734                    } else if (!strcasecmp(a->argv[3],"exclude") && !strstr(srch,a->argv[4])) {
2735                         print_line = 1;
2736                    } else if (!strcasecmp(a->argv[3],"begin") && !strncasecmp(srch,a->argv[4],strlen(a->argv[4]))) {
2737                         print_line = -1;
2738                    } else {
2739                         print_line = 0;
2740                   }
2741                 }
2742
2743         if (print_line) {
2744                         ast_cli(a->fd, FORMAT, ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
2745                                         peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
2746                                         peer->dynamic ? "(D)" : "(S)", ntohs(peer->addr.sin_port), model2str(peer->model), avgms, status);
2747                 }
2748         }
2749         ast_cli(a->fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
2750         AST_LIST_UNLOCK(&peers);
2751         return CLI_SUCCESS;
2752 #undef FORMAT
2753 #undef FORMAT2
2754 }
2755
2756 static char *dundi_show_trans(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2757 {
2758 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
2759 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
2760         struct dundi_transaction *trans;
2761         switch (cmd) {
2762         case CLI_INIT:
2763                 e->command = "dundi show trans";
2764                 e->usage =
2765                         "Usage: dundi show trans\n"
2766                         "       Lists all known DUNDi transactions.\n";
2767                 return NULL;
2768         case CLI_GENERATE:
2769                 return NULL;
2770         }
2771         if (a->argc != 3) {
2772                 return CLI_SHOWUSAGE;
2773         }
2774         AST_LIST_LOCK(&peers);
2775         ast_cli(a->fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
2776         AST_LIST_TRAVERSE(&alltrans, trans, all) {
2777                 ast_cli(a->fd, FORMAT, ast_inet_ntoa(trans->addr.sin_addr),
2778                         ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
2779         }
2780         AST_LIST_UNLOCK(&peers);
2781         return CLI_SUCCESS;
2782 #undef FORMAT
2783 #undef FORMAT2
2784 }
2785
2786 static char *dundi_show_entityid(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2787 {
2788         char eid_str[20];
2789         switch (cmd) {
2790         case CLI_INIT:
2791                 e->command = "dundi show entityid";
2792                 e->usage =
2793                         "Usage: dundi show entityid\n"
2794                         "       Displays the global entityid for this host.\n";
2795                 return NULL;
2796         case CLI_GENERATE:
2797                 return NULL;
2798         }
2799         if (a->argc != 3) {
2800                 return CLI_SHOWUSAGE;
2801         }
2802         AST_LIST_LOCK(&peers);
2803         ast_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
2804         AST_LIST_UNLOCK(&peers);
2805         ast_cli(a->fd, "Global EID for this system is '%s'\n", eid_str);
2806         return CLI_SUCCESS;
2807 }
2808
2809 static char *dundi_show_requests(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2810 {
2811 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
2812 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
2813         struct dundi_request *req;
2814         char eidstr[20];
2815         switch (cmd) {
2816         case CLI_INIT:
2817                 e->command = "dundi show requests";
2818                 e->usage =
2819                         "Usage: dundi show requests\n"
2820                         "       Lists all known pending DUNDi requests.\n";
2821                 return NULL;
2822         case CLI_GENERATE:
2823                 return NULL;
2824         }
2825         if (a->argc != 3) {
2826                 return CLI_SHOWUSAGE;
2827         }
2828         AST_LIST_LOCK(&peers);
2829         ast_cli(a->fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
2830         AST_LIST_TRAVERSE(&requests, req, list) {
2831                 ast_cli(a->fd, FORMAT, req->number, req->dcontext,
2832                         dundi_eid_zero(&req->root_eid) ? "<unspecified>" : ast_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
2833         }
2834         AST_LIST_UNLOCK(&peers);
2835         return CLI_SUCCESS;
2836 #undef FORMAT
2837 #undef FORMAT2
2838 }
2839
2840 /* Grok-a-dial DUNDi */
2841
2842 static char *dundi_show_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2843 {
2844 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2845 #define FORMAT "%-12.12s %-7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
2846         struct dundi_mapping *map;
2847         char fs[256];
2848         char weight[8];
2849         switch (cmd) {
2850         case CLI_INIT:
2851                 e->command = "dundi show mappings";
2852                 e->usage =
2853                         "Usage: dundi show mappings\n"
2854                         "       Lists all known DUNDi mappings.\n";
2855                 return NULL;
2856         case CLI_GENERATE:
2857                 return NULL;
2858         }
2859         if (a->argc != 3) {
2860                 return CLI_SHOWUSAGE;
2861         }
2862         AST_LIST_LOCK(&peers);
2863         ast_cli(a->fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
2864         AST_LIST_TRAVERSE(&mappings, map, list) {
2865                 snprintf(weight, sizeof(weight), "%d", get_mapping_weight(map, NULL));
2866                 ast_cli(a->fd, FORMAT, map->dcontext, weight,
2867                         ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
2868                         dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
2869         }
2870         AST_LIST_UNLOCK(&peers);
2871         return CLI_SUCCESS;
2872 #undef FORMAT
2873 #undef FORMAT2
2874 }
2875
2876 static char *dundi_show_precache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2877 {
2878 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
2879 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
2880         struct dundi_precache_queue *qe;
2881         int h,m,s;
2882         time_t now;
2883         switch (cmd) {
2884         case CLI_INIT:
2885                 e->command = "dundi show precache";
2886                 e->usage =
2887                         "Usage: dundi show precache\n"
2888                         "       Lists all known DUNDi scheduled precache updates.\n";
2889                 return NULL;
2890         case CLI_GENERATE:
2891                 return NULL;
2892         }
2893         if (a->argc != 3) {
2894                 return CLI_SHOWUSAGE;
2895         }
2896         time(&now);
2897         ast_cli(a->fd, FORMAT2, "Number", "Context", "Expiration");
2898         AST_LIST_LOCK(&pcq);
2899         AST_LIST_TRAVERSE(&pcq, qe, list) {
2900                 s = qe->expiration - now;
2901                 h = s / 3600;
2902                 s = s % 3600;
2903                 m = s / 60;
2904                 s = s % 60;
2905                 ast_cli(a->fd, FORMAT, qe->number, qe->context, h,m,s);
2906         }
2907         AST_LIST_UNLOCK(&pcq);
2908
2909         return CLI_SUCCESS;
2910 #undef FORMAT
2911 #undef FORMAT2
2912 }
2913
2914 static char *dundi_show_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2915 {
2916 #define FORMAT2 "%-12.12s %-16.16s %-10.10s  %-18s %-7s %s\n"
2917 #define FORMAT "%-12.12s %-16.16s %6d sec  %-18s %-7d %s/%s (%s)\n"
2918         struct ast_db_entry *db_tree, *db_entry;
2919         int cnt = 0;
2920         time_t ts, now;
2921         dundi_eid src_eid;
2922         char src_eid_str[20];
2923         int expiry, tech, weight;
2924         struct ast_flags flags;
2925         char fs[256];
2926         int length;
2927         char *ptr, *term, *src, *number, *context, *dst;
2928
2929         switch (cmd) {
2930         case CLI_INIT:
2931                 e->command = "dundi show cache";
2932                 e->usage =
2933                 "Usage: dundi show cache\n"
2934                 "       Lists all DUNDi cache entries.\n";
2935                 return NULL;
2936         case CLI_GENERATE:
2937                 return NULL;
2938         }
2939
2940         if (a->argc != 3) {
2941                 return CLI_SHOWUSAGE;
2942         }
2943
2944         time(&now);
2945         db_tree = ast_db_gettree("dundi/cache", NULL);
2946         ast_cli(a->fd, FORMAT2, "Number", "Context", "Expiration", "From", "Weight", "Destination (Flags)");
2947         for (db_entry = db_tree; db_entry; db_entry = db_entry->next) {
2948                 if ((strncmp(db_entry->key, "/dundi/cache/hint/", 18) == 0) || ast_get_time_t(db_entry->data, &ts, 0, &length)) {
2949                         continue;
2950                 }
2951
2952                 expiry = ts - now;
2953
2954                 if (expiry <= 0) {
2955                         continue;
2956                 }
2957
2958                 ptr = db_entry->key + sizeof("/dundi/cache");
2959                 strtok(ptr, "/");
2960                 number = strtok(NULL, "/");
2961                 context = strtok(NULL, "/");
2962                 ptr = strtok(NULL, "/");
2963
2964                 if (*ptr != 'e') {
2965                         continue;
2966                 }
2967
2968                 ptr = db_entry->data + length + 1;
2969
2970                 if ((sscanf(ptr, "%30u/%30d/%30d/%n", &(flags.flags), &weight, &tech, &length) != 3)) {
2971                         continue;
2972                 }
2973
2974                 ptr += length;
2975                 dst = ptr;
2976                 term = strchr(ptr, '|');
2977
2978                 if (!term) {
2979                         continue;
2980                 }
2981
2982                 /* Ok, at this point we know we aren't going to skp the entry, so we go ahead and increment the count. */
2983                 cnt++;
2984
2985                 *term = '\0';
2986                 src = strrchr(ptr, '/');
2987                 dundi_eid_zero(&src_eid);
2988
2989                 if (src) {
2990                         *src = '\0';
2991                         src++;
2992                         dundi_str_short_to_eid(&src_eid, src);
2993                         ast_eid_to_str(src_eid_str, sizeof(src_eid_str), &src_eid);
2994                 }
2995
2996                 ast_cli(a->fd, FORMAT, number, context, expiry, src_eid_str, weight, tech2str(tech), dst, dundi_flags2str(fs, sizeof(fs), flags.flags));
2997         }
2998
2999         ast_cli(a->fd, "Number of entries: %d\n", cnt);
3000         ast_db_freetree(db_tree);
3001
3002         return CLI_SUCCESS;
3003 #undef FORMAT
3004 #undef FORMAT2
3005 }
3006
3007 static char *dundi_show_hints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3008 {
3009 #define FORMAT2 "%-12.12s %-16.16s %-10.10s  %-18s\n"
3010 #define FORMAT "%-12.12s %-16.16s %6d sec  %-18s\n"
3011         struct ast_db_entry *db_tree, *db_entry;
3012         int cnt = 0;
3013         time_t ts, now;
3014         dundi_eid src_eid;
3015         char src_eid_str[20];
3016         int expiry;
3017         int length;
3018         char *ptr, *src, *number, *context;
3019
3020         switch (cmd) {
3021         case CLI_INIT:
3022                 e->command = "dundi show hints";
3023                 e->usage =
3024                         "Usage: dundi show hints\n"
3025                         "       Lists all DUNDi 'DONTASK' hints in the cache.\n";
3026                 return NULL;
3027         case CLI_GENERATE:
3028                 return NULL;
3029         }
3030
3031         if (a->argc != 3) {
3032                 return CLI_SHOWUSAGE;
3033         }
3034
3035         time(&now);
3036         db_tree = ast_db_gettree("dundi/cache/hint", NULL);
3037         ast_cli(a->fd, FORMAT2, "Prefix", "Context", "Expiration", "From");
3038
3039         for (db_entry = db_tree; db_entry; db_entry = db_entry->next) {
3040                 if (ast_get_time_t(db_entry->data, &ts, 0, &length)) {
3041                         continue;
3042                 }
3043
3044                 expiry = ts - now;
3045
3046                 if (expiry <= 0) {
3047                         continue;
3048                 }
3049
3050                 ptr = db_entry->key + sizeof("/dundi/cache/hint");
3051                 src = strtok(ptr, "/");
3052                 number = strtok(NULL, "/");
3053                 context = strtok(NULL, "/");
3054                 ptr = strtok(NULL, "/");
3055
3056                 if (*ptr != 'e') {
3057                         continue;
3058                 }
3059
3060                 cnt++;
3061                 dundi_str_short_to_eid(&src_eid, src);
3062                 ast_eid_to_str(src_eid_str, sizeof(src_eid_str), &src_eid);
3063                 ast_cli(a->fd, FORMAT, number, context, expiry, src_eid_str);
3064         }
3065
3066         ast_cli(a->fd, "Number of entries: %d\n", cnt);
3067         ast_db_freetree(db_tree);
3068
3069         return CLI_SUCCESS;
3070 #undef FORMAT
3071 #undef FORMAT2
3072 }
3073
3074 static struct ast_cli_entry cli_dundi[] = {
3075         AST_CLI_DEFINE(dundi_set_debug, "Enable/Disable DUNDi debugging"),
3076         AST_CLI_DEFINE(dundi_store_history, "Enable/Disable DUNDi historic records"),
3077         AST_CLI_DEFINE(dundi_flush, "Flush DUNDi cache"),
3078         AST_CLI_DEFINE(dundi_show_peers, "Show defined DUNDi peers"),
3079         AST_CLI_DEFINE(dundi_show_trans, "Show active DUNDi transactions"),
3080         AST_CLI_DEFINE(dundi_show_entityid, "Display Global Entity ID"),
3081         AST_CLI_DEFINE(dundi_show_mappings, "Show DUNDi mappings"),
3082         AST_CLI_DEFINE(dundi_show_precache, "Show DUNDi precache"),
3083         AST_CLI_DEFINE(dundi_show_requests, "Show DUNDi requests"),
3084         AST_CLI_DEFINE(dundi_show_peer, "Show info on a specific DUNDi peer"),
3085         AST_CLI_DEFINE(dundi_show_cache, "Show DUNDi cache"),
3086         AST_CLI_DEFINE(dundi_show_hints, "Show DUNDi hints in the cache"),
3087         AST_CLI_DEFINE(dundi_do_precache, "Precache a number in DUNDi"),
3088         AST_CLI_DEFINE(dundi_do_lookup, "Lookup a number in DUNDi"),
3089         AST_CLI_DEFINE(dundi_do_query, "Query a DUNDi EID"),
3090 };
3091
3092 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
3093 {
3094         struct dundi_transaction *trans;
3095         int tid;
3096
3097         /* Don't allow creation of transactions to non-registered peers */
3098         if (p && !p->addr.sin_addr.s_addr)
3099                 return NULL;
3100         tid = get_trans_id();
3101         if (tid < 1)
3102                 return NULL;
3103         if (!(trans = ast_calloc(1, sizeof(*trans))))
3104                 return NULL;
3105
3106         if (global_storehistory) {
3107                 trans->start = ast_tvnow();
3108                 ast_set_flag(trans, FLAG_STOREHIST);
3109         }
3110         trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
3111         trans->autokillid = -1;
3112         if (p) {
3113                 apply_peer(trans, p);
3114                 if (!p->sentfullkey)
3115                         ast_set_flag(trans, FLAG_SENDFULLKEY);
3116         }
3117         trans->strans = tid;
3118         AST_LIST_INSERT_HEAD(&alltrans, trans, all);
3119
3120         return trans;
3121 }
3122
3123 static int dundi_xmit(struct dundi_packet *pack)
3124 {
3125         int res;
3126         if (dundidebug)
3127                 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
3128         res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
3129         if (res < 0) {
3130                 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
3131                         ast_inet_ntoa(pack->parent->addr.sin_addr),
3132                         ntohs(pack->parent->addr.sin_port), strerror(errno));
3133         }
3134         if (res > 0)
3135                 res = 0;
3136         return res;
3137 }
3138
3139 static void destroy_packet(struct dundi_packet *pack, int needfree)
3140 {
3141         if (pack->parent)
3142                 AST_LIST_REMOVE(&pack->parent->packets, pack, list);
3143         AST_SCHED_DEL(sched, pack->retransid);
3144         if (needfree)
3145                 ast_free(pack);
3146 }
3147
3148 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
3149 {
3150         struct dundi_peer *peer;
3151         int ms;
3152         int x;
3153         int cnt;
3154         char eid_str[20];
3155         if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
3156                 AST_LIST_TRAVERSE(&peers, peer, list) {
3157                         if (peer->regtrans == trans)
3158                                 peer->regtrans = NULL;
3159                         if (peer->qualtrans == trans) {
3160                                 if (fromtimeout) {
3161                                         if (peer->lastms > -1)
3162                                                 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
3163                                         peer->lastms = -1;
3164                                 } else {
3165                                         ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
3166                                         if (ms < 1)
3167                                                 ms = 1;
3168                                         if (ms < peer->maxms) {
3169                                                 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
3170                                                         ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
3171                                         } else if (peer->lastms < peer->maxms) {
3172                                                 ast_log(LOG_NOTICE, "Peer '%s' has become TOO LAGGED (%d ms)\n", ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), ms);
3173                                         }
3174                                         peer->lastms = ms;
3175                                 }
3176                                 peer->qualtrans = NULL;
3177                         }
3178                         if (ast_test_flag(trans, FLAG_STOREHIST)) {
3179                                 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
3180                                         if (!ast_eid_cmp(&trans->them_eid, &peer->eid)) {
3181                                                 peer->avgms = 0;
3182                                                 cnt = 0;
3183                                                 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
3184                                                         ast_free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
3185                                                 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
3186                                                         peer->lookuptimes[x] = peer->lookuptimes[x-1];
3187                                                         peer->lookups[x] = peer->lookups[x-1];
3188                                                         if (peer->lookups[x]) {
3189                                                                 peer->avgms += peer->lookuptimes[x];
3190                                                                 cnt++;
3191                                                         }
3192                                                 }
3193                                                 peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
3194                                                 peer->lookups[0] = ast_malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
3195                                                 if (peer->lookups[0]) {
3196                                                         sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
3197                                                         peer->avgms += peer->lookuptimes[0];
3198                                                         cnt++;
3199                                                 }
3200                                                 if (cnt)
3201                                                         peer->avgms /= cnt;
3202                                         }
3203                                 }
3204                         }
3205                 }
3206         }
3207         if (trans->parent) {
3208                 /* Unlink from parent if appropriate */
3209                 AST_LIST_REMOVE(&trans->parent->trans, trans, parentlist);
3210                 if (AST_LIST_EMPTY(&trans->parent->trans)) {
3211                         /* Wake up sleeper */
3212                         if (trans->parent->pfds[1] > -1) {
3213                                 if (write(trans->parent->pfds[1], "killa!", 6) < 0) {
3214                                         ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
3215                                 }
3216                         }
3217                 }
3218         }
3219         /* Unlink from all trans */
3220         AST_LIST_REMOVE(&alltrans, trans, all);
3221         destroy_packets(&trans->packets);
3222         destroy_packets(&trans->lasttrans);
3223         AST_SCHED_DEL(sched, trans->autokillid);
3224         if (trans->thread) {
3225                 /* If used by a thread, mark as dead and be done */
3226                 ast_set_flag(trans, FLAG_DEAD);
3227         } else
3228                 ast_free(trans);
3229 }
3230
3231 static int dundi_rexmit(const void *data)
3232 {
3233         struct dundi_packet *pack = (struct dundi_packet *)data;
3234         int res;
3235         AST_LIST_LOCK(&peers);
3236         if (pack->retrans < 1) {
3237                 pack->retransid = -1;
3238                 if (!ast_test_flag(pack->parent, FLAG_ISQUAL))
3239                         ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n",
3240                                 ast_inet_ntoa(pack->parent->addr.sin_addr),
3241                                 ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
3242                 destroy_trans(pack->parent, 1);
3243                 res = 0;
3244         } else {
3245                 /* Decrement retransmission, try again */
3246                 pack->retrans--;
3247                 dundi_xmit(pack);
3248                 res = 1;
3249         }
3250         AST_LIST_UNLOCK(&peers);
3251         return res;
3252 }
3253
3254 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
3255 {
3256         struct dundi_packet *pack;
3257         int res;
3258         int len;
3259         char eid_str[20];
3260         len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
3261         /* Reserve enough space for encryption */
3262         if (ast_test_flag(trans, FLAG_ENCRYPT))
3263                 len += 384;
3264         pack = ast_calloc(1, len);
3265         if (pack) {
3266                 pack->h = (struct dundi_hdr *)(pack->data);
3267                 pack->retransid = -1;
3268                 if (cmdresp != DUNDI_COMMAND_ACK) {
3269                         pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
3270                         pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
3271                         AST_LIST_INSERT_HEAD(&trans->packets, pack, list);
3272                 }
3273                 pack->parent = trans;
3274                 pack->h->strans = htons(trans->strans);
3275                 pack->h->dtrans = htons(trans->dtrans);
3276                 pack->h->iseqno = trans->iseqno;
3277                 pack->h->oseqno = trans->oseqno;
3278                 pack->h->cmdresp = cmdresp;
3279                 pack->datalen = sizeof(struct dundi_hdr);
3280                 if (ied) {
3281                         memcpy(pack->h->ies, ied->buf, ied->pos);
3282                         pack->datalen += ied->pos;
3283                 }
3284                 if (final) {
3285                         pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
3286                         ast_set_flag(trans, FLAG_FINAL);
3287                 }
3288                 pack->h->cmdflags = flags;
3289                 if (cmdresp != DUNDI_COMMAND_ACK) {
3290                         trans->oseqno++;
3291                         trans->oseqno = trans->oseqno % 256;
3292                 }
3293                 trans->aseqno = trans->iseqno;
3294                 /* If we have their public key, encrypt */
3295                 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
3296                         switch(cmdresp) {
3297                         case DUNDI_COMMAND_REGREQ:
3298                         case DUNDI_COMMAND_REGRESPONSE:
3299                         case DUNDI_COMMAND_DPDISCOVER:
3300                         case DUNDI_COMMAND_DPRESPONSE:
3301                         case DUNDI_COMMAND_EIDQUERY:
3302                         case DUNDI_COMMAND_EIDRESPONSE:
3303                         case DUNDI_COMMAND_PRECACHERQ:
3304                         case DUNDI_COMMAND_PRECACHERP:
3305                                 if (dundidebug)
3306                                         dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
3307                                 res = dundi_encrypt(trans, pack);
3308                                 break;
3309                         default:
3310                                 res = 0;
3311                         }
3312                 } else
3313                         res = 0;
3314                 if (!res)
3315                         res = dundi_xmit(pack);
3316                 if (res)
3317                         ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", ast_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3318
3319                 if (cmdresp == DUNDI_COMMAND_ACK)
3320                         ast_free(pack);
3321                 return res;
3322         }
3323         return -1;
3324 }
3325
3326 static int do_autokill(const void *data)
3327 {
3328         struct dundi_transaction *trans = (struct dundi_transaction *)data;
3329         char eid_str[20];
3330         ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n",
3331                 ast_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
3332         trans->autokillid = -1;
3333         destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
3334         return 0;
3335 }
3336
3337 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
3338 {
3339         struct dundi_peer *p;
3340         if (!ast_eid_cmp(eid, us)) {
3341                 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3342                 return;
3343         }
3344         AST_LIST_LOCK(&peers);
3345         AST_LIST_TRAVERSE(&peers, p, list) {
3346                 if (!ast_eid_cmp(&p->eid, eid)) {
3347                         if (has_permission(&p->include, context))
3348                                 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
3349                         else
3350                                 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3351                         break;
3352                 }
3353         }
3354         if (!p)
3355                 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
3356         AST_LIST_UNLOCK(&peers);
3357 }
3358
3359 static int dundi_discover(struct dundi_transaction *trans)
3360 {
3361         struct dundi_ie_data ied;
3362         int x;
3363         if (!trans->parent) {
3364                 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
3365                 return -1;
3366         }
3367         memset(&ied, 0, sizeof(ied));
3368         dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
3369         if (!dundi_eid_zero(&trans->us_eid))<