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