backport asterisk changes from whiterussian [2300]
[openwrt.git] / package / asterisk / patches / asterisk-1.0.7-pbx_dundi.patch
1 diff -ruN asterisk-1.0.7-orig/channels/chan_iax2.c asterisk-1.0.7-pbx_dundi/channels/chan_iax2.c
2 --- asterisk-1.0.7-orig/channels/chan_iax2.c    2005-03-18 18:30:05.000000000 +0100
3 +++ asterisk-1.0.7-pbx_dundi/channels/chan_iax2.c       2005-06-02 20:21:37.000000000 +0200
4 @@ -197,6 +197,7 @@
5  struct iax2_user {
6         char name[80];
7         char secret[80];
8 +       char dbsecret[80];
9         int authmethods;
10         char accountcode[20];
11         char inkeys[80];                                /* Key(s) this user can use to authenticate to us */
12 @@ -219,6 +220,7 @@
13         char name[80];
14         char username[80];              
15         char secret[80];
16 +       char dbsecret[80];
17         char outkey[80];                /* What key we use to talk to this peer */
18         char context[AST_MAX_EXTENSION];        /* Default context (for transfer really) */
19         char regexten[AST_MAX_EXTENSION];       /* Extension to register (if regcontext is used) */
20 @@ -2194,6 +2196,26 @@
21                                 *notransfer=p->notransfer;
22                         if (usejitterbuf)
23                                 *usejitterbuf=p->usejitterbuf;
24 +                       if (secret) {
25 +                               if (!ast_strlen_zero(p->dbsecret)) {
26 +                                       char *family, *key=NULL;
27 +                                       family = ast_strdupa(p->dbsecret);
28 +                                       if (family) {
29 +                                               key = strchr(family, '/');
30 +                                               if (key) {
31 +                                                       *key = '\0';
32 +                                                       key++;
33 +                                               }
34 +                                       }
35 +                                       if (!family || !key || ast_db_get(family, key, secret, seclen)) {
36 +                                               ast_log(LOG_WARNING, "Unable to retrieve database password for family/key '%s'!\n", p->dbsecret);
37 +                                               if (p->temponly)
38 +                                                       free(p);
39 +                                               p = NULL;
40 +                                       }
41 +                               } else
42 +                                       strncpy(secret, p->secret, seclen); /* safe */
43 +                       }
44                 } else {
45                         if (p->temponly)
46                                 free(p);
47 @@ -3624,6 +3646,15 @@
48         return 0;
49  }
50  
51 +static void free_context(struct iax2_context *con)
52 +{
53 +       struct iax2_context *conl;
54 +       while(con) {
55 +               conl = con;
56 +               con = con->next;
57 +               free(conl);
58 +       }
59 +}
60  
61  static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies)
62  {
63 @@ -3769,6 +3800,28 @@
64                         strncpy(iaxs[callno]->language, user->language, sizeof(iaxs[callno]->language)-1);
65                 iaxs[callno]->notransfer = user->notransfer;
66                 iaxs[callno]->usejitterbuf = user->usejitterbuf;
67 +               /* Keep this check last */
68 +               if (!ast_strlen_zero(user->dbsecret)) {
69 +                       char *family, *key=NULL;
70 +                       family = ast_strdupa(user->dbsecret);
71 +                       if (family) {
72 +                               key = strchr(family, '/');
73 +                               if (key) {
74 +                                       *key = '\0';
75 +                                       key++;
76 +                               }
77 +                       }
78 +                       if (!family || !key || ast_db_get(family, key, iaxs[callno]->secret, sizeof(iaxs[callno]->secret))) {
79 +                               ast_log(LOG_WARNING, "Unable to retrieve database password for family/key '%s'!\n", user->dbsecret);
80 +                               if (user->temponly) {
81 +                                       ast_free_ha(user->ha);
82 +                                       free_context(user->contexts);
83 +                                       free(user);
84 +                                       user = NULL;
85 +                               }
86 +                       }
87 +               } else
88 +                       strncpy(iaxs[callno]->secret, user->secret, sizeof(iaxs[callno]->secret) - 1);
89                 res = 0;
90         }
91         iaxs[callno]->trunk = iax2_getpeertrunk(*sin);
92 @@ -3844,15 +3897,23 @@
93         } else if (p->authmethods & IAX_AUTH_MD5) {
94                 struct MD5Context md5;
95                 unsigned char digest[16];
96 -               MD5Init(&md5);
97 -               MD5Update(&md5, p->challenge, strlen(p->challenge));
98 -               MD5Update(&md5, p->secret, strlen(p->secret));
99 -               MD5Final(digest, &md5);
100 -               /* If they support md5, authenticate with it.  */
101 -               for (x=0;x<16;x++)
102 -                       sprintf(requeststr + (x << 1), "%2.2x", digest[x]); /* safe */
103 -               if (!strcasecmp(requeststr, md5secret))
104 -                       res = 0;
105 +               char *tmppw, *stringp;
106 +               
107 +               tmppw = ast_strdupa(p->secret);
108 +               stringp = tmppw;
109 +               while((tmppw = strsep(&stringp, ";"))) {
110 +                       MD5Init(&md5);
111 +                       MD5Update(&md5, p->challenge, strlen(p->challenge));
112 +                       MD5Update(&md5, tmppw, strlen(tmppw));
113 +                       MD5Final(digest, &md5);
114 +                       /* If they support md5, authenticate with it.  */
115 +                       for (x=0;x<16;x++)
116 +                               sprintf(requeststr + (x << 1), "%2.2x", digest[x]); /* safe */
117 +                       if (!strcasecmp(requeststr, md5secret)) {
118 +                               res = 0;
119 +                               break;
120 +                       }
121 +               }
122         } else if (p->authmethods & IAX_AUTH_PLAINTEXT) {
123                 if (!strcmp(secret, p->secret))
124                         res = 0;
125 @@ -6237,16 +6298,6 @@
126         return 0;
127  }
128  
129 -static void free_context(struct iax2_context *con)
130 -{
131 -       struct iax2_context *conl;
132 -       while(con) {
133 -               conl = con;
134 -               con = con->next;
135 -               free(conl);
136 -       }
137 -}
138 -
139  static struct ast_channel *iax2_request(char *type, int format, void *data)
140  {
141         int callno;
142 @@ -6469,6 +6520,8 @@
143                                 strncpy(peer->secret, v->value, sizeof(peer->secret)-1);
144                         else if (!strcasecmp(v->name, "mailbox"))
145                                 strncpy(peer->mailbox, v->value, sizeof(peer->mailbox) - 1);
146 +                       else if (!strcasecmp(v->name, "dbsecret"))
147 +                               strncpy(peer->dbsecret, v->value, sizeof(peer->dbsecret)-1);
148                         else if (!strcasecmp(v->name, "mailboxdetail"))
149                                 peer->messagedetail = ast_true(v->value);
150                         else if (!strcasecmp(v->name, "trunk")) {
151 @@ -6665,6 +6718,8 @@
152                                 user->notransfer = ast_true(v->value);
153                         } else if (!strcasecmp(v->name, "jitterbuffer")) {
154                                 user->usejitterbuf = ast_true(v->value);
155 +                       } else if (!strcasecmp(v->name, "dbsecret")) {
156 +                               strncpy(user->dbsecret, v->value, sizeof(user->dbsecret)-1);
157                         } else if (!strcasecmp(v->name, "secret")) {
158                                 strncpy(user->secret, v->value, sizeof(user->secret)-1);
159                         } else if (!strcasecmp(v->name, "callerid")) {
160 diff -ruN asterisk-1.0.7-orig/configs/dundi.conf.sample asterisk-1.0.7-pbx_dundi/configs/dundi.conf.sample
161 --- asterisk-1.0.7-orig/configs/dundi.conf.sample       1970-01-01 01:00:00.000000000 +0100
162 +++ asterisk-1.0.7-pbx_dundi/configs/dundi.conf.sample  2005-06-02 20:21:37.000000000 +0200
163 @@ -0,0 +1,225 @@
164 +;
165 +; DUNDi configuration file
166 +;
167 +;
168 +[general]
169 +;
170 +; The "general" section contains general parameters relating
171 +; to the operation of the dundi client and server.
172 +;
173 +; The first part should be your complete contact information
174 +; should someone else in your peer group need to contact you.
175 +;
176 +;department=Your Department
177 +;organization=Your Company, Inc.
178 +;locality=Your City
179 +;stateprov=ST
180 +;country=US
181 +;email=your@email.com
182 +;phone=+12565551212
183 +;
184 +;
185 +; Specify bind address and port number.  Default is
186 +; 4520
187 +;
188 +;bindaddr=0.0.0.0
189 +;port=4520
190 +;
191 +; Our entity identifier (Should generally be the MAC address of the
192 +; machine it's running on.  Defaults to the first eth address, but you
193 +; can override it here, as long as you set it to the MAC of *something*
194 +; you own!)
195 +;
196 +;entityid=00:07:E9:3B:76:60
197 +;
198 +; Define the max depth in which to search the DUNDi system (also max # of 
199 +; seconds to wait for a reply)
200 +;
201 +ttl=32
202 +;
203 +; If we don't get ACK to our DPREQUEST within 2000ms, and autokill is set
204 +; to yes, then we cancel the whole thing (that's enough time for one 
205 +; retransmission only).  This is used to keep things from stalling for a long
206 +; time for a host that is not available, but would be ill advised for bad 
207 +; connections.  In addition to 'yes' or 'no' you can also specify a number
208 +; of milliseconds.  See 'qualify' for individual peers to turn on for just
209 +; a specific peer.
210 +;
211 +autokill=yes
212 +;
213 +; pbx_dundi creates a rotating key called "secret", under the family
214 +; 'secretpath'.  The default family is dundi (resulting in 
215 +; the key being held at dundi/secret).
216 +;
217 +;secretpath=dundi
218 +;
219 +; The 'storehistory' option (also changeable at runtime with
220 +; 'dundi store history' and 'dundi no store history') will
221 +; cause the DUNDi engine to keep track of the last several
222 +; queries and the amount of time each query took to execute
223 +; for the purpose of tracking slow nodes.  This option is
224 +; off by default due to performance impacts.
225 +;
226 +;storehistory=yes
227 +
228 +[mappings]
229 +;
230 +; The "mappings" section maps DUNDi contexts
231 +; to contexts on the local asterisk system.  Remember
232 +; that numbers that are made available under the e164 
233 +; DUNDi context are regulated by the DUNDi General Peering 
234 +; Agreement (GPA) if you are a member of the DUNDi E.164
235 +; Peering System.
236 +;
237 +; dundi_context => local_context,weight,tech,dest[,options]]
238 +;
239 +; dundi_context is the name of the context being requested
240 +; within the DUNDi request
241 +;
242 +; local_context is the name of the context on the local system
243 +; in which numbers can be looked up for which responses shall be given.
244 +;
245 +; tech is the technology to use (IAX, SIP, H323)
246 +;
247 +; dest is the destination to supply for reaching that number.  The
248 +; following variables can be used in the destination string and will
249 +; be automatically substituted:
250 +; ${NUMBER}: The number being requested
251 +; ${IPADDR}: The IP address to connect to
252 +; ${SECRET}: The current rotating secret key to be used
253 +;
254 +; Further options may include:
255 +;
256 +; nounsolicited:  No unsolicited calls of any type permitted via this 
257 +;                 route
258 +; nocomunsolicit: No commercial unsolicited calls permitted via 
259 +;                 this route
260 +; residential:    This number is known to be a residence
261 +; commercial:     This number is known to be a business
262 +; mobile:         This number is known to be a mobile phone
263 +; nocomunsolicit: No commercial unsolicited calls permitted via 
264 +;                 this route
265 +; nopartial:      Do not search for partial matches
266 +;
267 +; There *must* exist an entry in mappings for DUNDi to respond
268 +; to any request, although it may be empty.
269 +;
270 +;e164 => dundi-e164-canonical,0,IAX2,dundi:${SECRET}@${IPADDR}/${NUMBER},nounsolicited,nocomunsolicit,nopartial
271 +;e164 => dundi-e164-customers,100,IAX2,dundi:${SECRET}@${IPADDR}/${NUMBER},nounsolicited,nocomunsolicit,nopartial
272 +;e164 => dundi-e164-via-pstn,400,IAX2,dundi:${SECRET}@${IPADDR}/${NUMBER},nounsolicited,nocomunsolicit,nopartial
273 +
274 +;digexten => default,0,IAX2,guest@lappy/${NUMBER}
275 +;asdf =>
276 +
277 +
278 +;
279 +;
280 +; The remaining sections represent the peers
281 +; that we fundamentally trust.  The section name
282 +; represents the name and optionally at a specific
283 +; DUNDi context if you want the trust to be established
284 +; for only a specific DUNDi context.
285 +;
286 +; inkey - What key they will be authenticating to us with
287 +;
288 +; outkey - What key we use to authenticate to them
289 +;
290 +; host - What their host is
291 +;
292 +; order - What search order to use.  May be 'primary', 'secondary', 
293 +;         'tertiary' or 'quartiary'.  In large systems, it is beneficial
294 +;         to only query one up-stream host in order to maximize caching
295 +;         value.  Adding one with primary and one with secondary gives you
296 +;         redundancy without sacraficing performance.
297 +;
298 +; include - Includes this peer when searching a particular context
299 +;           for lookup (set "all" to perform all lookups with that
300 +;           host.  This is also the context in which peers are permitted
301 +;           to precache.
302 +;
303 +; noinclude - Disincludes this peer when searching a particular context
304 +;             for lookup (set "all" to perform no lookups with that
305 +;             host.
306 +;
307 +; permit - Permits this peer to search a given DUNDi context on
308 +;          the local system.  Set "all" to permit this host to
309 +;          lookup all contexts.  This is also a context for which
310 +;          we will create/forward PRECACHE commands.
311 +;
312 +; deny -   Denies this peer to search a given DUNDi context on
313 +;          the local system.  Set "all" to deny this host to
314 +;          lookup all contexts.
315 +;
316 +; model - inbound, outbound, or symmetric for whether we receive 
317 +;         requests only, transmit requests only, or do both.
318 +;
319 +; precache - Utilize/Permit precaching with this peer (to pre
320 +;            cache means to provide an answer when no request
321 +;            was made and is used so that machines with few
322 +;            routes can push those routes up a to a higher level).
323 +;            outgoing means we send precache routes to this peer,
324 +;            incoming means we permit this peer to send us
325 +;            precache routes.  symmetric means we do both.
326 +;
327 +; Note: You cannot mix symmetric/outbound model with symmetric/inbound
328 +; precache, nor can you mix symmetric/inbound model with symmetric/outbound
329 +; precache.
330 +;
331 +;
332 +; The '*' peer is special and matches an unspecified entity
333 +;
334 +
335 +;
336 +; Sample Primary e164 DUNDi peer
337 +;
338 +[00:50:8B:F3:75:BB]
339 +model = symmetric
340 +host = 64.215.96.114
341 +inkey = digium
342 +outkey = misery
343 +include = e164
344 +permit = e164
345 +qualify = yes
346 +
347 +;
348 +; Sample Secondary e164 DUNDi peer
349 +;
350 +;[00:A0:C9:96:92:84]
351 +;model = symmetric
352 +;host = misery.digium.com
353 +;inkey = misery
354 +;outkey = ourkey
355 +;include = e164
356 +;permit = e164
357 +;qualify = yes
358 +;order = secondary
359 +
360 +;
361 +; Sample "push mode" downstream host
362 +;
363 +;[00:0C:76:96:75:28]
364 +;model = incoming
365 +;host = dynamic
366 +;precache = incoming
367 +;inkey = littleguy
368 +;outkey = ourkey
369 +;include = e164        ; In this case used only for precaching
370 +;permit = e164      
371 +;qualify = yes
372 +
373 +;
374 +; Sample "push mode" upstream host
375 +;
376 +;[00:07:E9:3B:76:60]
377 +;model = outbound
378 +;precache = outbound
379 +;host = 216.207.245.34
380 +;register = yes
381 +;inkey = dhcp34
382 +;permit = all ; In this case used only for precaching
383 +;include = all 
384 +;qualify = yes
385 +;outkey=foo
386 +
387 +;[*]
388 +;
389 diff -ruN asterisk-1.0.7-orig/include/asterisk/dundi.h asterisk-1.0.7-pbx_dundi/include/asterisk/dundi.h
390 --- asterisk-1.0.7-orig/include/asterisk/dundi.h        1970-01-01 01:00:00.000000000 +0100
391 +++ asterisk-1.0.7-pbx_dundi/include/asterisk/dundi.h   2005-06-02 20:21:37.000000000 +0200
392 @@ -0,0 +1,212 @@
393 +/*
394 + * Distributed Universal Number Discovery (DUNDi)
395 + *
396 + * Copyright (C) 2004, Digium Inc.
397 + *
398 + * Written by Mark Spencer <markster@digium.com>
399 + *
400 + * This program is Free Software distributed under the terms of
401 + * of the GNU General Public License.
402 + */
403 +#ifndef _ASTERISK_DUNDI_H 
404 +#define _ASTERISK_DUNDI_H
405 +
406 +#include <asterisk/channel.h>
407 +
408 +#define DUNDI_PORT 4520
409 +
410 +/* A DUNDi Entity ID is essentially a MAC address, brief and unique */
411 +struct _dundi_eid {
412 +       unsigned char eid[6];
413 +} __attribute__ ((__packed__));
414 +
415 +typedef struct _dundi_eid dundi_eid;
416 +
417 +struct dundi_hdr {
418 +       unsigned short strans;                  /* Source transaction */
419 +       unsigned short dtrans;                  /* Destination transaction */
420 +       unsigned char iseqno;                   /* Next expected incoming sequence number */
421 +       unsigned char oseqno;                   /* Outgoing sequence number */
422 +       unsigned char cmdresp;                  /* Command / Response */
423 +       unsigned char cmdflags;                 /* Command / Response specific flags*/
424 +       unsigned char ies[0];
425 +} __attribute__ ((__packed__));
426 +
427 +struct dundi_ie_hdr {
428 +       unsigned char ie;
429 +       unsigned char len;
430 +       unsigned char iedata[0];
431 +} __attribute__ ((__packed__));
432 +
433 +#define DUNDI_FLAG_RETRANS                     (1 << 16)       /* Applies to dtrans */
434 +#define DUNDI_FLAG_RESERVED                    (1 << 16)       /* Applies to strans */
435 +
436 +#define DUNDI_PROTO_NONE                       0       /* No answer yet */
437 +#define DUNDI_PROTO_IAX                                1       /* IAX version 2 */
438 +#define DUNDI_PROTO_SIP                                2       /* Session Initiation Protocol */
439 +#define DUNDI_PROTO_H323                       3       /* ITU H.323 */
440 +
441 +#define DUNDI_FLAG_NONEXISTANT         (0)                             /* Isn't and can't be a valid number */
442 +#define DUNDI_FLAG_EXISTS                      (1 << 0)                /* Is a valid number */
443 +#define DUNDI_FLAG_MATCHMORE           (1 << 1)                /* Might be valid if you add more digits */
444 +#define DUNDI_FLAG_CANMATCH                    (1 << 2)                /* Might be a match */
445 +#define DUNDI_FLAG_IGNOREPAT           (1 << 3)                /* Keep dialtone */
446 +#define DUNDI_FLAG_RESIDENTIAL         (1 << 4)                /* Destination known to be residential */
447 +#define DUNDI_FLAG_COMMERCIAL          (1 << 5)                /* Destination known to be commercial */
448 +#define DUNDI_FLAG_MOBILE                      (1 << 6)                /* Destination known to be cellular/mobile */
449 +#define DUNDI_FLAG_NOUNSOLICITED       (1 << 7)                /* No unsolicited calls of any kind through this route */
450 +#define DUNDI_FLAG_NOCOMUNSOLICIT      (1 << 8)                /* No commercial unsolicited calls through this route */
451 +
452 +#define DUNDI_HINT_NONE                                (0)
453 +#define DUNDI_HINT_TTL_EXPIRED         (1 << 0)                /* TTL Expired */
454 +#define DUNDI_HINT_DONT_ASK                    (1 << 1)                /* Don't ask for anything beginning with data */
455 +#define DUNDI_HINT_UNAFFECTED          (1 << 2)                /* Answer not affected by entity list */
456 +
457 +struct dundi_encblock {                                /* AES-128 encrypted block */
458 +       unsigned char iv[16];                   /* Initialization vector of random data */
459 +       unsigned char encdata[0];               /* Encrypted / compressed data */
460 +} __attribute__ ((__packed__));
461 +
462 +struct dundi_answer {
463 +       dundi_eid eid;                                  /* Original source of answer */
464 +       unsigned char protocol;                 /* Protocol (DUNDI_PROTO_*) */
465 +       unsigned short flags;                   /* Flags relating to answer */
466 +       unsigned short weight;                  /* Weight of answers */
467 +       unsigned char data[0];                  /* Protocol specific URI */
468 +} __attribute__ ((__packed__));
469 +
470 +struct dundi_hint {
471 +       unsigned short flags;                   /* Flags relating to answer */
472 +       unsigned char data[0];                  /* For data for hint */
473 +} __attribute__ ((__packed__));
474 +
475 +#define DUNDI_CAUSE_SUCCESS                    0       /* Success */
476 +#define DUNDI_CAUSE_GENERAL                    1       /* General unspecified failure */
477 +#define DUNDI_CAUSE_DYNAMIC                    2       /* Requested entity is dynamic */
478 +#define DUNDI_CAUSE_NOAUTH                     3       /* No or improper authorization */
479 +#define DUNDI_CAUSE_DUPLICATE          4       /* Duplicate request */
480 +#define DUNDI_CAUSE_TTL_EXPIRED                5       /* Expired TTL */
481 +#define DUNDI_CAUSE_NEEDKEY                    6       /* Need new session key to decode */
482 +#define DUNDI_CAUSE_BADENCRYPT         7       /* Badly encrypted data */
483 +
484 +struct dundi_cause {                   
485 +       unsigned char causecode;                /* Numerical cause (DUNDI_CAUSE_*) */
486 +       char desc[0];                                   /* Textual description */
487 +} __attribute__ ((__packed__));
488 +
489 +struct dundi_peer_status {
490 +       unsigned int flags;
491 +       unsigned short netlag;
492 +       unsigned short querylag;
493 +       dundi_eid peereid;
494 +} __attribute__ ((__packed__));
495 +
496 +#define DUNDI_PEER_PRIMARY                     (1 << 0)
497 +#define DUNDI_PEER_SECONDARY           (1 << 1)
498 +#define DUNDI_PEER_UNAVAILABLE         (1 << 2)
499 +#define DUNDI_PEER_REGISTERED          (1 << 3)
500 +#define DUNDI_PEER_MOD_OUTBOUND                (1 << 4)
501 +#define DUNDI_PEER_MOD_INBOUND         (1 << 5)
502 +#define DUNDI_PEER_PCMOD_OUTBOUND      (1 << 6)
503 +#define DUNDI_PEER_PCMOD_INBOUND       (1 << 7)
504 +
505 +#define DUNDI_COMMAND_FINAL                    (0x80)          /* Or'd with other flags */
506 +
507 +#define DUNDI_COMMAND_ACK                      (0 | 0x40)      /* Ack a message */
508 +#define DUNDI_COMMAND_DPDISCOVER       1                       /* Request discovery */
509 +#define DUNDI_COMMAND_DPRESPONSE       (2 | 0x40)      /* Respond to a discovery request */
510 +#define DUNDI_COMMAND_EIDQUERY         3                       /* Request information for a peer */
511 +#define DUNDI_COMMAND_EIDRESPONSE      (4 | 0x40)      /* Response to a peer query */
512 +#define DUNDI_COMMAND_PRECACHERQ       5                       /* Pre-cache Request */
513 +#define DUNDI_COMMAND_PRECACHERP       (6 | 0x40)      /* Pre-cache Response */
514 +#define DUNDI_COMMAND_INVALID          (7 | 0x40)      /* Invalid dialog state (does not require ack) */
515 +#define DUNDI_COMMAND_UNKNOWN          (8 | 0x40)      /* Unknown command */
516 +#define DUNDI_COMMAND_NULL                     9                       /* No-op */
517 +#define DUNDI_COMMAND_REGREQ           (10)            /* Register Request */
518 +#define DUNDI_COMMAND_REGRESPONSE      (11 | 0x40)     /* Register Response */
519 +#define DUNDI_COMMAND_CANCEL           (12)            /* Cancel transaction entirely */
520 +#define DUNDI_COMMAND_ENCRYPT          (13)            /* Send an encrypted message */
521 +#define DUNDI_COMMAND_ENCREJ           (14 | 0x40)     /* Reject an encrypted message */
522 +
523 +#define DUNDI_COMMAND_STATUS           15                      /* Status command */
524 +
525 +/*
526 + * Remember that some information elements may occur
527 + * more than one time within a message
528 + */
529 +
530 +#define DUNDI_IE_EID                           1       /* Entity identifier (dundi_eid) */
531 +#define DUNDI_IE_CALLED_CONTEXT                2       /* DUNDi Context (string) */
532 +#define DUNDI_IE_CALLED_NUMBER         3       /* Number of equivalent (string) */
533 +#define DUNDI_IE_EID_DIRECT                    4       /* Entity identifier (dundi_eid), direct connect */
534 +#define DUNDI_IE_ANSWER                                5       /* An answer (struct dundi_answer) */
535 +#define DUNDI_IE_TTL                           6       /* Max TTL for this request / Remaining TTL for the response  (short)*/
536 +#define DUNDI_IE_VERSION                       10      /* DUNDi version (should be 1) (short) */
537 +#define DUNDI_IE_EXPIRATION                    11      /* Recommended expiration (short) */
538 +#define DUNDI_IE_UNKNOWN                       12      /* Unknown command (byte) */
539 +#define DUNDI_IE_CAUSE                         14      /* Success or cause of failure */
540 +#define DUNDI_IE_REQEID                                15      /* EID being requested for EIDQUERY*/
541 +#define DUNDI_IE_ENCDATA                       16      /* AES-128 encrypted data */
542 +#define DUNDI_IE_SHAREDKEY                     17      /* RSA encrypted AES-128 key */
543 +#define DUNDI_IE_SIGNATURE                     18      /* RSA Signature of encrypted shared key */
544 +#define DUNDI_IE_KEYCRC32                      19      /* CRC32 of encrypted key (int) */
545 +#define DUNDI_IE_HINT                          20      /* Answer hints (struct ast_hint) */
546 +
547 +#define DUNDI_IE_DEPARTMENT                    21      /* Department, for EIDQUERY (string) */
548 +#define DUNDI_IE_ORGANIZATION          22      /* Organization, for EIDQUERY (string) */
549 +#define DUNDI_IE_LOCALITY                      23      /* City/Locality, for EIDQUERY (string) */
550 +#define DUNDI_IE_STATE_PROV                    24      /* State/Province, for EIDQUERY (string) */
551 +#define DUNDI_IE_COUNTRY                       25      /* Country, for EIDQUERY (string) */
552 +#define DUNDI_IE_EMAIL                         26      /* E-mail addy, for EIDQUERY (string) */
553 +#define DUNDI_IE_PHONE                         27      /* Contact Phone, for EIDQUERY (string) */
554 +#define DUNDI_IE_IPADDR                                28      /* IP Address, for EIDQUERY (string) */
555 +#define DUNDI_IE_CACHEBYPASS           29      /* Bypass cache (empty) */
556 +
557 +#define DUNDI_IE_PEERSTATUS                    30      /* Peer/peer status (struct dundi_peer_status) */
558 +
559 +#define DUNDI_FLUFF_TIME                       2000    /* Amount of time for answer */
560 +#define DUNDI_TTL_TIME                         200             /* Incremental average time */
561 +
562 +#define DUNDI_DEFAULT_RETRANS          5
563 +#define DUNDI_DEFAULT_RETRANS_TIMER    1000
564 +#define DUNDI_DEFAULT_TTL                      120     /* In seconds/hops like TTL */
565 +#define DUNDI_DEFAULT_VERSION          1
566 +#define DUNDI_DEFAULT_CACHE_TIME       3600    /* In seconds */
567 +#define DUNDI_DEFAULT_KEY_EXPIRE       3600    /* Life of shared key In seconds */
568 +#define DUNDI_DEF_EMPTY_CACHE_TIME     60      /* In seconds, cache of empty answer */
569 +#define DUNDI_WINDOW                           1       /* Max 1 message in window */
570 +
571 +#define DEFAULT_MAXMS                          2000
572 +
573 +struct dundi_result {
574 +       int flags;
575 +       int weight;
576 +       int expiration;
577 +       int techint;
578 +       dundi_eid eid;
579 +       char eid_str[20];
580 +       char tech[10];
581 +       char dest[256];
582 +};
583 +
584 +struct dundi_entity_info {
585 +       char country[80];
586 +       char stateprov[80];
587 +       char locality[80];
588 +       char org[80];
589 +       char orgunit[80];
590 +       char email[80];
591 +       char phone[80]; 
592 +       char ipaddr[80];
593 +};
594 +
595 +/* Lookup the given number in the given dundi context (or e164 if unspecified) using the given callerid (if specified) and return up to maxret results in the array specified.
596 +   returns the number of results found or -1 on a hangup of teh channel. */
597 +int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int nocache);
598 +
599 +/* Retrieve information on a specific EID */
600 +int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid);
601 +
602 +/* Pre-cache to push upstream peers */
603 +int dundi_precache(const char *dcontext, const char *number);
604 +#endif /* _ASTERISK_DUNDI_H */
605 diff -ruN asterisk-1.0.7-orig/pbx/Makefile asterisk-1.0.7-pbx_dundi/pbx/Makefile
606 --- asterisk-1.0.7-orig/pbx/Makefile    2003-10-26 19:50:49.000000000 +0100
607 +++ asterisk-1.0.7-pbx_dundi/pbx/Makefile       2005-06-02 20:21:37.000000000 +0200
608 @@ -13,7 +13,7 @@
609  
610  
611  
612 -PBX_LIBS=pbx_config.so pbx_wilcalu.so pbx_spool.so     # pbx_gtkconsole.so pbx_kdeconsole.so
613 +PBX_LIBS=pbx_config.so pbx_wilcalu.so pbx_spool.so pbx_dundi.so # pbx_gtkconsole.so pbx_kdeconsole.so
614  
615  # Add GTK console if appropriate
616  PBX_LIBS+=$(shell gtk-config --cflags >/dev/null 2>/dev/null && echo "pbx_gtkconsole.so")
617 @@ -51,6 +51,9 @@
618  pbx_kdeconsole.so: $(KDE_CONSOLE_OBJS)
619         $(CC) $(SOLINK) -o $@ $(KDE_CONSOLE_OBJS) $(KDE_LIBS)
620  
621 +pbx_dundi.so: dundi-parser.o pbx_dundi.o
622 +       $(CC) $(SOLINK) -o $@ pbx_dundi.o dundi-parser.o $(LDFLAGS_EXTRA) -lz
623 +
624  %.moc : %.h
625         $(MOC) $< -o $@
626  
627 diff -ruN asterisk-1.0.7-orig/pbx/dundi-parser.c asterisk-1.0.7-pbx_dundi/pbx/dundi-parser.c
628 --- asterisk-1.0.7-orig/pbx/dundi-parser.c      1970-01-01 01:00:00.000000000 +0100
629 +++ asterisk-1.0.7-pbx_dundi/pbx/dundi-parser.c 2005-06-02 20:21:37.000000000 +0200
630 @@ -0,0 +1,813 @@
631 +/*
632 + * Distributed Universal Number Discovery (DUNDi)
633 + *
634 + * Copyright (C) 2004, Digium Inc.
635 + *
636 + * Written by Mark Spencer <markster@digium.com>
637 + *
638 + * This program is Free Software distributed under the terms of
639 + * of the GNU General Public License.
640 + */
641 +
642 +#include <sys/types.h>
643 +#include <sys/socket.h>
644 +#include <string.h>
645 +#include <netinet/in.h>
646 +#include <asterisk/frame.h>
647 +#include <asterisk/utils.h>
648 +#include <arpa/inet.h>
649 +#include <unistd.h>
650 +#include <stdlib.h>
651 +#include <stdio.h>
652 +#include <asterisk/dundi.h>
653 +#include "dundi-parser.h"
654 +#include <asterisk/dundi.h>
655 +
656 +static void internaloutput(const char *str)
657 +{
658 +       fputs(str, stdout);
659 +}
660 +
661 +static void internalerror(const char *str)
662 +{
663 +       fprintf(stderr, "WARNING: %s", str);
664 +}
665 +
666 +static void (*outputf)(const char *str) = internaloutput;
667 +static void (*errorf)(const char *str) = internalerror;
668 +
669 +char *dundi_eid_to_str(char *s, int maxlen, dundi_eid *eid)
670 +{
671 +       int x;
672 +       char *os = s;
673 +       if (maxlen < 18) {
674 +               if (s && (maxlen > 0))
675 +                       *s = '\0';
676 +       } else {
677 +               for (x=0;x<5;x++) {
678 +                       sprintf(s, "%02x:", eid->eid[x]);
679 +                       s += 3;
680 +               }
681 +               sprintf(s, "%02x", eid->eid[5]);
682 +       }
683 +       return os;
684 +}
685 +
686 +char *dundi_eid_to_str_short(char *s, int maxlen, dundi_eid *eid)
687 +{
688 +       int x;
689 +       char *os = s;
690 +       if (maxlen < 13) {
691 +               if (s && (maxlen > 0))
692 +                       *s = '\0';
693 +       } else {
694 +               for (x=0;x<6;x++) {
695 +                       sprintf(s, "%02X", eid->eid[x]);
696 +                       s += 2;
697 +               }
698 +       }
699 +       return os;
700 +}
701 +
702 +int dundi_str_to_eid(dundi_eid *eid, char *s)
703 +{
704 +       unsigned int eid_int[6];
705 +       int x;
706 +       if (sscanf(s, "%x:%x:%x:%x:%x:%x", &eid_int[0], &eid_int[1], &eid_int[2],
707 +                &eid_int[3], &eid_int[4], &eid_int[5]) != 6)
708 +                       return -1;
709 +       for (x=0;x<6;x++)
710 +               eid->eid[x] = eid_int[x];
711 +       return 0;
712 +}
713 +
714 +int dundi_str_short_to_eid(dundi_eid *eid, char *s)
715 +{
716 +       unsigned int eid_int[6];
717 +       int x;
718 +       if (sscanf(s, "%2x%2x%2x%2x%2x%2x", &eid_int[0], &eid_int[1], &eid_int[2],
719 +                &eid_int[3], &eid_int[4], &eid_int[5]) != 6)
720 +                       return -1;
721 +       for (x=0;x<6;x++)
722 +               eid->eid[x] = eid_int[x];
723 +       return 0;
724 +}
725 +
726 +int dundi_eid_zero(dundi_eid *eid)
727 +{
728 +       int x;
729 +       for (x=0;x<sizeof(eid->eid) / sizeof(eid->eid[0]);x++)
730 +               if (eid->eid[x]) return 0;
731 +       return 1;
732 +}
733 +
734 +int dundi_eid_cmp(dundi_eid *eid1, dundi_eid *eid2)
735 +{
736 +       return memcmp(eid1, eid2, sizeof(dundi_eid));
737 +}
738 +
739 +static void dump_string(char *output, int maxlen, void *value, int len)
740 +{
741 +       maxlen--;
742 +       if (maxlen > len)
743 +               maxlen = len;
744 +       strncpy(output,value, maxlen);
745 +       output[maxlen] = '\0';
746 +}
747 +
748 +static void dump_cbypass(char *output, int maxlen, void *value, int len)
749 +{
750 +       strncpy(output, "Bypass Caches", maxlen);
751 +       output[maxlen] = '\0';
752 +}
753 +
754 +static void dump_eid(char *output, int maxlen, void *value, int len)
755 +{
756 +       if (len == 6)
757 +               dundi_eid_to_str(output, maxlen, (dundi_eid *)value);
758 +       else
759 +               snprintf(output, maxlen, "Invalid EID len %d", len);
760 +}
761 +
762 +char *dundi_hint2str(char *buf, int bufsiz, int flags)
763 +{
764 +       strcpy(buf, "");
765 +       buf[bufsiz-1] = '\0';
766 +       if (flags & DUNDI_HINT_TTL_EXPIRED) {
767 +               strncat(buf, "TTLEXPIRED|", bufsiz - strlen(buf) - 1);
768 +       }
769 +       if (flags & DUNDI_HINT_DONT_ASK) {
770 +               strncat(buf, "DONTASK|", bufsiz - strlen(buf) - 1);
771 +       }
772 +       if (flags & DUNDI_HINT_UNAFFECTED) {
773 +               strncat(buf, "UNAFFECTED|", bufsiz - strlen(buf) - 1);
774 +       }
775 +       /* Get rid of trailing | */
776 +       if (ast_strlen_zero(buf))
777 +               strcpy(buf, "NONE|");
778 +       buf[strlen(buf)-1] = '\0';
779 +       return buf;
780 +}
781 +
782 +static void dump_hint(char *output, int maxlen, void *value, int len)
783 +{
784 +       unsigned short flags;
785 +       char tmp[512];
786 +       char tmp2[256];
787 +       if (len < 2) {
788 +               strncpy(output, "<invalid contents>", maxlen);
789 +               return;
790 +       }
791 +       memcpy(&flags, value, sizeof(flags));
792 +       flags = ntohs(flags);
793 +       memset(tmp, 0, sizeof(tmp));
794 +       dundi_hint2str(tmp2, sizeof(tmp2), flags);
795 +       snprintf(tmp, sizeof(tmp), "[%s] ", tmp2);
796 +       memcpy(tmp + strlen(tmp), value + 2, len - 2);
797 +       strncpy(output, tmp, maxlen - 1);
798 +}
799 +
800 +static void dump_cause(char *output, int maxlen, void *value, int len)
801 +{
802 +       static char *causes[] = {
803 +               "SUCCESS",
804 +               "GENERAL",
805 +               "DYNAMIC",
806 +               "NOAUTH" ,
807 +               };
808 +       char tmp[256];
809 +       char tmp2[256];
810 +       int mlen;
811 +       unsigned char cause;
812 +       if (len < 1) {
813 +               strncpy(output, "<invalid contents>", maxlen);
814 +               return;
815 +       }
816 +       cause = *((unsigned char *)value);
817 +       memset(tmp2, 0, sizeof(tmp2));
818 +       mlen = len - 1;
819 +       if (mlen > 255)
820 +               mlen = 255;
821 +       memcpy(tmp2, value + 1, mlen);
822 +       if (cause < sizeof(causes) / sizeof(causes[0])) {
823 +               if (len > 1)
824 +                       snprintf(tmp, sizeof(tmp), "%s: %s", causes[cause], tmp2);
825 +               else
826 +                       snprintf(tmp, sizeof(tmp), "%s", causes[cause]);
827 +       } else {
828 +               if (len > 1)
829 +                       snprintf(tmp, sizeof(tmp), "%d: %s", cause, tmp2);
830 +               else
831 +                       snprintf(tmp, sizeof(tmp), "%d", cause);
832 +       }
833 +       
834 +       strncpy(output,tmp, maxlen);
835 +       output[maxlen] = '\0';
836 +}
837 +
838 +static void dump_int(char *output, int maxlen, void *value, int len)
839 +{
840 +       if (len == (int)sizeof(unsigned int))
841 +               snprintf(output, maxlen, "%lu", (unsigned long)ntohl(*((unsigned int *)value)));
842 +       else
843 +               snprintf(output, maxlen, "Invalid INT");
844 +}
845 +
846 +static void dump_short(char *output, int maxlen, void *value, int len)
847 +{
848 +       if (len == (int)sizeof(unsigned short))
849 +               snprintf(output, maxlen, "%d", ntohs(*((unsigned short *)value)));
850 +       else
851 +               snprintf(output, maxlen, "Invalid SHORT");
852 +}
853 +
854 +static void dump_byte(char *output, int maxlen, void *value, int len)
855 +{
856 +       if (len == (int)sizeof(unsigned char))
857 +               snprintf(output, maxlen, "%d", *((unsigned char *)value));
858 +       else
859 +               snprintf(output, maxlen, "Invalid BYTE");
860 +}
861 +
862 +static char *proto2str(int proto, char *buf, int bufsiz)
863 +{      
864 +       switch(proto) {
865 +       case DUNDI_PROTO_NONE:
866 +               strncpy(buf, "None", bufsiz - 1);
867 +               break;
868 +       case DUNDI_PROTO_IAX:
869 +               strncpy(buf, "IAX", bufsiz - 1);
870 +               break;
871 +       case DUNDI_PROTO_SIP:
872 +               strncpy(buf, "SIP", bufsiz - 1);
873 +               break;
874 +       case DUNDI_PROTO_H323:
875 +               strncpy(buf, "H.323", bufsiz - 1);
876 +               break;
877 +       default:
878 +               snprintf(buf, bufsiz, "Unknown Proto(%d)", proto);
879 +       }
880 +       buf[bufsiz-1] = '\0';
881 +       return buf;
882 +}
883 +
884 +char *dundi_flags2str(char *buf, int bufsiz, int flags)
885 +{
886 +       strcpy(buf, "");
887 +       buf[bufsiz-1] = '\0';
888 +       if (flags & DUNDI_FLAG_EXISTS) {
889 +               strncat(buf, "EXISTS|", bufsiz - strlen(buf) - 1);
890 +       }
891 +       if (flags & DUNDI_FLAG_MATCHMORE) {
892 +               strncat(buf, "MATCHMORE|", bufsiz - strlen(buf) - 1);
893 +       }
894 +       if (flags & DUNDI_FLAG_CANMATCH) {
895 +               strncat(buf, "CANMATCH|", bufsiz - strlen(buf) - 1);
896 +       }
897 +       if (flags & DUNDI_FLAG_IGNOREPAT) {
898 +               strncat(buf, "IGNOREPAT|", bufsiz - strlen(buf) - 1);
899 +       }
900 +       if (flags & DUNDI_FLAG_RESIDENTIAL) {
901 +               strncat(buf, "RESIDENCE|", bufsiz - strlen(buf) - 1);
902 +       }
903 +       if (flags & DUNDI_FLAG_COMMERCIAL) {
904 +               strncat(buf, "COMMERCIAL|", bufsiz - strlen(buf) - 1);
905 +       }
906 +       if (flags & DUNDI_FLAG_MOBILE) {
907 +               strncat(buf, "MOBILE", bufsiz - strlen(buf) - 1);
908 +       }
909 +       if (flags & DUNDI_FLAG_NOUNSOLICITED) {
910 +               strncat(buf, "NOUNSLCTD|", bufsiz - strlen(buf) - 1);
911 +       }
912 +       if (flags & DUNDI_FLAG_NOCOMUNSOLICIT) {
913 +               strncat(buf, "NOCOMUNSLTD|", bufsiz - strlen(buf) - 1);
914 +       }
915 +       /* Get rid of trailing | */
916 +       if (ast_strlen_zero(buf))
917 +               strcpy(buf, "NONE|");
918 +       buf[strlen(buf)-1] = '\0';
919 +       return buf;
920 +}
921 +
922 +static void dump_answer(char *output, int maxlen, void *value, int len)
923 +{
924 +       struct dundi_answer *answer;
925 +       char proto[40];
926 +       char flags[40];
927 +       char eid_str[40];
928 +       char tmp[512]="";
929 +       if (len >= 10) {
930 +               answer = (struct dundi_answer *)(value);
931 +               memcpy(tmp, answer->data, (len >= 500) ? 500 : len - 10);
932 +               dundi_eid_to_str(eid_str, sizeof(eid_str), &answer->eid);
933 +               snprintf(output, maxlen, "[%s] %d <%s/%s> from [%s]", 
934 +                       dundi_flags2str(flags, sizeof(flags), ntohs(answer->flags)), 
935 +                       ntohs(answer->weight),
936 +                       proto2str(answer->protocol, proto, sizeof(proto)), 
937 +                               tmp, eid_str);
938 +       } else
939 +               strncpy(output, "Invalid Answer", maxlen - 1);
940 +}
941 +
942 +static void dump_encrypted(char *output, int maxlen, void *value, int len)
943 +{
944 +       char iv[33];
945 +       int x;
946 +       if ((len > 16) && !(len % 16)) {
947 +               /* Build up IV */
948 +               for (x=0;x<16;x++) {
949 +                       snprintf(iv + (x << 1), 3, "%02x", ((unsigned char *)value)[x]);
950 +               }
951 +               snprintf(output, maxlen, "[IV %s] %d encrypted blocks\n", iv, len / 16);
952 +       } else
953 +               snprintf(output, maxlen, "Invalid Encrypted Datalen %d", len);
954 +}
955 +
956 +static void dump_raw(char *output, int maxlen, void *value, int len)
957 +{
958 +       int x;
959 +       unsigned char *u = value;
960 +       output[maxlen - 1] = '\0';
961 +       strcpy(output, "[ ");
962 +       for (x=0;x<len;x++) {
963 +               snprintf(output + strlen(output), maxlen - strlen(output) - 1, "%02x ", u[x]);
964 +       }
965 +       strncat(output + strlen(output), "]", maxlen - strlen(output) - 1);
966 +}
967 +
968 +static struct dundi_ie {
969 +       int ie;
970 +       char *name;
971 +       void (*dump)(char *output, int maxlen, void *value, int len);
972 +} ies[] = {
973 +       { DUNDI_IE_EID, "ENTITY IDENT", dump_eid },
974 +       { DUNDI_IE_CALLED_CONTEXT, "CALLED CONTEXT", dump_string },
975 +       { DUNDI_IE_CALLED_NUMBER, "CALLED NUMBER", dump_string },
976 +       { DUNDI_IE_EID_DIRECT, "DIRECT EID", dump_eid },
977 +       { DUNDI_IE_ANSWER, "ANSWER", dump_answer },
978 +       { DUNDI_IE_TTL, "TTL", dump_short },
979 +       { DUNDI_IE_VERSION, "VERSION", dump_short },
980 +       { DUNDI_IE_EXPIRATION, "EXPIRATION", dump_short },
981 +       { DUNDI_IE_UNKNOWN, "UKWN DUNDI CMD", dump_byte },
982 +       { DUNDI_IE_CAUSE, "CAUSE", dump_cause },
983 +       { DUNDI_IE_REQEID, "REQUEST EID", dump_eid },
984 +       { DUNDI_IE_ENCDATA, "ENCDATA", dump_encrypted },
985 +       { DUNDI_IE_SHAREDKEY, "SHAREDKEY", dump_raw },
986 +       { DUNDI_IE_SIGNATURE, "SIGNATURE", dump_raw },
987 +       { DUNDI_IE_KEYCRC32, "KEYCRC32", dump_int },
988 +       { DUNDI_IE_HINT, "HINT", dump_hint },
989 +       { DUNDI_IE_DEPARTMENT, "DEPARTMENT", dump_string },
990 +       { DUNDI_IE_ORGANIZATION, "ORGANIZTN", dump_string },
991 +       { DUNDI_IE_LOCALITY, "LOCALITY", dump_string },
992 +       { DUNDI_IE_STATE_PROV, "STATEPROV", dump_string },
993 +       { DUNDI_IE_COUNTRY, "COUNTRY", dump_string },
994 +       { DUNDI_IE_EMAIL, "EMAIL", dump_string },
995 +       { DUNDI_IE_PHONE, "PHONE", dump_string },
996 +       { DUNDI_IE_IPADDR, "ADDRESS", dump_string },
997 +       { DUNDI_IE_CACHEBYPASS, "CBYPASS", dump_cbypass },
998 +};
999 +
1000 +const char *dundi_ie2str(int ie)
1001 +{
1002 +       int x;
1003 +       for (x=0;x<(int)sizeof(ies) / (int)sizeof(ies[0]); x++) {
1004 +               if (ies[x].ie == ie)
1005 +                       return ies[x].name;
1006 +       }
1007 +       return "Unknown IE";
1008 +}
1009 +
1010 +static void dump_ies(unsigned char *iedata, int spaces, int len)
1011 +{
1012 +       int ielen;
1013 +       int ie;
1014 +       int x;
1015 +       int found;
1016 +       char interp[1024];
1017 +       char tmp[1024];
1018 +       if (len < 2)
1019 +               return;
1020 +       while(len >= 2) {
1021 +               ie = iedata[0];
1022 +               ielen = iedata[1];
1023 +               /* Encrypted data is the remainder */
1024 +               if (ie == DUNDI_IE_ENCDATA)
1025 +                       ielen = len - 2;
1026 +               if (ielen + 2> len) {
1027 +                       snprintf(tmp, (int)sizeof(tmp), "Total IE length of %d bytes exceeds remaining frame length of %d bytes\n", ielen + 2, len);
1028 +                       outputf(tmp);
1029 +                       return;
1030 +               }
1031 +               found = 0;
1032 +               for (x=0;x<(int)sizeof(ies) / (int)sizeof(ies[0]); x++) {
1033 +                       if (ies[x].ie == ie) {
1034 +                               if (ies[x].dump) {
1035 +                                       ies[x].dump(interp, (int)sizeof(interp), iedata + 2, ielen);
1036 +                                       snprintf(tmp, (int)sizeof(tmp), "   %s%-15.15s : %s\n", (spaces ? "     " : "" ), ies[x].name, interp);
1037 +                                       outputf(tmp);
1038 +                               } else {
1039 +                                       if (ielen)
1040 +                                               snprintf(interp, (int)sizeof(interp), "%d bytes", ielen);
1041 +                                       else
1042 +                                               strcpy(interp, "Present");
1043 +                                       snprintf(tmp, (int)sizeof(tmp), "   %s%-15.15s : %s\n", (spaces ? "     " : "" ), ies[x].name, interp);
1044 +                                       outputf(tmp);
1045 +                               }
1046 +                               found++;
1047 +                       }
1048 +               }
1049 +               if (!found) {
1050 +                       snprintf(tmp, (int)sizeof(tmp), "   %sUnknown IE %03d  : Present\n", (spaces ? "     " : "" ), ie);
1051 +                       outputf(tmp);
1052 +               }
1053 +               iedata += (2 + ielen);
1054 +               len -= (2 + ielen);
1055 +       }
1056 +       outputf("\n");
1057 +}
1058 +
1059 +void dundi_showframe(struct dundi_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen)
1060 +{
1061 +       char *pref[] = {
1062 +               "Tx",
1063 +               "Rx",
1064 +               "    ETx",
1065 +               "    Erx" };
1066 +       char *commands[] = {
1067 +               "ACK         ",
1068 +               "DPDISCOVER  ",
1069 +               "DPRESPONSE  ",
1070 +               "EIDQUERY    ",
1071 +               "EIDRESPONSE ",
1072 +               "PRECACHERQ  ",
1073 +               "PRECACHERP  ",
1074 +               "INVALID     ",
1075 +               "UNKNOWN CMD ",
1076 +               "NULL        ",
1077 +               "REQREQ      ",
1078 +               "REGRESPONSE ",
1079 +               "CANCEL      ",
1080 +               "ENCRYPT     ",
1081 +               "ENCREJ      " };
1082 +       char class2[20];
1083 +       char *class;
1084 +       char subclass2[20];
1085 +       char *subclass;
1086 +       char tmp[256];
1087 +       char retries[20];
1088 +       char iabuf[INET_ADDRSTRLEN];
1089 +       if (ntohs(fhi->dtrans) & DUNDI_FLAG_RETRANS)
1090 +               strcpy(retries, "Yes");
1091 +       else
1092 +               strcpy(retries, "No");
1093 +       if ((ntohs(fhi->strans) & DUNDI_FLAG_RESERVED)) {
1094 +               /* Ignore frames with high bit set to 1 */
1095 +               return;
1096 +       }
1097 +       if ((fhi->cmdresp & 0x3f) > (int)sizeof(commands)/(int)sizeof(char *)) {
1098 +               snprintf(class2, (int)sizeof(class2), "(%d?)", fhi->cmdresp);
1099 +               class = class2;
1100 +       } else {
1101 +               class = commands[(int)(fhi->cmdresp & 0x3f)];
1102 +       }
1103 +       snprintf(subclass2, (int)sizeof(subclass2), "%02x", fhi->cmdflags);
1104 +       subclass = subclass2;
1105 +       snprintf(tmp, (int)sizeof(tmp), 
1106 +               "%s-Frame Retry[%s] -- OSeqno: %3.3d ISeqno: %3.3d Type: %s (%s)\n",
1107 +               pref[rx],
1108 +               retries, fhi->oseqno, fhi->iseqno, class, fhi->cmdresp & 0x40 ? "Response" : "Command");
1109 +       outputf(tmp);
1110 +       snprintf(tmp, (int)sizeof(tmp), 
1111 +               "%s     Flags: %s STrans: %5.5d  DTrans: %5.5d [%s:%d]%s\n", (rx > 1) ? "     " : "",
1112 +               subclass, ntohs(fhi->strans) & ~DUNDI_FLAG_RESERVED, ntohs(fhi->dtrans) & ~DUNDI_FLAG_RETRANS,
1113 +               ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), ntohs(sin->sin_port),
1114 +               fhi->cmdresp & 0x80 ? " (Final)" : "");
1115 +       outputf(tmp);
1116 +       dump_ies(fhi->ies, rx > 1, datalen);
1117 +}
1118 +
1119 +int dundi_ie_append_raw(struct dundi_ie_data *ied, unsigned char ie, void *data, int datalen)
1120 +{
1121 +       char tmp[256];
1122 +       if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
1123 +               snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
1124 +               errorf(tmp);
1125 +               return -1;
1126 +       }
1127 +       ied->buf[ied->pos++] = ie;
1128 +       ied->buf[ied->pos++] = datalen;
1129 +       memcpy(ied->buf + ied->pos, data, datalen);
1130 +       ied->pos += datalen;
1131 +       return 0;
1132 +}
1133 +
1134 +int dundi_ie_append_cause(struct dundi_ie_data *ied, unsigned char ie, unsigned char cause, unsigned char *data)
1135 +{
1136 +       char tmp[256];
1137 +       int datalen = data ? strlen(data) + 1 : 1;
1138 +       if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
1139 +               snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
1140 +               errorf(tmp);
1141 +               return -1;
1142 +       }
1143 +       ied->buf[ied->pos++] = ie;
1144 +       ied->buf[ied->pos++] = datalen;
1145 +       ied->buf[ied->pos++] = cause;
1146 +       memcpy(ied->buf + ied->pos, data, datalen-1);
1147 +       ied->pos += datalen-1;
1148 +       return 0;
1149 +}
1150 +
1151 +int dundi_ie_append_hint(struct dundi_ie_data *ied, unsigned char ie, unsigned short flags, unsigned char *data)
1152 +{
1153 +       char tmp[256];
1154 +       int datalen = data ? strlen(data) + 2 : 2;
1155 +       if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
1156 +               snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
1157 +               errorf(tmp);
1158 +               return -1;
1159 +       }
1160 +       ied->buf[ied->pos++] = ie;
1161 +       ied->buf[ied->pos++] = datalen;
1162 +       flags = htons(flags);
1163 +       memcpy(ied->buf + ied->pos, &flags, sizeof(flags));
1164 +       ied->pos += 2;
1165 +       memcpy(ied->buf + ied->pos, data, datalen-1);
1166 +       ied->pos += datalen-2;
1167 +       return 0;
1168 +}
1169 +
1170 +int dundi_ie_append_encdata(struct dundi_ie_data *ied, unsigned char ie, unsigned char *iv, void *data, int datalen)
1171 +{
1172 +       char tmp[256];
1173 +       datalen += 16;
1174 +       if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
1175 +               snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
1176 +               errorf(tmp);
1177 +               return -1;
1178 +       }
1179 +       ied->buf[ied->pos++] = ie;
1180 +       ied->buf[ied->pos++] = datalen;
1181 +       memcpy(ied->buf + ied->pos, iv, 16);
1182 +       ied->pos += 16;
1183 +       if (data) {
1184 +               memcpy(ied->buf + ied->pos, data, datalen-16);
1185 +               ied->pos += datalen-16;
1186 +       }
1187 +       return 0;
1188 +}
1189 +
1190 +int dundi_ie_append_answer(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid, unsigned char protocol, unsigned short flags, unsigned short weight, unsigned char *data)
1191 +{
1192 +       char tmp[256];
1193 +       int datalen = data ? strlen(data) + 11 : 11;
1194 +       int x;
1195 +       unsigned short myw;
1196 +       if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
1197 +               snprintf(tmp, (int)sizeof(tmp), "Out of space for ie '%s' (%d), need %d have %d\n", dundi_ie2str(ie), ie, datalen, (int)sizeof(ied->buf) - ied->pos);
1198 +               errorf(tmp);
1199 +               return -1;
1200 +       }
1201 +       ied->buf[ied->pos++] = ie;
1202 +       ied->buf[ied->pos++] = datalen;
1203 +       for (x=0;x<6;x++)
1204 +               ied->buf[ied->pos++] = eid->eid[x];
1205 +       ied->buf[ied->pos++] = protocol;
1206 +       myw = htons(flags);
1207 +       memcpy(ied->buf + ied->pos, &myw, 2);
1208 +       ied->pos += 2;
1209 +       myw = htons(weight);
1210 +       memcpy(ied->buf + ied->pos, &myw, 2);
1211 +       ied->pos += 2;
1212 +       memcpy(ied->buf + ied->pos, data, datalen-11);
1213 +       ied->pos += datalen-11;
1214 +       return 0;
1215 +}
1216 +
1217 +int dundi_ie_append_addr(struct dundi_ie_data *ied, unsigned char ie, struct sockaddr_in *sin)
1218 +{
1219 +       return dundi_ie_append_raw(ied, ie, sin, (int)sizeof(struct sockaddr_in));
1220 +}
1221 +
1222 +int dundi_ie_append_int(struct dundi_ie_data *ied, unsigned char ie, unsigned int value) 
1223 +{
1224 +       unsigned int newval;
1225 +       newval = htonl(value);
1226 +       return dundi_ie_append_raw(ied, ie, &newval, (int)sizeof(newval));
1227 +}
1228 +
1229 +int dundi_ie_append_short(struct dundi_ie_data *ied, unsigned char ie, unsigned short value) 
1230 +{
1231 +       unsigned short newval;
1232 +       newval = htons(value);
1233 +       return dundi_ie_append_raw(ied, ie, &newval, (int)sizeof(newval));
1234 +}
1235 +
1236 +int dundi_ie_append_str(struct dundi_ie_data *ied, unsigned char ie, unsigned char *str)
1237 +{
1238 +       return dundi_ie_append_raw(ied, ie, str, strlen(str));
1239 +}
1240 +
1241 +int dundi_ie_append_eid(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid)
1242 +{
1243 +       return dundi_ie_append_raw(ied, ie, (unsigned char *)eid, sizeof(dundi_eid));
1244 +}
1245 +
1246 +int dundi_ie_append_byte(struct dundi_ie_data *ied, unsigned char ie, unsigned char dat)
1247 +{
1248 +       return dundi_ie_append_raw(ied, ie, &dat, 1);
1249 +}
1250 +
1251 +int dundi_ie_append(struct dundi_ie_data *ied, unsigned char ie) 
1252 +{
1253 +       return dundi_ie_append_raw(ied, ie, NULL, 0);
1254 +}
1255 +
1256 +void dundi_set_output(void (*func)(const char *))
1257 +{
1258 +       outputf = func;
1259 +}
1260 +
1261 +void dundi_set_error(void (*func)(const char *))
1262 +{
1263 +       errorf = func;
1264 +}
1265 +
1266 +int dundi_parse_ies(struct dundi_ies *ies, unsigned char *data, int datalen)
1267 +{
1268 +       /* Parse data into information elements */
1269 +       int len;
1270 +       int ie;
1271 +       char tmp[256];
1272 +       memset(ies, 0, (int)sizeof(struct dundi_ies));
1273 +       ies->ttl = -1;
1274 +       ies->expiration = -1;
1275 +       ies->unknowncmd = -1;
1276 +       ies->cause = -1;
1277 +       while(datalen >= 2) {
1278 +               ie = data[0];
1279 +               len = data[1];
1280 +               if (len > datalen - 2) {
1281 +                       errorf("Information element length exceeds message size\n");
1282 +                       return -1;
1283 +               }
1284 +               switch(ie) {
1285 +               case DUNDI_IE_EID:
1286 +               case DUNDI_IE_EID_DIRECT:
1287 +                       if (len != (int)sizeof(dundi_eid)) {
1288 +                               errorf("Improper entity identifer, expecting 6 bytes!\n");
1289 +                       } else if (ies->eidcount < DUNDI_MAX_STACK) {
1290 +                               ies->eids[ies->eidcount] = (dundi_eid *)(data + 2);
1291 +                               ies->eid_direct[ies->eidcount] = (ie == DUNDI_IE_EID_DIRECT);
1292 +                               ies->eidcount++;
1293 +                       } else
1294 +                               errorf("Too many entities in stack!\n");
1295 +                       break;
1296 +               case DUNDI_IE_REQEID:
1297 +                       if (len != (int)sizeof(dundi_eid)) {
1298 +                               errorf("Improper requested entity identifer, expecting 6 bytes!\n");
1299 +                       } else
1300 +                               ies->reqeid = (dundi_eid *)(data + 2);
1301 +                       break;
1302 +               case DUNDI_IE_CALLED_CONTEXT:
1303 +                       ies->called_context = data + 2;
1304 +                       break;
1305 +               case DUNDI_IE_CALLED_NUMBER:
1306 +                       ies->called_number = data + 2;
1307 +                       break;
1308 +               case DUNDI_IE_ANSWER:
1309 +                       if (len < sizeof(struct dundi_answer)) {
1310 +                               snprintf(tmp, (int)sizeof(tmp), "Answer expected to be >=%d bytes long but was %d\n", (int)sizeof(struct dundi_answer), len);
1311 +                               errorf(tmp);
1312 +                       } else {
1313 +                               if (ies->anscount < DUNDI_MAX_ANSWERS)
1314 +                                       ies->answers[ies->anscount++]= (struct dundi_answer *)(data + 2);
1315 +                               else 
1316 +                                       errorf("Ignoring extra answers!\n");
1317 +                       }
1318 +                       break;
1319 +               case DUNDI_IE_TTL:
1320 +                       if (len != (int)sizeof(unsigned short)) {
1321 +                               snprintf(tmp, (int)sizeof(tmp), "Expecting ttl to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
1322 +                               errorf(tmp);
1323 +                       } else
1324 +                               ies->ttl = ntohs(*((unsigned short *)(data + 2)));
1325 +                       break;
1326 +               case DUNDI_IE_VERSION:
1327 +                       if (len != (int)sizeof(unsigned short)) {
1328 +                               snprintf(tmp, (int)sizeof(tmp),  "Expecting version to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
1329 +                               errorf(tmp);
1330 +                       } else
1331 +                               ies->version = ntohs(*((unsigned short *)(data + 2)));
1332 +                       break;
1333 +               case DUNDI_IE_EXPIRATION:
1334 +                       if (len != (int)sizeof(unsigned short)) {
1335 +                               snprintf(tmp, (int)sizeof(tmp),  "Expecting expiration to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
1336 +                               errorf(tmp);
1337 +                       } else
1338 +                               ies->expiration = ntohs(*((unsigned short *)(data + 2)));
1339 +                       break;
1340 +               case DUNDI_IE_KEYCRC32:
1341 +                       if (len != (int)sizeof(unsigned int)) {
1342 +                               snprintf(tmp, (int)sizeof(tmp),  "Expecting expiration to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
1343 +                               errorf(tmp);
1344 +                       } else
1345 +                               ies->keycrc32 = ntohl(*((unsigned int *)(data + 2)));
1346 +                       break;
1347 +               case DUNDI_IE_UNKNOWN:
1348 +                       if (len == 1)
1349 +                               ies->unknowncmd = data[2];
1350 +                       else {
1351 +                               snprintf(tmp, (int)sizeof(tmp), "Expected single byte Unknown command, but was %d long\n", len);
1352 +                               errorf(tmp);
1353 +                       }
1354 +                       break;
1355 +               case DUNDI_IE_CAUSE:
1356 +                       if (len >= 1) {
1357 +                               ies->cause = data[2];
1358 +                               ies->causestr = data + 3;
1359 +                       } else {
1360 +                               snprintf(tmp, (int)sizeof(tmp), "Expected at least one byte cause, but was %d long\n", len);
1361 +                               errorf(tmp);
1362 +                       }
1363 +                       break;
1364 +               case DUNDI_IE_HINT:
1365 +                       if (len >= 2) {
1366 +                               ies->hint = (struct dundi_hint *)(data + 2);
1367 +                       } else {
1368 +                               snprintf(tmp, (int)sizeof(tmp), "Expected at least two byte hint, but was %d long\n", len);
1369 +                               errorf(tmp);
1370 +                       }
1371 +                       break;
1372 +               case DUNDI_IE_DEPARTMENT:
1373 +                       ies->q_dept = data + 2;
1374 +                       break;
1375 +               case DUNDI_IE_ORGANIZATION:
1376 +                       ies->q_org = data + 2;
1377 +                       break;
1378 +               case DUNDI_IE_LOCALITY:
1379 +                       ies->q_locality = data + 2;
1380 +                       break;
1381 +               case DUNDI_IE_STATE_PROV:
1382 +                       ies->q_stateprov = data + 2;
1383 +                       break;
1384 +               case DUNDI_IE_COUNTRY:
1385 +                       ies->q_country = data + 2;
1386 +                       break;
1387 +               case DUNDI_IE_EMAIL:
1388 +                       ies->q_email = data + 2;
1389 +                       break;
1390 +               case DUNDI_IE_PHONE:
1391 +                       ies->q_phone = data + 2;
1392 +                       break;
1393 +               case DUNDI_IE_IPADDR:
1394 +                       ies->q_ipaddr = data + 2;
1395 +                       break;
1396 +               case DUNDI_IE_ENCDATA:
1397 +                       /* Recalculate len as the remainder of the message, regardless of
1398 +                          theoretical length */
1399 +                       len = datalen - 2;
1400 +                       if ((len > 16) && !(len % 16)) {
1401 +                               ies->encblock = (struct dundi_encblock *)(data + 2);
1402 +                               ies->enclen = len - 16;
1403 +                       } else {
1404 +                               snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted data length %d\n", len);
1405 +                               errorf(tmp);
1406 +                       }
1407 +                       break;
1408 +               case DUNDI_IE_SHAREDKEY:
1409 +                       if (len == 128) {
1410 +                               ies->encsharedkey = (unsigned char *)(data + 2);
1411 +                       } else {
1412 +                               snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted shared key length %d\n", len);
1413 +                               errorf(tmp);
1414 +                       }
1415 +                       break;
1416 +               case DUNDI_IE_SIGNATURE:
1417 +                       if (len == 128) {
1418 +                               ies->encsig = (unsigned char *)(data + 2);
1419 +                       } else {
1420 +                               snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted signature length %d\n", len);
1421 +                               errorf(tmp);
1422 +                       }
1423 +                       break;
1424 +               case DUNDI_IE_CACHEBYPASS:
1425 +                       ies->cbypass = 1;
1426 +                       break;
1427 +               default:
1428 +                       snprintf(tmp, (int)sizeof(tmp), "Ignoring unknown information element '%s' (%d) of length %d\n", dundi_ie2str(ie), ie, len);
1429 +                       outputf(tmp);
1430 +               }
1431 +               /* Overwrite information element with 0, to null terminate previous portion */
1432 +               data[0] = 0;
1433 +               datalen -= (len + 2);
1434 +               data += (len + 2);
1435 +       }
1436 +       /* Null-terminate last field */
1437 +       *data = '\0';
1438 +       if (datalen) {
1439 +               errorf("Invalid information element contents, strange boundary\n");
1440 +               return -1;
1441 +       }
1442 +       return 0;
1443 +}
1444 diff -ruN asterisk-1.0.7-orig/pbx/dundi-parser.h asterisk-1.0.7-pbx_dundi/pbx/dundi-parser.h
1445 --- asterisk-1.0.7-orig/pbx/dundi-parser.h      1970-01-01 01:00:00.000000000 +0100
1446 +++ asterisk-1.0.7-pbx_dundi/pbx/dundi-parser.h 2005-06-02 20:21:37.000000000 +0200
1447 @@ -0,0 +1,88 @@
1448 +/*
1449 + * Distributed Universal Number Discovery (DUNDi)
1450 + *
1451 + * Copyright (C) 2004, Digium Inc.
1452 + *
1453 + * Written by Mark Spencer <markster@digium.com>
1454 + *
1455 + * This program is Free Software distributed under the terms of
1456 + * of the GNU General Public License.
1457 + */
1458 +
1459 +#ifndef _DUNDI_PARSER_H
1460 +#define _DUNDI_PARSER_H
1461 +
1462 +#include <asterisk/dundi.h>
1463 +#include <asterisk/aes.h>
1464 +
1465 +#define DUNDI_MAX_STACK 512
1466 +#define DUNDI_MAX_ANSWERS      100
1467 +
1468 +struct dundi_ies {
1469 +       dundi_eid *eids[DUNDI_MAX_STACK + 1];
1470 +       int eid_direct[DUNDI_MAX_STACK + 1];
1471 +       dundi_eid *reqeid;
1472 +       int eidcount;
1473 +       char *called_context;
1474 +       char *called_number;
1475 +       struct dundi_answer *answers[DUNDI_MAX_ANSWERS + 1];
1476 +       struct dundi_hint *hint;
1477 +       int anscount;
1478 +       int ttl;
1479 +       int version;
1480 +       int expiration;
1481 +       int unknowncmd;
1482 +       unsigned char *pubkey;
1483 +       int cause;
1484 +       unsigned char *q_dept;
1485 +       unsigned char *q_org;
1486 +       unsigned char *q_locality;
1487 +       unsigned char *q_stateprov;
1488 +       unsigned char *q_country;
1489 +       unsigned char *q_email;
1490 +       unsigned char *q_phone;
1491 +       unsigned char *q_ipaddr;
1492 +       unsigned char *causestr;
1493 +       unsigned char *encsharedkey;
1494 +       unsigned char *encsig;
1495 +       unsigned long keycrc32;
1496 +       struct dundi_encblock *encblock;
1497 +       int enclen;
1498 +       int cbypass;
1499 +};
1500 +
1501 +struct dundi_ie_data {
1502 +       int pos;
1503 +       unsigned char buf[8192];
1504 +};
1505 +
1506 +/* Choose a different function for output */
1507 +extern void dundi_set_output(void (*output)(const char *data));
1508 +/* Choose a different function for errors */
1509 +extern void dundi_set_error(void (*output)(const char *data));
1510 +extern void dundi_showframe(struct dundi_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen);
1511 +
1512 +extern const char *dundi_ie2str(int ie);
1513 +
1514 +extern int dundi_ie_append_raw(struct dundi_ie_data *ied, unsigned char ie, void *data, int datalen);
1515 +extern int dundi_ie_append_addr(struct dundi_ie_data *ied, unsigned char ie, struct sockaddr_in *sin);
1516 +extern int dundi_ie_append_int(struct dundi_ie_data *ied, unsigned char ie, unsigned int value);
1517 +extern int dundi_ie_append_short(struct dundi_ie_data *ied, unsigned char ie, unsigned short value);
1518 +extern int dundi_ie_append_str(struct dundi_ie_data *ied, unsigned char ie, unsigned char *str);
1519 +extern int dundi_ie_append_eid(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid);
1520 +extern int dundi_ie_append_cause(struct dundi_ie_data *ied, unsigned char ie, unsigned char cause, unsigned char *desc);
1521 +extern int dundi_ie_append_hint(struct dundi_ie_data *ied, unsigned char ie, unsigned short flags, unsigned char *data);
1522 +extern int dundi_ie_append_answer(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid, unsigned char protocol, unsigned short flags, unsigned short weight, unsigned char *desc);
1523 +extern int dundi_ie_append_encdata(struct dundi_ie_data *ied, unsigned char ie, unsigned char *iv, void *data, int datalen);
1524 +extern int dundi_ie_append_byte(struct dundi_ie_data *ied, unsigned char ie, unsigned char dat);
1525 +extern int dundi_ie_append(struct dundi_ie_data *ied, unsigned char ie);
1526 +extern int dundi_parse_ies(struct dundi_ies *ies, unsigned char *data, int datalen);
1527 +extern char *dundi_eid_to_str(char *s, int maxlen, dundi_eid *eid);
1528 +extern char *dundi_eid_to_str_short(char *s, int maxlen, dundi_eid *eid);
1529 +extern int dundi_str_to_eid(dundi_eid *eid, char *s);
1530 +extern int dundi_str_short_to_eid(dundi_eid *eid, char *s);
1531 +extern int dundi_eid_zero(dundi_eid *eid);
1532 +extern int dundi_eid_cmp(dundi_eid *eid1, dundi_eid *eid2);
1533 +extern char *dundi_flags2str(char *s, int maxlen, int flags);
1534 +extern char *dundi_hint2str(char *s, int maxlen, int flags);
1535 +#endif
1536 diff -ruN asterisk-1.0.7-orig/pbx/pbx_dundi.c asterisk-1.0.7-pbx_dundi/pbx/pbx_dundi.c
1537 --- asterisk-1.0.7-orig/pbx/pbx_dundi.c 1970-01-01 01:00:00.000000000 +0100
1538 +++ asterisk-1.0.7-pbx_dundi/pbx/pbx_dundi.c    2005-06-02 20:21:37.000000000 +0200
1539 @@ -0,0 +1,4881 @@
1540 +/*
1541 + * Distributed Universal Number Discovery (DUNDi)
1542 + *
1543 + * Copyright (C) 2004, Digium Inc.
1544 + *
1545 + * Written by Mark Spencer <markster@digium.com>
1546 + *
1547 + * This program is Free Software distributed under the terms of
1548 + * of the GNU General Public License.
1549 + */
1550 +
1551 +#include <asterisk/file.h>
1552 +#include <asterisk/logger.h>
1553 +#include <asterisk/channel.h>
1554 +#include <asterisk/config.h>
1555 +#include <asterisk/options.h>
1556 +#include <asterisk/pbx.h>
1557 +#include <asterisk/module.h>
1558 +#include <asterisk/frame.h>
1559 +#include <asterisk/file.h>
1560 +#include <asterisk/channel_pvt.h>
1561 +#include <asterisk/cli.h>
1562 +#include <asterisk/lock.h>
1563 +#include <asterisk/md5.h>
1564 +#include <asterisk/dundi.h>
1565 +#include <asterisk/sched.h>
1566 +#include <asterisk/io.h>
1567 +#include <asterisk/utils.h>
1568 +#include <asterisk/crypto.h>
1569 +#include <asterisk/astdb.h>
1570 +#include <asterisk/acl.h>
1571 +#include <asterisk/aes.h>
1572 +
1573 +#include "dundi-parser.h"
1574 +#include <stdlib.h>
1575 +#include <unistd.h>
1576 +#include <arpa/inet.h>
1577 +#include <netinet/in.h>
1578 +#include <sys/socket.h>
1579 +#include <string.h>
1580 +#include <errno.h>
1581 +#if defined(__FreeBSD__) || defined(__NetBSD__)
1582 +#include <sys/types.h>
1583 +#include <netinet/in_systm.h>
1584 +#endif
1585 +#include <netinet/ip.h>
1586 +#include <sys/ioctl.h>
1587 +#include <netinet/in.h>
1588 +#include <net/if.h>
1589 +#if defined(__FreeBSD__) || defined(__NetBSD__)
1590 +#include <net/if_dl.h>
1591 +#include <ifaddrs.h>
1592 +#endif
1593 +#include <zlib.h>
1594 +
1595 +#define MAX_RESULTS    64
1596 +
1597 +#define MAX_PACKET_SIZE 8192
1598 +
1599 +extern char ast_config_AST_KEY_DIR[];
1600 +
1601 +static char *tdesc = "Distributed Universal Number Discovery (DUNDi)";
1602 +
1603 +static char *app = "DUNDiLookup";
1604 +static char *synopsis = "Look up a number with DUNDi";
1605 +static char *descrip = 
1606 +"DUNDiLookup(number[|context[|options]])\n"
1607 +"      Looks up a given number in the global context specified or in\n"
1608 +"the reserved 'e164' context if not specified.  Returns -1 if the channel\n"
1609 +"is hungup during the lookup or 0 otherwise.  On completion, the variable\n"
1610 +"${DUNDTECH} and ${DUNDDEST} will contain the technology and destination\n"
1611 +"of the appropriate technology and destination to access the number. If no\n"
1612 +"answer was found, and the priority n + 101 exists, execution will continue\n"
1613 +"at that location.\n";
1614 +
1615 +#define DUNDI_MODEL_INBOUND            (1 << 0)
1616 +#define DUNDI_MODEL_OUTBOUND   (1 << 1)
1617 +#define DUNDI_MODEL_SYMMETRIC  (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
1618 +
1619 +/* Keep times of last 10 lookups */
1620 +#define DUNDI_TIMING_HISTORY   10
1621 +
1622 +#define FLAG_ISREG             (1 << 0)                /* Transaction is register request */
1623 +#define FLAG_DEAD              (1 << 1)                /* Transaction is dead */
1624 +#define FLAG_FINAL             (1 << 2)                /* Transaction has final message sent */
1625 +#define FLAG_ISQUAL            (1 << 3)                /* Transaction is a qualification */
1626 +#define FLAG_ENCRYPT   (1 << 4)                /* Transaction is encrypted wiht ECX/DCX */
1627 +#define FLAG_SENDFULLKEY       (1 << 5)                /* Send full key on transaction */
1628 +#define FLAG_STOREHIST (1 << 6)                /* Record historic performance */
1629 +
1630 +#define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
1631 +
1632 +#if 0
1633 +#define DUNDI_SECRET_TIME 15   /* Testing only */
1634 +#else
1635 +#define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
1636 +#endif
1637 +
1638 +#define KEY_OUT                        0
1639 +#define KEY_IN                 1
1640 +
1641 +static struct io_context *io;
1642 +static struct sched_context *sched;
1643 +static int netsocket = -1;
1644 +static pthread_t netthreadid = AST_PTHREADT_NULL;
1645 +static pthread_t precachethreadid = AST_PTHREADT_NULL;
1646 +static int tos = 0;
1647 +static int dundidebug = 0;
1648 +static int authdebug = 0;
1649 +static int dundi_ttl = DUNDI_DEFAULT_TTL;
1650 +static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
1651 +static int global_autokilltimeout = 0;
1652 +static dundi_eid global_eid;
1653 +static int default_expiration = 60;
1654 +static int global_storehistory = 0;
1655 +static int map_update_interval = 0;
1656 +static int map_updates_per_pkt = 45;
1657 +static int map_peering_sid = -1;
1658 +static int map_contact_sid = -1;
1659 +static char map_context[80];
1660 +static struct sockaddr_in map_addr;
1661 +static char dept[80];
1662 +static char org[80];
1663 +static char locality[80];
1664 +static char stateprov[80];
1665 +static char country[80];
1666 +static char email[80];
1667 +static char phone[80];
1668 +static char secretpath[80];
1669 +static char cursecret[80];
1670 +static char ipaddr[80];
1671 +static time_t rotatetime;
1672 +static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
1673 +struct permission {
1674 +       struct permission *next;
1675 +       int allow;
1676 +       char name[0];
1677 +};
1678 +
1679 +struct dundi_packet {
1680 +       struct dundi_hdr *h;
1681 +       struct dundi_packet *next;
1682 +       int datalen;
1683 +       struct dundi_transaction *parent;
1684 +       int retransid;
1685 +       int retrans;
1686 +       unsigned char data[0];
1687 +};
1688 +
1689 +struct dundi_hint_metadata {
1690 +       unsigned short flags;
1691 +       char exten[AST_MAX_EXTENSION];
1692 +};
1693 +
1694 +struct dundi_precache_queue {
1695 +       struct dundi_precache_queue *next;
1696 +       char *context;
1697 +       time_t expiration;
1698 +       char number[0];
1699 +};
1700 +
1701 +struct dundi_request;
1702 +
1703 +struct dundi_transaction {
1704 +       struct sockaddr_in addr;        /* Other end of transaction */
1705 +       struct timeval start;           /* When this transaction was created */
1706 +       dundi_eid eids[DUNDI_MAX_STACK + 1];
1707 +       int eidcount;                           /* Number of eids in eids */
1708 +       dundi_eid us_eid;                       /* Our EID, to them */
1709 +       dundi_eid them_eid;                     /* Their EID, to us */
1710 +       aes_encrypt_ctx ecx;            /* AES 128 Encryption context */
1711 +       aes_decrypt_ctx dcx;            /* AES 128 Decryption context */
1712 +       int flags;                                      /* Has final packet been sent */
1713 +       int ttl;                                        /* Remaining TTL for queries on this one */
1714 +       int thread;                                     /* We have a calling thread */
1715 +       int retranstimer;                       /* How long to wait before retransmissions */
1716 +       int autokillid;                         /* ID to kill connection if answer doesn't come back fast enough */
1717 +       int autokilltimeout;            /* Recommended timeout for autokill */
1718 +       unsigned short strans;          /* Our transaction identifier */
1719 +       unsigned short dtrans;          /* Their transaction identifer */
1720 +       unsigned char iseqno;           /* Next expected received seqno */
1721 +       unsigned char oiseqno;          /* Last received incoming seqno */
1722 +       unsigned char oseqno;           /* Next transmitted seqno */
1723 +       unsigned char aseqno;           /* Last acknowledge seqno */
1724 +       struct dundi_packet *packets;   /* Packets to be retransmitted */
1725 +       struct dundi_packet *lasttrans; /* Last transmitted / ACK'd packet */
1726 +       struct dundi_transaction *next; /* Next with respect to the parent */
1727 +       struct dundi_request *parent;   /* Parent request (if there is one) */
1728 +       struct dundi_transaction *allnext; /* Next with respect to all DUNDi transactions */
1729 +} *alltrans;
1730 +
1731 +struct dundi_request {
1732 +       char dcontext[AST_MAX_EXTENSION];
1733 +       char number[AST_MAX_EXTENSION];
1734 +       dundi_eid query_eid;
1735 +       dundi_eid root_eid;
1736 +       struct dundi_result *dr;
1737 +       struct dundi_entity_info *dei;
1738 +       struct dundi_hint_metadata *hmd;
1739 +       int maxcount;
1740 +       int respcount;
1741 +       int expiration;
1742 +       int cbypass;
1743 +       int pfds[2];
1744 +       unsigned long crc32;                                                    /* CRC-32 of all but root EID's in avoid list */
1745 +       struct dundi_transaction *trans;        /* Transactions */
1746 +       struct dundi_request *next;
1747 +} *requests;
1748 +
1749 +static struct dundi_mapping {
1750 +       char dcontext[AST_MAX_EXTENSION];
1751 +       char lcontext[AST_MAX_EXTENSION];
1752 +       int weight;
1753 +       int options;
1754 +       int tech;
1755 +       int dead;
1756 +       char dest[AST_MAX_EXTENSION];
1757 +       struct dundi_mapping *next;
1758 +} *mappings = NULL;
1759 +
1760 +static struct dundi_peer {
1761 +       dundi_eid eid;
1762 +       struct sockaddr_in addr;        /* Address of DUNDi peer */
1763 +       struct permission *permit;
1764 +       struct permission *include;
1765 +       struct permission *precachesend;
1766 +       struct permission *precachereceive;
1767 +       dundi_eid us_eid;
1768 +       char inkey[80];
1769 +       char outkey[80];
1770 +       int dead;
1771 +       int registerid;
1772 +       int qualifyid;
1773 +       int sentfullkey;
1774 +       int order;
1775 +       unsigned char txenckey[256]; /* Transmitted encrypted key + sig */
1776 +       unsigned char rxenckey[256]; /* Cache received encrypted key + sig */
1777 +       unsigned long us_keycrc32;      /* CRC-32 of our key */
1778 +       aes_encrypt_ctx us_ecx;         /* Cached AES 128 Encryption context */
1779 +       aes_decrypt_ctx us_dcx;         /* Cached AES 128 Decryption context */
1780 +       unsigned long them_keycrc32;/* CRC-32 of our key */
1781 +       aes_encrypt_ctx them_ecx;       /* Cached AES 128 Encryption context */
1782 +       aes_decrypt_ctx them_dcx;       /* Cached AES 128 Decryption context */
1783 +       time_t keyexpire;                       /* When to expire/recreate key */
1784 +       int registerexpire;
1785 +       int lookuptimes[DUNDI_TIMING_HISTORY];
1786 +       char *lookups[DUNDI_TIMING_HISTORY];
1787 +       int avgms;
1788 +       struct dundi_transaction *regtrans;     /* Registration transaction */
1789 +       struct dundi_transaction *qualtrans;    /* Qualify transaction */
1790 +       struct dundi_transaction *keypending;
1791 +       int model;                                      /* Pull model */
1792 +       int pcmodel;                            /* Push/precache model */
1793 +       int dynamic;                            /* Are we dynamic? */
1794 +       int lastms;                                     /* Last measured latency */
1795 +       int maxms;                                      /* Max permissible latency */
1796 +       struct timeval qualtx;          /* Time of transmit */
1797 +       struct dundi_peer *next;
1798 +} *peers;
1799 +
1800 +static struct dundi_precache_queue *pcq;
1801 +
1802 +AST_MUTEX_DEFINE_STATIC(peerlock);
1803 +AST_MUTEX_DEFINE_STATIC(pclock);
1804 +
1805 +static int dundi_xmit(struct dundi_packet *pack);
1806 +
1807 +static void dundi_debug_output(const char *data)
1808 +{
1809 +       if (dundidebug)
1810 +               ast_verbose("%s", data);
1811 +}
1812 +
1813 +static void dundi_error_output(const char *data)
1814 +{
1815 +       ast_log(LOG_WARNING, "%s", data);
1816 +}
1817 +
1818 +static int has_permission(struct permission *ps, char *cont)
1819 +{
1820 +       int res=0;
1821 +       while(ps) {
1822 +               if (!strcasecmp(ps->name, "all") || !strcasecmp(ps->name, cont))
1823 +                       res = ps->allow;
1824 +               ps = ps->next;
1825 +       }
1826 +       return res;
1827 +}
1828 +
1829 +static char *tech2str(int tech)
1830 +{
1831 +       switch(tech) {
1832 +       case DUNDI_PROTO_NONE:
1833 +               return "None";
1834 +       case DUNDI_PROTO_IAX:
1835 +               return "IAX2";
1836 +       case DUNDI_PROTO_SIP:
1837 +               return "SIP";
1838 +       case DUNDI_PROTO_H323:
1839 +               return "H323";
1840 +       default:
1841 +               return "Unknown";
1842 +       }
1843 +}
1844 +
1845 +static int str2tech(char *str)
1846 +{
1847 +       if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2")) 
1848 +               return DUNDI_PROTO_IAX;
1849 +       else if (!strcasecmp(str, "SIP"))
1850 +               return DUNDI_PROTO_SIP;
1851 +       else if (!strcasecmp(str, "H323"))
1852 +               return DUNDI_PROTO_H323;
1853 +       else
1854 +               return -1;
1855 +}
1856 +
1857 +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[]);
1858 +static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
1859 +static struct dundi_transaction *create_transaction(struct dundi_peer *p);
1860 +static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
1861 +{
1862 +       /* Look for an exact match first */
1863 +       struct dundi_transaction *trans;
1864 +       trans = alltrans;
1865 +       while(trans) {
1866 +               if (!inaddrcmp(&trans->addr, sin) && 
1867 +                    ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
1868 +                         ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
1869 +                         if (hdr->strans)
1870 +                                 trans->dtrans = ntohs(hdr->strans) & 32767;
1871 +                         break;
1872 +               }
1873 +               trans = trans->allnext;
1874 +       }
1875 +       if (!trans) {
1876 +               switch(hdr->cmdresp & 0x7f) {
1877 +               case DUNDI_COMMAND_DPDISCOVER:
1878 +               case DUNDI_COMMAND_EIDQUERY:
1879 +               case DUNDI_COMMAND_PRECACHERQ:
1880 +               case DUNDI_COMMAND_REGREQ:
1881 +               case DUNDI_COMMAND_NULL:
1882 +               case DUNDI_COMMAND_ENCRYPT:
1883 +                       if (hdr->strans) {      
1884 +                               /* Create new transaction */
1885 +                               trans = create_transaction(NULL);
1886 +                               if (trans) {
1887 +                                       memcpy(&trans->addr, sin, sizeof(trans->addr));
1888 +                                       trans->dtrans = ntohs(hdr->strans) & 32767;
1889 +                               } else
1890 +                                       ast_log(LOG_WARNING, "Out of memory!\n");
1891 +                       }
1892 +                       break;
1893 +               default:
1894 +                       break;
1895 +               }
1896 +       }
1897 +       return trans;
1898 +}
1899 +
1900 +static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
1901 +
1902 +static int dundi_ack(struct dundi_transaction *trans, int final)
1903 +{
1904 +       return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
1905 +}
1906 +static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
1907 +{
1908 +       struct {
1909 +               struct dundi_packet pack;
1910 +               struct dundi_hdr hdr;
1911 +       } tmp;
1912 +       struct dundi_transaction trans;
1913 +       /* Never respond to an INVALID with another INVALID */
1914 +       if (h->cmdresp == DUNDI_COMMAND_INVALID)
1915 +               return;
1916 +       memset(&tmp, 0, sizeof(tmp));
1917 +       memset(&trans, 0, sizeof(trans));
1918 +       memcpy(&trans.addr, sin, sizeof(trans.addr));
1919 +       tmp.hdr.strans = h->dtrans;
1920 +       tmp.hdr.dtrans = h->strans;
1921 +       tmp.hdr.iseqno = h->oseqno;
1922 +       tmp.hdr.oseqno = h->iseqno;
1923 +       tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
1924 +       tmp.hdr.cmdflags = 0;
1925 +       tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
1926 +       tmp.pack.datalen = sizeof(struct dundi_hdr);
1927 +       tmp.pack.parent = &trans;
1928 +       dundi_xmit(&tmp.pack);
1929 +}
1930 +
1931 +static void reset_global_eid(void)
1932 +{
1933 +#if defined(SIOCGIFHWADDR)
1934 +       int x,s;
1935 +       char eid_str[20];
1936 +       struct ifreq ifr;
1937 +
1938 +       s = socket(AF_INET, SOCK_STREAM, 0);
1939 +       if (s > 0) {
1940 +               x = 0;
1941 +               for(x=0;x<10;x++) {
1942 +                       memset(&ifr, 0, sizeof(ifr));
1943 +                       snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
1944 +                       if (!ioctl(s, SIOCGIFHWADDR, &ifr)) {
1945 +                               memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
1946 +                               ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifr.ifr_name);
1947 +                               return;
1948 +                       }
1949 +        }
1950 +       }
1951 +#else
1952 +#if defined(ifa_broadaddr)
1953 +       char eid_str[20];
1954 +       struct ifaddrs *ifap;
1955 +       
1956 +       if (getifaddrs(&ifap) == 0) {
1957 +               struct ifaddrs *p;
1958 +               for (p = ifap; p; p = p->ifa_next) {
1959 +                       if (p->ifa_addr->sa_family == AF_LINK) {
1960 +                               struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
1961 +                               memcpy(
1962 +                                       &(global_eid.eid),
1963 +                                       sdp->sdl_data + sdp->sdl_nlen, 6);
1964 +                               ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifap->ifa_name);
1965 +                               freeifaddrs(ifap);
1966 +                               return;
1967 +                       }
1968 +               }
1969 +               freeifaddrs(ifap);
1970 +       }
1971 +#endif
1972 +#endif
1973 +       ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID  You will have to set it manually.\n");
1974 +}
1975 +
1976 +static int get_trans_id(void)
1977 +{
1978 +       struct dundi_transaction *t;
1979 +       int stid = (rand() % 32766) + 1;
1980 +       int tid = stid;
1981 +       do {
1982 +               t = alltrans;
1983 +               while(t) {
1984 +                       if (t->strans == tid) 
1985 +                               break;
1986 +                       t = t->allnext;
1987 +               }
1988 +               if (!t)
1989 +                       return tid;
1990 +               tid = (tid % 32766) + 1;
1991 +       } while (tid != stid);
1992 +       return 0;
1993 +}
1994 +
1995 +static int reset_transaction(struct dundi_transaction *trans)
1996 +{
1997 +       int tid;
1998 +       tid = get_trans_id();
1999 +       if (tid < 1)
2000 +               return -1;
2001 +       trans->strans = tid;
2002 +       trans->dtrans = 0;
2003 +       trans->iseqno = 0;
2004 +       trans->oiseqno = 0;
2005 +       trans->oseqno = 0;
2006 +       trans->aseqno = 0;
2007 +       trans->flags &= ~FLAG_FINAL;
2008 +       return 0;
2009 +}
2010 +
2011 +static struct dundi_peer *find_peer(dundi_eid *eid)
2012 +{
2013 +       struct dundi_peer *cur;
2014 +       if (!eid)
2015 +               eid = &empty_eid;
2016 +       cur = peers;
2017 +       while(cur) {
2018 +               if (!dundi_eid_cmp(&cur->eid,eid))
2019 +                       return cur;
2020 +               cur = cur->next;
2021 +       }
2022 +       return NULL;
2023 +}
2024 +
2025 +static void build_iv(unsigned char *iv)
2026 +{
2027 +       /* XXX Would be nice to be more random XXX */
2028 +       unsigned int *fluffy;
2029 +       int x;
2030 +       fluffy = (unsigned int *)(iv);
2031 +       for (x=0;x<4;x++)
2032 +               fluffy[x] = rand();
2033 +}
2034 +
2035 +struct dundi_query_state {
2036 +       dundi_eid *eids[DUNDI_MAX_STACK + 1]; 
2037 +       int directs[DUNDI_MAX_STACK + 1]; 
2038 +       dundi_eid reqeid;
2039 +       char called_context[AST_MAX_EXTENSION];
2040 +       char called_number[AST_MAX_EXTENSION];
2041 +       struct dundi_mapping *maps;
2042 +       int nummaps;
2043 +       int nocache;
2044 +       struct dundi_transaction *trans;
2045 +       void *chal;
2046 +       int challen;
2047 +       int ttl;
2048 +       char fluffy[0];
2049 +};
2050 +
2051 +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)
2052 +{
2053 +       int flags;
2054 +       int x;
2055 +       if (!ast_strlen_zero(map->lcontext)) {
2056 +               flags = 0;
2057 +               if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
2058 +                       flags |= DUNDI_FLAG_EXISTS;
2059 +               if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
2060 +                       flags |= DUNDI_FLAG_CANMATCH;
2061 +               if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
2062 +                       flags |= DUNDI_FLAG_MATCHMORE;
2063 +               if (ast_ignore_pattern(map->lcontext, called_number))
2064 +                       flags |= DUNDI_FLAG_IGNOREPAT;
2065 +
2066 +               /* Clearly we can't say 'don't ask' anymore if we found anything... */
2067 +               if (flags) 
2068 +                       hmd->flags &= ~DUNDI_HINT_DONT_ASK;
2069 +
2070 +               if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
2071 +                       /* Skip partial answers */
2072 +                       flags &= ~(DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
2073 +               }
2074 +               if (flags) {
2075 +                       struct varshead headp;
2076 +                       struct ast_var_t *newvariable;
2077 +                       flags |= map->options & 0xffff;
2078 +                       dr[anscnt].flags = flags;
2079 +                       dr[anscnt].techint = map->tech;
2080 +                       dr[anscnt].weight = map->weight;
2081 +                       dr[anscnt].expiration = DUNDI_DEFAULT_CACHE_TIME;
2082 +                       strncpy(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
2083 +                       dr[anscnt].eid = *us_eid;
2084 +                       dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
2085 +                       if (flags & DUNDI_FLAG_EXISTS) {
2086 +                               AST_LIST_HEAD_INIT(&headp);
2087 +                               newvariable = ast_var_assign("NUMBER", called_number);
2088 +                               AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
2089 +                               newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
2090 +                               AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
2091 +                               newvariable = ast_var_assign("SECRET", cursecret);
2092 +                               AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
2093 +                               newvariable = ast_var_assign("IPADDR", ipaddr);
2094 +                               AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
2095 +                               pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
2096 +                               while (!AST_LIST_EMPTY(&headp)) {           /* List Deletion. */
2097 +                                       newvariable = AST_LIST_FIRST(&headp);
2098 +                                       AST_LIST_REMOVE_HEAD(&headp, entries);
2099 +                                       ast_var_delete(newvariable);
2100 +                               }
2101 +                       } else
2102 +                               dr[anscnt].dest[0] = '\0';
2103 +                       anscnt++;
2104 +               } else {
2105 +                       /* No answers...  Find the fewest number of digits from the
2106 +                          number for which we have no answer. */
2107 +                       char tmp[AST_MAX_EXTENSION]="";
2108 +                       for (x=0;x<AST_MAX_EXTENSION;x++) {
2109 +                               tmp[x] = called_number[x];
2110 +                               if (!tmp[x])
2111 +                                       break;
2112 +                               if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
2113 +                                       /* Oops found something we can't match.  If this is longer
2114 +                                          than the running hint, we have to consider it */
2115 +                                       if (strlen(tmp) > strlen(hmd->exten)) {
2116 +                                               strncpy(hmd->exten, tmp, sizeof(hmd->exten) - 1);
2117 +                                       }
2118 +                                       break;
2119 +                               }
2120 +                       }
2121 +               }
2122 +       }
2123 +       return anscnt;
2124 +}
2125 +
2126 +static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
2127 +
2128 +static void *dundi_lookup_thread(void *data)
2129 +{
2130 +       struct dundi_query_state *st = data;
2131 +       struct dundi_result dr[MAX_RESULTS];
2132 +       struct dundi_ie_data ied;
2133 +       struct dundi_hint_metadata hmd;
2134 +       char eid_str[20];
2135 +       int res, x;
2136 +       int ouranswers=0;
2137 +       int max = 999999;
2138 +       int expiration = DUNDI_DEFAULT_CACHE_TIME;
2139 +
2140 +       ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context, 
2141 +               st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
2142 +       memset(&ied, 0, sizeof(ied));
2143 +       memset(&dr, 0, sizeof(dr));
2144 +       memset(&hmd, 0, sizeof(hmd));
2145 +       /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
2146 +       hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
2147 +       for (x=0;x<st->nummaps;x++)
2148 +               ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
2149 +       if (ouranswers < 0)
2150 +               ouranswers = 0;
2151 +       for (x=0;x<ouranswers;x++) {
2152 +               if (dr[x].weight < max)
2153 +                       max = dr[x].weight;
2154 +       }
2155 +               
2156 +       if (max) {
2157 +               /* If we do not have a canonical result, keep looking */
2158 +               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);
2159 +               if (res > 0) {
2160 +                       /* Append answer in result */
2161 +                       ouranswers += res;
2162 +               } else {
2163 +                       if ((res < -1) && (!ouranswers))
2164 +                               dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
2165 +               }
2166 +       }
2167 +       ast_mutex_lock(&peerlock);
2168 +       /* Truncate if "don't ask" isn't present */
2169 +       if (!(hmd.flags & DUNDI_HINT_DONT_ASK))
2170 +               hmd.exten[0] = '\0';
2171 +       if (st->trans->flags & FLAG_DEAD) {
2172 +               ast_log(LOG_DEBUG, "Our transaction went away!\n");
2173 +               st->trans->thread = 0;
2174 +               destroy_trans(st->trans, 0);
2175 +       } else {
2176 +               for (x=0;x<ouranswers;x++) {
2177 +                       /* Add answers */
2178 +                       if (dr[x].expiration && (expiration > dr[x].expiration))
2179 +                               expiration = dr[x].expiration;
2180 +                       dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
2181 +               }
2182 +               dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
2183 +               dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
2184 +               dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
2185 +               st->trans->thread = 0;
2186 +       }
2187 +       ast_mutex_unlock(&peerlock);
2188 +       free(st);
2189 +       return NULL;    
2190 +}
2191 +
2192 +static void *dundi_precache_thread(void *data)
2193 +{
2194 +       struct dundi_query_state *st = data;
2195 +       struct dundi_ie_data ied;
2196 +       struct dundi_hint_metadata hmd;
2197 +       char eid_str[20];
2198 +
2199 +       ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context, 
2200 +               st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
2201 +       memset(&ied, 0, sizeof(ied));
2202 +
2203 +       /* Now produce precache */
2204 +       dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
2205 +
2206 +       ast_mutex_lock(&peerlock);
2207 +       /* Truncate if "don't ask" isn't present */
2208 +       if (!(hmd.flags & DUNDI_HINT_DONT_ASK))
2209 +               hmd.exten[0] = '\0';
2210 +       if (st->trans->flags & FLAG_DEAD) {
2211 +               ast_log(LOG_DEBUG, "Our transaction went away!\n");
2212 +               st->trans->thread = 0;
2213 +               destroy_trans(st->trans, 0);
2214 +       } else {
2215 +               dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
2216 +               st->trans->thread = 0;
2217 +       }
2218 +       ast_mutex_unlock(&peerlock);
2219 +       free(st);
2220 +       return NULL;    
2221 +}
2222 +
2223 +static inline int calc_ms(struct timeval *start)
2224 +{
2225 +       struct timeval tv;
2226 +       gettimeofday(&tv, NULL);
2227 +       return ((tv.tv_sec - start->tv_sec) * 1000 + (tv.tv_usec - start->tv_usec) / 1000);
2228 +}
2229 +
2230 +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[]);
2231 +
2232 +static void *dundi_query_thread(void *data)
2233 +{
2234 +       struct dundi_query_state *st = data;
2235 +       struct dundi_entity_info dei;
2236 +       struct dundi_ie_data ied;
2237 +       struct dundi_hint_metadata hmd;
2238 +       char eid_str[20];
2239 +       int res;
2240 +       ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context, 
2241 +               st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
2242 +       memset(&ied, 0, sizeof(ied));
2243 +       memset(&dei, 0, sizeof(dei));
2244 +       memset(&hmd, 0, sizeof(hmd));
2245 +       if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
2246 +               /* Ooh, it's us! */
2247 +               ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
2248 +               strncpy(dei.orgunit, dept, sizeof(dei.orgunit));
2249 +               strncpy(dei.org, org, sizeof(dei.org));
2250 +               strncpy(dei.locality, locality, sizeof(dei.locality));
2251 +               strncpy(dei.stateprov, stateprov, sizeof(dei.stateprov));
2252 +               strncpy(dei.country, country, sizeof(dei.country));
2253 +               strncpy(dei.email, email, sizeof(dei.email));
2254 +               strncpy(dei.phone, phone, sizeof(dei.phone));
2255 +               res = 1;
2256 +       } else {
2257 +               /* If we do not have a canonical result, keep looking */
2258 +               res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
2259 +       }
2260 +       ast_mutex_lock(&peerlock);
2261 +       if (st->trans->flags & FLAG_DEAD) {
2262 +               ast_log(LOG_DEBUG, "Our transaction went away!\n");
2263 +               st->trans->thread = 0;
2264 +               destroy_trans(st->trans, 0);
2265 +       } else {
2266 +               if (res) {
2267 +                       dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
2268 +                       dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
2269 +                       dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
2270 +                       dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
2271 +                       dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
2272 +                       dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
2273 +                       dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
2274 +                       if (!ast_strlen_zero(dei.ipaddr))
2275 +                               dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
2276 +               }
2277 +               dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
2278 +               dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
2279 +               st->trans->thread = 0;
2280 +       }
2281 +       ast_mutex_unlock(&peerlock);
2282 +       free(st);
2283 +       return NULL;    
2284 +}
2285 +
2286 +static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
2287 +{
2288 +       struct dundi_query_state *st;
2289 +       int totallen;
2290 +       int x;
2291 +       int skipfirst=0;
2292 +       struct dundi_ie_data ied;
2293 +       char eid_str[20];
2294 +       char *s;
2295 +       pthread_t lookupthread;
2296 +       pthread_attr_t attr;
2297 +       if (ies->eidcount > 1) {
2298 +               /* Since it is a requirement that the first EID is the authenticating host
2299 +                  and the last EID is the root, it is permissible that the first and last EID
2300 +                  could be the same.  In that case, we should go ahead copy only the "root" section
2301 +                  since we will not need it for authentication. */
2302 +               if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
2303 +                       skipfirst = 1;
2304 +       }
2305 +       totallen = sizeof(struct dundi_query_state);
2306 +       totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
2307 +       st = malloc(totallen);
2308 +       if (st) {
2309 +               memset(st, 0, totallen);
2310 +               strncpy(st->called_context, ies->called_context, sizeof(st->called_context) - 1);
2311 +               memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
2312 +               st->trans = trans;
2313 +               st->ttl = ies->ttl - 1;
2314 +               if (st->ttl < 0)
2315 +                       st->ttl = 0;
2316 +               s = st->fluffy;
2317 +               for (x=skipfirst;ies->eids[x];x++) {
2318 +                       st->eids[x-skipfirst] = (dundi_eid *)s;
2319 +                       *st->eids[x-skipfirst] = *ies->eids[x];
2320 +                       s += sizeof(dundi_eid);
2321 +               }
2322 +               ast_log(LOG_DEBUG, "Answering EID query for '%s@%s'!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), ies->reqeid), ies->called_context);
2323 +               pthread_attr_init(&attr);
2324 +               pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2325 +               trans->thread = 1;
2326 +               if (ast_pthread_create(&lookupthread, &attr, dundi_query_thread, st)) {
2327 +                       trans->thread = 0;
2328 +                       ast_log(LOG_WARNING, "Unable to create thread!\n");
2329 +                       free(st);
2330 +                       memset(&ied, 0, sizeof(ied));
2331 +                       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
2332 +                       dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
2333 +                       return -1;
2334 +               }
2335 +       } else {
2336 +               ast_log(LOG_WARNING, "Out of memory!\n");
2337 +               memset(&ied, 0, sizeof(ied));
2338 +               dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
2339 +               dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
2340 +               return -1;
2341 +       }
2342 +       return 0;
2343 +}
2344 +
2345 +static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
2346 +{
2347 +       int unaffected;
2348 +       char key1[256];
2349 +       char key2[256];
2350 +       char eidpeer_str[20];
2351 +       char eidroot_str[20];
2352 +       char data[80]="";
2353 +       time_t timeout;
2354 +
2355 +       if (expiration < 0)
2356 +               expiration = DUNDI_DEFAULT_CACHE_TIME;
2357 +
2358 +       /* Only cache hint if "don't ask" is there... */
2359 +       if (!(ntohs(hint->flags)& DUNDI_HINT_DONT_ASK))
2360 +               return 0;
2361 +
2362 +       unaffected = ntohs(hint->flags) & DUNDI_HINT_UNAFFECTED;
2363 +
2364 +       dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
2365 +       dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
2366 +       snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
2367 +       snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
2368 +
2369 +       time(&timeout);
2370 +       timeout += expiration;
2371 +       snprintf(data, sizeof(data), "%ld|", (long)(timeout));
2372 +       
2373 +       ast_db_put("dundi/cache", key1, data);
2374 +       ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
2375 +       ast_db_put("dundi/cache", key2, data);
2376 +       ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
2377 +       return 0;
2378 +}
2379 +
2380 +static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
2381 +{
2382 +       int x;
2383 +       char key1[256];
2384 +       char key2[256];
2385 +       char data[1024]="";
2386 +       char eidpeer_str[20];
2387 +       char eidroot_str[20];
2388 +       time_t timeout;
2389 +
2390 +       if (expiration < 1)     
2391 +               expiration = DUNDI_DEFAULT_CACHE_TIME;
2392 +
2393 +       /* Keep pushes a little longer, cut pulls a little short */
2394 +       if (push)
2395 +               expiration += 10;
2396 +       else
2397 +               expiration -= 10;
2398 +       if (expiration < 1)
2399 +               expiration = 1;
2400 +       dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
2401 +       dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
2402 +       snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
2403 +       snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
2404 +       /* Build request string */
2405 +       time(&timeout);
2406 +       timeout += expiration;
2407 +       snprintf(data, sizeof(data), "%ld|", (long)(timeout));
2408 +       for (x=start;x<req->respcount;x++) {
2409 +               /* Skip anything with an illegal pipe in it */
2410 +               if (strchr(req->dr[x].dest, '|'))
2411 +                       continue;
2412 +               snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|", 
2413 +                       req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest, 
2414 +                       dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
2415 +       }
2416 +       ast_db_put("dundi/cache", key1, data);
2417 +       ast_db_put("dundi/cache", key2, data);
2418 +       return 0;
2419 +}
2420 +
2421 +static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
2422 +{
2423 +       struct dundi_query_state *st;
2424 +       int totallen;
2425 +       int x,z;
2426 +       struct dundi_ie_data ied;
2427 +       char *s;
2428 +       struct dundi_result dr2[MAX_RESULTS];
2429 +       struct dundi_request dr;
2430 +       struct dundi_hint_metadata hmd;
2431 +
2432 +       struct dundi_mapping *cur;
2433 +       int mapcount;
2434 +       int skipfirst = 0;
2435 +       
2436 +       pthread_t lookupthread;
2437 +       pthread_attr_t attr;
2438 +
2439 +       memset(&dr2, 0, sizeof(dr2));
2440 +       memset(&dr, 0, sizeof(dr));
2441 +       memset(&hmd, 0, sizeof(hmd));
2442 +       
2443 +       /* Forge request structure to hold answers for cache */
2444 +       hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
2445 +       dr.dr = dr2;
2446 +       dr.maxcount = MAX_RESULTS;
2447 +       dr.expiration = DUNDI_DEFAULT_CACHE_TIME;
2448 +       dr.hmd = &hmd;
2449 +       dr.pfds[0] = dr.pfds[1] = -1;
2450 +       trans->parent = &dr;
2451 +       strncpy(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
2452 +       strncpy(dr.number, ies->called_number, sizeof(dr.number) - 1);
2453 +       
2454 +       for (x=0;x<ies->anscount;x++) {
2455 +               if (trans->parent->respcount < trans->parent->maxcount) {
2456 +                       /* Make sure it's not already there */
2457 +                       for (z=0;z<trans->parent->respcount;z++) {
2458 +                               if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
2459 +                                   !strcmp(trans->parent->dr[z].dest, ies->answers[x]->data)) 
2460 +                                               break;
2461 +                       }
2462 +                       if (z == trans->parent->respcount) {
2463 +                               /* Copy into parent responses */
2464 +                               trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
2465 +                               trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
2466 +                               trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
2467 +                               trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
2468 +                               if (ies->expiration > 0)
2469 +                                       trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
2470 +                               else
2471 +                                       trans->parent->dr[trans->parent->respcount].expiration = DUNDI_DEFAULT_CACHE_TIME;
2472 +                               dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str, 
2473 +                                       sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
2474 +                                       &ies->answers[x]->eid);
2475 +                               strncpy(trans->parent->dr[trans->parent->respcount].dest, ies->answers[x]->data,
2476 +                                       sizeof(trans->parent->dr[trans->parent->respcount].dest));
2477 +                                       strncpy(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
2478 +                                       sizeof(trans->parent->dr[trans->parent->respcount].tech));
2479 +                               trans->parent->respcount++;
2480 +                               trans->parent->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
2481 +                       } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
2482 +                               /* Update weight if appropriate */
2483 +                               trans->parent->dr[z].weight = ies->answers[x]->weight;
2484 +                       }
2485 +               } else
2486 +                       ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
2487 +                               trans->parent->number, trans->parent->dcontext);
2488 +
2489 +       }
2490 +       /* Save all the results (if any) we had.  Even if no results, still cache lookup. */
2491 +       cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
2492 +       if (ies->hint)
2493 +               cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
2494 +
2495 +       totallen = sizeof(struct dundi_query_state);
2496 +       /* Count matching map entries */
2497 +       mapcount = 0;
2498 +       cur = mappings;
2499 +       while(cur) {
2500 +               if (!strcasecmp(cur->dcontext, ccontext))
2501 +                       mapcount++;
2502 +               cur = cur->next;
2503 +       }
2504 +       
2505 +       /* If no maps, return -1 immediately */
2506 +       if (!mapcount)
2507 +               return -1;
2508 +
2509 +       if (ies->eidcount > 1) {
2510 +               /* Since it is a requirement that the first EID is the authenticating host
2511 +                  and the last EID is the root, it is permissible that the first and last EID
2512 +                  could be the same.  In that case, we should go ahead copy only the "root" section
2513 +                  since we will not need it for authentication. */
2514 +               if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
2515 +                       skipfirst = 1;
2516 +       }
2517 +
2518 +       /* Prepare to run a query and then propagate that as necessary */
2519 +       totallen += mapcount * sizeof(struct dundi_mapping);
2520 +       totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
2521 +       st = malloc(totallen);
2522 +       if (st) {
2523 +               memset(st, 0, totallen);
2524 +               strncpy(st->called_context, ies->called_context, sizeof(st->called_context) - 1);
2525 +               strncpy(st->called_number, ies->called_number, sizeof(st->called_number) - 1);
2526 +               st->trans = trans;
2527 +               st->ttl = ies->ttl - 1;
2528 +               st->nocache = ies->cbypass;
2529 +               if (st->ttl < 0)
2530 +                       st->ttl = 0;
2531 +               s = st->fluffy;
2532 +               for (x=skipfirst;ies->eids[x];x++) {
2533 +                       st->eids[x-skipfirst] = (dundi_eid *)s;
2534 +                       *st->eids[x-skipfirst] = *ies->eids[x];
2535 +                       st->directs[x-skipfirst] = ies->eid_direct[x];
2536 +                       s += sizeof(dundi_eid);
2537 +               }
2538 +               /* Append mappings */
2539 +               x = 0;
2540 +               st->maps = (struct dundi_mapping *)s;
2541 +               cur = mappings;
2542 +               while(cur) {
2543 +                       if (!strcasecmp(cur->dcontext, ccontext)) {
2544 +                               if (x < mapcount) {
2545 +                                       st->maps[x] = *cur;
2546 +                                       st->maps[x].next = NULL;
2547 +                                       x++;
2548 +                               }
2549 +                       }
2550 +                       cur = cur->next;
2551 +               }
2552 +               st->nummaps = mapcount;
2553 +               ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
2554 +               pthread_attr_init(&attr);
2555 +               pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2556 +               trans->thread = 1;
2557 +               if (ast_pthread_create(&lookupthread, &attr, dundi_precache_thread, st)) {
2558 +                       trans->thread = 0;
2559 +                       ast_log(LOG_WARNING, "Unable to create thread!\n");
2560 +                       free(st);
2561 +                       memset(&ied, 0, sizeof(ied));
2562 +                       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
2563 +                       dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
2564 +                       return -1;
2565 +               }
2566 +       } else {
2567 +               ast_log(LOG_WARNING, "Out of memory!\n");
2568 +               memset(&ied, 0, sizeof(ied));
2569 +               dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
2570 +               dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
2571 +               return -1;
2572 +       }
2573 +       return 0;
2574 +}
2575 +
2576 +static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
2577 +{
2578 +       struct dundi_query_state *st;
2579 +       int totallen;
2580 +       int x;
2581 +       struct dundi_ie_data ied;
2582 +       char *s;
2583 +       struct dundi_mapping *cur;
2584 +       int mapcount;
2585 +       int skipfirst = 0;
2586 +       
2587 +       pthread_t lookupthread;
2588 +       pthread_attr_t attr;
2589 +       totallen = sizeof(struct dundi_query_state);
2590 +       /* Count matching map entries */
2591 +       mapcount = 0;
2592 +       cur = mappings;
2593 +       while(cur) {
2594 +               if (!strcasecmp(cur->dcontext, ccontext))
2595 +                       mapcount++;
2596 +               cur = cur->next;
2597 +       }
2598 +       /* If no maps, return -1 immediately */
2599 +       if (!mapcount)
2600 +               return -1;
2601 +
2602 +       if (ies->eidcount > 1) {
2603 +               /* Since it is a requirement that the first EID is the authenticating host
2604 +                  and the last EID is the root, it is permissible that the first and last EID
2605 +                  could be the same.  In that case, we should go ahead copy only the "root" section
2606 +                  since we will not need it for authentication. */
2607 +               if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
2608 +                       skipfirst = 1;
2609 +       }
2610 +
2611 +       totallen += mapcount * sizeof(struct dundi_mapping);
2612 +       totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
2613 +       st = malloc(totallen);
2614 +       if (st) {
2615 +               memset(st, 0, totallen);
2616 +               strncpy(st->called_context, ies->called_context, sizeof(st->called_context) - 1);
2617 +               strncpy(st->called_number, ies->called_number, sizeof(st->called_number) - 1);
2618 +               st->trans = trans;
2619 +               st->ttl = ies->ttl - 1;
2620 +               st->nocache = ies->cbypass;
2621 +               if (st->ttl < 0)
2622 +                       st->ttl = 0;
2623 +               s = st->fluffy;
2624 +               for (x=skipfirst;ies->eids[x];x++) {
2625 +                       st->eids[x-skipfirst] = (dundi_eid *)s;
2626 +                       *st->eids[x-skipfirst] = *ies->eids[x];
2627 +                       st->directs[x-skipfirst] = ies->eid_direct[x];
2628 +                       s += sizeof(dundi_eid);
2629 +               }
2630 +               /* Append mappings */
2631 +               x = 0;
2632 +               st->maps = (struct dundi_mapping *)s;
2633 +               cur = mappings;
2634 +               while(cur) {
2635 +                       if (!strcasecmp(cur->dcontext, ccontext)) {
2636 +                               if (x < mapcount) {
2637 +                                       st->maps[x] = *cur;
2638 +                                       st->maps[x].next = NULL;
2639 +                                       x++;
2640 +                               }
2641 +                       }
2642 +                       cur = cur->next;
2643 +               }
2644 +               st->nummaps = mapcount;
2645 +               ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
2646 +               pthread_attr_init(&attr);
2647 +               pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2648 +               trans->thread = 1;
2649 +               if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) {
2650 +                       trans->thread = 0;
2651 +                       ast_log(LOG_WARNING, "Unable to create thread!\n");
2652 +                       free(st);
2653 +                       memset(&ied, 0, sizeof(ied));
2654 +                       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
2655 +                       dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
2656 +                       return -1;
2657 +               }
2658 +       } else {
2659 +               ast_log(LOG_WARNING, "Out of memory!\n");
2660 +               memset(&ied, 0, sizeof(ied));
2661 +               dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
2662 +               dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
2663 +               return -1;
2664 +       }
2665 +       return 0;
2666 +}
2667 +
2668 +static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
2669 +{
2670 +       char data[1024]="";
2671 +       char *ptr, *term, *src;
2672 +       int tech;
2673 +       int flags;
2674 +       int weight;
2675 +       int length;
2676 +       int z;
2677 +       int expiration;
2678 +       char fs[256];
2679 +       time_t timeout;
2680 +       /* Build request string */
2681 +       if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
2682 +               ptr = data;
2683 +               if (sscanf(ptr, "%ld|%n", &timeout, &length) == 1) {
2684 +                       expiration = timeout - now;
2685 +                       if (expiration > 0) {
2686 +                               ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", (int)(timeout - now));
2687 +                               ptr += length;
2688 +                               while((sscanf(ptr, "%d/%d/%d/%n", &flags, &weight, &tech, &length) == 3)) {
2689 +                                       ptr += length;
2690 +                                       term = strchr(ptr, '|');
2691 +                                       if (term) {
2692 +                                               *term = '\0';
2693 +                                               src = strrchr(ptr, '/');
2694 +                                               if (src) {
2695 +                                                       *src = '\0';
2696 +                                                       src++;
2697 +                                               } else
2698 +                                                       src = "";
2699 +                                               ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n", 
2700 +                                                       tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags), eid_str_full);
2701 +                                               /* Make sure it's not already there */
2702 +                                               for (z=0;z<req->respcount;z++) {
2703 +                                                       if ((req->dr[z].techint == tech) &&
2704 +                                                           !strcmp(req->dr[z].dest, ptr)) 
2705 +                                                                       break;
2706 +                                               }
2707 +                                               if (z == req->respcount) {
2708 +                                                       /* Copy into parent responses */
2709 +                                                       req->dr[req->respcount].flags = flags;
2710 +                                                       req->dr[req->respcount].weight = weight;
2711 +                                                       req->dr[req->respcount].techint = tech;
2712 +                                                       req->dr[req->respcount].expiration = expiration;
2713 +                                                       dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
2714 +                                                       dundi_eid_to_str(req->dr[req->respcount].eid_str, 
2715 +                                                               sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
2716 +                                                       strncpy(req->dr[req->respcount].dest, ptr,
2717 +                                                               sizeof(req->dr[req->respcount].dest));
2718 +                                                       strncpy(req->dr[req->respcount].tech, tech2str(tech),
2719 +                                                               sizeof(req->dr[req->respcount].tech));
2720 +                                                       req->respcount++;
2721 +                                                       req->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
2722 +                                               } else if (req->dr[z].weight > weight)
2723 +                                                       req->dr[z].weight = weight;
2724 +                                               ptr = term + 1;
2725 +                                       }
2726 +                               }
2727 +                               /* We found *something* cached */
2728 +                               if (expiration < *lowexpiration)
2729 +                                       *lowexpiration = expiration;
2730 +                               return 1;
2731 +                       } else 
2732 +                               ast_db_del("dundi/cache", key);
2733 +               } else 
2734 +                       ast_db_del("dundi/cache", key);
2735 +       }
2736 +               
2737 +       return 0;
2738 +}
2739 +
2740 +static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
2741 +{
2742 +       char key[256];
2743 +       char eid_str[20];
2744 +       char eidroot_str[20];
2745 +       time_t now;
2746 +       int res=0;
2747 +       int res2=0;
2748 +       char eid_str_full[20];
2749 +       char tmp[256]="";
2750 +       int x;
2751 +
2752 +       time(&now);
2753 +       dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
2754 +       dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
2755 +       dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
2756 +       snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
2757 +       res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
2758 +       snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
2759 +       res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
2760 +       snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
2761 +       res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
2762 +       x = 0;
2763 +       if (!req->respcount) {
2764 +               while(!res2) {
2765 +                       /* Look and see if we have a hint that would preclude us from looking at this
2766 +                          peer for this number. */
2767 +                       if (!(tmp[x] = req->number[x])) 
2768 +                               break;
2769 +                       x++;
2770 +                       /* Check for hints */
2771 +                       snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
2772 +                       res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
2773 +                       snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
2774 +                       res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
2775 +                       snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
2776 +                       res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
2777 +                       if (res2) {
2778 +                               if (strlen(tmp) > strlen(req->hmd->exten)) {
2779 +                                       /* Update meta data if appropriate */
2780 +                                       strncpy(req->hmd->exten, tmp, sizeof(req->hmd->exten) - 1);
2781 +                               }
2782 +                       }
2783 +               }
2784 +               res |= res2;
2785 +       }
2786 +
2787 +       return res;
2788 +}
2789 +
2790 +static void qualify_peer(struct dundi_peer *peer, int schedonly);
2791 +
2792 +static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
2793 +{
2794 +       if (!trans->addr.sin_addr.s_addr)
2795 +               memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
2796 +       trans->us_eid = p->us_eid;
2797 +       trans->them_eid = p->eid;
2798 +       /* Enable encryption if appropriate */
2799 +       if (!ast_strlen_zero(p->inkey))
2800 +               trans->flags |= FLAG_ENCRYPT;
2801 +       if (p->maxms) {
2802 +               trans->autokilltimeout = p->maxms;
2803 +               trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2804 +               if (p->lastms > 1) {
2805 +                       trans->retranstimer = p->lastms * 2;
2806 +                       /* Keep it from being silly */
2807 +                       if (trans->retranstimer < 150)
2808 +                               trans->retranstimer = 150;
2809 +               }
2810 +               if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
2811 +                       trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
2812 +       } else
2813 +               trans->autokilltimeout = global_autokilltimeout;
2814 +}
2815 +
2816 +static int do_register_expire(void *data)
2817 +{
2818 +       struct dundi_peer *peer = data;
2819 +       char eid_str[20];
2820 +       /* Called with peerlock already held */
2821 +       ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2822 +       peer->registerexpire = -1;
2823 +       peer->lastms = 0;
2824 +       memset(&peer->addr, 0, sizeof(peer->addr));
2825 +       return 0;
2826 +}
2827 +
2828 +static int update_key(struct dundi_peer *peer)
2829 +{
2830 +       unsigned char key[16];
2831 +       struct ast_key *ekey, *skey;
2832 +       char eid_str[20];
2833 +       int res;
2834 +       if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
2835 +               build_iv(key);
2836 +               aes_encrypt_key128(key, &peer->us_ecx);
2837 +               aes_decrypt_key128(key, &peer->us_dcx);
2838 +               ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
2839 +               if (!ekey) {
2840 +                       ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
2841 +                               peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2842 +                       return -1;
2843 +               }
2844 +               skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
2845 +               if (!skey) {
2846 +                       ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
2847 +                               peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
2848 +                       return -1;
2849 +               }
2850 +               if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
2851 +                       ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
2852 +                       return -1;
2853 +               }
2854 +               if ((res = ast_sign_bin(skey, peer->txenckey, 128, peer->txenckey + 128))) {
2855 +                       ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
2856 +                       return -1;
2857 +               }
2858 +               peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
2859 +               peer->sentfullkey = 0;
2860 +               /* Looks good */
2861 +               time(&peer->keyexpire);
2862 +               peer->keyexpire += dundi_key_ttl;
2863 +       }
2864 +       return 0;
2865 +}
2866 +
2867 +static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_encrypt_ctx *ecx) 
2868 +{
2869 +       unsigned char curblock[16];
2870 +       int x;
2871 +       memcpy(curblock, iv, sizeof(curblock));
2872 +       while(len > 0) {
2873 +               for (x=0;x<16;x++)
2874 +                       curblock[x] ^= src[x];
2875 +               aes_encrypt(curblock, dst, ecx);
2876 +               memcpy(curblock, dst, sizeof(curblock)); 
2877 +               dst += 16;
2878 +               src += 16;
2879 +               len -= 16;
2880 +       }
2881 +       return 0;
2882 +}
2883 +static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_decrypt_ctx *dcx) 
2884 +{
2885 +       unsigned char lastblock[16];
2886 +       int x;
2887 +       memcpy(lastblock, iv, sizeof(lastblock));
2888 +       while(len > 0) {
2889 +               aes_decrypt(src, dst, dcx);
2890 +               for (x=0;x<16;x++)
2891 +                       dst[x] ^= lastblock[x];
2892 +               memcpy(lastblock, src, sizeof(lastblock));
2893 +               dst += 16;
2894 +               src += 16;
2895 +               len -= 16;
2896 +       }
2897 +       return 0;
2898 +}
2899 +
2900 +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)
2901 +{
2902 +       int space = *dstlen;
2903 +       unsigned long bytes;
2904 +       struct dundi_hdr *h;
2905 +       unsigned char *decrypt_space;
2906 +       decrypt_space = alloca(srclen);
2907 +       if (!decrypt_space)
2908 +               return NULL;
2909 +       decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
2910 +       /* Setup header */
2911 +       h = (struct dundi_hdr *)dst;
2912 +       *h = *ohdr;
2913 +       bytes = space - 6;
2914 +       if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
2915 +               ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n");
2916 +               return NULL;
2917 +       }
2918 +       /* Update length */
2919 +       *dstlen = bytes + 6;
2920 +       /* Return new header */
2921 +       return h;
2922 +}
2923 +
2924 +static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
2925 +{
2926 +       unsigned char *compress_space;
2927 +       int len;
2928 +       int res;
2929 +       unsigned long bytes;
2930 +       struct dundi_ie_data ied;
2931 +       struct dundi_peer *peer;
2932 +       unsigned char iv[16];
2933 +       len = pack->datalen + pack->datalen / 100 + 42;
2934 +       compress_space = alloca(len);
2935 +       if (compress_space) {
2936 +               memset(compress_space, 0, len);
2937 +               /* We care about everthing save the first 6 bytes of header */
2938 +               bytes = len;
2939 +               res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
2940 +               if (res != Z_OK) {
2941 +                       ast_log(LOG_DEBUG, "Ouch, compression failed!\n");
2942 +                       return -1;
2943 +               }
2944 +               memset(&ied, 0, sizeof(ied));
2945 +               /* Say who we are */
2946 +               if (!pack->h->iseqno && !pack->h->oseqno) {
2947 +                       /* Need the key in the first copy */
2948 +                       if (!(peer = find_peer(&trans->them_eid))) 
2949 +                               return -1;
2950 +                       if (update_key(peer))
2951 +                               return -1;
2952 +                       if (!peer->sentfullkey)
2953 +                               trans->flags |= FLAG_SENDFULLKEY;
2954 +                       /* Append key data */
2955 +                       dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
2956 +                       if (trans->flags & FLAG_SENDFULLKEY) {
2957 +                               dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
2958 +                               dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
2959 +                       } else {
2960 +                               dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
2961 +                       }
2962 +                       /* Setup contexts */
2963 +                       trans->ecx = peer->us_ecx;
2964 +                       trans->dcx = peer->us_dcx;
2965 +
2966 +                       /* We've sent the full key */
2967 +                       peer->sentfullkey = 1;
2968 +               }
2969 +               /* Build initialization vector */
2970 +               build_iv(iv);
2971 +               /* Add the field, rounded up to 16 bytes */
2972 +               dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
2973 +               /* Copy the data */
2974 +               if ((ied.pos + bytes) >= sizeof(ied.buf)) {
2975 +                       ast_log(LOG_NOTICE, "Final packet too large!\n");
2976 +                       return -1;
2977 +               }
2978 +               encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
2979 +               ied.pos += ((bytes + 15) / 16) * 16;
2980 +               /* Reconstruct header */
2981 +               pack->datalen = sizeof(struct dundi_hdr);
2982 +               pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
2983 +               pack->h->cmdflags = 0;
2984 +               memcpy(pack->h->ies, ied.buf, ied.pos);
2985 +               pack->datalen += ied.pos;
2986 +               return 0;
2987 +       }
2988 +       return -1;
2989 +}
2990 +
2991 +static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
2992 +{
2993 +       unsigned char dst[128];
2994 +       int res;
2995 +       struct ast_key *key, *skey;
2996 +       char eid_str[20];
2997 +       if (option_debug)
2998 +               ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
2999 +       if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
3000 +               /* A match */
3001 +               return 1;
3002 +       } else if (!newkey || !newsig)
3003 +               return 0;
3004 +       if (!memcmp(peer->rxenckey, newkey, 128) &&
3005 +           !memcmp(peer->rxenckey + 128, newsig, 128)) {
3006 +               /* By definition, a match */
3007 +               return 1;
3008 +       }
3009 +       /* Decrypt key */
3010 +       key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
3011 +       if (!key) {
3012 +               ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
3013 +                       peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
3014 +               return -1;
3015 +       }
3016 +
3017 +       skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
3018 +       if (!skey) {
3019 +               ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
3020 +                       peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
3021 +               return -1;
3022 +       }
3023 +
3024 +       /* First check signature */
3025 +       res = ast_check_signature_bin(skey, newkey, 128, newsig);
3026 +       if (res) 
3027 +               return 0;
3028 +
3029 +       res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
3030 +       if (res != 16) {
3031 +               if (res >= 0)
3032 +                       ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
3033 +               return 0;
3034 +       }
3035 +       /* Decrypted, passes signature */
3036 +       ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n");
3037 +       memcpy(peer->rxenckey, newkey, 128);
3038 +       memcpy(peer->rxenckey + 128, newsig, 128);
3039 +       peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
3040 +       aes_decrypt_key128(dst, &peer->them_dcx);
3041 +       aes_encrypt_key128(dst, &peer->them_ecx);
3042 +       return 1;
3043 +}
3044 +
3045 +static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
3046 +{
3047 +       /* Handle canonical command / response */
3048 +       int final = hdr->cmdresp & 0x80;
3049 +       int cmd = hdr->cmdresp & 0x7f;
3050 +       int x,y,z;
3051 +       int resp;
3052 +       int res;
3053 +       int authpass=0;
3054 +       unsigned char *bufcpy;
3055 +       struct dundi_ie_data ied;
3056 +       struct dundi_ies ies;
3057 +       struct dundi_peer *peer;
3058 +       char eid_str[20];
3059 +       char eid_str2[20];
3060 +       memset(&ied, 0, sizeof(ied));
3061 +       memset(&ies, 0, sizeof(ies));
3062 +       if (datalen) {
3063 +               bufcpy = alloca(datalen);
3064 +               if (!bufcpy)
3065 +                       return -1;
3066 +               /* Make a copy for parsing */
3067 +               memcpy(bufcpy, hdr->ies, datalen);
3068 +               ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
3069 +               if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
3070 +                       ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
3071 +                       return -1;
3072 +               }
3073 +       }
3074 +       switch(cmd) {
3075 +       case DUNDI_COMMAND_DPDISCOVER:
3076 +       case DUNDI_COMMAND_EIDQUERY:
3077 +       case DUNDI_COMMAND_PRECACHERQ:
3078 +               if (cmd == DUNDI_COMMAND_EIDQUERY)
3079 +                       resp = DUNDI_COMMAND_EIDRESPONSE;
3080 +               else if (cmd == DUNDI_COMMAND_PRECACHERQ)
3081 +                       resp = DUNDI_COMMAND_PRECACHERP;
3082 +               else
3083 +                       resp = DUNDI_COMMAND_DPRESPONSE;
3084 +               /* A dialplan or entity discover -- qualify by highest level entity */
3085 +               peer = find_peer(ies.eids[0]);
3086 +               if (!peer) {
3087 +                       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
3088 +                       dundi_send(trans, resp, 0, 1, &ied);
3089 +               } else {
3090 +                       int hasauth = 0;
3091 +                       trans->us_eid = peer->us_eid;
3092 +                       if (strlen(peer->inkey)) {
3093 +                               hasauth = encrypted;
3094 +                       } else 
3095 +                               hasauth = 1;
3096 +                       if (hasauth) {
3097 +                               /* Okay we're authentiated and all, now we check if they're authorized */
3098 +                               if (!ies.called_context)
3099 +                                       ies.called_context = "e164";
3100 +                               if (cmd == DUNDI_COMMAND_EIDQUERY) {
3101 +                                       res = dundi_answer_entity(trans, &ies, ies.called_context);
3102 +                               } else {
3103 +                                       if (!ies.called_number || ast_strlen_zero(ies.called_number)) {
3104 +                                               /* They're not permitted to access that context */
3105 +                                               dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
3106 +                                               dundi_send(trans, resp, 0, 1, &ied);
3107 +                                       } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) && 
3108 +                                                  (peer->model & DUNDI_MODEL_INBOUND) && 
3109 +                                                          has_permission(peer->permit, ies.called_context)) {
3110 +                                               res = dundi_answer_query(trans, &ies, ies.called_context);
3111 +                                               if (res < 0) {
3112 +                                                       /* There is no such dundi context */
3113 +                                                       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
3114 +                                                       dundi_send(trans, resp, 0, 1, &ied);
3115 +                                               }
3116 +                                       } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) && 
3117 +                                                  (peer->pcmodel & DUNDI_MODEL_INBOUND) && 
3118 +                                                          has_permission(peer->include, ies.called_context)) {
3119 +                                               res = dundi_prop_precache(trans, &ies, ies.called_context);
3120 +                                               if (res < 0) {
3121 +                                                       /* There is no such dundi context */
3122 +                                                       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
3123 +                                                       dundi_send(trans, resp, 0, 1, &ied);
3124 +                                               }
3125 +                                       } else {
3126 +                                               /* They're not permitted to access that context */
3127 +                                               dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
3128 +                                               dundi_send(trans, resp, 0, 1, &ied);
3129 +                                       }
3130 +                               }
3131 +                       } else {
3132 +                               /* They're not permitted to access that context */
3133 +                               dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
3134 +                               dundi_send(trans, resp, 0, 1, &ied);
3135 +                       }
3136 +               }
3137 +               break;
3138 +       case DUNDI_COMMAND_REGREQ:
3139 +               /* A register request -- should only have one entity */
3140 +               peer = find_peer(ies.eids[0]);
3141 +               if (!peer || !peer->dynamic) {
3142 +                       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
3143 +                       dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
3144 +               } else {
3145 +                       int hasauth = 0;
3146 +                       trans->us_eid = peer->us_eid;
3147 +                       if (!ast_strlen_zero(peer->inkey)) {
3148 +                               hasauth = encrypted;
3149 +                       } else
3150 +                               hasauth = 1;
3151 +                       if (hasauth) {
3152 +                               int expire = default_expiration;
3153 +                               char iabuf[INET_ADDRSTRLEN];
3154 +                               char data[256];
3155 +                               int needqual = 0;
3156 +                               if (peer->registerexpire > -1)
3157 +                                       ast_sched_del(sched, peer->registerexpire);
3158 +                               peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
3159 +                               ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr);
3160 +                               snprintf(data, sizeof(data), "%s:%d:%d", iabuf, ntohs(trans->addr.sin_port), expire);
3161 +                               ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
3162 +                               if (inaddrcmp(&peer->addr, &trans->addr)) {
3163 +                                       if (option_verbose > 2)
3164 +                                               ast_verbose(VERBOSE_PREFIX_3 "Registered DUNDi peer '%s' at '%s:%d'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), iabuf, ntohs(trans->addr.sin_port));
3165 +                                       needqual = 1;
3166 +                               }
3167 +                                       
3168 +                               memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
3169 +                               dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
3170 +                               dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
3171 +                               if (needqual)
3172 +                                       qualify_peer(peer, 1);
3173 +                       }
3174 +               }
3175 +               break;
3176 +       case DUNDI_COMMAND_DPRESPONSE:
3177 +               /* A dialplan response, lets see what we got... */
3178 +               if (ies.cause < 1) {
3179 +                       /* Success of some sort */
3180 +                       ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
3181 +                       if (trans->flags & FLAG_ENCRYPT) {
3182 +                               authpass = encrypted;
3183 +                       } else 
3184 +                               authpass = 1;
3185 +                       if (authpass) {
3186 +                               /* Pass back up answers */
3187 +                               if (trans->parent && trans->parent->dr) {
3188 +                                       y = trans->parent->respcount;
3189 +                                       for (x=0;x<ies.anscount;x++) {
3190 +                                               if (trans->parent->respcount < trans->parent->maxcount) {
3191 +                                                       /* Make sure it's not already there */
3192 +                                                       for (z=0;z<trans->parent->respcount;z++) {
3193 +                                                               if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
3194 +                                                                   !strcmp(trans->parent->dr[z].dest, ies.answers[x]->data)) 
3195 +                                                                               break;
3196 +                                                       }
3197 +                                                       if (z == trans->parent->respcount) {
3198 +                                                               /* Copy into parent responses */
3199 +                                                               trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
3200 +                                                               trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
3201 +                                                               trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
3202 +                                                               trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
3203 +                                                               if (ies.expiration > 0)
3204 +                                                                       trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
3205 +                                                               else
3206 +                                                                       trans->parent->dr[trans->parent->respcount].expiration = DUNDI_DEFAULT_CACHE_TIME;
3207 +                                                               dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str, 
3208 +                                                                       sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
3209 +                                                                       &ies.answers[x]->eid);
3210 +                                                               strncpy(trans->parent->dr[trans->parent->respcount].dest, ies.answers[x]->data,
3211 +                                                                       sizeof(trans->parent->dr[trans->parent->respcount].dest));
3212 +                                                               strncpy(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
3213 +                                                                       sizeof(trans->parent->dr[trans->parent->respcount].tech));
3214 +                                                               trans->parent->respcount++;
3215 +                                                               trans->parent->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
3216 +                                                       } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
3217 +                                                               /* Update weight if appropriate */
3218 +                                                               trans->parent->dr[z].weight = ies.answers[x]->weight;
3219 +                                                       }
3220 +                                               } else
3221 +                                                       ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
3222 +                                                               trans->parent->number, trans->parent->dcontext);
3223 +                                       }
3224 +                                       /* Save all the results (if any) we had.  Even if no results, still cache lookup.  Let
3225 +                                          the cache know if this request was unaffected by our entity list. */
3226 +                                       cache_save(&trans->them_eid, trans->parent, y, 
3227 +                                                       ies.hint ? ntohs(ies.hint->flags) & DUNDI_HINT_UNAFFECTED : 0, ies.expiration, 0);
3228 +                                       if (ies.hint) {
3229 +                                               cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
3230 +                                               if (ntohs(ies.hint->flags) & DUNDI_HINT_TTL_EXPIRED)
3231 +                                                       trans->parent->hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
3232 +                                               if (ntohs(ies.hint->flags) & DUNDI_HINT_DONT_ASK) { 
3233 +                                                       if (strlen(ies.hint->data) > strlen(trans->parent->hmd->exten)) {
3234 +                                                               strncpy(trans->parent->hmd->exten, ies.hint->data, 
3235 +                                                                       sizeof(trans->parent->hmd->exten) - 1);
3236 +                                                       }
3237 +                                               } else {
3238 +                                                       trans->parent->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
3239 +                                               }
3240 +                                       }
3241 +                                       if (ies.expiration > 0) {
3242 +                                               if (trans->parent->expiration > ies.expiration) {
3243 +                                                       trans->parent->expiration = ies.expiration;
3244 +                                               }
3245 +                                       }
3246 +                               }
3247 +                               /* Close connection if not final */
3248 +                               if (!final) 
3249 +                                       dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3250 +                       }
3251 +                       
3252 +               } else {
3253 +                       /* Auth failure, check for data */
3254 +                       if (!final) {
3255 +                               /* Cancel if they didn't already */
3256 +                               dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3257 +                       }
3258 +               }
3259 +               break;
3260 +       case DUNDI_COMMAND_EIDRESPONSE:
3261 +               /* A dialplan response, lets see what we got... */
3262 +               if (ies.cause < 1) {
3263 +                       /* Success of some sort */
3264 +                       ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
3265 +                       if (trans->flags & FLAG_ENCRYPT) {
3266 +                               authpass = encrypted;
3267 +                       } else 
3268 +                               authpass = 1;
3269 +                       if (authpass) {
3270 +                               /* Pass back up answers */
3271 +                               if (trans->parent && trans->parent->dei && ies.q_org) {
3272 +                                       if (!trans->parent->respcount) {
3273 +                                               trans->parent->respcount++;
3274 +                                               if (ies.q_dept)
3275 +                                                       strncpy(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit) - 1);
3276 +                                               if (ies.q_org)
3277 +                                                       strncpy(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org) - 1);
3278 +                                               if (ies.q_locality)
3279 +                                                       strncpy(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality) - 1);
3280 +                                               if (ies.q_stateprov)
3281 +                                                       strncpy(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov) - 1);
3282 +                                               if (ies.q_country)
3283 +                                                       strncpy(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country) - 1);
3284 +                                               if (ies.q_email)
3285 +                                                       strncpy(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email) - 1);
3286 +                                               if (ies.q_phone)
3287 +                                                       strncpy(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone) - 1);
3288 +                                               if (ies.q_ipaddr)
3289 +                                                       strncpy(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr) - 1);
3290 +                                               if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
3291 +                                                       /* If it's them, update our address */
3292 +                                                       ast_inet_ntoa(trans->parent->dei->ipaddr, sizeof(trans->parent->dei->ipaddr),
3293 +                                                               trans->addr.sin_addr);
3294 +                                               }
3295 +                                       }
3296 +                                       if (ies.hint) {
3297 +                                               if (ntohs(ies.hint->flags) & DUNDI_HINT_TTL_EXPIRED)
3298 +                                                       trans->parent->hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
3299 +                                       }
3300 +                               }
3301 +                               /* Close connection if not final */
3302 +                               if (!final) 
3303 +                                       dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3304 +                       }
3305 +                       
3306 +               } else {
3307 +                       /* Auth failure, check for data */
3308 +                       if (!final) {
3309 +                               /* Cancel if they didn't already */
3310 +                               dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3311 +                       }
3312 +               }
3313 +               break;
3314 +       case DUNDI_COMMAND_REGRESPONSE:
3315 +               /* A dialplan response, lets see what we got... */
3316 +               if (ies.cause < 1) {
3317 +                       int hasauth;
3318 +                       /* Success of some sort */
3319 +                       if (trans->flags & FLAG_ENCRYPT) {
3320 +                               hasauth = encrypted;
3321 +                       } else 
3322 +                               hasauth = 1;
3323 +                       
3324 +                       if (!hasauth) {
3325 +                               ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
3326 +                               if (!final) {
3327 +                                       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
3328 +                                       dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
3329 +                               }
3330 +                       } else {
3331 +                               ast_log(LOG_DEBUG, "Yay, we've registered as '%s' to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->us_eid),
3332 +                                                       dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
3333 +                               /* Close connection if not final */
3334 +                               if (!final) 
3335 +                                       dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3336 +                       }
3337 +               } else {
3338 +                       /* Auth failure, cancel if they didn't for some reason */
3339 +                       if (!final) {
3340 +                               dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3341 +                       }
3342 +               }
3343 +               break;
3344 +       case DUNDI_COMMAND_INVALID:
3345 +       case DUNDI_COMMAND_NULL:
3346 +       case DUNDI_COMMAND_PRECACHERP:
3347 +               /* Do nothing special */
3348 +               if (!final) 
3349 +                       dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3350 +               break;
3351 +       case DUNDI_COMMAND_ENCREJ:
3352 +               if ((trans->flags & FLAG_SENDFULLKEY) || !trans->lasttrans || !(peer = find_peer(&trans->them_eid))) {
3353 +                       /* No really, it's over at this point */
3354 +                       if (!final) 
3355 +                               dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
3356 +               } else {
3357 +                       /* Send with full key */
3358 +                       trans->flags |= FLAG_SENDFULLKEY;
3359 +                       if (final) {
3360 +                               /* Ooops, we got a final message, start by sending ACK... */
3361 +                               dundi_ack(trans, hdr->cmdresp & 0x80);
3362 +                               trans->aseqno = trans->iseqno;
3363 +                               /* Now, we gotta create a new transaction */
3364 +                               if (!reset_transaction(trans)) {
3365 +                                       /* Make sure handle_frame doesn't destroy us */
3366 +                                       hdr->cmdresp &= 0x7f;
3367 +                                       /* Parse the message we transmitted */
3368 +                                       memset(&ies, 0, sizeof(ies));
3369 +                                       dundi_parse_ies(&ies, trans->lasttrans->h->ies, trans->lasttrans->datalen - sizeof(struct dundi_hdr));
3370 +                                       /* Reconstruct outgoing encrypted packet */
3371 +                                       memset(&ied, 0, sizeof(ied));
3372 +                                       dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
3373 +                                       dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
3374 +                                       dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
3375 +                                       if (ies.encblock) 
3376 +                                               dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
3377 +                                       dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, trans->lasttrans->h->cmdresp & 0x80, &ied);
3378 +                                       peer->sentfullkey = 1;
3379 +                               }
3380 +                       }
3381 +               }
3382 +               break;
3383 +       case DUNDI_COMMAND_ENCRYPT:
3384 +               if (!encrypted) {
3385 +                       /* No nested encryption! */
3386 +                       if ((trans->iseqno == 1) && !trans->oseqno) {
3387 +                               if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) || 
3388 +                                       ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) || 
3389 +                                       (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
3390 +                                       if (!final) {
3391 +                                               dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
3392 +                                       }
3393 +                                       break;
3394 +                               }
3395 +                               apply_peer(trans, peer);
3396 +                               /* Key passed, use new contexts for this session */
3397 +                               trans->ecx = peer->them_ecx;
3398 +                               trans->dcx = peer->them_dcx;
3399 +                       }
3400 +                       if ((trans->flags & FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
3401 +                               struct dundi_hdr *dhdr;
3402 +                               unsigned char decoded[MAX_PACKET_SIZE];
3403 +                               int ddatalen;
3404 +                               ddatalen = sizeof(decoded);
3405 +                               dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
3406 +                               if (dhdr) {
3407 +                                       /* Handle decrypted response */
3408 +                                       if (dundidebug)
3409 +                                               dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
3410 +                                       handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
3411 +                                       /* Carry back final flag */
3412 +                                       hdr->cmdresp |= dhdr->cmdresp & 0x80;
3413 +                                       break;
3414 +                               } else
3415 +                                       ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
3416 +                       }
3417 +               }
3418 +               if (!final) {
3419 +                       /* Turn off encryption */
3420 +                       trans->flags &= ~FLAG_ENCRYPT;
3421 +                       dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
3422 +               }
3423 +               break;
3424 +       default:
3425 +               /* Send unknown command if we don't know it, with final flag IFF it's the
3426 +                  first command in the dialog and only if we haven't recieved final notification */
3427 +               if (!final) {
3428 +                       dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
3429 +                       dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
3430 +               }
3431 +       }
3432 +       return 0;
3433 +}
3434 +
3435 +static void destroy_packet(struct dundi_packet *pack, int needfree);
3436 +static void destroy_packets(struct dundi_packet *p)
3437 +{
3438 +       struct dundi_packet *prev;
3439 +       while(p) {
3440 +               prev = p;
3441 +               p = p->next;
3442 +               if (prev->retransid > -1)
3443 +                       ast_sched_del(sched, prev->retransid);
3444 +               free(prev);
3445 +       }
3446 +}
3447 +
3448 +
3449 +static int ack_trans(struct dundi_transaction *trans, int iseqno)
3450 +{
3451 +       /* Ack transmitted packet corresponding to iseqno */
3452 +       struct dundi_packet *pack;
3453 +       pack = trans->packets;
3454 +       while(pack) {
3455 +               if ((pack->h->oseqno + 1) % 255 == iseqno) {
3456 +                       destroy_packet(pack, 0);
3457 +                       if (trans->lasttrans) {
3458 +                               ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
3459 +                               destroy_packets(trans->lasttrans);
3460 +                       }
3461 +                       trans->lasttrans = pack;
3462 +                       if (trans->autokillid > -1)
3463 +                               ast_sched_del(sched, trans->autokillid);
3464 +                       trans->autokillid = -1;
3465 +                       return 1;
3466 +               }
3467 +               pack = pack->next;
3468 +       }
3469 +       return 0;
3470 +}
3471 +
3472 +static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
3473 +{
3474 +       struct dundi_transaction *trans;
3475 +       trans = find_transaction(h, sin);
3476 +       if (!trans) {
3477 +               dundi_reject(h, sin);
3478 +               return 0;
3479 +       }
3480 +       /* Got a transaction, see where this header fits in */
3481 +       if (h->oseqno == trans->iseqno) {
3482 +               /* Just what we were looking for...  Anything but ack increments iseqno */
3483 +               if (ack_trans(trans, h->iseqno) && (trans->flags & FLAG_FINAL)) {
3484 +                       /* If final, we're done */
3485 +                       destroy_trans(trans, 0);
3486 +                       return 0;
3487 +               }
3488 +               if (h->cmdresp != DUNDI_COMMAND_ACK) {
3489 +                       trans->oiseqno = trans->iseqno;
3490 +                       trans->iseqno++;
3491 +                       handle_command_response(trans, h, datalen, 0);
3492 +               }
3493 +               if (trans->aseqno != trans->iseqno) {
3494 +                       dundi_ack(trans, h->cmdresp & 0x80);
3495 +                       trans->aseqno = trans->iseqno;
3496 +               }
3497 +               /* Delete any saved last transmissions */
3498 +               destroy_packets(trans->lasttrans);
3499 +               trans->lasttrans = NULL;
3500 +               if (h->cmdresp & 0x80) {
3501 +                       /* Final -- destroy now */
3502 +                       destroy_trans(trans, 0);
3503 +               }
3504 +       } else if (h->oseqno == trans->oiseqno) {
3505 +               /* Last incoming sequence number -- send ACK without processing */
3506 +               dundi_ack(trans, 0);
3507 +       } else {
3508 +               /* Out of window -- simply drop */
3509 +               ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
3510 +       }
3511 +       return 0;
3512 +}
3513 +
3514 +static int socket_read(int *id, int fd, short events, void *cbdata)
3515 +{
3516 +       struct sockaddr_in sin;
3517 +       int res;
3518 +       struct dundi_hdr *h;
3519 +       unsigned char buf[MAX_PACKET_SIZE];
3520 +       int len;
3521 +       len = sizeof(sin);
3522 +       res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
3523 +       if (res < 0) {
3524 +               if (errno != ECONNREFUSED)
3525 +                       ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
3526 +               return 1;
3527 +       }
3528 +       if (res < sizeof(struct dundi_hdr)) {
3529 +               ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
3530 +               return 1;
3531 +       }
3532 +       buf[res] = '\0';
3533 +       h = (struct dundi_hdr *)buf;
3534 +       if (dundidebug)
3535 +               dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
3536 +       ast_mutex_lock(&peerlock);
3537 +       handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
3538 +       ast_mutex_unlock(&peerlock);
3539 +       return 1;
3540 +}
3541 +
3542 +static void build_secret(char *secret, int seclen)
3543 +{
3544 +       char tmp[16];
3545 +       char *s;
3546 +       build_iv(tmp);
3547 +       secret[0] = '\0';
3548 +       ast_base64encode(secret ,tmp, sizeof(tmp), seclen);
3549 +       /* Eliminate potential bad characters */
3550 +       while((s = strchr(secret, ';'))) *s = '+';
3551 +       while((s = strchr(secret, '/'))) *s = '+';
3552 +       while((s = strchr(secret, ':'))) *s = '+';
3553 +       while((s = strchr(secret, '@'))) *s = '+';
3554 +}
3555 +
3556 +
3557 +static void save_secret(const char *newkey, const char *oldkey)
3558 +{
3559 +       char tmp[256];
3560 +       if (oldkey)
3561 +               snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
3562 +       else
3563 +               snprintf(tmp, sizeof(tmp), "%s", newkey);
3564 +       rotatetime = time(NULL) + DUNDI_SECRET_TIME;
3565 +       ast_db_put(secretpath, "secret", tmp);
3566 +       snprintf(tmp, sizeof(tmp), "%ld", rotatetime);
3567 +       ast_db_put(secretpath, "secretexpiry", tmp);
3568 +}
3569 +
3570 +static void load_password(void)
3571 +{
3572 +       char *current=NULL;
3573 +       char *last=NULL;
3574 +       char tmp[256];
3575 +       time_t expired;
3576 +       
3577 +       ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
3578 +       if (sscanf(tmp, "%ld", &expired) == 1) {
3579 +               ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
3580 +               current = strchr(tmp, ';');
3581 +               if (!current)
3582 +                       current = tmp;
3583 +               else {
3584 +                       *current = '\0';
3585 +                       current++;
3586 +               };
3587 +               if ((time(NULL) - expired) < 0) {
3588 +                       if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
3589 +                               expired = time(NULL) + DUNDI_SECRET_TIME;
3590 +               } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
3591 +                       last = current;
3592 +                       current = NULL;
3593 +               } else {
3594 +                       last = NULL;
3595 +                       current = NULL;
3596 +               }
3597 +       }
3598 +       if (current) {
3599 +               /* Current key is still valid, just setup rotatation properly */
3600 +               strncpy(cursecret, current, sizeof(cursecret) - 1);
3601 +               rotatetime = expired;
3602 +       } else {
3603 +               /* Current key is out of date, rotate or eliminate all together */
3604 +               build_secret(cursecret, sizeof(cursecret));
3605 +               save_secret(cursecret, last);
3606 +       }
3607 +}
3608 +
3609 +static void check_password(void)
3610 +{
3611 +       char oldsecret[80];
3612 +       time_t now;
3613 +       
3614 +       time(&now);     
3615 +#if 0
3616 +       printf("%ld/%ld\n", now, rotatetime);
3617 +#endif
3618 +       if ((now - rotatetime) >= 0) {
3619 +               /* Time to rotate keys */
3620 +               strncpy(oldsecret, cursecret, sizeof(oldsecret) - 1);
3621 +               build_secret(cursecret, sizeof(cursecret));
3622 +               save_secret(cursecret, oldsecret);
3623 +       }
3624 +}
3625 +
3626 +static void *network_thread(void *ignore)
3627 +{
3628 +       /* Our job is simple: Send queued messages, retrying if necessary.  Read frames 
3629 +          from the network, and queue them for delivery to the channels */
3630 +       int res;
3631 +       /* Establish I/O callback for socket read */
3632 +       ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
3633 +       for(;;) {
3634 +               res = ast_sched_wait(sched);
3635 +               if ((res > 1000) || (res < 0))
3636 +                       res = 1000;
3637 +               res = ast_io_wait(io, res);
3638 +               if (res >= 0) {
3639 +                       ast_mutex_lock(&peerlock);
3640 +                       ast_sched_runq(sched);
3641 +                       ast_mutex_unlock(&peerlock);
3642 +               }
3643 +               check_password();
3644 +       }
3645 +       return NULL;
3646 +}
3647 +
3648 +static void *process_precache(void *ign)
3649 +{
3650 +       struct dundi_precache_queue *qe;
3651 +       time_t now;
3652 +       char context[256]="";
3653 +       char number[256]="";
3654 +       int run;
3655 +       for (;;) {
3656 +               time(&now);
3657 +               run = 0;
3658 +               ast_mutex_lock(&pclock);
3659 +               if (pcq) {
3660 +                       if (!pcq->expiration) {
3661 +                               /* Gone...  Remove... */
3662 +                               qe = pcq;
3663 +                               pcq = pcq->next;
3664 +                               free(qe);
3665 +                       } else if (pcq->expiration < now) {
3666 +                               /* Process this entry */
3667 +                               pcq->expiration = 0;
3668 +                               strncpy(context, pcq->context, sizeof(context) - 1);
3669 +                               strncpy(number, pcq->number, sizeof(number) - 1);
3670 +                               run = 1;
3671 +                       }
3672 +               }
3673 +               ast_mutex_unlock(&pclock);
3674 +               if (run) {
3675 +                       dundi_precache(context, number);
3676 +               } else
3677 +                       sleep(1);
3678 +       }
3679 +       return NULL;
3680 +}
3681 +
3682 +static int start_network_thread(void)
3683 +{
3684 +       ast_pthread_create(&netthreadid, NULL, network_thread, NULL);
3685 +       ast_pthread_create(&precachethreadid, NULL, process_precache, NULL);
3686 +       return 0;
3687 +}
3688 +
3689 +static int dundi_do_debug(int fd, int argc, char *argv[])
3690 +{
3691 +       if (argc != 2)
3692 +               return RESULT_SHOWUSAGE;
3693 +       dundidebug = 1;
3694 +       ast_cli(fd, "DUNDi Debugging Enabled\n");
3695 +       return RESULT_SUCCESS;
3696 +}
3697 +
3698 +static int dundi_do_store_history(int fd, int argc, char *argv[])
3699 +{
3700 +       if (argc != 3)
3701 +               return RESULT_SHOWUSAGE;
3702 +       global_storehistory = 1;
3703 +       ast_cli(fd, "DUNDi History Storage Enabled\n");
3704 +       return RESULT_SUCCESS;
3705 +}
3706 +
3707 +static int dundi_flush(int fd, int argc, char *argv[])
3708 +{
3709 +       int stats=0;
3710 +       if ((argc < 2) || (argc > 3))
3711 +               return RESULT_SHOWUSAGE;
3712 +       if (argc > 2) {
3713 +               if (!strcasecmp(argv[2], "stats"))
3714 +                       stats = 1;
3715 +               else
3716 +                       return RESULT_SHOWUSAGE;
3717 +       }
3718 +       if (stats) {
3719 +               /* Flush statistics */
3720 +               struct dundi_peer *p;
3721 +               int x;
3722 +               ast_mutex_lock(&peerlock);
3723 +               p = peers;
3724 +               while(p) {
3725 +                       for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
3726 +                               if (p->lookups[x])
3727 +                                       free(p->lookups[x]);
3728 +                               p->lookups[x] = NULL;
3729 +                               p->lookuptimes[x] = 0;
3730 +                       }
3731 +                       p->avgms = 0;
3732 +                       p = p->next;
3733 +               }
3734 +               ast_mutex_unlock(&peerlock);
3735 +       } else {
3736 +               ast_db_deltree("dundi/cache", NULL);
3737 +               ast_cli(fd, "DUNDi Cache Flushed\n");
3738 +       }
3739 +       return RESULT_SUCCESS;
3740 +}
3741 +
3742 +static int dundi_no_debug(int fd, int argc, char *argv[])
3743 +{
3744 +       if (argc != 3)
3745 +               return RESULT_SHOWUSAGE;
3746 +       dundidebug = 0;
3747 +       ast_cli(fd, "DUNDi Debugging Disabled\n");
3748 +       return RESULT_SUCCESS;
3749 +}
3750 +
3751 +static int dundi_no_store_history(int fd, int argc, char *argv[])
3752 +{
3753 +       if (argc != 4)
3754 +               return RESULT_SHOWUSAGE;
3755 +       global_storehistory = 0;
3756 +       ast_cli(fd, "DUNDi History Storage Disabled\n");
3757 +       return RESULT_SUCCESS;
3758 +}
3759 +
3760 +static char *model2str(int model)
3761 +{
3762 +       switch(model) {
3763 +       case DUNDI_MODEL_INBOUND:
3764 +               return "Inbound";
3765 +       case DUNDI_MODEL_OUTBOUND:
3766 +               return "Outbound";
3767 +       case DUNDI_MODEL_SYMMETRIC:
3768 +               return "Symmetric";
3769 +       default:
3770 +               return "Unknown";
3771 +       }
3772 +}
3773 +
3774 +static char *complete_peer_helper(char *line, char *word, int pos, int state, int rpos)
3775 +{
3776 +       int which=0;
3777 +       char *ret;
3778 +       struct dundi_peer *p;
3779 +       char eid_str[20];
3780 +       if (pos != rpos)
3781 +               return NULL;
3782 +       ast_mutex_lock(&peerlock);
3783 +       p = peers;
3784 +       while(p) {
3785 +               if (!strncasecmp(word, dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), strlen(word))) {
3786 +                       if (++which > state)
3787 +                               break;
3788 +               }
3789 +               p = p->next;
3790 +       }
3791 +       if (p) {
3792 +               ret = strdup(dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid));
3793 +       } else
3794 +               ret = NULL;
3795 +       ast_mutex_unlock(&peerlock);
3796 +       return ret;
3797 +}
3798 +
3799 +static char *complete_peer_4(char *line, char *word, int pos, int state)
3800 +{
3801 +       return complete_peer_helper(line, word, pos, state, 3);
3802 +}
3803 +
3804 +static int rescomp(const void *a, const void *b)
3805 +{
3806 +       const struct dundi_result *resa, *resb;
3807 +       resa = a;
3808 +       resb = b;
3809 +       if (resa->weight < resb->weight)
3810 +               return -1;
3811 +       if (resa->weight > resb->weight)
3812 +               return 1;
3813 +       return 0;
3814 +}
3815 +
3816 +static void sort_results(struct dundi_result *results, int count)
3817 +{
3818 +       qsort(results, count, sizeof(results[0]), rescomp);
3819 +}
3820 +
3821 +static int dundi_do_lookup(int fd, int argc, char *argv[])
3822 +{
3823 +       int res;
3824 +       char tmp[256] = "";
3825 +       char fs[80] = "";
3826 +       char *context;
3827 +       int x;
3828 +       int bypass = 0;
3829 +       struct dundi_result dr[MAX_RESULTS];
3830 +       struct timeval start;
3831 +       if ((argc < 3) || (argc > 4))
3832 +               return RESULT_SHOWUSAGE;
3833 +       if (argc > 3) {
3834 +               if (!strcasecmp(argv[3], "bypass"))
3835 +                       bypass=1;
3836 +               else
3837 +                       return RESULT_SHOWUSAGE;
3838 +       }
3839 +       strncpy(tmp, argv[2], sizeof(tmp) - 1);
3840 +       context = strchr(tmp, '@');
3841 +       if (context) {
3842 +               *context = '\0';
3843 +               context++;
3844 +       }
3845 +       gettimeofday(&start, NULL);
3846 +       res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
3847 +       
3848 +       if (res < 0) 
3849 +               ast_cli(fd, "DUNDi lookup returned error.\n");
3850 +       else if (!res) 
3851 +               ast_cli(fd, "DUNDi lookup returned no results.\n");
3852 +       else
3853 +               sort_results(dr, res);
3854 +       for (x=0;x<res;x++) {
3855 +               ast_cli(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));
3856 +               ast_cli(fd, "     from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
3857 +       }
3858 +       ast_cli(fd, "DUNDi lookup completed in %d ms\n", calc_ms(&start));
3859 +       return RESULT_SUCCESS;
3860 +}
3861 +
3862 +static int dundi_do_precache(int fd, int argc, char *argv[])
3863 +{
3864 +       int res;
3865 +       char tmp[256] = "";
3866 +       char *context;
3867 +       struct timeval start;
3868 +       if ((argc < 3) || (argc > 3))
3869 +               return RESULT_SHOWUSAGE;
3870 +       strncpy(tmp, argv[2], sizeof(tmp) - 1);
3871 +       context = strchr(tmp, '@');
3872 +       if (context) {
3873 +               *context = '\0';
3874 +               context++;
3875 +       }
3876 +       gettimeofday(&start, NULL);
3877 +       res = dundi_precache(context, tmp);
3878 +       
3879 +       if (res < 0) 
3880 +               ast_cli(fd, "DUNDi precache returned error.\n");
3881 +       else if (!res) 
3882 +               ast_cli(fd, "DUNDi precache returned no error.\n");
3883 +       ast_cli(fd, "DUNDi lookup completed in %d ms\n", calc_ms(&start));
3884 +       return RESULT_SUCCESS;
3885 +}
3886 +
3887 +static int dundi_do_query(int fd, int argc, char *argv[])
3888 +{
3889 +       int res;
3890 +       char tmp[256] = "";
3891 +       char *context;
3892 +       dundi_eid eid;
3893 +       struct dundi_entity_info dei;
3894 +       if ((argc < 3) || (argc > 3))
3895 +               return RESULT_SHOWUSAGE;
3896 +       if (dundi_str_to_eid(&eid, argv[2])) {
3897 +               ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
3898 +               return RESULT_SHOWUSAGE;
3899 +       }
3900 +       strncpy(tmp, argv[2], sizeof(tmp) - 1);
3901 +       context = strchr(tmp, '@');
3902 +       if (context) {
3903 +               *context = '\0';
3904 +               context++;
3905 +       }
3906 +       res = dundi_query_eid(&dei, context, eid);
3907 +       if (res < 0) 
3908 +               ast_cli(fd, "DUNDi Query EID returned error.\n");
3909 +       else if (!res) 
3910 +               ast_cli(fd, "DUNDi Query EID returned no results.\n");
3911 +       else {
3912 +               ast_cli(fd, "DUNDi Query EID succeeded:\n");
3913 +               ast_cli(fd, "Department:      %s\n", dei.orgunit);
3914 +               ast_cli(fd, "Organization:    %s\n", dei.org);
3915 +               ast_cli(fd, "City/Locality:   %s\n", dei.locality);
3916 +               ast_cli(fd, "State/Province:  %s\n", dei.stateprov);
3917 +               ast_cli(fd, "Country:         %s\n", dei.country);
3918 +               ast_cli(fd, "E-mail:          %s\n", dei.email);
3919 +               ast_cli(fd, "Phone:           %s\n", dei.phone);
3920 +               ast_cli(fd, "IP Address:      %s\n", dei.ipaddr);
3921 +       }
3922 +       return RESULT_SUCCESS;
3923 +}
3924 +
3925 +static int dundi_show_peer(int fd, int argc, char *argv[])
3926 +{
3927 +       struct dundi_peer *peer;
3928 +       struct permission *p;
3929 +       char *order;
3930 +       char iabuf[INET_ADDRSTRLEN];
3931 +       char eid_str[20];
3932 +       int x, cnt;
3933 +       
3934 +       if (argc != 4)
3935 +               return RESULT_SHOWUSAGE;
3936 +       ast_mutex_lock(&peerlock);
3937 +       peer = peers;
3938 +       while(peer) {
3939 +               if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
3940 +                       break;
3941 +               peer = peer->next;
3942 +       }
3943 +       if (peer) {
3944 +               switch(peer->order) {
3945 +               case 0:
3946 +                       order = "Primary";
3947 +                       break;
3948 +               case 1:
3949 +                       order = "Secondary";
3950 +                       break;
3951 +               case 2:
3952 +                       order = "Tertiary";
3953 +                       break;
3954 +               case 3:
3955 +                       order = "Quartiary";
3956 +                       break;
3957 +               default:
3958 +                       order = "Unknown";
3959 +               }
3960 +               ast_cli(fd, "Peer:    %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
3961 +               ast_cli(fd, "Model:   %s\n", model2str(peer->model));
3962 +               ast_cli(fd, "Order:   %s\n", order);
3963 +               ast_cli(fd, "Host:    %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "<Unspecified>");
3964 +               ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
3965 +               ast_cli(fd, "KeyPend: %s\n", peer->keypending ? "yes" : "no");
3966 +               ast_cli(fd, "Reg:     %s\n", peer->registerid < 0 ? "No" : "Yes");
3967 +               ast_cli(fd, "In Key:  %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
3968 +               ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
3969 +               if (peer->include) {
3970 +                       ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
3971 +               }
3972 +               p = peer->include;
3973 +               while(p) {
3974 +                       ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
3975 +                       p = p->next;
3976 +               }
3977 +               if (peer->permit) {
3978 +                       ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
3979 +               }
3980 +               p = peer->permit;
3981 +               while(p) {
3982 +                       ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
3983 +                       p = p->next;
3984 +               }
3985 +               cnt = 0;
3986 +               for (x=0;x<DUNDI_TIMING_HISTORY;x++) {
3987 +                       if (peer->lookups[x]) {
3988 +                               if (!cnt)
3989 +                                       ast_cli(fd, "Last few query times:\n");
3990 +                               ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
3991 +                               cnt++;
3992 +                       }
3993 +               }
3994 +               if (cnt)
3995 +                       ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
3996 +       } else
3997 +               ast_cli(fd, "No such peer '%s'\n", argv[3]);
3998 +       ast_mutex_unlock(&peerlock);
3999 +       return RESULT_SUCCESS;
4000 +}
4001 +
4002 +static int dundi_show_peers(int fd, int argc, char *argv[])
4003 +{
4004 +#define FORMAT2 "%-20.20s %-15.15s     %-10.10s %-10.10s %-8.8s %-15.15s\n"
4005 +#define FORMAT "%-20.20s %-15.15s %s %-10.10s %-10.10s %-8.8s %-15.15s\n"
4006 +       struct dundi_peer *peer;
4007 +       char iabuf[INET_ADDRSTRLEN];
4008 +       int registeredonly=0;
4009 +       char avgms[20];
4010 +       char eid_str[20];
4011 +       char *order = NULL;
4012 +
4013 +       if ((argc != 3) && (argc != 4) && (argc != 5))
4014 +               return RESULT_SHOWUSAGE;
4015 +       if ((argc == 4)) {
4016 +               if (!strcasecmp(argv[3], "registered")) {
4017 +                       registeredonly = 1;
4018 +               } else
4019 +                       return RESULT_SHOWUSAGE;
4020 +       }
4021 +       ast_mutex_lock(&peerlock);
4022 +       ast_cli(fd, FORMAT2, "EID", "Host", "Model", "Order", "AvgTime", "Status");
4023 +       for (peer = peers;peer;peer = peer->next) {
4024 +               char status[20] = "";
4025 +        int print_line = -1;
4026 +               char srch[2000] = "";
4027 +               if (registeredonly && !peer->addr.sin_addr.s_addr)
4028 +                       continue;
4029 +               if (peer->maxms) {
4030 +                       if (peer->lastms < 0)
4031 +                               strncpy(status, "UNREACHABLE", sizeof(status) - 1);
4032 +                       else if (peer->lastms > peer->maxms) 
4033 +                               snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
4034 +                       else if (peer->lastms) 
4035 +                               snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
4036 +                       else 
4037 +                               strncpy(status, "UNKNOWN", sizeof(status) - 1);
4038 +               } else 
4039 +                       strncpy(status, "Unmonitored", sizeof(status) - 1);
4040 +               if (peer->avgms) 
4041 +                       snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
4042 +               else
4043 +                       strcpy(avgms, "Unavail");
4044 +               switch(peer->order) {
4045 +               case 0:
4046 +                       order = "Primary";
4047 +                       break;
4048 +               case 1:
4049 +                       order = "Secondary";
4050 +                       break;
4051 +               case 2:
4052 +                       order = "Tertiary";
4053 +                       break;
4054 +               case 3:
4055 +                       order = "Quartiary";
4056 +                       break;
4057 +               default:
4058 +                       order = "Unknown";
4059 +               }
4060 +               snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
4061 +                                       peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
4062 +                                       peer->dynamic ? "(D)" : "(S)", model2str(peer->model), order, avgms, status);
4063 +
4064 +                if (argc == 5) {
4065 +                  if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
4066 +                        print_line = -1;
4067 +                   } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
4068 +                        print_line = 1;
4069 +                   } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
4070 +                        print_line = -1;
4071 +                   } else {
4072 +                        print_line = 0;
4073 +                  }
4074 +                }
4075 +               
4076 +        if (print_line) {
4077 +                       ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
4078 +                                       peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
4079 +                                       peer->dynamic ? "(D)" : "(S)", model2str(peer->model), order, avgms, status);
4080 +               }
4081 +       }
4082 +       ast_mutex_unlock(&peerlock);
4083 +       return RESULT_SUCCESS;
4084 +#undef FORMAT
4085 +#undef FORMAT2
4086 +}
4087 +
4088 +static int dundi_show_trans(int fd, int argc, char *argv[])
4089 +{
4090 +#define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
4091 +#define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
4092 +       struct dundi_transaction *trans;
4093 +       char iabuf[INET_ADDRSTRLEN];
4094 +       if (argc != 3)
4095 +               return RESULT_SHOWUSAGE;
4096 +       ast_mutex_lock(&peerlock);
4097 +       ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
4098 +       for (trans = alltrans;trans;trans = trans->allnext) {
4099 +                       ast_cli(fd, FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr), 
4100 +                                       ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
4101 +       }
4102 +       ast_mutex_unlock(&peerlock);
4103 +       return RESULT_SUCCESS;
4104 +#undef FORMAT
4105 +#undef FORMAT2
4106 +}
4107 +
4108 +static int dundi_show_entityid(int fd, int argc, char *argv[])
4109 +{
4110 +       char eid_str[20];
4111 +       if (argc != 3)
4112 +               return RESULT_SHOWUSAGE;
4113 +       ast_mutex_lock(&peerlock);
4114 +       dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
4115 +       ast_mutex_unlock(&peerlock);
4116 +       ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
4117 +       return RESULT_SUCCESS;
4118 +}
4119 +
4120 +static int dundi_show_requests(int fd, int argc, char *argv[])
4121 +{
4122 +#define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
4123 +#define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
4124 +       struct dundi_request *req;
4125 +       char eidstr[20];
4126 +       if (argc != 3)
4127 +               return RESULT_SHOWUSAGE;
4128 +       ast_mutex_lock(&peerlock);
4129 +       ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
4130 +       for (req = requests;req;req = req->next) {
4131 +                       ast_cli(fd, FORMAT, req->number, req->dcontext,
4132 +                                               dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
4133 +       }
4134 +       ast_mutex_unlock(&peerlock);
4135 +       return RESULT_SUCCESS;
4136 +#undef FORMAT
4137 +#undef FORMAT2
4138 +}
4139 +
4140 +/* Grok-a-dial DUNDi */
4141 +
4142 +static int dundi_show_mappings(int fd, int argc, char *argv[])
4143 +{
4144 +#define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
4145 +#define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
4146 +       struct dundi_mapping *map;
4147 +       char fs[256];
4148 +       if (argc != 3)
4149 +               return RESULT_SHOWUSAGE;
4150 +       ast_mutex_lock(&peerlock);
4151 +       ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
4152 +       for (map = mappings;map;map = map->next) {
4153 +                       ast_cli(fd, FORMAT, map->dcontext, map->weight, 
4154 +                                           ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext, 
4155 +                                                               dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
4156 +       }
4157 +       ast_mutex_unlock(&peerlock);
4158 +       return RESULT_SUCCESS;
4159 +#undef FORMAT
4160 +#undef FORMAT2
4161 +}
4162 +
4163 +static int dundi_show_precache(int fd, int argc, char *argv[])
4164 +{
4165 +#define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
4166 +#define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
4167 +       struct dundi_precache_queue *qe;
4168 +       int h,m,s;
4169 +       time_t now;
4170 +       
4171 +       if (argc != 3)
4172 +               return RESULT_SHOWUSAGE;
4173 +       time(&now);
4174 +       ast_mutex_lock(&pclock);
4175 +       ast_cli(fd, FORMAT2, "Number", "Context", "Expiration");
4176 +       for (qe = pcq;qe;qe = qe->next) {
4177 +               s = qe->expiration - now;
4178 +               h = s / 3600;
4179 +               s = s % 3600;
4180 +               m = s / 60;
4181 +               s = s % 60;
4182 +               ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s);
4183 +       }
4184 +       ast_mutex_unlock(&pclock);
4185 +       return RESULT_SUCCESS;
4186 +#undef FORMAT
4187 +#undef FORMAT2
4188 +}
4189 +
4190 +static char debug_usage[] = 
4191 +"Usage: dundi debug\n"
4192 +"       Enables dumping of DUNDi packets for debugging purposes\n";
4193 +
4194 +static char no_debug_usage[] = 
4195 +"Usage: dundi no debug\n"
4196 +"       Disables dumping of DUNDi packets for debugging purposes\n";
4197 +
4198 +static char store_history_usage[] = 
4199 +"Usage: dundi store history\n"
4200 +"       Enables storing of DUNDi requests and times for debugging\n"
4201 +"purposes\n";
4202 +
4203 +static char no_store_history_usage[] = 
4204 +"Usage: dundi no store history\n"
4205 +"       Disables storing of DUNDi requests and times for debugging\n"
4206 +"purposes\n";
4207 +
4208 +static char show_peers_usage[] = 
4209 +"Usage: dundi show peers\n"
4210 +"       Lists all known DUNDi peers.\n";
4211 +
4212 +static char show_trans_usage[] = 
4213 +"Usage: dundi show trans\n"
4214 +"       Lists all known DUNDi transactions.\n";
4215 +
4216 +static char show_mappings_usage[] = 
4217 +"Usage: dundi show mappings\n"
4218 +"       Lists all known DUNDi mappings.\n";
4219 +
4220 +static char show_precache_usage[] = 
4221 +"Usage: dundi show precache\n"
4222 +"       Lists all known DUNDi scheduled precache updates.\n";
4223 +
4224 +static char show_entityid_usage[] = 
4225 +"Usage: dundi show entityid\n"
4226 +"       Displays the global entityid for this host.\n";
4227 +
4228 +static char show_peer_usage[] = 
4229 +"Usage: dundi show peer [peer]\n"
4230 +"       Provide a detailed description of a specifid DUNDi peer.\n";
4231 +
4232 +static char show_requests_usage[] = 
4233 +"Usage: dundi show requests\n"
4234 +"       Lists all known pending DUNDi requests.\n";
4235 +
4236 +static char lookup_usage[] =
4237 +"Usage: dundi lookup <number>[@context] [bypass]\n"
4238 +"       Lookup the given number within the given DUNDi context\n"
4239 +"(or e164 if none is specified).  Bypasses cache if 'bypass'\n"
4240 +"keyword is specified.\n";
4241 +
4242 +static char precache_usage[] =
4243 +"Usage: dundi precache <number>[@context]\n"
4244 +"       Lookup the given number within the given DUNDi context\n"
4245 +"(or e164 if none is specified) and precaches the results to any\n"
4246 +"upstream DUNDi push servers.\n";
4247 +
4248 +static char query_usage[] =
4249 +"Usage: dundi query <entity>[@context]\n"
4250 +"       Attempts to retrieve contact information for a specific\n"
4251 +"DUNDi entity identifier (EID) within a given DUNDi context (or\n"
4252 +"e164 if none is specified).\n";
4253 +
4254 +static char flush_usage[] =
4255 +"Usage: dundi flush [stats]\n"
4256 +"       Flushes DUNDi answer cache, used primarily for debug.  If\n"
4257 +"'stats' is present, clears timer statistics instead of normal\n"
4258 +"operation.\n";
4259 +
4260 +static struct ast_cli_entry  cli_debug =
4261 +       { { "dundi", "debug", NULL }, dundi_do_debug, "Enable DUNDi debugging", debug_usage };
4262 +
4263 +static struct ast_cli_entry  cli_store_history =
4264 +       { { "dundi", "store", "history", NULL }, dundi_do_store_history, "Enable DUNDi historic records", store_history_usage };
4265 +
4266 +static struct ast_cli_entry  cli_no_store_history =
4267 +       { { "dundi", "no", "store", "history", NULL }, dundi_no_store_history, "Disable DUNDi historic records", no_store_history_usage };
4268 +
4269 +static struct ast_cli_entry  cli_flush =
4270 +       { { "dundi", "flush", NULL }, dundi_flush, "Flush DUNDi cache", flush_usage };
4271 +
4272 +static struct ast_cli_entry  cli_no_debug =
4273 +       { { "dundi", "no", "debug", NULL }, dundi_no_debug, "Disable DUNDi debugging", no_debug_usage };
4274 +
4275 +static struct ast_cli_entry  cli_show_peers =
4276 +       { { "dundi", "show", "peers", NULL }, dundi_show_peers, "Show defined DUNDi peers", show_peers_usage };
4277 +
4278 +static struct ast_cli_entry  cli_show_trans =
4279 +       { { "dundi", "show", "trans", NULL }, dundi_show_trans, "Show active DUNDi transactions", show_trans_usage };
4280 +
4281 +static struct ast_cli_entry  cli_show_entityid =
4282 +       { { "dundi", "show", "entityid", NULL }, dundi_show_entityid, "Display Global Entity ID", show_entityid_usage };
4283 +
4284 +static struct ast_cli_entry  cli_show_mappings =
4285 +       { { "dundi", "show", "mappings", NULL }, dundi_show_mappings, "Show DUNDi mappings", show_mappings_usage };
4286 +
4287 +static struct ast_cli_entry  cli_show_precache =
4288 +       { { "dundi", "show", "precache", NULL }, dundi_show_precache, "Show DUNDi precache", show_precache_usage };
4289 +
4290 +static struct ast_cli_entry  cli_show_requests =
4291 +       { { "dundi", "show", "requests", NULL }, dundi_show_requests, "Show DUNDi requests", show_requests_usage };
4292 +
4293 +static struct ast_cli_entry  cli_show_peer =
4294 +       { { "dundi", "show", "peer", NULL }, dundi_show_peer, "Show info on a specific DUNDi peer", show_peer_usage, complete_peer_4 };
4295 +
4296 +static struct ast_cli_entry  cli_lookup =
4297 +       { { "dundi", "lookup", NULL }, dundi_do_lookup, "Lookup a number in DUNDi", lookup_usage };
4298 +
4299 +static struct ast_cli_entry  cli_precache =
4300 +       { { "dundi", "precache", NULL }, dundi_do_precache, "Precache a number in DUNDi", precache_usage };
4301 +
4302 +static struct ast_cli_entry  cli_queryeid =
4303 +       { { "dundi", "query", NULL }, dundi_do_query, "Query a DUNDi EID", query_usage };
4304 +
4305 +STANDARD_LOCAL_USER;
4306 +
4307 +LOCAL_USER_DECL;
4308 +
4309 +static struct dundi_transaction *create_transaction(struct dundi_peer *p)
4310 +{
4311 +       struct dundi_transaction *trans;
4312 +       int tid;
4313 +       
4314 +       /* Don't allow creation of transactions to non-registered peers */
4315 +       if (p && !p->addr.sin_addr.s_addr)
4316 +               return NULL;
4317 +       tid = get_trans_id();
4318 +       if (tid < 1)
4319 +               return NULL;
4320 +       trans = malloc(sizeof(struct dundi_transaction));
4321 +       if (trans) {
4322 +               memset(trans, 0, sizeof(struct dundi_transaction));
4323 +               if (global_storehistory) {
4324 +                       gettimeofday(&trans->start, NULL);
4325 +                       trans->flags |= FLAG_STOREHIST;
4326 +               }
4327 +               trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
4328 +               trans->autokillid = -1;
4329 +               if (p) {
4330 +                       apply_peer(trans, p);
4331 +                       if (!p->sentfullkey)
4332 +                               trans->flags |= FLAG_SENDFULLKEY;
4333 +               }
4334 +               trans->strans = tid;
4335 +               trans->allnext = alltrans;
4336 +               alltrans = trans;
4337 +       }
4338 +       return trans;
4339 +}
4340 +
4341 +static int dundi_xmit(struct dundi_packet *pack)
4342 +{
4343 +       int res;
4344 +       char iabuf[INET_ADDRSTRLEN];
4345 +       if (dundidebug)
4346 +               dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
4347 +       res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
4348 +       if (res < 0) {
4349 +               ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n", 
4350 +                       ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
4351 +                       ntohs(pack->parent->addr.sin_port), strerror(errno));
4352 +       }
4353 +       if (res > 0)
4354 +               res = 0;
4355 +       return res;
4356 +}
4357 +
4358 +static inline char *dstatus_append_long(char *buf, long val) {
4359 +       val = htonl(val);
4360 +       memcpy(buf, &val, sizeof(long));
4361 +       return (buf + sizeof(long));
4362 +}
4363 +
4364 +static inline char *dstatus_append_short(char *buf, short val) {
4365 +       val = htons(val);
4366 +       memcpy(buf, &val, sizeof(short));
4367 +       return (buf + sizeof(short));
4368 +}
4369 +
4370 +static inline char *dstatus_append_string(char *buf, char *str) {
4371 +       unsigned short len = (unsigned short)strlen(str);
4372 +       buf = dstatus_append_short(buf, len);
4373 +       memcpy(buf, str, len);
4374 +       return (buf + len);
4375 +}
4376 +
4377 +/* Create a packet for the DUNDi Status Updates */
4378 +static char *dstatus_v2_pkt_header(char *buf, char pkt_type, time_t ts, short seq, short totseq) {
4379 +       *buf++ = '\003';
4380 +       *buf++ = pkt_type;
4381 +
4382 +       /* Timestamp this sequence */
4383 +       buf = dstatus_append_long(buf, ts);
4384 +
4385 +       /* Setup sequence headers */
4386 +       buf = dstatus_append_short(buf, seq);
4387 +       buf = dstatus_append_short(buf, totseq);
4388 +
4389 +       /* Append our global EID */
4390 +       memcpy(buf, &global_eid, sizeof(global_eid));
4391 +       buf += sizeof(global_eid);
4392 +
4393 +       buf = dstatus_append_string(buf, map_context);
4394 +
4395 +       return buf;
4396 +}
4397 +
4398 +static int dundi_xmit_peering(void *data)
4399 +{
4400 +       char buf[1500];
4401 +       char *ptr = buf;
4402 +       short seq = 1, totseq = 0;
4403 +       int need_send = 0, peer_count = 0;
4404 +       struct dundi_peer *peer;
4405 +       time_t send_t = time(NULL);
4406 +
4407 +       /* If we have no where to send, don't bother */
4408 +       if(!map_addr.sin_addr.s_addr) {
4409 +               if(option_verbose)
4410 +                       ast_verbose(VERBOSE_PREFIX_1 "No server to send mapping update to...\n");
4411 +               map_peering_sid = ast_sched_add(sched, map_update_interval, dundi_xmit_peering, 0);
4412 +               return 0;
4413 +       }
4414 +   
4415 +       /* Provide a sequence number for the packet and totals */
4416 +       ast_mutex_lock(&peerlock);
4417 +       for (peer = peers;peer;peer = peer->next) {
4418 +               if(has_permission(peer->include, map_context) || has_permission(peer->permit, map_context)) {
4419 +                       peer_count++;
4420 +                       need_send = 1;
4421 +                       if(peer_count == map_updates_per_pkt) {
4422 +                               peer_count = 0;
4423 +                               need_send = 0;
4424 +                               totseq++;
4425 +                       }
4426 +               }
4427 +       }
4428 +       totseq += need_send;
4429 +
4430 +       /* Initialize the packet header */
4431 +       ptr = dstatus_v2_pkt_header(buf, 1, send_t, seq, totseq);
4432 +
4433 +       /* Include our update interval, in seconds */
4434 +       ptr = dstatus_append_short(ptr, map_update_interval/1000);
4435 +
4436 +       /* Include peers/packet configured */
4437 +       *ptr++ = (unsigned char)map_updates_per_pkt;
4438 +
4439 +       peer_count = 0;
4440 +       need_send = 0;
4441 +       for (peer = peers;peer;peer = peer->next) {
4442 +               if(!has_permission(peer->include, map_context) && !has_permission(peer->permit, map_context))
4443 +                       continue;
4444 +
4445 +               peer_count++;
4446 +               need_send = 1;
4447 +
4448 +               /* Copy the peers EID */
4449 +               memcpy(ptr, &peer->eid, sizeof(peer->eid));
4450 +               ptr += sizeof(peer->eid);
4451 +
4452 +               /* This is the remote peer */
4453 +               *ptr++ = (peer->dynamic?0:1);
4454 +               memcpy(ptr, &peer->addr.sin_addr.s_addr, sizeof(peer->addr.sin_addr.s_addr));
4455 +               ptr += sizeof(peer->addr.sin_addr.s_addr);
4456 +
4457 +               /* Append the model and order */
4458 +               *ptr++ = peer->model;
4459 +               *ptr++ = peer->order;
4460 +
4461 +               /* Do some long encoding of the timings */
4462 +               ptr = dstatus_append_long(ptr, peer->maxms);
4463 +               ptr = dstatus_append_long(ptr, peer->lastms);
4464 +               ptr = dstatus_append_long(ptr, peer->avgms);
4465 +
4466 +               if(peer_count == map_updates_per_pkt) { /* Okay, it's actually arbitrary */
4467 +                       peer_count = 0;
4468 +                       need_send = 0;
4469 +                       sendto(netsocket, buf, ptr - buf, 0, (struct sockaddr *)&map_addr, sizeof(map_addr));
4470 +                       seq++; /* We sent one, so move on */
4471 +
4472 +                       ptr = dstatus_v2_pkt_header(buf, 1, send_t, seq, totseq);
4473 +                       /* Include our update interval, in seconds */
4474 +                       ptr = dstatus_append_short(ptr, map_update_interval/1000);
4475 +
4476 +                       /* Include peers/packet configured */
4477 +                       *ptr++ = (unsigned char)map_updates_per_pkt;
4478 +               }
4479 +       }
4480 +
4481 +       /* If we get here, and haven't sent the packet, send it now */
4482 +       if(need_send) 
4483 +               sendto(netsocket, buf, ptr - buf, 0, (struct sockaddr *)&map_addr, sizeof(map_addr));
4484 +   
4485 +       /* Unlock */
4486 +       ast_mutex_unlock(&peerlock);
4487 +
4488 +       /* Reschedule yourselves */
4489 +       map_peering_sid = ast_sched_add(sched, map_update_interval, dundi_xmit_peering, 0);
4490 +       return RESULT_SUCCESS;   
4491 +}
4492 +
4493 +static int dundi_xmit_contact(void *data) {
4494 +       char buf[1500];
4495 +       char *ptr = buf;
4496 +
4497 +       /* Packet type 2, sent now, 1/1 packets */
4498 +       ptr = dstatus_v2_pkt_header(buf, 2, time(NULL), 1, 1);
4499 +       
4500 +       ptr = dstatus_append_string(ptr, dept);
4501 +       ptr = dstatus_append_string(ptr, org);
4502 +       ptr = dstatus_append_string(ptr, locality);
4503 +       ptr = dstatus_append_string(ptr, stateprov);
4504 +       ptr = dstatus_append_string(ptr, country);
4505 +       ptr = dstatus_append_string(ptr, email);
4506 +       ptr = dstatus_append_string(ptr, phone);
4507 +
4508 +       sendto(netsocket, buf, ptr - buf, 0, (struct sockaddr *)&map_addr, sizeof(map_addr));
4509 +
4510 +       map_contact_sid = ast_sched_add(sched, map_update_interval * 5, dundi_xmit_contact, 0);
4511 +       return RESULT_SUCCESS;
4512 +}
4513 +
4514 +static void destroy_packet(struct dundi_packet *pack, int needfree)
4515 +{
4516 +       struct dundi_packet *prev, *cur;
4517 +       if (pack->parent) {
4518 +               prev = NULL;
4519 +               cur = pack->parent->packets;
4520 +               while(cur) {
4521 +                       if (cur == pack) {
4522 +                               if (prev)
4523 +                                       prev->next = cur->next;
4524 +                               else
4525 +                                       pack->parent->packets = cur->next;
4526 +                               break;
4527 +                       }
4528 +                       prev = cur;
4529 +                       cur = cur->next;
4530 +               }
4531 +       }
4532 +       if (pack->retransid > -1)
4533 +               ast_sched_del(sched, pack->retransid);
4534 +       if (needfree)
4535 +               free(pack);
4536 +       else {
4537 +               pack->retransid = -1;
4538 +               pack->next = NULL;
4539 +       }
4540 +}
4541 +
4542 +static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
4543 +{
4544 +       struct dundi_transaction *cur, *prev;
4545 +       struct dundi_peer *peer;
4546 +       struct timeval tv;
4547 +       int ms;
4548 +       int x;
4549 +       int cnt;
4550 +       char eid_str[20];
4551 +       if (trans->flags & (FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
4552 +               peer = peers;
4553 +               while (peer) {
4554 +                       if (peer->regtrans == trans)
4555 +                               peer->regtrans = NULL;
4556 +                       if (peer->keypending == trans)
4557 +                               peer->keypending = NULL;
4558 +                       if (peer->qualtrans == trans) {
4559 +                               if (fromtimeout) {
4560 +                                       if (peer->lastms > -1)
4561 +                                               ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4562 +                                       peer->lastms = -1;
4563 +                               } else {
4564 +                                       gettimeofday(&tv, NULL);
4565 +                                       ms = (tv.tv_sec - peer->qualtx.tv_sec) * 1000 + 
4566 +                                                       (tv.tv_usec - peer->qualtx.tv_usec) / 1000;
4567 +                                       if (ms < 1)
4568 +                                               ms = 1;
4569 +                                       if (ms < peer->maxms) {
4570 +                                               if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
4571 +                                                       ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
4572 +                                       } else if (peer->lastms < peer->maxms) {
4573 +                                               ast_log(LOG_NOTICE, "Peer '%s' has become TOO LAGGED (%d ms)\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), ms);
4574 +                                       }
4575 +                                       peer->lastms = ms;
4576 +                               }
4577 +                               peer->qualtrans = NULL;
4578 +                       }
4579 +                       if (trans->flags & FLAG_STOREHIST) {
4580 +                               if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
4581 +                                       if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
4582 +                                               peer->avgms = 0;
4583 +                                               cnt = 0;
4584 +                                               if (peer->lookups[DUNDI_TIMING_HISTORY-1])
4585 +                                                       free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
4586 +                                               for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
4587 +                                                       peer->lookuptimes[x] = peer->lookuptimes[x-1];
4588 +                                                       peer->lookups[x] = peer->lookups[x-1];
4589 +                                                       if (peer->lookups[x]) {
4590 +                                                               peer->avgms += peer->lookuptimes[x];
4591 +                                                               cnt++;
4592 +                                                       }
4593 +                                               }
4594 +                                               peer->lookuptimes[0] = calc_ms(&trans->start);
4595 +                                               peer->lookups[0] = malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
4596 +                                               if (peer->lookups[0]) {
4597 +                                                       sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
4598 +                                                       peer->avgms += peer->lookuptimes[0];
4599 +                                                       cnt++;
4600 +                                               }
4601 +                                               if (cnt)
4602 +                                                       peer->avgms /= cnt;
4603 +                                       }
4604 +                               }
4605 +                       }
4606 +                       peer = peer->next;
4607 +               }
4608 +       }
4609 +       if (trans->parent) {
4610 +               /* Unlink from parent if appropriate */
4611 +               prev = NULL;
4612 +               cur = trans->parent->trans;
4613 +               while(cur) {
4614 +                       if (cur == trans) {
4615 +                               if (prev)
4616 +                                       prev->next = trans->next;
4617 +                               else
4618 +                                       trans->parent->trans = trans->next;
4619 +                               break;
4620 +                       }
4621 +                       prev = cur;
4622 +                       cur = cur->next;
4623 +               }
4624 +               if (!trans->parent->trans) {
4625 +                       /* Wake up sleeper */
4626 +                       if (trans->parent->pfds[1] > -1) {
4627 +                               write(trans->parent->pfds[1], "killa!", 6);
4628 +                       }
4629 +               }
4630 +       }
4631 +       /* Unlink from all trans */
4632 +       prev = NULL;
4633 +       cur = alltrans;
4634 +       while(cur) {
4635 +               if (cur == trans) {
4636 +                       if (prev)
4637 +                               prev->allnext = trans->allnext;
4638 +                       else
4639 +                               alltrans = trans->allnext;
4640 +                       break;
4641 +               }
4642 +               prev = cur;
4643 +               cur = cur->allnext;
4644 +       }
4645 +       destroy_packets(trans->packets);
4646 +       destroy_packets(trans->lasttrans);
4647 +       trans->packets = NULL;
4648 +       if (trans->autokillid > -1)
4649 +               ast_sched_del(sched, trans->autokillid);
4650 +       trans->autokillid = -1;
4651 +       if (trans->thread) {
4652 +               /* If used by a thread, mark as dead and be done */
4653 +               trans->flags |= FLAG_DEAD;
4654 +       } else
4655 +               free(trans);
4656 +}
4657 +
4658 +static int dundi_rexmit(void *data)
4659 +{
4660 +       struct dundi_packet *pack;
4661 +       char iabuf[INET_ADDRSTRLEN];
4662 +       int res;
4663 +       ast_mutex_lock(&peerlock);
4664 +       pack = data;
4665 +       if (pack->retrans < 1) {
4666 +               pack->retransid = -1;
4667 +               if (!(pack->parent->flags & FLAG_ISQUAL))
4668 +                       ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n", 
4669 +                               ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr), 
4670 +                               ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
4671 +               destroy_trans(pack->parent, 1);
4672 +               res = 0;
4673 +       } else {
4674 +               /* Decrement retransmission, try again */
4675 +               pack->retrans--;
4676 +               dundi_xmit(pack);
4677 +               res = 1;
4678 +       }
4679 +       ast_mutex_unlock(&peerlock);
4680 +       return res;
4681 +}
4682 +
4683 +static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
4684 +{
4685 +       struct dundi_packet *pack;
4686 +       int res;
4687 +       int len;
4688 +       char eid_str[20];
4689 +       len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
4690 +       /* Reserve enough space for encryption */
4691 +       if (trans->flags & FLAG_ENCRYPT)
4692 +               len += 384;
4693 +       pack = malloc(len);
4694 +       if (pack) {
4695 +               memset(pack, 0, len);
4696 +               pack->h = (struct dundi_hdr *)(pack->data);
4697 +               if (cmdresp != DUNDI_COMMAND_ACK) {
4698 +                       pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
4699 +                       pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
4700 +                       pack->next = trans->packets;
4701 +                       trans->packets = pack;
4702 +               }
4703 +               pack->parent = trans;
4704 +               pack->h->strans = htons(trans->strans);
4705 +               pack->h->dtrans = htons(trans->dtrans);
4706 +               pack->h->iseqno = trans->iseqno;
4707 +               pack->h->oseqno = trans->oseqno;
4708 +               pack->h->cmdresp = cmdresp;
4709 +               pack->datalen = sizeof(struct dundi_hdr);
4710 +               if (ied) {
4711 +                       memcpy(pack->h->ies, ied->buf, ied->pos);
4712 +                       pack->datalen += ied->pos;
4713 +               } 
4714 +               if (final) {
4715 +                       pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
4716 +                       trans->flags |= FLAG_FINAL;
4717 +               }
4718 +               pack->h->cmdflags = flags;
4719 +               if (cmdresp != DUNDI_COMMAND_ACK) {
4720 +                       trans->oseqno++;
4721 +                       trans->oseqno = trans->oseqno % 256;
4722 +               }
4723 +               trans->aseqno = trans->iseqno;
4724 +               /* If we have their public key, encrypt */
4725 +               if (trans->flags & FLAG_ENCRYPT) {
4726 +                       switch(cmdresp) {
4727 +                       case DUNDI_COMMAND_REGREQ:
4728 +                       case DUNDI_COMMAND_REGRESPONSE:
4729 +                       case DUNDI_COMMAND_DPDISCOVER:
4730 +                       case DUNDI_COMMAND_DPRESPONSE:
4731 +                       case DUNDI_COMMAND_EIDQUERY:
4732 +                       case DUNDI_COMMAND_EIDRESPONSE:
4733 +                       case DUNDI_COMMAND_PRECACHERQ:
4734 +                       case DUNDI_COMMAND_PRECACHERP:
4735 +                               if (dundidebug)
4736 +                                       dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
4737 +                               res = dundi_encrypt(trans, pack);
4738 +                               break;
4739 +                       default:
4740 +                               res = 0;
4741 +                       }
4742 +               } else 
4743 +                       res = 0;
4744 +               if (!res) 
4745 +                       res = dundi_xmit(pack);
4746 +               if (res)
4747 +                       ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
4748 +                               
4749 +               if (cmdresp == DUNDI_COMMAND_ACK)
4750 +                       free(pack);
4751 +               return res;
4752 +       }
4753 +       return -1;
4754 +}
4755 +
4756 +static int do_autokill(void *data)
4757 +{
4758 +       struct dundi_transaction *trans = data;
4759 +       char eid_str[20];
4760 +       ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n", 
4761 +               dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
4762 +       trans->autokillid = -1;
4763 +       destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
4764 +       return 0;
4765 +}
4766 +
4767 +static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
4768 +{
4769 +       struct dundi_peer *p;
4770 +       if (!dundi_eid_cmp(eid, us)) {
4771 +               dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
4772 +               return;
4773 +       }
4774 +       ast_mutex_lock(&peerlock);
4775 +       p = peers;
4776 +       while(p) {
4777 +               if (!dundi_eid_cmp(&p->eid, eid)) {
4778 +                       if (has_permission(p->include, context))
4779 +                               dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
4780 +                       else
4781 +                               dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
4782 +                       break;
4783 +               }
4784 +               p = p->next;
4785 +       }
4786 +       if (!p)
4787 +               dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
4788 +       ast_mutex_unlock(&peerlock);
4789 +}
4790 +
4791 +static int dundi_discover(struct dundi_transaction *trans)
4792 +{
4793 +       struct dundi_ie_data ied;
4794 +       int x;
4795 +       if (!trans->parent) {
4796 +               ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
4797 +               return -1;
4798 +       }
4799 +       memset(&ied, 0, sizeof(ied));
4800 +       dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
4801 +       if (!dundi_eid_zero(&trans->us_eid))
4802 +               dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
4803 +       for (x=0;x<trans->eidcount;x++)
4804 +               dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
4805 +       dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
4806 +       dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
4807 +       dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
4808 +       if (trans->parent->cbypass)
4809 +               dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
4810 +       if (trans->autokilltimeout)
4811 +               trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
4812 +       return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
4813 +}
4814 +
4815 +static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
4816 +{
4817 +       struct dundi_ie_data ied;
4818 +       int x, res;
4819 +       int max = 999999;
4820 +       int expiration = DUNDI_DEFAULT_CACHE_TIME;
4821 +       int ouranswers=0;
4822 +       dundi_eid *avoid[1] = { NULL, };
4823 +       int direct[1] = { 0, };
4824 +       struct dundi_result dr[MAX_RESULTS];
4825 +       struct dundi_hint_metadata hmd;
4826 +       if (!trans->parent) {
4827 +               ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
4828 +               return -1;
4829 +       }
4830 +       memset(&hmd, 0, sizeof(hmd));
4831 +       memset(&dr, 0, sizeof(dr));
4832 +       /* Look up the answers we're going to include */
4833 +       for (x=0;x<mapcount;x++)
4834 +               ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
4835 +       if (ouranswers < 0)
4836 +               ouranswers = 0;
4837 +       for (x=0;x<ouranswers;x++) {
4838 +               if (dr[x].weight < max)
4839 +                       max = dr[x].weight;
4840 +       }
4841 +       if (max) {
4842 +               /* If we do not have a canonical result, keep looking */
4843 +               res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, trans->parent->dcontext, trans->parent->number, trans->ttl, 1, &hmd, &expiration, 0, 1, &trans->them_eid, avoid, direct);
4844 +               if (res > 0) {
4845 +                       /* Append answer in result */
4846 +                       ouranswers += res;
4847 +               }
4848 +       }
4849 +       
4850 +       if (ouranswers > 0) {
4851 +               *foundanswers += ouranswers;
4852 +               memset(&ied, 0, sizeof(ied));
4853 +               dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
4854 +               if (!dundi_eid_zero(&trans->us_eid))
4855 +                       dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
4856 +               for (x=0;x<trans->eidcount;x++)
4857 +                       dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
4858 +               dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
4859 +               dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
4860 +               dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
4861 +               for (x=0;x<ouranswers;x++) {
4862 +                       /* Add answers */
4863 +                       if (dr[x].expiration && (expiration > dr[x].expiration))
4864 +                               expiration = dr[x].expiration;
4865 +                       dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
4866 +               }
4867 +               dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
4868 +               dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
4869 +               if (trans->autokilltimeout)
4870 +                       trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
4871 +               if (expiration < *minexp)
4872 +                       *minexp = expiration;
4873 +               return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
4874 +       } else {
4875 +               /* Oops, nothing to send... */
4876 +               destroy_trans(trans, 0);
4877 +               return 0;
4878 +       }
4879 +}
4880 +
4881 +static int dundi_query(struct dundi_transaction *trans)
4882 +{
4883 +       struct dundi_ie_data ied;
4884 +       int x;
4885 +       if (!trans->parent) {
4886 +               ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
4887 +               return -1;
4888 +       }
4889 +       memset(&ied, 0, sizeof(ied));
4890 +       dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
4891 +       if (!dundi_eid_zero(&trans->us_eid))
4892 +               dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
4893 +       for (x=0;x<trans->eidcount;x++)
4894 +               dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
4895 +       dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
4896 +       dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
4897 +       dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
4898 +       if (trans->autokilltimeout)
4899 +               trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
4900 +       return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
4901 +}
4902 +
4903 +static int discover_transactions(struct dundi_request *dr)
4904 +{
4905 +       struct dundi_transaction *trans;
4906 +       trans = dr->trans;
4907 +       while(trans) {
4908 +               dundi_discover(trans);
4909 +               trans = trans->next;
4910 +       }
4911 +       return 0;
4912 +}
4913 +
4914 +static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
4915 +{
4916 +       struct dundi_transaction *trans;
4917 +       trans = dr->trans;
4918 +       while(trans) {
4919 +               precache_trans(trans, maps, mapcount, expiration, foundanswers);
4920 +               trans = trans->next;
4921 +       }
4922 +       return 0;
4923 +}
4924 +
4925 +static int query_transactions(struct dundi_request *dr)
4926 +{
4927 +       struct dundi_transaction *trans;
4928 +       ast_mutex_lock(&peerlock);
4929 +       trans = dr->trans;
4930 +       while(trans) {
4931 +               dundi_query(trans);
4932 +               trans = trans->next;
4933 +       }
4934 +       ast_mutex_unlock(&peerlock);
4935 +       return 0;
4936 +}
4937 +
4938 +static int optimize_transactions(struct dundi_request *dr, int order)
4939 +{
4940 +       /* Minimize the message propagation through DUNDi by
4941 +          alerting the network to hops which should be not be considered */
4942 +       struct dundi_transaction *trans;
4943 +       struct dundi_peer *peer;
4944 +       dundi_eid tmp;
4945 +       int x;
4946 +       int needpush;
4947 +       ast_mutex_lock(&peerlock);
4948 +       trans = dr->trans;
4949 +       while(trans) {
4950 +               /* Pop off the true root */
4951 +               if (trans->eidcount) {
4952 +                       tmp = trans->eids[--trans->eidcount];
4953 +                       needpush = 1;
4954 +               } else {
4955 +                       tmp = trans->us_eid;
4956 +                       needpush = 0;
4957 +               }
4958 +
4959 +               peer = peers;
4960 +               while(peer) {
4961 +                       if (has_permission(peer->include, dr->dcontext) && 
4962 +                           dundi_eid_cmp(&peer->eid, &trans->them_eid) &&
4963 +                               (peer->order <= order)) {
4964 +                               /* For each other transaction, make sure we don't
4965 +                                  ask this EID about the others if they're not
4966 +                                  already in the list */
4967 +                               if (!dundi_eid_cmp(&tmp, &peer->eid)) 
4968 +                                       x = -1;
4969 +                               else {
4970 +                                       for (x=0;x<trans->eidcount;x++) {
4971 +                                               if (!dundi_eid_cmp(&trans->eids[x], &peer->eid))
4972 +                                                       break;
4973 +                                       }
4974 +                               }
4975 +                               if (x == trans->eidcount) {
4976 +                                       /* Nope not in the list, if needed, add us at the end since we're the source */
4977 +                                       if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
4978 +                                               trans->eids[trans->eidcount++] = peer->eid;
4979 +                                               /* Need to insert the real root (or us) at the bottom now as
4980 +                                                  a requirement now.  */
4981 +                                               needpush = 1;
4982 +                                       }
4983 +                               }
4984 +                       }
4985 +                       peer = peer->next;
4986 +               }
4987 +               /* If necessary, push the true root back on the end */
4988 +               if (needpush)
4989 +                       trans->eids[trans->eidcount++] = tmp;
4990 +               trans = trans->next;
4991 +       }
4992 +       ast_mutex_unlock(&peerlock);
4993 +       return 0;
4994 +}
4995 +
4996 +static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
4997 +{
4998 +       struct dundi_transaction *trans;
4999 +       int x;
5000 +       char eid_str[20];
5001 +       char eid_str2[20];
5002 +       /* Ignore if not registered */
5003 +       if (!p->addr.sin_addr.s_addr)
5004 +               return 0;
5005 +       if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
5006 +               return 0;
5007 +       if (ast_strlen_zero(dr->number))
5008 +               ast_log(LOG_DEBUG, "Will query peer '%s' for '%s' (context '%s')\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dundi_eid_to_str(eid_str2, sizeof(eid_str2), &dr->query_eid), dr->dcontext);
5009 +       else
5010 +               ast_log(LOG_DEBUG, "Will query peer '%s' for '%s@%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dr->number, dr->dcontext);
5011 +       trans = create_transaction(p);
5012 +       if (!trans)
5013 +               return -1;
5014 +       trans->next = dr->trans;
5015 +       trans->parent = dr;
5016 +       trans->ttl = ttl;
5017 +       for (x=0;avoid[x] && (x <DUNDI_MAX_STACK);x++)
5018 +               trans->eids[x] = *avoid[x];
5019 +       trans->eidcount = x;
5020 +       dr->trans = trans;
5021 +       return 0;
5022 +}
5023 +
5024 +static void cancel_request(struct dundi_request *dr)
5025 +{
5026 +       struct dundi_transaction *trans, *next;
5027 +
5028 +       ast_mutex_lock(&peerlock);
5029 +       trans = dr->trans;
5030 +       
5031 +       while(trans) {
5032 +               next = trans->next;
5033 +               /* Orphan transaction from request */
5034 +               trans->parent = NULL;
5035 +               trans->next = NULL;
5036 +               /* Send final cancel */
5037 +               dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
5038 +               trans = next;
5039 +       }
5040 +       ast_mutex_unlock(&peerlock);
5041 +}
5042 +
5043 +static void abort_request(struct dundi_request *dr)
5044 +{
5045 +       ast_mutex_lock(&peerlock);
5046 +       while(dr->trans) 
5047 +               destroy_trans(dr->trans, 0);
5048 +       ast_mutex_unlock(&peerlock);
5049 +}
5050 +
5051 +static void build_transactions(struct dundi_request *dr, int ttl, int order, int *foundcache, int *skipped, int blockempty, int nocache, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int directs[])
5052 +{
5053 +       struct dundi_peer *p;
5054 +       int x;
5055 +       int res;
5056 +       int pass;
5057 +       int allowconnect;
5058 +       char eid_str[20];
5059 +       ast_mutex_lock(&peerlock);
5060 +       p = peers;
5061 +       while(p) {
5062 +               if (modeselect == 1) {
5063 +                       /* Send the precache to push upstreams only! */
5064 +                       pass = has_permission(p->permit, dr->dcontext) && (p->pcmodel & DUNDI_MODEL_OUTBOUND);
5065 +                       allowconnect = 1;
5066 +               } else {
5067 +                       /* Normal lookup / EID query */
5068 +                       pass = has_permission(p->include, dr->dcontext);
5069 +                       allowconnect = p->model & DUNDI_MODEL_OUTBOUND;
5070 +               }
5071 +               if (skip) {
5072 +                       if (!dundi_eid_cmp(skip, &p->eid))
5073 +                               pass = 0;
5074 +               }
5075 +               if (pass) {
5076 +                       if (p->order <= order) {
5077 +                               /* Check order first, then check cache, regardless of
5078 +                                  omissions, this gets us more likely to not have an
5079 +                                  affected answer. */
5080 +                               if((nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration)))) {
5081 +                                       res = 0;
5082 +                                       /* Make sure we haven't already seen it and that it won't
5083 +                                          affect our answer */
5084 +                                       for (x=0;avoid[x];x++) {
5085 +                                               if (!dundi_eid_cmp(avoid[x], &p->eid) || !dundi_eid_cmp(avoid[x], &p->us_eid)) {
5086 +                                                       /* If not a direct connection, it affects our answer */
5087 +                                                       if (directs && !directs[x]) 
5088 +                                                               dr->hmd->flags &= ~DUNDI_HINT_UNAFFECTED;
5089 +                                                       break;
5090 +                                               }
5091 +                                       }
5092 +                                       /* Make sure we can ask */
5093 +                                       if (allowconnect) {
5094 +                                               if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) {
5095 +                                                       /* Check for a matching or 0 cache entry */
5096 +                                                       append_transaction(dr, p, ttl, avoid);
5097 +                                               } else
5098 +                                                       ast_log(LOG_DEBUG, "Avoiding '%s' in transaction\n", dundi_eid_to_str(eid_str, sizeof(eid_str), avoid[x]));
5099 +                                       }
5100 +                               }
5101 +                               *foundcache |= res;
5102 +                       } else if (!*skipped || (p->order < *skipped))
5103 +                               *skipped = p->order;
5104 +               }
5105 +               p = p->next;
5106 +       }
5107 +       ast_mutex_unlock(&peerlock);
5108 +}
5109 +
5110 +static int register_request(struct dundi_request *dr, struct dundi_request **pending)
5111 +{
5112 +       struct dundi_request *cur;
5113 +       int res=0;
5114 +       char eid_str[20];
5115 +       ast_mutex_lock(&peerlock);
5116 +       cur = requests;
5117 +       while(cur) {
5118 +               if (option_debug)
5119 +                       ast_log(LOG_DEBUG, "Checking '%s@%s' vs '%s@%s'\n", cur->dcontext, cur->number,
5120 +                               dr->dcontext, dr->number);
5121 +               if (!strcasecmp(cur->dcontext, dr->dcontext) &&
5122 +                   !strcasecmp(cur->number, dr->number) &&
5123 +                       (!dundi_eid_cmp(&cur->root_eid, &dr->root_eid) || (cur->crc32 == dr->crc32))) {
5124 +                               ast_log(LOG_DEBUG, "Found existing query for '%s@%s' for '%s' crc '%08lx'\n", 
5125 +                                       cur->dcontext, cur->number, dundi_eid_to_str(eid_str, sizeof(eid_str), &cur->root_eid), cur->crc32);
5126 +                               *pending = cur;
5127 +                       res = 1;
5128 +                       break;
5129 +               }
5130 +               cur = cur->next;
5131 +       }
5132 +       if (!res) {
5133 +               ast_log(LOG_DEBUG, "Registering request for '%s@%s' on behalf of '%s' crc '%08lx'\n", 
5134 +                               dr->number, dr->dcontext, dundi_eid_to_str(eid_str, sizeof(eid_str), &dr->root_eid), dr->crc32);
5135 +               /* Go ahead and link us in since nobody else is searching for this */
5136 +               dr->next = requests;
5137 +               requests = dr;
5138 +               *pending = NULL;
5139 +       }
5140 +       ast_mutex_unlock(&peerlock);
5141 +       return res;
5142 +}
5143 +
5144 +static void unregister_request(struct dundi_request *dr)
5145 +{
5146 +       struct dundi_request *cur, *prev;
5147 +       ast_mutex_lock(&peerlock);
5148 +       prev = NULL;
5149 +       cur = requests;
5150 +       while(cur) {
5151 +               if (cur == dr) {
5152 +                       if (prev)
5153 +                               prev->next = cur->next;
5154 +                       else
5155 +                               requests = cur->next;
5156 +                       break;
5157 +               }
5158 +               prev = cur;
5159 +               cur = cur->next;
5160 +       }
5161 +       ast_mutex_unlock(&peerlock);
5162 +}
5163 +
5164 +static int check_request(struct dundi_request *dr)
5165 +{
5166 +       struct dundi_request *cur;
5167 +       int res = 0;
5168 +       ast_mutex_lock(&peerlock);
5169 +       cur = requests;
5170 +       while(cur) {
5171 +               if (cur == dr) {
5172 +                       res = 1;
5173 +                       break;
5174 +               }
5175 +               cur = cur->next;
5176 +       }
5177 +       ast_mutex_unlock(&peerlock);
5178 +       return res;
5179 +}
5180 +
5181 +static unsigned long avoid_crc32(dundi_eid *avoid[])
5182 +{
5183 +       /* Idea is that we're calculating a checksum which is independent of
5184 +          the order that the EID's are listed in */
5185 +       unsigned long acrc32 = 0;
5186 +       int x;
5187 +       for (x=0;avoid[x];x++) {
5188 +               /* Order doesn't matter */
5189 +               if (avoid[x+1]) {
5190 +                       acrc32 ^= crc32(0L, (unsigned char *)avoid[x], sizeof(dundi_eid));
5191 +               }
5192 +       }
5193 +       return acrc32;
5194 +}
5195 +
5196 +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 *hmd, int *expiration, int cbypass, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int direct[])
5197 +{
5198 +       int res;
5199 +       struct dundi_request dr, *pending;
5200 +       dundi_eid *rooteid=NULL;
5201 +       int x;
5202 +       int ttlms;
5203 +       int ms;
5204 +       int foundcache;
5205 +       int skipped=0;
5206 +       int order=0;
5207 +       char eid_str[20];
5208 +       struct timeval start;
5209 +       
5210 +       /* Don't do anthing for a hungup channel */
5211 +       if (chan && chan->_softhangup)
5212 +               return 0;
5213 +
5214 +       ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
5215 +
5216 +       for (x=0;avoid[x];x++)
5217 +               rooteid = avoid[x];
5218 +       /* Now perform real check */
5219 +       memset(&dr, 0, sizeof(dr));
5220 +       if (pipe(dr.pfds)) {
5221 +               ast_log(LOG_WARNING, "pipe failed: %s\n" , strerror(errno));
5222 +               return -1;
5223 +       }
5224 +       dr.dr = result;
5225 +       dr.hmd = hmd;
5226 +       dr.maxcount = maxret;
5227 +       dr.expiration = *expiration;
5228 +       dr.cbypass = cbypass;
5229 +       dr.crc32 = avoid_crc32(avoid);
5230 +       strncpy(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext) - 1);
5231 +       strncpy(dr.number, number, sizeof(dr.number) - 1);
5232 +       if (rooteid)
5233 +               dr.root_eid = *rooteid;
5234 +       res = register_request(&dr, &pending);
5235 +       if (res) {
5236 +               /* Already a request */
5237 +               if (rooteid && !dundi_eid_cmp(&dr.root_eid, &pending->root_eid)) {
5238 +                       /* This is on behalf of someone else.  Go ahead and close this out since
5239 +                          they'll get their answer anyway. */
5240 +                       ast_log(LOG_DEBUG, "Oooh, duplicate request for '%s@%s' for '%s'\n",
5241 +                               dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &dr.root_eid));
5242 +                       close(dr.pfds[0]);
5243 +                       close(dr.pfds[1]);
5244 +                       return -2;
5245 +               } else {
5246 +                       /* Wait for the cache to populate */
5247 +                       ast_log(LOG_DEBUG, "Waiting for similar request for '%s@%s' for '%s'\n",
5248 +                               dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &pending->root_eid));
5249 +                       gettimeofday(&start, NULL);
5250 +                       while(check_request(pending) && (calc_ms(&start) < ttlms) && (!chan || !chan->_softhangup)) {
5251 +                               /* XXX Would be nice to have a way to poll/select here XXX */
5252 +                               usleep(1);
5253 +                       }
5254 +                       /* Continue on as normal, our cache should kick in */
5255 +               }
5256 +       }
5257 +       /* Create transactions */
5258 +       do {
5259 +               order = skipped;
5260 +               skipped = 0;
5261 +               foundcache = 0;
5262 +               build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, modeselect, skip, avoid, direct);
5263 +       } while (skipped && !foundcache && !dr.trans);
5264 +       /* If no TTL, abort and return 0 now after setting TTL expired hint.  Couldn't
5265 +          do this earlier because we didn't know if we were going to have transactions
5266 +          or not. */
5267 +       if (!ttl) {
5268 +               hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
5269 +               abort_request(&dr);
5270 +               unregister_request(&dr);
5271 +               close(dr.pfds[0]);
5272 +               close(dr.pfds[1]);
5273 +               return 0;
5274 +       }
5275 +               
5276 +       /* Optimize transactions */
5277 +       optimize_transactions(&dr, order);
5278 +       /* Actually perform transactions */
5279 +       discover_transactions(&dr);
5280 +       /* Wait for transaction to come back */
5281 +       gettimeofday(&start, NULL);
5282 +       while(dr.trans && (calc_ms(&start) < ttlms) && (!chan || !chan->_softhangup)) {
5283 +               ms = 100;
5284 +               ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
5285 +       }
5286 +       if (chan && chan->_softhangup)
5287 +               ast_log(LOG_DEBUG, "Hrm, '%s' hungup before their query for %s@%s finished\n", chan->name, dr.number, dr.dcontext);
5288 +       cancel_request(&dr);
5289 +       unregister_request(&dr);
5290 +       res = dr.respcount;
5291 +       *expiration = dr.expiration;
5292 +       close(dr.pfds[0]);
5293 +       close(dr.pfds[1]);
5294 +       return res;
5295 +}
5296 +
5297 +int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass)
5298 +{
5299 +       struct dundi_hint_metadata hmd;
5300 +       dundi_eid *avoid[1] = { NULL, };
5301 +       int direct[1] = { 0, };
5302 +       int expiration = DUNDI_DEFAULT_CACHE_TIME;
5303 +       memset(&hmd, 0, sizeof(hmd));
5304 +       hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
5305 +       return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, 0, NULL, avoid, direct);
5306 +}
5307 +
5308 +static void reschedule_precache(const char *number, const char *context, int expiration)
5309 +{
5310 +       int len;
5311 +       struct dundi_precache_queue *qe, *prev=NULL;
5312 +       ast_mutex_lock(&pclock);
5313 +       qe = pcq;
5314 +       while(qe) {
5315 +               if (!strcmp(number, qe->number) && !strcasecmp(context, qe->context)) {
5316 +                       if (prev)
5317 +                               prev->next = qe->next;
5318 +                       else
5319 +                               pcq = qe->next;
5320 +                       qe->next = NULL;
5321 +                       break;
5322 +               }
5323 +               prev = qe;
5324 +               qe = qe->next;
5325 +       };
5326 +       if (!qe) {
5327 +               len = sizeof(struct dundi_precache_queue);
5328 +               len += strlen(number) + 1;
5329 +               len += strlen(context) + 1;
5330 +               qe = malloc(len);
5331 +               if (qe) {
5332 +                       memset(qe, 0, len);
5333 +                       strcpy(qe->number, number);
5334 +                       qe->context = qe->number + strlen(number) + 1;
5335 +                       strcpy(qe->context, context);
5336 +               }
5337 +       }
5338 +       time(&qe->expiration);
5339 +       qe->expiration += expiration;
5340 +       prev = pcq;
5341 +       if (prev) {
5342 +               while(prev->next && (prev->next->expiration <= qe->expiration))
5343 +                       prev = prev->next;
5344 +               qe->next = prev->next;
5345 +               prev->next = qe;
5346 +       } else
5347 +               pcq = qe;
5348 +       ast_mutex_unlock(&pclock);
5349 +       
5350 +}
5351 +
5352 +static void dundi_precache_full(void)
5353 +{
5354 +       struct dundi_mapping *cur;
5355 +       struct ast_context *con;
5356 +       struct ast_exten *e;
5357 +       cur = mappings;
5358 +       while(cur) {
5359 +               ast_log(LOG_NOTICE, "Should precache context '%s'\n", cur->dcontext);
5360 +               ast_lock_contexts();
5361 +               con = ast_walk_contexts(NULL);
5362 +               while(con) {
5363 +                       if (!strcasecmp(cur->lcontext, ast_get_context_name(con))) {
5364 +                               /* Found the match, now queue them all up */
5365 +                               ast_lock_context(con);
5366 +                               e = ast_walk_context_extensions(con, NULL);
5367 +                               while(e) {
5368 +                                       reschedule_precache(ast_get_extension_name(e), cur->dcontext, 0);
5369 +                                       e = ast_walk_context_extensions(con, e);
5370 +                               }
5371 +                               ast_unlock_context(con);
5372 +                       }
5373 +                       con = ast_walk_contexts(con);
5374 +               }
5375 +               ast_unlock_contexts();
5376 +               cur = cur->next;
5377 +       }
5378 +}
5379 +
5380 +static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[])
5381 +{
5382 +       struct dundi_request dr;
5383 +       struct dundi_hint_metadata hmd;
5384 +       struct dundi_result dr2[MAX_RESULTS];
5385 +       struct timeval start;
5386 +       struct dundi_mapping *maps=NULL, *cur;
5387 +       int nummaps;
5388 +       int foundanswers;
5389 +       int foundcache, skipped, ttlms, ms;
5390 +       if (!context)
5391 +               context = "e164";
5392 +       ast_log(LOG_DEBUG, "Precache internal (%s@%s)!\n", number, context);
5393 +
5394 +       ast_mutex_lock(&peerlock);
5395 +       nummaps = 0;
5396 +       cur = mappings;
5397 +       while(cur) {
5398 +               if (!strcasecmp(cur->dcontext, context))
5399 +                       nummaps++;
5400 +               cur = cur->next;
5401 +       }
5402 +       if (nummaps) {
5403 +               maps = alloca(nummaps * sizeof(struct dundi_mapping));
5404 +               nummaps = 0;
5405 +               if (maps) {
5406 +                       cur = mappings;
5407 +                       while(cur) {
5408 +                               if (!strcasecmp(cur->dcontext, context))
5409 +                                       maps[nummaps++] = *cur;
5410 +                               cur = cur->next;
5411 +                       }
5412 +               }
5413 +       }
5414 +       ast_mutex_unlock(&peerlock);
5415 +       if (!nummaps || !maps)
5416 +               return -1;
5417 +       ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
5418 +       memset(&dr2, 0, sizeof(dr2));
5419 +       memset(&dr, 0, sizeof(dr));
5420 +       memset(&hmd, 0, sizeof(hmd));
5421 +       dr.dr = dr2;
5422 +       strncpy(dr.number, number, sizeof(dr.number) - 1);
5423 +       strncpy(dr.dcontext, context ? context : "e164", sizeof(dr.dcontext) - 1);
5424 +       dr.maxcount = MAX_RESULTS;
5425 +       dr.expiration = DUNDI_DEFAULT_CACHE_TIME;
5426 +       dr.hmd = &hmd;
5427 +       pipe(dr.pfds);
5428 +       dr.pfds[0] = dr.pfds[1] = -1;
5429 +       build_transactions(&dr, ttl, 0, &foundcache, &skipped, 0, 1, 1, NULL, avoids, NULL);
5430 +       optimize_transactions(&dr, 0);
5431 +       foundanswers = 0;
5432 +       precache_transactions(&dr, maps, nummaps, &dr.expiration, &foundanswers);
5433 +       if (foundanswers) {
5434 +               if (dr.expiration > 0) 
5435 +                       reschedule_precache(dr.number, dr.dcontext, dr.expiration);
5436 +               else
5437 +                       ast_log(LOG_NOTICE, "Weird, expiration = %d, but need to precache for %s@%s?!\n", dr.expiration, dr.number, dr.dcontext);
5438 +       }
5439 +       gettimeofday(&start, NULL);
5440 +       while(dr.trans && (calc_ms(&start) < ttlms)) {
5441 +               if (dr.pfds[0] > -1) {
5442 +                       ms = 100;
5443 +                       ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
5444 +               } else
5445 +                       usleep(1);
5446 +       }
5447 +       cancel_request(&dr);
5448 +       if (dr.pfds[0] > -1) {
5449 +               close(dr.pfds[0]);
5450 +               close(dr.pfds[1]);
5451 +       }
5452 +       return 0;
5453 +}
5454 +
5455 +int dundi_precache(const char *context, const char *number)
5456 +{
5457 +       dundi_eid *avoid[1] = { NULL, };
5458 +       return dundi_precache_internal(context, number, dundi_ttl, avoid);
5459 +}
5460 +
5461 +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[])
5462 +{
5463 +       int res;
5464 +       struct dundi_request dr;
5465 +       dundi_eid *rooteid=NULL;
5466 +       int x;
5467 +       int ttlms;
5468 +       int skipped=0;
5469 +       int foundcache=0;
5470 +       struct timeval start;
5471 +       
5472 +       ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
5473 +
5474 +       for (x=0;avoid[x];x++)
5475 +               rooteid = avoid[x];
5476 +       /* Now perform real check */
5477 +       memset(&dr, 0, sizeof(dr));
5478 +       dr.hmd = hmd;
5479 +       dr.dei = dei;
5480 +       strncpy(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext) - 1);
5481 +       memcpy(&dr.query_eid, eid, sizeof(dr.query_eid));
5482 +       if (rooteid)
5483 +               dr.root_eid = *rooteid;
5484 +       /* Create transactions */
5485 +       build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, 0, 0, NULL, avoid, NULL);
5486 +
5487 +       /* If no TTL, abort and return 0 now after setting TTL expired hint.  Couldn't
5488 +          do this earlier because we didn't know if we were going to have transactions
5489 +          or not. */
5490 +       if (!ttl) {
5491 +               hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
5492 +               return 0;
5493 +       }
5494 +               
5495 +       /* Optimize transactions */
5496 +       optimize_transactions(&dr, 9999);
5497 +       /* Actually perform transactions */
5498 +       query_transactions(&dr);
5499 +       /* Wait for transaction to come back */
5500 +       gettimeofday(&start, NULL);
5501 +       while(dr.trans && (calc_ms(&start) < ttlms))
5502 +               usleep(1);
5503 +       res = dr.respcount;
5504 +       return res;
5505 +}
5506 +
5507 +int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid)
5508 +{
5509 +       dundi_eid *avoid[1] = { NULL, };
5510 +       struct dundi_hint_metadata hmd;
5511 +       memset(&hmd, 0, sizeof(hmd));
5512 +       return dundi_query_eid_internal(dei, dcontext, &eid, &hmd, dundi_ttl, 0, avoid);
5513 +}
5514 +
5515 +static int dundi_lookup_exec(struct ast_channel *chan, void *data)
5516 +{
5517 +       char *tmp;
5518 +       char *context;
5519 +       char *opts;
5520 +       int res = -1;
5521 +       struct localuser *u;
5522 +
5523 +       if (!data || !strlen(data)) {
5524 +               ast_log(LOG_WARNING, "DUNDiLookup requires an argument (number)\n");
5525 +               return 0;
5526 +       }
5527 +       LOCAL_USER_ADD(u);
5528 +       tmp = ast_strdupa(data);
5529 +       if (tmp) {
5530 +               context = strchr(tmp, '|');
5531 +               if (context) {
5532 +                       *context = '\0';
5533 +                       context++;
5534 +                       opts = strchr(context, '|');
5535 +                       if (opts) {
5536 +                               *opts = '\0';
5537 +                               opts++;
5538 +                       }
5539 +               } else
5540 +                       opts = NULL;
5541 +               if (!context || !strlen(context))
5542 +                       context = "e164";
5543 +               if (!opts)
5544 +                       opts = "";
5545 +               
5546 +       }
5547 +       LOCAL_USER_REMOVE(u);
5548 +       return res;
5549 +}
5550 +
5551 +
5552 +static void mark_peers(void)
5553 +{
5554 +       struct dundi_peer *peer;
5555 +       ast_mutex_lock(&peerlock);
5556 +       peer = peers;
5557 +       while(peer) {
5558 +               peer->dead = 1;
5559 +               peer = peer->next;
5560 +       }
5561 +       ast_mutex_unlock(&peerlock);
5562 +}
5563 +
5564 +static void mark_mappings(void)
5565 +{
5566 +       struct dundi_mapping *map;
5567 +       ast_mutex_lock(&peerlock);
5568 +       map = mappings;
5569 +       while(map) {
5570 +               map->dead = 1;
5571 +               map = map->next;
5572 +       }
5573 +       ast_mutex_unlock(&peerlock);
5574 +}
5575 +
5576 +static void destroy_permissions(struct permission *p)
5577 +{
5578 +       struct permission *prev;
5579 +       while(p) {
5580 +               prev = p;
5581 +               p = p->next;
5582 +               free(prev);
5583 +       }
5584 +}
5585 +
5586 +static void destroy_peer(struct dundi_peer *peer)
5587 +{
5588 +       if (peer->registerid > -1)
5589 +               ast_sched_del(sched, peer->registerid);
5590 +       if (peer->regtrans)
5591 +               destroy_trans(peer->regtrans, 0);
5592 +       if (peer->keypending)
5593 +               destroy_trans(peer->keypending, 0);
5594 +       if (peer->qualifyid > -1)
5595 +               ast_sched_del(sched, peer->qualifyid);
5596 +       destroy_permissions(peer->permit);
5597 +       destroy_permissions(peer->include);
5598 +       free(peer);
5599 +}
5600 +
5601 +static void destroy_map(struct dundi_mapping *map)
5602 +{
5603 +       free(map);
5604 +}
5605 +
5606 +static void prune_peers(void)
5607 +{
5608 +       struct dundi_peer *peer, *prev, *next;
5609 +       ast_mutex_lock(&peerlock);
5610 +       peer = peers;
5611 +       prev = NULL;
5612 +       while(peer) {
5613 +               next = peer->next;
5614 +               if (peer->dead) {
5615 +                       if (prev)
5616 +                               prev->next = peer->next;
5617 +                       else
5618 +                               peers = peer->next;
5619 +                       destroy_peer(peer);
5620 +               } else
5621 +                       prev = peer;
5622 +               peer = next;
5623 +       }
5624 +       ast_mutex_unlock(&peerlock);
5625 +}
5626 +
5627 +static void prune_mappings(void)
5628 +{
5629 +       struct dundi_mapping *map, *prev, *next;
5630 +       ast_mutex_lock(&peerlock);
5631 +       map = mappings;
5632 +       prev = NULL;
5633 +       while(map) {
5634 +               next = map->next;
5635 +               if (map->dead) {
5636 +                       if (prev)
5637 +                               prev->next = map->next;
5638 +                       else
5639 +                               mappings = map->next;
5640 +                       destroy_map(map);
5641 +               } else
5642 +                       prev = map;
5643 +               map = next;
5644 +       }
5645 +       ast_mutex_unlock(&peerlock);
5646 +}
5647 +
5648 +static struct permission *append_permission(struct permission *p, char *s, int allow)
5649 +{
5650 +       struct permission *start;
5651 +       start = p;
5652 +       if (p) {
5653 +               while(p->next)
5654 +                       p = p->next;
5655 +       }
5656 +       if (p) {
5657 +               p->next = malloc(sizeof(struct permission) + strlen(s) + 1);
5658 +               p = p->next;
5659 +       } else {
5660 +               p = malloc(sizeof(struct permission) + strlen(s) + 1);
5661 +       }
5662 +       if (p) {
5663 +               memset(p, 0, sizeof(struct permission));
5664 +               memcpy(p->name, s, strlen(s) + 1);
5665 +               p->allow = allow;
5666 +       }
5667 +       return start ? start : p;
5668 +}
5669 +
5670 +#define MAX_OPTS 128
5671 +
5672 +static void build_mapping(char *name, char *value)
5673 +{
5674 +       char *t, *fields[MAX_OPTS];
5675 +       struct dundi_mapping *map;
5676 +       int x;
5677 +       int y;
5678 +       t = ast_strdupa(value);
5679 +       if (t) {
5680 +               map = mappings;
5681 +               while(map) {
5682 +                       /* Find a double match */
5683 +                       if (!strcasecmp(map->dcontext, name) && 
5684 +                               (!strncasecmp(map->lcontext, value, strlen(map->lcontext)) && 
5685 +                                 (!value[strlen(map->lcontext)] || 
5686 +                                  (value[strlen(map->lcontext)] == ','))))
5687 +                               break;
5688 +                       map = map->next;
5689 +               }
5690 +               if (!map) {
5691 +                       map = malloc(sizeof(struct dundi_mapping));
5692 +                       if (map) {
5693 +                               memset(map, 0, sizeof(struct dundi_mapping));
5694 +                               map->next = mappings;
5695 +                               mappings = map;
5696 +                               map->dead = 1;
5697 +                       }
5698 +               }
5699 +               if (map) {
5700 +                       map->options = 0;
5701 +                       memset(fields, 0, sizeof(fields));
5702 +                       x = 0;
5703 +                       while(t && x < MAX_OPTS) {
5704 +                               fields[x++] = t;
5705 +                               t = strchr(t, ',');
5706 +                               if (t) {
5707 +                                       *t = '\0';
5708 +                                       t++;
5709 +                               }
5710 +                       } /* Russell was here, arrrr! */
5711 +                       if ((x == 1) && ast_strlen_zero(fields[0])) {
5712 +                               /* Placeholder mapping */
5713 +                               strncpy(map->dcontext, name, sizeof(map->dcontext) - 1);
5714 +                               map->dead = 0;
5715 +                       } else if (x >= 4) {
5716 +                               strncpy(map->dcontext, name, sizeof(map->dcontext) - 1);
5717 +                               strncpy(map->lcontext, fields[0], sizeof(map->lcontext) - 1);
5718 +                               if ((sscanf(fields[1], "%i", &map->weight) == 1) && (map->weight >= 0) && (map->weight < 60000)) {
5719 +                                       strncpy(map->dest, fields[3], sizeof(map->dest) - 1);
5720 +                                       if ((map->tech = str2tech(fields[2]))) {
5721 +                                               map->dead = 0;
5722 +                                       }
5723 +                               } else {
5724 +                                       ast_log(LOG_WARNING, "Invalid weight '%s' specified, deleting entry '%s/%s'\n", fields[1], map->dcontext, map->lcontext);
5725 +                               }
5726 +                               for (y=4;y<x;y++) {
5727 +                                       if (!strcasecmp(fields[y], "nounsolicited"))
5728 +                                               map->options |= DUNDI_FLAG_NOUNSOLICITED;
5729 +                                       else if (!strcasecmp(fields[y], "nocomunsolicit"))
5730 +                                               map->options |= DUNDI_FLAG_NOCOMUNSOLICIT;
5731 +                                       else if (!strcasecmp(fields[y], "residential"))
5732 +                                               map->options |= DUNDI_FLAG_RESIDENTIAL;
5733 +                                       else if (!strcasecmp(fields[y], "commercial"))
5734 +                                               map->options |= DUNDI_FLAG_COMMERCIAL;
5735 +                                       else if (!strcasecmp(fields[y], "mobile"))
5736 +                                               map->options |= DUNDI_FLAG_MOBILE;
5737 +                                       else if (!strcasecmp(fields[y], "nopartial"))
5738 +                                               map->options |= DUNDI_FLAG_INTERNAL_NOPARTIAL;
5739 +                                       else
5740 +                                               ast_log(LOG_WARNING, "Don't know anything about option '%s'\n", fields[y]);
5741 +                               }
5742 +                       } else 
5743 +                               ast_log(LOG_WARNING, "Expected at least %d arguments in map, but got only %d\n", 4, x);
5744 +               }
5745 +       }
5746 +}
5747 +
5748 +static int do_register(void *data)
5749 +{
5750 +       struct dundi_ie_data ied;
5751 +       struct dundi_peer *peer = data;
5752 +       char eid_str[20];
5753 +       char eid_str2[20];
5754 +       /* Called with peerlock already held */
5755 +       ast_log(LOG_DEBUG, "Register us as '%s' to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->us_eid), dundi_eid_to_str(eid_str2, sizeof(eid_str2), &peer->eid));
5756 +       peer->registerid = ast_sched_add(sched, default_expiration * 1000, do_register, data);
5757 +       /* Destroy old transaction if there is one */
5758 +       if (peer->regtrans)
5759 +               destroy_trans(peer->regtrans, 0);
5760 +       peer->regtrans = create_transaction(peer);
5761 +       if (peer->regtrans) {
5762 +               peer->regtrans->flags |= FLAG_ISREG;
5763 +               memset(&ied, 0, sizeof(ied));
5764 +               dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
5765 +               dundi_ie_append_eid(&ied, DUNDI_IE_EID, &peer->regtrans->us_eid);
5766 +               dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
5767 +               dundi_send(peer->regtrans, DUNDI_COMMAND_REGREQ, 0, 0, &ied);
5768 +               
5769 +       } else
5770 +               ast_log(LOG_NOTICE, "Unable to create new transaction for registering to '%s'!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
5771 +
5772 +       return 0;
5773 +}
5774 +
5775 +static int do_qualify(void *data)
5776 +{
5777 +       struct dundi_peer *peer;
5778 +       peer = data;
5779 +       peer->qualifyid = -1;
5780 +       qualify_peer(peer, 0);
5781 +       return 0;
5782 +}
5783 +
5784 +static void qualify_peer(struct dundi_peer *peer, int schedonly)
5785 +{
5786 +       int when;
5787 +       if (peer->qualifyid > -1)
5788 +               ast_sched_del(sched, peer->qualifyid);
5789 +       peer->qualifyid = -1;
5790 +       if (peer->qualtrans)
5791 +               destroy_trans(peer->qualtrans, 0);
5792 +       peer->qualtrans = NULL;
5793 +       if (peer->maxms > 0) {
5794 +               when = 60000;
5795 +               if (peer->lastms < 0)
5796 +                       when = 10000;
5797 +               if (schedonly)
5798 +                       when = 5000;
5799 +               peer->qualifyid = ast_sched_add(sched, when, do_qualify, peer);
5800 +               if (!schedonly)
5801 +                       peer->qualtrans = create_transaction(peer);
5802 +               if (peer->qualtrans) {
5803 +                       gettimeofday(&peer->qualtx, NULL);
5804 +                       peer->qualtrans->flags |= FLAG_ISQUAL;
5805 +                       dundi_send(peer->qualtrans, DUNDI_COMMAND_NULL, 0, 1, NULL);
5806 +               }
5807 +       }
5808 +}
5809 +static void populate_addr(struct dundi_peer *peer, dundi_eid *eid)
5810 +{
5811 +       char data[256];
5812 +       char *c;
5813 +       int port, expire;
5814 +       char eid_str[20];
5815 +       dundi_eid_to_str(eid_str, sizeof(eid_str), eid);
5816 +       if (!ast_db_get("dundi/dpeers", eid_str, data, sizeof(data))) {
5817 +               c = strchr(data, ':');
5818 +               if (c) {
5819 +                       *c = '\0';
5820 +                       c++;
5821 +                       if (sscanf(c, "%d:%d", &port, &expire) == 2) {
5822 +                               /* Got it! */
5823 +                               inet_aton(data, &peer->addr.sin_addr);
5824 +                               peer->addr.sin_family = AF_INET;
5825 +                               peer->addr.sin_port = htons(port);
5826 +                               peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
5827 +                       }
5828 +               }
5829 +       }
5830 +}
5831 +
5832 +
5833 +static void build_peer(dundi_eid *eid, struct ast_variable *v, int *globalpcmode)
5834 +{
5835 +       struct dundi_peer *peer;
5836 +       struct ast_hostent he;
5837 +       struct hostent *hp;
5838 +       dundi_eid testeid;
5839 +       int needregister=0;
5840 +       char eid_str[20];
5841 +
5842 +       ast_mutex_lock(&peerlock);
5843 +       peer = peers;
5844 +       while(peer) {
5845 +               if (!dundi_eid_cmp(&peer->eid, eid)) {  
5846 +                       break;
5847 +               }
5848 +               peer = peer->next;
5849 +       }
5850 +       if (!peer) {
5851 +               /* Add us into the list */
5852 +               peer = malloc(sizeof(struct dundi_peer));
5853 +               if (peer) {
5854 +                       memset(peer, 0, sizeof(struct dundi_peer));
5855 +                       peer->registerid = -1;
5856 +                       peer->registerexpire = -1;
5857 +                       peer->qualifyid = -1;
5858 +                       peer->addr.sin_family = AF_INET;
5859 +                       peer->addr.sin_port = htons(DUNDI_PORT);
5860 +                       populate_addr(peer, eid);
5861 +                       peer->next = peers;
5862 +                       peers = peer;
5863 +               }
5864 +       }
5865 +       if (peer) {
5866 +               peer->dead = 0;
5867 +               peer->eid = *eid;
5868 +               peer->us_eid = global_eid;
5869 +               destroy_permissions(peer->permit);
5870 +               destroy_permissions(peer->include);
5871 +               peer->permit = NULL;
5872 +               peer->include = NULL;
5873 +               if (peer->registerid > -1)
5874 +                       ast_sched_del(sched, peer->registerid);
5875 +               peer->registerid = -1;
5876 +               while(v) {
5877 +                       if (!strcasecmp(v->name, "inkey")) {
5878 +                               strncpy(peer->inkey, v->value, sizeof(peer->inkey) - 1);
5879 +                       } else if (!strcasecmp(v->name, "outkey")) {
5880 +                               strncpy(peer->outkey, v->value, sizeof(peer->outkey) - 1);
5881 +                       } else if (!strcasecmp(v->name, "host")) {
5882 +                               if (!strcasecmp(v->value, "dynamic")) {
5883 +                                       peer->dynamic = 1;
5884 +                               } else {
5885 +                                       hp = ast_gethostbyname(v->value, &he);
5886 +                                       if (hp) {
5887 +                                               memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr));
5888 +                                               peer->dynamic = 0;
5889 +                                       } else {
5890 +                                               ast_log(LOG_WARNING, "Unable to find host '%s' at line %d\n", v->value, v->lineno);
5891 +                                               peer->dead = 1;
5892 +                                       }
5893 +                               }
5894 +                       } else if (!strcasecmp(v->name, "ustothem")) {
5895 +                               if (!dundi_str_to_eid(&testeid, v->value))
5896 +                                       peer->us_eid = testeid;
5897 +                               else
5898 +                                       ast_log(LOG_WARNING, "'%s' is not a valid DUNDi Entity Identifier at line %d\n", v->value, v->lineno);
5899 +                       } else if (!strcasecmp(v->name, "include")) {
5900 +                               peer->include = append_permission(peer->include, v->value, 1);
5901 +                       } else if (!strcasecmp(v->name, "permit")) {
5902 +                               peer->permit = append_permission(peer->permit, v->value, 1);
5903 +                       } else if (!strcasecmp(v->name, "noinclude")) {
5904 +                               peer->include = append_permission(peer->include, v->value, 0);
5905 +                       } else if (!strcasecmp(v->name, "deny")) {
5906 +                               peer->permit = append_permission(peer->permit, v->value, 0);
5907 +                       } else if (!strcasecmp(v->name, "register")) {
5908 +                               needregister = ast_true(v->value);
5909 +                       } else if (!strcasecmp(v->name, "order")) {
5910 +                               if (!strcasecmp(v->value, "primary"))
5911 +                                       peer->order = 0;
5912 +                               else if (!strcasecmp(v->value, "secondary"))
5913 +                                       peer->order = 1;
5914 +                               else if (!strcasecmp(v->value, "tertiary"))
5915 +                                       peer->order = 2;
5916 +                               else if (!strcasecmp(v->value, "quartiary"))
5917 +                                       peer->order = 3;
5918 +                               else {
5919 +                                       ast_log(LOG_WARNING, "'%s' is not a valid order, should be primary, secondary, tertiary or quartiary at line %d\n", v->value, v->lineno);
5920 +                               }
5921 +                       } else if (!strcasecmp(v->name, "qualify")) {
5922 +                               if (!strcasecmp(v->value, "no")) {
5923 +                                       peer->maxms = 0;
5924 +                               } else if (!strcasecmp(v->value, "yes")) {
5925 +                                       peer->maxms = DEFAULT_MAXMS;
5926 +                               } else if (sscanf(v->value, "%d", &peer->maxms) != 1) {
5927 +                                       ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of dundi.conf\n", 
5928 +                                               dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), v->lineno);
5929 +                                       peer->maxms = 0;
5930 +                               }
5931 +                       } else if (!strcasecmp(v->name, "model")) {
5932 +                               if (!strcasecmp(v->value, "inbound"))
5933 +                                       peer->model = DUNDI_MODEL_INBOUND;
5934 +                               else if (!strcasecmp(v->value, "outbound")) 
5935 +                                       peer->model = DUNDI_MODEL_OUTBOUND;
5936 +                               else if (!strcasecmp(v->value, "symmetric"))
5937 +                                       peer->model = DUNDI_MODEL_SYMMETRIC;
5938 +                               else if (!strcasecmp(v->value, "none"))
5939 +                                       peer->model = 0;
5940 +                               else {
5941 +                                       ast_log(LOG_WARNING, "Unknown model '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n", 
5942 +                                               v->value, v->lineno);
5943 +                               }
5944 +                       } else if (!strcasecmp(v->name, "precache")) {
5945 +                               if (!strcasecmp(v->value, "inbound"))
5946 +                                       peer->pcmodel = DUNDI_MODEL_INBOUND;
5947 +                               else if (!strcasecmp(v->value, "outbound")) 
5948 +                                       peer->pcmodel = DUNDI_MODEL_OUTBOUND;
5949 +                               else if (!strcasecmp(v->value, "symmetric"))
5950 +                                       peer->pcmodel = DUNDI_MODEL_SYMMETRIC;
5951 +                               else if (!strcasecmp(v->value, "none"))
5952 +                                       peer->pcmodel = 0;
5953 +                               else {
5954 +                                       ast_log(LOG_WARNING, "Unknown pcmodel '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n", 
5955 +                                               v->value, v->lineno);
5956 +                               }
5957 +                       }
5958 +                       v = v->next;
5959 +               }
5960 +               (*globalpcmode) |= peer->pcmodel;
5961 +               if (!peer->model && !peer->pcmodel) {
5962 +                       ast_log(LOG_WARNING, "Peer '%s' lacks a model or pcmodel, discarding!\n", 
5963 +                               dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
5964 +                       peer->dead = 1;
5965 +               } else if ((peer->model & DUNDI_MODEL_INBOUND) && (peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
5966 +                       ast_log(LOG_WARNING, "Peer '%s' may not be both inbound/symmetric model and outbound/symmetric precache model, discarding!\n", 
5967 +                               dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
5968 +                       peer->dead = 1;
5969 +               } else if ((peer->model & DUNDI_MODEL_OUTBOUND) && (peer->pcmodel & DUNDI_MODEL_INBOUND)) {
5970 +                       ast_log(LOG_WARNING, "Peer '%s' may not be both outbound/symmetric model and inbound/symmetric precache model, discarding!\n", 
5971 +                               dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
5972 +                       peer->dead = 1;
5973 +               } else if (peer->include && !(peer->model & DUNDI_MODEL_OUTBOUND) && !(peer->pcmodel & DUNDI_MODEL_INBOUND)) {
5974 +                       ast_log(LOG_WARNING, "Peer '%s' is supposed to be included in outbound searches but isn't an outbound peer or inbound precache!\n", 
5975 +                               dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
5976 +               } else if (peer->permit && !(peer->model & DUNDI_MODEL_INBOUND) && !(peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
5977 +                       ast_log(LOG_WARNING, "Peer '%s' is supposed to have permission for some inbound searches but isn't an inbound peer or outbound precache!\n", 
5978 +                               dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
5979 +               } else { 
5980 +                       if (needregister) {
5981 +                               peer->registerid = ast_sched_add(sched, 2000, do_register, peer);
5982 +                       }
5983 +                       qualify_peer(peer, 1);
5984 +               }
5985 +       }
5986 +       ast_mutex_unlock(&peerlock);
5987 +}
5988 +
5989 +static int dundi_helper(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *data, int flag)
5990 +{
5991 +       struct dundi_result results[MAX_RESULTS];
5992 +       int res;
5993 +       int x;
5994 +       int found = 0;
5995 +       if (!strncasecmp(context, "macro-", 6)) {
5996 +               if (!chan) {    
5997 +                       ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
5998 +                       return -1;
5999 +               }
6000 +               /* If done as a macro, use macro extension */
6001 +               if (!strcasecmp(exten, "s")) {
6002 +                       exten = pbx_builtin_getvar_helper(chan, "ARG1");
6003 +                       if (!exten || ast_strlen_zero(exten))
6004 +                               exten = chan->macroexten;
6005 +                       if (!exten || ast_strlen_zero(exten))
6006 +                               exten = chan->exten;
6007 +                       if (!exten || ast_strlen_zero(exten)) { 
6008 +                               ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
6009 +                               return -1;
6010 +                       }
6011 +               }
6012 +               if (!data || ast_strlen_zero(data))
6013 +                       data = "e164";
6014 +       } else {
6015 +               if (!data || ast_strlen_zero(data))
6016 +                       data = context;
6017 +       }
6018 +       res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
6019 +       for (x=0;x<res;x++) {
6020 +               if (results[x].flags & flag)
6021 +                       found++;
6022 +       }
6023 +       if (found >= priority)
6024 +               return 1;
6025 +       return 0;
6026 +}
6027 +
6028 +static int dundi_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
6029 +{
6030 +       return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_EXISTS);
6031 +}
6032 +
6033 +static int dundi_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
6034 +{
6035 +       return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_CANMATCH);
6036 +}
6037 +
6038 +static int dundi_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, int newstack, const char *data)
6039 +{
6040 +       struct dundi_result results[MAX_RESULTS];
6041 +       int res;
6042 +       int x=0;
6043 +       char req[1024];
6044 +       struct ast_app *dial;
6045 +       
6046 +       if (!strncasecmp(context, "macro-", 6)) {
6047 +               if (!chan) {    
6048 +                       ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
6049 +                       return -1;
6050 +               }
6051 +               /* If done as a macro, use macro extension */
6052 +               if (!strcasecmp(exten, "s")) {
6053 +                       exten = pbx_builtin_getvar_helper(chan, "ARG1");
6054 +                       if (!exten || ast_strlen_zero(exten))
6055 +                               exten = chan->macroexten;
6056 +                       if (!exten || ast_strlen_zero(exten))
6057 +                               exten = chan->exten;
6058 +                       if (!exten || ast_strlen_zero(exten)) { 
6059 +                               ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
6060 +                               return -1;
6061 +                       }
6062 +               }
6063 +               if (!data || ast_strlen_zero(data))
6064 +                       data = "e164";
6065 +       } else {
6066 +               if (!data || ast_strlen_zero(data))
6067 +                       data = context;
6068 +       }
6069 +       res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
6070 +       if (res > 0) {
6071 +               sort_results(results, res);
6072 +               for (x=0;x<res;x++) {
6073 +                       if (results[x].flags & DUNDI_FLAG_EXISTS) {
6074 +                               if (!--priority)
6075 +                                       break;
6076 +                       }
6077 +               }
6078 +       }
6079 +       if (x < res) {
6080 +               /* Got a hit! */
6081 +               snprintf(req, sizeof(req), "%s/%s", results[x].tech, results[x].dest);
6082 +               dial = pbx_findapp("Dial");
6083 +               if (dial)
6084 +                       res = pbx_exec(chan, dial, req, newstack);
6085 +       } else
6086 +               res = -1;
6087 +       return res;
6088 +}
6089 +
6090 +static int dundi_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
6091 +{
6092 +       return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_MATCHMORE);
6093 +}
6094 +
6095 +static struct ast_switch dundi_switch =
6096 +{
6097 +        name:                   "DUNDi",
6098 +        description:                   "DUNDi Discovered Dialplan Switch",
6099 +        exists:                 dundi_exists,
6100 +        canmatch:               dundi_canmatch,
6101 +        exec:                   dundi_exec,
6102 +        matchmore:              dundi_matchmore,
6103 +};
6104 +
6105 +static int set_config(char *config_file, struct sockaddr_in* sin)
6106 +{
6107 +       struct ast_config *cfg;
6108 +       struct ast_variable *v;
6109 +       char *cat;
6110 +       int format;
6111 +       int x;
6112 +       char hn[256];
6113 +       struct ast_hostent he;
6114 +       struct hostent *hp;
6115 +       struct sockaddr_in sin2;
6116 +       static int last_port = 0;
6117 +       int globalpcmodel = 0;
6118 +       dundi_eid testeid;
6119 +
6120 +       dundi_ttl = DUNDI_DEFAULT_TTL;
6121 +       cfg = ast_load(config_file);
6122 +       
6123 +       
6124 +       if (!cfg) {
6125 +               ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
6126 +               return -1;
6127 +       }
6128 +       ipaddr[0] = '\0';
6129 +       if (!gethostname(hn, sizeof(hn))) {
6130 +               hp = ast_gethostbyname(hn, &he);
6131 +               if (hp) {
6132 +                       memcpy(&sin2.sin_addr, hp->h_addr, sizeof(sin2.sin_addr));
6133 +                       ast_inet_ntoa(ipaddr, sizeof(ipaddr), sin2.sin_addr);
6134 +               } else
6135 +                       ast_log(LOG_WARNING, "Unable to look up host '%s'\n", hn);
6136 +       } else
6137 +               ast_log(LOG_WARNING, "Unable to get host name!\n");
6138 +       ast_mutex_lock(&peerlock);
6139 +       reset_global_eid();
6140 +       global_storehistory = 0;
6141 +       strncpy(secretpath, "dundi", sizeof(secretpath) - 1);
6142 +       v = ast_variable_browse(cfg, "general");
6143 +       while(v) {
6144 +               if (!strcasecmp(v->name, "port")){ 
6145 +                       sin->sin_port = ntohs(atoi(v->value));
6146 +                       if(last_port==0){
6147 +                               last_port=sin->sin_port;
6148 +                       } else if(sin->sin_port != last_port)
6149 +                               ast_log(LOG_WARNING, "change to port ignored until next asterisk re-start\n");
6150 +               } else if (!strcasecmp(v->name, "bindaddr")) {
6151 +                       struct hostent *hp;
6152 +                       struct ast_hostent he;
6153 +                       hp = ast_gethostbyname(v->value, &he);
6154 +                       if (hp) {
6155 +                               memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
6156 +                       } else
6157 +                               ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
6158 +               } else if (!strcasecmp(v->name, "authdebug")) {
6159 +                       authdebug = ast_true(v->value);
6160 +               } else if (!strcasecmp(v->name, "ttl")) {
6161 +                       if ((sscanf(v->value, "%i", &x) == 1) && (x > 0) && (x < DUNDI_DEFAULT_TTL)) {
6162 +                               dundi_ttl = x;
6163 +                       } else {
6164 +                               ast_log(LOG_WARNING, "'%s' is not a valid TTL at line %d, must be number from 1 to %d\n",
6165 +                                       v->value, v->lineno, DUNDI_DEFAULT_TTL);
6166 +                       }
6167 +               } else if (!strcasecmp(v->name, "autokill")) {
6168 +                       if (sscanf(v->value, "%i", &x) == 1) {
6169 +                               if (x >= 0)
6170 +                                       global_autokilltimeout = x;
6171 +                               else
6172 +                                       ast_log(LOG_NOTICE, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno);
6173 +                       } else if (ast_true(v->value)) {
6174 +                               global_autokilltimeout = DEFAULT_MAXMS;
6175 +                       } else {
6176 +                               global_autokilltimeout = 0;
6177 +                       }
6178 +               } else if (!strcasecmp(v->name, "entityid")) {
6179 +                       if (!dundi_str_to_eid(&testeid, v->value))
6180 +                               global_eid = testeid;
6181 +                       else
6182 +                               ast_log(LOG_WARNING, "Invalid global endpoint identifier '%s' at line %d\n", v->value, v->lineno);
6183 +               } else if (!strcasecmp(v->name, "tos")) {
6184 +                       if (sscanf(v->value, "%i", &format) == 1)
6185 +                               tos = format & 0xff;
6186 +                       else if (!strcasecmp(v->value, "lowdelay"))
6187 +                               tos = IPTOS_LOWDELAY;
6188 +                       else if (!strcasecmp(v->value, "throughput"))
6189 +                               tos = IPTOS_THROUGHPUT;
6190 +                       else if (!strcasecmp(v->value, "reliability"))
6191 +                               tos = IPTOS_RELIABILITY;
6192 +#if !defined(__NetBSD__)
6193 +                       else if (!strcasecmp(v->value, "mincost"))
6194 +                               tos = IPTOS_MINCOST;
6195 +#endif
6196 +                       else if (!strcasecmp(v->value, "none"))
6197 +                               tos = 0;
6198 +                       else
6199 +#if defined(__NetBSD__)
6200 +                               ast_log(LOG_WARNING, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', 'mincost', or 'none'\n", v->lineno);
6201 +#else
6202 +                               ast_log(LOG_WARNING, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', or 'none'\n", v->lineno);
6203 +#endif
6204 +               } else if (!strcasecmp(v->name, "department")) {
6205 +                       strncpy(dept, v->value, sizeof(dept) - 1);
6206 +               } else if (!strcasecmp(v->name, "organization")) {
6207 +                       strncpy(org, v->value, sizeof(org) - 1);
6208 +               } else if (!strcasecmp(v->name, "locality")) {
6209 +                       strncpy(locality, v->value, sizeof(locality) - 1);
6210 +               } else if (!strcasecmp(v->name, "stateprov")) {
6211 +                       strncpy(stateprov, v->value, sizeof(stateprov) - 1);
6212 +               } else if (!strcasecmp(v->name, "country")) {
6213 +                       strncpy(country, v->value, sizeof(country) - 1);
6214 +               } else if (!strcasecmp(v->name, "email")) {
6215 +                       strncpy(email, v->value, sizeof(email) - 1);
6216 +               } else if (!strcasecmp(v->name, "phone")) {
6217 +                       strncpy(phone, v->value, sizeof(phone) - 1);
6218 +               } else if (!strcasecmp(v->name, "storehistory")) {
6219 +                       global_storehistory = ast_true(v->value);
6220 +               } else if (!strcasecmp(v->name, "mapserver")) {
6221 +                       hp = ast_gethostbyname(v->value, &he);
6222 +                       if (hp) {
6223 +                               memcpy(&map_addr.sin_addr, hp->h_addr, sizeof(map_addr.sin_addr));
6224 +                               if (option_verbose > 1)
6225 +                                       ast_verbose(VERBOSE_PREFIX_2 "Using mapping server at %s\n", v->value);
6226 +                       } else {
6227 +                               memset(&map_addr.sin_addr, 0, sizeof(map_addr.sin_addr));
6228 +                               ast_log(LOG_WARNING, "Could not find host '%s' for mapping host\n", v->value);
6229 +                       }
6230 +               } else if (!strcasecmp(v->name, "mappeers_per_pkt")) {
6231 +                       map_updates_per_pkt = atoi(v->value);
6232 +                       if(map_updates_per_pkt < 1 || map_updates_per_pkt > 45) {
6233 +                               ast_log(LOG_WARNING, "Map updates must be between 1 and 45 inclusive. Setting to 45.\n");
6234 +                               map_updates_per_pkt = 45;
6235 +                       }
6236 +               } else if (!strcasecmp(v->name, "mapport")) {
6237 +                       if (option_verbose > 1)
6238 +                               ast_verbose(VERBOSE_PREFIX_2 "Using mapping server at port %d\n", atoi(v->value));
6239 +                       map_addr.sin_port = htons(atoi(v->value));
6240 +               } else if (!strcasecmp(v->name, "mapinterval")) {
6241 +                       map_update_interval = atoi(v->value) * 1000;
6242 +                       if(map_update_interval < 5000 && map_update_interval != 0)
6243 +                               map_update_interval = 5000;
6244 +                       if (option_verbose > 1)
6245 +                               ast_verbose(VERBOSE_PREFIX_2 "Using mapping update interval of %d ms\n", map_update_interval);
6246 +               } else if(!strcasecmp(v->name, "mapcontext")) {
6247 +                       strncpy(map_context, v->value, sizeof(map_context) - 1);
6248 +               }
6249 +               v = v->next;
6250 +       }
6251 +       ast_mutex_unlock(&peerlock);
6252 +       mark_mappings();
6253 +       v = ast_variable_browse(cfg, "mappings");
6254 +       while(v) {
6255 +               build_mapping(v->name, v->value);
6256 +               v = v->next;
6257 +       }
6258 +       prune_mappings();
6259 +       mark_peers();
6260 +       cat = ast_category_browse(cfg, NULL);
6261 +       while(cat) {
6262 +               if (strcasecmp(cat, "general") && strcasecmp(cat, "mappings")) {
6263 +                       /* Entries */
6264 +                       if (!dundi_str_to_eid(&testeid, cat))
6265 +                               build_peer(&testeid, ast_variable_browse(cfg, cat), &globalpcmodel);
6266 +                       else
6267 +                               ast_log(LOG_NOTICE, "Ignoring invalid EID entry '%s'\n", cat);
6268 +               }
6269 +               cat = ast_category_browse(cfg, cat);
6270 +       }
6271 +       prune_peers();
6272 +       ast_destroy(cfg);
6273 +       load_password();
6274 +   
6275 +       /* Schedule updates */
6276 +       if(map_peering_sid > -1)
6277 +               ast_sched_del(sched, map_peering_sid);
6278 +       if(map_contact_sid > -1)
6279 +               ast_sched_del(sched, map_contact_sid);
6280 +       if(map_update_interval) {
6281 +               map_peering_sid = ast_sched_add(sched, 1000, dundi_xmit_peering, 0);
6282 +               map_contact_sid = ast_sched_add(sched, 1000, dundi_xmit_contact, 0);
6283 +       }
6284 +
6285 +       if (globalpcmodel & DUNDI_MODEL_OUTBOUND)
6286 +               dundi_precache_full();
6287 +
6288 +       return 0;
6289 +}
6290 +
6291 +int unload_module(void)
6292 +{
6293 +       int res;
6294 +       STANDARD_HANGUP_LOCALUSERS;
6295 +       ast_cli_unregister(&cli_debug);
6296 +       ast_cli_unregister(&cli_store_history);
6297 +       ast_cli_unregister(&cli_flush);
6298 +       ast_cli_unregister(&cli_no_debug);
6299 +       ast_cli_unregister(&cli_no_store_history);
6300 +       ast_cli_unregister(&cli_show_peers);
6301 +       ast_cli_unregister(&cli_show_entityid);
6302 +       ast_cli_unregister(&cli_show_trans);
6303 +       ast_cli_unregister(&cli_show_requests);
6304 +       ast_cli_unregister(&cli_show_mappings);
6305 +       ast_cli_unregister(&cli_show_precache);
6306 +       ast_cli_unregister(&cli_show_peer);
6307 +       ast_cli_unregister(&cli_lookup);
6308 +       ast_cli_unregister(&cli_precache);
6309 +       ast_cli_unregister(&cli_queryeid);
6310 +       ast_unregister_switch(&dundi_switch);
6311 +       res = ast_unregister_application(app);
6312 +       return res;
6313 +}
6314 +
6315 +int reload(void)
6316 +{
6317 +       struct sockaddr_in sin;
6318 +       set_config("dundi.conf",&sin);
6319 +       return 0;
6320 +}
6321 +
6322 +int load_module(void)
6323 +{
6324 +       int res=0;
6325 +       struct sockaddr_in sin;
6326 +       char iabuf[INET_ADDRSTRLEN];
6327 +       
6328 +       dundi_set_output(dundi_debug_output);
6329 +       dundi_set_error(dundi_error_output);
6330 +       
6331 +       /* Seed random number generator */
6332 +       srand(time(NULL));
6333 +       
6334 +       sin.sin_family = AF_INET;
6335 +       sin.sin_port = ntohs(DUNDI_PORT);
6336 +       sin.sin_addr.s_addr = INADDR_ANY;
6337 +
6338 +       /* INADDR_ANY should be 0.0.0.0 */
6339 +       map_addr.sin_family = AF_INET;
6340 +       map_addr.sin_port = ntohs(4525);
6341 +       map_addr.sin_addr.s_addr = INADDR_ANY;
6342 +       strncpy(map_context, "open-e164", sizeof(map_context) - 1);
6343 +
6344 +       /* Make a UDP socket */
6345 +       io = io_context_create();
6346 +       sched = sched_context_create();
6347 +       
6348 +       if (!io || !sched) {
6349 +               ast_log(LOG_ERROR, "Out of memory\n");
6350 +               return -1;
6351 +       }
6352 +
6353 +       ast_cli_register(&cli_debug);
6354 +       ast_cli_register(&cli_store_history);
6355 +       ast_cli_register(&cli_flush);
6356 +       ast_cli_register(&cli_no_debug);
6357 +       ast_cli_register(&cli_no_store_history);
6358 +       ast_cli_register(&cli_show_peers);
6359 +       ast_cli_register(&cli_show_entityid);
6360 +       ast_cli_register(&cli_show_trans);
6361 +       ast_cli_register(&cli_show_requests);
6362 +       ast_cli_register(&cli_show_mappings);
6363 +       ast_cli_register(&cli_show_precache);
6364 +       ast_cli_register(&cli_show_peer);
6365 +       ast_cli_register(&cli_lookup);
6366 +       ast_cli_register(&cli_precache);
6367 +       ast_cli_register(&cli_queryeid);
6368 +       if (ast_register_switch(&dundi_switch))
6369 +               ast_log(LOG_ERROR, "Unable to register DUNDi switch\n");
6370 +
6371 +       set_config("dundi.conf",&sin);
6372 +
6373 +       netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
6374 +       
6375 +       if (netsocket < 0) {
6376 +               ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
6377 +               return -1;
6378 +       }
6379 +       if (bind(netsocket,(struct sockaddr *)&sin, sizeof(sin))) {
6380 +               ast_log(LOG_ERROR, "Unable to bind to %s port %d: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port), strerror(errno));
6381 +               return -1;
6382 +       }
6383 +
6384 +       if (option_verbose > 1)
6385 +               ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", tos);
6386 +
6387 +       if (setsockopt(netsocket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) 
6388 +               ast_log(LOG_WARNING, "Unable to set TOS to %d\n", tos);
6389 +       
6390 +       if (!res) {
6391 +               res = start_network_thread();
6392 +               if (option_verbose > 1) 
6393 +                       ast_verbose(VERBOSE_PREFIX_2 "DUNDi Ready and Listening on %s port %d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port));
6394 +       } else {
6395 +               ast_log(LOG_ERROR, "Unable to start network thread\n");
6396 +               close(netsocket);
6397 +       }
6398 +       res = ast_register_application(app, dundi_lookup_exec, synopsis, descrip);
6399 +
6400 +       return 0;
6401 +}
6402 +
6403 +char *description(void)
6404 +{
6405 +       return tdesc;
6406 +}
6407 +
6408 +int usecount(void)
6409 +{
6410 +       int res;
6411 +       /* XXX DUNDi cannot be unloaded XXX */
6412 +       return 1;
6413 +       STANDARD_USECOUNT(res);
6414 +       return res;
6415 +}
6416 +
6417 +char *key()
6418 +{
6419 +       return ASTERISK_GPL_KEY;
6420 +}
6421 diff -ruN asterisk-1.0.7-orig/pbx.c asterisk-1.0.7-pbx_dundi/pbx.c
6422 --- asterisk-1.0.7-orig/pbx.c   2005-02-19 01:27:52.000000000 +0100
6423 +++ asterisk-1.0.7-pbx_dundi/pbx.c      2005-06-02 20:21:37.000000000 +0200
6424 @@ -820,7 +820,7 @@
6425  /*--- pbx_retrieve_variable: Support for Asterisk built-in variables and
6426        functions in the dialplan
6427    ---*/
6428 -static void pbx_substitute_variables_temp(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen)
6429 +static void pbx_substitute_variables_temp(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
6430  {
6431         char *first,*second;
6432         char tmpvar[80] = "";
6433 @@ -829,7 +829,6 @@
6434         int offset,offset2;
6435         struct ast_var_t *variables;
6436         char *name, *num; /* for callerid name + num variables */
6437 -       struct varshead *headp=NULL;
6438  
6439         if (c) 
6440                 headp=&c->varshead;
6441 @@ -854,7 +853,7 @@
6442                 if (!first)
6443                         first = tmpvar + strlen(tmpvar);
6444                 *first='\0';
6445 -               pbx_substitute_variables_temp(c,tmpvar,ret,workspace,workspacelen - 1);
6446 +               pbx_substitute_variables_temp(c,tmpvar,ret,workspace,workspacelen - 1, headp);
6447                 if (!(*ret)) 
6448                         return;
6449                 offset=atoi(first+1);   /* The number of characters, 
6450 @@ -993,7 +992,7 @@
6451                 strncpy(workspace, c->language, workspacelen - 1);
6452                 *ret = workspace;
6453         } else {
6454 -               if (c) {
6455 +               if (headp) {
6456                         AST_LIST_TRAVERSE(headp,variables,entries) {
6457  #if 0
6458                                 ast_log(LOG_WARNING,"Comparing variable '%s' with '%s'\n",var,ast_var_name(variables));
6459 @@ -1040,7 +1039,7 @@
6460         }
6461  }
6462  
6463 -void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
6464 +static void pbx_substitute_variables_helper_full(struct ast_channel *c, const char *cp1, char *cp2, int count, struct varshead *headp)
6465  {
6466         char *cp4;
6467         const char *tmp, *whereweare;
6468 @@ -1131,7 +1130,7 @@
6469                         
6470                         /* Retrieve variable value */
6471                         workspace[0] = '\0';
6472 -                       pbx_substitute_variables_temp(c,vars,&cp4, workspace, sizeof(workspace));
6473 +                       pbx_substitute_variables_temp(c,vars,&cp4, workspace, sizeof(workspace), headp);
6474                         if (cp4) {
6475                                 length = strlen(cp4);
6476                                 if (length > count)
6477 @@ -1206,6 +1205,16 @@
6478         }
6479  }
6480  
6481 +void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
6482 +{
6483 +       pbx_substitute_variables_helper_full(c, cp1, cp2, count, NULL);
6484 +}
6485 +
6486 +void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
6487 +{
6488 +       pbx_substitute_variables_helper_full(NULL, cp1, cp2, count, headp);
6489 +}
6490 +
6491  static void pbx_substitute_variables(char *passdata, int datalen, struct ast_channel *c, struct ast_exten *e) {
6492          
6493         memset(passdata, 0, datalen);