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