Add DUNDi.... (http://www.dundi.com)
authorMark Spencer <markster@digium.com>
Mon, 18 Oct 2004 21:45:13 +0000 (21:45 +0000)
committerMark Spencer <markster@digium.com>
Mon, 18 Oct 2004 21:45:13 +0000 (21:45 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@4033 65c4cc65-6c06-0410-ace0-fbb531ad65f3

configs/dundi.conf.sample [new file with mode: 0755]
configs/extensions.conf.sample
configs/iax.conf.sample
doc/PEERING [new file with mode: 0755]
doc/README.dundi [new file with mode: 0755]
include/asterisk/dundi.h [new file with mode: 0755]
pbx/Makefile
pbx/dundi-parser.c [new file with mode: 0755]
pbx/dundi-parser.h [new file with mode: 0755]
pbx/pbx_dundi.c [new file with mode: 0755]

diff --git a/configs/dundi.conf.sample b/configs/dundi.conf.sample
new file mode 100755 (executable)
index 0000000..e63b1df
--- /dev/null
@@ -0,0 +1,172 @@
+;
+; DUNDi configuration file
+;
+;
+[general]
+;
+; The "general" section contains general parameters relating
+; to the operation of the dundi client and server.
+;
+; The first part should be your complete contact information
+; should someone else in your peer group need to contact you.
+;
+;department=Your Department
+;organization=Your Company, Inc.
+;locality=Your City
+;stateprov=ST
+;country=US
+;email=your@email.com
+;phone=+12565551212
+;
+;
+; Specify bind address and port number.  Default is
+; 4520
+;
+;bindaddr=0.0.0.0
+;port=4520
+;
+; Our entity identifier (Should generally be the MAC address of the
+; machine it's running on.  Defaults to the first eth address, but you
+; can override it here, as long as you set it to the MAC of *something*
+; you own!)
+;
+;entityid=00:07:E9:3B:76:60
+;
+; Define the max depth in which to search the DUNDi system (also max # of 
+; seconds to wait for a reply)
+;
+ttl=32
+;
+; If we don't get ACK to our DPREQUEST within 2000ms, and autokill is set
+; to yes, then we cancel the whole thing (that's enough time for one 
+; retransmission only).  This is used to keep things from stalling for a long
+; time for a host that is not available, but would be ill advised for bad 
+; connections.  In addition to 'yes' or 'no' you can also specify a number
+; of milliseconds.  See 'qualify' for individual peers to turn on for just
+; a specific peer.
+;
+autokill=yes
+;
+; pbx_dundi creates a rotating key called "secret", under the family
+; 'secretpath'.  The default family is dundi (resulting in 
+; the key being held at dundi/secret).
+;
+;secretpath=dundi
+
+[mappings]
+;
+; The "mappings" section maps DUNDi contexts
+; to contexts on the local asterisk system.  Remember
+; that numbers that are made available under the e164 
+; DUNDi context are regulated by the DUNDi General Peering 
+; Agreement (GPA) if you are a member of the DUNDi E.164
+; Peering System.
+;
+; dundi_context => local_context,weight,tech,dest[,options]]
+;
+; dundi_context is the name of the context being requested
+; within the DUNDi request
+;
+; local_context is the name of the context on the local system
+; in which numbers can be looked up for which responses shall be given.
+;
+; tech is the technology to use (IAX, SIP, H323)
+;
+; dest is the destination to supply for reaching that number.  Note
+; that the variable ${NUMBER} shall be replaced by the number being
+; requested.
+;
+; Further options may include:
+;
+; nounsolicited:  No unsolicited calls of any type permitted via this 
+;                 route
+; nocomunsolicit: No commercial unsolicited calls permitted via 
+;                 this route
+; residential:    This number is known to be a residence
+; commercial:     This number is known to be a business
+; mobile:         This number is known to be a mobile phone
+; nocomunsolicit: No commercial unsolicited calls permitted via 
+;                 this route
+; nopartial:      Do not search for partial matches
+;
+; There *must* exist an entry in mappings for DUNDi to respond
+; to any request, although it may be empty.
+;
+;e164 => dundi-e164-canonical,0,IAX2,dundi:${SECRET}@${IPADDR}/${NUMBER},nounsolicited,nocomunsolicit,nopartial
+;e164 => dundi-e164-customers,100,IAX2,dundi:${SECRET}@${IPADDR}/${NUMBER},nounsolicited,nocomunsolicit,nopartial
+;e164 => dundi-e164-via-pstn,400,IAX2,dundi:${SECRET}@${IPADDR}/${NUMBER},nounsolicited,nocomunsolicit,nopartial
+
+;digexten => default,0,IAX2,guest@lappy/${NUMBER}
+;asdf =>
+
+
+;
+;
+; The remaining sections represent the peers
+; that we fundamentally trust.  The section name
+; represents the name and optionally at a specific
+; DUNDi context if you want the trust to be established
+; for only a specific DUNDi context.
+;
+; inkey - What key they will be authenticating to us with
+;
+; outkey - What key we use to authenticate to them
+;
+; host - What their host is
+;
+; order - What search order to use.  May be 'primary', 'secondary', 
+;         'tertiary' or 'quartiary'.  In large systems, it is beneficial
+;         to only query one up-stream host in order to maximize caching
+;         value.  Adding one with primary and one with secondary gives you
+;         redundancy without sacraficing performance.
+;
+; include - Includes this peer when searching a particular context
+;           for lookup (set "all" to perform all lookups with that
+;           host.
+;
+; noinclude - Disincludes this peer when searching a particular context
+;             for lookup (set "all" to perform no lookups with that
+;             host.
+;
+; permit - Permits this peer to search a given DUNDi context on
+;          the local system.  Set "all" to permit this host to
+;          lookup all contexts.
+;
+; deny -   Denies this peer to search a given DUNDi context on
+;          the local system.  Set "all" to deny this host to
+;          lookup all contexts.
+;
+; model - inbound, outbound, or symmetric for whether we receive 
+;         requests only, transmit requests only, or do both.
+;
+; The '*' peer is special and matches an unspecified entity
+;
+
+;
+; Sample Primary e164 DUNDi peer
+;
+[00:50:8B:F3:75:BB]
+model = symmetric
+host = 64.215.96.114
+inkey = digium
+outkey = misery
+include = e164
+permit = e164
+qualify = yes
+
+;
+; Sample Secondary e164 DUNDi peer
+;
+;[00:A0:C9:96:92:84]
+;model = symmetric
+;host = misery.digium.com
+;inkey = misery
+;outkey = ourkey
+;include = e164
+;permit = e164
+;qualify = yes
+;order = secondary
+
+
+;[*]
+;
index 8e925be..32e43e0 100755 (executable)
@@ -109,6 +109,64 @@ TRUNKMSD=1                                 ; MSD digits to strip (usually 1 or 0)
 ;
 
 ;
+; Sample entries for extensions.conf
+;
+;
+[dundi-e164-canonical]
+;
+; List canonical entries here
+;
+;exten => 12564286000,1,Macro(std-exten,6000,IAX2/foo)
+;exten => _125642860XX,1,Dial(IAX2/otherbox/${EXTEN:7})
+
+[dundi-e164-customers]
+;
+; If you are an ITSP or Reseller, list your customers here.
+;
+;exten => _12564286000,1,Dial(SIP/customer1)
+;exten => _12564286001,1,Dial(IAX2/customer2)
+
+[dundi-e164-via-pstn]
+;
+; If you are freely delivering calls to the PSTN, list them here
+;
+;exten => _1256428XXXX,1,Dial(Zap/g2/${EXTEN:7}) ; Expose all of 256-428 
+;exten => _1256325XXXX,1,Dial(Zap/g2/${EXTEN:7}) ; Ditto for 256-325
+
+[dundi-e164-local]
+;
+; Context to put your dundi IAX2 or SIP user in for
+; full access
+;
+include => dundi-e164-canonical
+include => dundi-e164-customers
+include => dundi-e164-via-pstn
+
+[dundi-e164-switch]
+;
+; Just a wrapper for the switch
+;
+switch => DUNDi/e164
+
+[dundi-e164-lookup]
+;
+; Locally to lookup, try looking for a local E.164 solution
+; then try DUNDi if we don't have one.
+;
+include => dundi-e164-local
+include => dundi-e164-switch
+;
+; DUNDi can also be implemented as a Macro instead of using 
+; the Local channel driver. 
+;
+[macro-dundi-e164]
+;
+; ARG1 is the extension to Dial
+;
+exten => s,1,Goto(${ARG1},1)
+include => dundi-e164-lookup
+
+;
 ; Here are the entries you need to participate in the IAXTEL
 ; call routing system.  Most IAXTEL numbers begin with 1-700, but
 ; there are exceptions.  For more information, and to sign
@@ -130,13 +188,15 @@ exten => _91700XXXXXXX,1,Dial(IAX2/${IAXINFO}@iaxtel.com/${EXTEN:1}@iaxtel)
 ;
 ; International long distance through trunk
 ;
-exten => _9011.,1,Dial(${TRUNK}/${EXTEN:${TRUNKMSD}})
+exten => _9011.,1,Macro(dundi-e164,${EXTEN:4})
+exten => _9011.,n,Dial(${TRUNK}/${EXTEN:${TRUNKMSD}})
 
 [trunkld]
 ;
 ; Long distance context accessed through trunk
 ;
-exten => _91NXXNXXXXXX,1,Dial(${TRUNK}/${EXTEN:${TRUNKMSD}})
+exten => _91NXXNXXXXXX,1,Macro(dundi-e164,${EXTEN:1})
+exten => _91NXXNXXXXXX,n,Dial(${TRUNK}/${EXTEN:${TRUNKMSD}})
 
 [trunklocal]
 ;
index 90b195e..13e1a63 100755 (executable)
@@ -200,6 +200,15 @@ auth=rsa
 inkeys=freeworlddialup
 
 ;
+; Trust callerid delivered over DUNDi/e164
+;
+;
+;[dundi]
+;type=user
+;dbsecret=dundi/secret
+;context=dundi-e164-local
+
+;
 ; Further user sections may be added, specifying a context and a
 ; secret used for connections with that given authentication name.
 ; Limited IP based access control is allowed by use of "allow" and
diff --git a/doc/PEERING b/doc/PEERING
new file mode 100755 (executable)
index 0000000..c1f0b38
--- /dev/null
@@ -0,0 +1,499 @@
+                    DIGIUM GENERAL PEERING AGREEMENT (TM)
+                      Version 1.0.0, September 2004 
+ Copyright (C) 2004 Digium, Inc.
+            150 West Park Loop Suite 100, Huntsville, AL 35806 USA 
+
+ Everyone is permitted to copy and distribute complete verbatim copies
+ of this General Peering Agreement provided it is not modified in any
+ manner.
+
+        ------------------------------------------------------
+
+                    DIGIUM GENERAL PEERING AGREEMENT
+
+                                PREAMBLE
+
+  For most of the history of telecommunications, the power of being able
+to locate and communicate with another person in a system, be it across
+a hall or around the world, has always centered around a centralized
+authority -- from a local PBX administrator to regional and national
+RBOCs, generally requiring fees, taxes or regulation.  By contrast,
+DUNDi is a technology developed to provide users the freedom to
+communicate with each other without the necessity of any centralized
+authority.  This General Peering Agreement ("GPA") is used by individual
+parties (each, a "Participant") to allow them to build the E164 trust
+group for the DUNDi protocol.
+
+  To protect the usefulness of the E164 trust group for those who use
+it, while keeping the system wholly decentralized, it is necessary to
+replace many of the responsibilities generally afforded to a company or
+government agency, with a set of responsibilities implemented by the
+parties who use the system, themselves.  It is the goal of this document
+to provide all the protections necessary to keep the DUNDi E164 trust
+group useful and reliable.
+
+  The Participants wish to protect competition, promote innovation and
+value added services and make this service valuable both commercially
+and non-commercially.  To that end, this GPA provides special terms and
+conditions outlining some permissible and non-permissible revenue
+sources.
+
+  This GPA is independent of any software license or other license
+agreement for a program or technology employing the DUNDi protocol.  For
+example, the implementation of DUNDi used by Asterisk is covered under a
+separate license.  Each Participant is responsible for compliance with
+any licenses or other agreements governing use of such program or
+technology that they use to peer.
+
+  You do not have to execute this GPA to use a program or technology
+employing the DUNDi protocol, however if you do not execute this GPA,
+you will not be able to peer using DUNDi and the E164 context with
+anyone who is a member of the trust group by virtue of their having
+executed this GPA with another member.
+
+The parties to this GPA agree as follows: 
+
+  0. DEFINITIONS.  As used herein, certain terms shall be defined as
+follows:
+
+    (a) The term "DUNDi" means the DUNDi protocol as published by
+        Digium, Inc. or its successor in interest with respect to the
+        DUNDi protocol specification.
+
+    (b) The terms "E.164" and "E164" mean ITU-T specification E.164 as
+        published by the International Telecommunications Union (ITU) in
+        May, 1997.
+
+    (c) The term "Service" refers to any communication facility (e.g.,
+        telephone, fax, modem, etc.), identified by an E.164-compatible
+        number, and assigned by the appropriate authority in that
+        jurisdiction.
+
+    (d) The term "Egress Gateway" refers an Internet facility that
+        provides a communications path to a Service or Services that may
+        not be directly addressable via the Internet.
+
+    (e) The term "Route" refers to an Internet address, policies, and
+        other characteristics defined by the DUNDi protocol and
+        associated with the Service, or the Egress Gateway which
+        provides access to the specified Service.
+
+    (f) The term "Propagate" means to accept or transmit Service and/or
+        Egress Gateway Routes only using the DUNDi protocol and the
+        DUNDi context "e164" without regard to case, and does not apply
+        to the exchange of information using any other protocol or
+        context. 
+
+    (g) The term "Peering System" means the network of systems that
+        Propagate Routes.
+
+    (h) The term "Subscriber" means the owner of, or someone who
+        contracts to receive, the services identified by an E.164
+        number.
+
+    (i) The term "Authorizing Individual" means the Subscriber to a
+        number who has authorized a Participant to provide Routes
+        regarding their services via this Peering System. 
+
+    (j) The term "Route Authority" refers to a Participant that provides 
+        an original source of said Route within the Peering System. 
+        Routes are propagated from the Route Authorities through the
+        Peering System and may be cached at intermediate points.  There
+        may be multiple Route Authorities for any Service.
+
+    (k) The term "Participant" (introduced above) refers to any member
+        of the Peering System. 
+
+    (l) The term "Service Provider" refers to the carrier (e.g.,
+        exchange carrier, Internet Telephony Service Provider, or other
+        reseller) that provides communication facilities for a
+        particular Service to a Subscriber, Customer or other End User.
+
+    (m) The term "Weight" refers to a numeric quality assigned to a
+        Route as per the DUNDi protocol specification.  The current
+        Weight definitions are shown in Exhibit A.
+
+  1. PEERING. The undersigned Participants agree to Propagate Routes
+with each other and any other member of the Peering System and further
+agree not to Propagate DUNDi Routes with a third party unless they have
+first have executed this GPA (in its unmodified form) with such third
+party.  The Participants further agree only to Propagate Routes with
+Participants whom they reasonably believe to be honoring the terms of
+the GPA.  Participants may not insert, remove, amend, or otherwise
+modify any of the terms of the GPA. 
+
+  2. ACCEPTABLE USE POLICY.  The DUNDi protocol contains information
+that reflect a Subscriber's or Egress Gateway's decisions to receive
+calls.  In addition to the terms and conditions set forth in this GPA,
+the Participants agree to honor the intent of restrictions encoded in
+the DUNDi protocol.  To that end, Participants agree to the following:
+
+    (a) A Participant may not utilize or permit the utilization of 
+        Routes for which the Subscriber or Egress Gateway provider has
+        indicated that they do not wish to receive "Unsolicited Calls"
+        for the purpose of making an unsolicited phone call on behalf of
+        any party or organization.
+
+    (b) A Participant may not utilize or permit the utilization of
+        Routes which have indicated that they do not wish to receive
+        "Unsolicited Commercial Calls" for the purpose of making an
+        unsolicited phone call on behalf of a commercial organization.
+
+    (c) A Participant may never utilize or permit the utilization of any 
+        DUNDi route for the purpose of making harassing phone calls.
+
+    (d) A Party may not utilize or permit the utilization of DUNDi 
+        provided Routes for any systematic or random calling of numbers
+        (e.g., for the purpose of locating facsimile, modem services, or
+        systematic telemarketing).
+
+    (e) Initial control signaling for all communication sessions that 
+        utilize Routes obtained from the Peering System must be sent
+        from a member of the Peering System to the Service or Egress
+        Gateway identified in the selected Route.  For example, 'SIP
+        INVITES' and IAX2 "NEW" commands must be sent from the
+        requesting DUNDi node to the terminating Service.
+
+    (f) A Participant may not disclose any specific Route, Service or 
+        Participant contact information obtained from the Peering System
+        to any party outside of the Peering System except as a
+        by-product of facilitating communication in accordance with
+        section 2e (e.g., phone books or other databases may not be
+        published, but the Internet addresses of the Egress Gateway or
+        Service does not need to be obfuscated.)
+
+    (g) The DUNDi Protocol requires that each Participant include valid
+        contact information about itself (including information about
+        nodes connected to each Participant).  Participants may use or
+        disclose the contact information only to ensure enforcement of
+        legal furtherance of this Agreement.
+
+  3. ROUTES. The Participants shall only propagate valid Routes, as
+defined herein, through the Peering System, regardless of the original
+source.  The Participants may only provide Routes as set forth below,
+and then only if such Participant has no good faith reason to believe
+such Route to be invalid or unauthorized.
+
+    (a) A Participant may provide Routes if each Route has as its
+        original source another member of the Peering System who has
+        duly executed the GPA and such Routes are provided in accordance
+        with this Agreement; provided that the Routes are not modified
+        (e.g., with regards to existence, destination, technology or
+        Weight); or
+
+    (b) A Participant may provide Routes for Services with any Weight
+        for which it is the Subscriber; or
+
+    (c) A Participant may provide Routes for those Services whose
+        Subscriber has authorized the Participant to do so, provided 
+        that the Participant is able to confirm that the Authorizing
+        Individual is the Subscriber through:
+
+            i. a written statement of ownership from the Authorizing
+               Individual, which the Participant believes in good faith 
+               to be accurate (e.g., a phone bill with the name of the
+               Authorizing Individual and the number in question); or
+
+           ii. the Participant's own direct personal knowledge that the
+               Authorizing Individual is the Subscriber.
+    (d) A Participant may provide Routes for Services, with Weight in
+        accordance with the Current DUNDi Specification, if it can in
+        good faith provide an Egress Gateway to that Service on the
+        traditional telephone network without cost to the calling party.
+
+  4. REVOCATION. A Participant must provide a free, easily accessible
+mechanism by which a Subscriber may revoke permission to act as a Route
+Authority for his Service.  A Participant must stop acting as a Route
+Authority for that Service within 7 days after:
+
+    (a) receipt of a revocation request; 
+
+    (b) receiving other notice that the Service is no longer valid; or
+
+    (c) determination that the Subscriber's information is no longer
+        accurate (including that the Subscriber is no longer the service
+        owner or the service owner's authorized delegate).
+
+  5. SERVICE FEES. A Participant may charge a fee to act as a Route
+Authority for a Service, with any Weight, provided that no Participant
+may charge a fee to propagate the Route received through the Peering
+System.
+
+  6. TOLL SERVICES. No Participant may provide Routes for any Services
+that require payment from the calling party or their customer for
+communication with the Service.  Nothing in this section shall prohibit
+a Participant from providing routes for Services where the calling party
+may later enter into a financial transaction with the called party
+(e.g., a Participant may provide Routes for calling cards services). 
+
+  7. QUALITY. A Participant may not intentionally impair communication
+using a Route provided to the Peering System (e.g. by adding delay,
+advertisements, reduced quality).  If for any reason a Participant is
+unable to deliver a call via a Route provided to the Peering System,
+that Participant shall return out-of-band Network Congestion
+notification (e.g. "503 Service Unavailable" with SIP protocol or
+"CONGESTION" with IAX protocol).
+
+  8. PROTOCOL COMPLIANCE.  Participants agree to Propagate Routes in
+strict compliance with current DUNDi protocol specifications.
+
+  9. ADMINISTRATIVE FEES. A Participant may charge (but is not required
+to charge) another Participant a reasonable fee to cover administrative
+expenses incurred in the execution of this Agreement.  A Participant may
+not charge any fee to continue the relationship or to provide Routes to
+another Participant in the Peering System.
+
+  10. CALLER IDENTIFICATION. A Participant will make a good faith effort
+to ensure the accuracy and appropriate nature of any caller
+identification that it transmits via any Route obtained from the Peering
+System. Caller identification shall at least be provided as a valid
+E.164 number.
+
+  11. COMPLIANCE WITH LAWS.  The Participants are solely responsible for
+determining to what extent, if any, the obligations set forth in this
+GPA conflict with any laws or regulations their region.  A Participant
+may not provide any service or otherwise use DUNDi under this GPA if
+doing so is prohibited by law or regulation, or if any law or regulation
+imposes requirements on the Participant that are inconsistent with the
+terms of this GPA or the Acceptable Use Policy. 
+
+  12. WARRANTY. EACH PARTICIPANT WARRANTS TO THE OTHER PARTICIPANTS THAT
+IT MADE, AND WILL CONTINUE TO MAKE, A GOOD FAITH EFFORT TO AUTHENTICATE
+OTHERS IN THE PEERING SYSTEM AND TO PROVIDE ACCURATE INFORMATION IN
+ACCORDANCE WITH THE TERMS OF THIS GPA.  THIS WARRANTY IS MADE BETWEEN
+THE PARTICIPANTS, AND THE PARTICIPANTS MAY NOT EXTEND THIS WARRANTY TO
+ANY NON-PARTICIPANT INCLUDING END-USERS. 
+
+  13. DISCLAIMER OF WARRANTIES. THE PARTICIPANTS UNDERSTAND AND AGREE
+THAT ANY SERVICE PROVIDED AS A RESULT OF THIS GPA IS "AS IS." EXCEPT FOR
+THOSE WARRANTIES OTHERWISE EXPRESSLY SET FORTH HEREIN, THE PARTICIPANTS
+DISCLAIM ANY REPRESENTATIONS OR WARRANTIES OF ANY KIND OR NATURE,
+EXPRESS OR IMPLIED, AS TO THE CONDITION, VALUE OR QUALITIES OF THE
+SERVICES PROVIDED HEREUNDER, AND SPECIFICALLY DISCLAIM ANY
+REPRESENTATION OR WARRANTY OF MERCHANTABILITY, SUITABILITY OR FITNESS
+FOR A PARTICULAR PURPOSE OR AS TO THE CONDITION OR WORKMANSHIP THEREOF,
+OR THE ABSENCE OF ANY DEFECTS THEREIN, WHETHER LATENT OR PATENT,
+INCLUDING ANY WARRANTIES ARISING FROM A COURSE OF DEALING, USAGE OR
+TRADE PRACTICE.  EXCEPT AS EXPRESSLY PROVIDED HEREIN, THE PARTICIPANTS
+EXPRESSLY DISCLAIM ANY REPRESENTATIONS OR WARRANTIES THAT THE PEERING
+SERVICE WILL BE CONTINUOUS, UNINTERRUPTED OR ERROR-FREE, THAT ANY DATA
+SHARED OR OTHERWISE MADE AVAILABLE WILL BE ACCURATE OR COMPLETE OR
+OTHERWISE COMPLETELY SECURE FROM UNAUTHORIZED ACCESS.
+
+  14. LIMITATION OF LIABILITIES.  NO PARTICIPANT SHALL BE LIABLE TO ANY
+OTHER PARTICIPANT FOR INCIDENTAL, INDIRECT, CONSEQUENTIAL, SPECIAL,
+PUNITIVE OR EXEMPLARY DAMAGES OF ANY KIND (INCLUDING LOST REVENUES OR
+PROFITS, LOSS OF BUSINESS OR LOSS OF DATA) IN ANY WAY RELATED TO THIS
+GPA, WHETHER IN CONTRACT OR IN TORT, REGARDLESS OF WHETHER SUCH
+PARTICIPANT WAS ADVISED OF THE POSSIBILITY THEREOF.
+
+  15. END-USER AGREEMENTS.  The Participants may independently enter
+into agreements with end-users to provide certain services (e.g., fees
+to a Subscriber to originate Routes for that Service).  To the extent
+that provision of these services employs the Peering System, the Parties
+will include in their agreements with their end-users terms and
+conditions consistent with the terms of this GPA with respect to the
+exclusion of warranties, limitation of liability and Acceptable Use
+Policy.  In no event may a Participant extend the warranty described in
+Section 12 in this GPA to any end-users. 
+
+  16. INDEMNIFICATION.  Each Participant agrees to defend, indemnify and
+hold harmless the other Participant or third-party beneficiaries to this
+GPA (including their affiliates, successors, assigns, agents and
+representatives and their respective officers, directors and employees)
+from and against any and all actions, suits, proceedings,
+investigations, demands, claims, judgments, liabilities, obligations,
+liens, losses, damages, expenses (including, without limitation,
+attorneys' fees) and any other fees arising out of or relating to (i)
+personal injury or property damage caused by that Participant, its
+employees, agents, servants, or other representatives; (ii) any act or
+omission by the Participant, its employees, agents, servants or other
+representatives, including, but not limited to, unauthorized
+representations or warranties made by the Participant; or (iii) any
+breach by the Participant of any of the terms or conditions of this GPA. 
+
+  17. THIRD PARTY BENEFICIARIES. This GPA is intended to benefit those
+Participants who have executed the GPA and who are in the Peering
+System. It is the intent of the Parties to this GPA to give to those
+Participants who are in the Peering System standing to bring any
+necessary legal action to enforce the terms of this GPA.
+
+  18. TERMINATION. Any Participant may terminate this GPA at any time,
+with or without cause.  A Participant that terminates must immediately
+cease to Propagate. 
+
+  19. CHOICE OF LAW. This GPA and the rights and duties of the Parties
+hereto shall be construed and determined in accordance with the internal
+laws of the State of New York, United States of America, without regard
+to its conflict of laws principles and without application of the United
+Nations Convention on Contracts for the International Sale of Goods.
+
+  20. DISPUTE RESOLUTION. Unless otherwise agreed in writing, the
+exclusive procedure for handling disputes shall be as set forth herein.
+Notwithstanding such procedures, any Participant may, at any time, seek
+injunctive relief in addition to the process described below. 
+
+    (a) Prior to mediation or arbitration the disputing Participants
+        shall seek informal resolution of disputes. The process shall be
+        initiated with written notice of one Participant to the other
+        describing the dispute with reasonable particularity followed
+        with a written response within ten (10) days of receipt of
+        notice. Each Participant shall promptly designate an executive
+        with requisite authority to resolve the dispute.  The informal
+        procedure shall commence within ten (10) days of the date of
+        response. All reasonable requests for non-privileged information
+        reasonably related to the dispute shall be honored. If the
+        dispute is not resolved within thirty (30) days of commencement
+        of the procedure either Participant may proceed to mediation or
+        arbitration pursuant to the rules set forth in (b) or (c) below.
+
+    (b) If the dispute has not been resolved pursuant to (a) above or,
+        if the disputing Participants fail to commence informal dispute
+        resolution pursuant to (a) above, either Participant may, in
+        writing and within twenty (20) days of the response date noted
+        in (a) above, ask the other Participant to participate in a one
+        (1) day mediation with an impartial mediator, and the other
+        Participant shall do so. Each Participant will bear its own
+        expenses and an equal share of the fees of the mediator.  If the
+        mediation is not successful the Participants may proceed with
+        arbitration pursuant to (c) below.
+
+    (c) If the dispute has not been resolved pursuant to (a) or (b)
+        above, the dispute shall be promptly referred, no later than one
+        (1) year from the date of original notice and subject to
+        applicable statute of limitations, to binding arbitration in
+        accordance with the UNCITRAL Arbitration Rules in effect on the
+        date of this contract.  The appointing authority shall be the
+        International Centre for Dispute Resolution. The case shall be
+        administered by the International Centre for Dispute Resolution
+        under its Procedures for Cases under the UNCITRAL Arbitration
+        Rules.  Each Participant shall bear its own expenses and shall
+        share equally in fees of the arbitrator. All arbitrators shall
+        have substantial experience in information technology and/or in
+        the telecommunications business and shall be selected by the
+        disputing participants in accordance with UNCITRAL Arbitration
+        Rules. If any arbitrator, once selected is unable or unwilling
+        to continue for any reason, replacement shall be filled via the
+        process described above and a re-hearing shall be conducted. The
+        disputing Participants will provide each other with all
+        requested documents and records reasonably related to the
+        dispute in a manner that will minimize the expense and
+        inconvenience of both parties. Discovery will not include
+        depositions or interrogatories except as the arbitrators
+        expressly allow upon a showing of need. If disputes arise 
+        concerning discovery requests, the arbitrators shall have sole
+        and complete discretion to resolve the disputes. The parties and
+        arbitrator shall be guided in resolving discovery disputes by
+        the Federal Rules of Civil Procedure. The Participants agree
+        that time of the essence principles shall guide the hearing and
+        that the arbitrator shall have the right and authority to issue
+        monetary sanctions in the event of unreasonable delay. The
+        arbitrator shall deliver a written opinion setting forth
+        findings of fact and the rationale for the award within thirty
+        (30) days following conclusion of the hearing. The award of the
+        arbitrator, which may include legal and equitable relief, but
+        which may not include punitive damages, will be final and
+        binding upon the disputing Participants, and judgment may be
+        entered upon it in accordance with applicable law in any court
+        having jurisdiction thereof.  In addition to award the
+        arbitrator shall have the discretion to award the prevailing
+        Participant all or part of its attorneys' fees and costs,
+        including fees associated with arbitrator, if the arbitrator
+        determines that the positions taken by the other Participant on
+        material issues of the dispute were without substantial
+        foundation. Any conflict between the UNCITRAL Arbitration Rules
+        and the provisions of this GPA shall be controlled by this GPA.
+
+  21. INTEGRATED AGREEMENT. This GPA, constitutes the complete
+integrated agreement between the parties concerning the subject matter
+hereof.  All prior and contemporaneous agreements, understandings,
+negotiations or representations, whether oral or in writing, relating to
+the subject matter of this GPA are superseded and canceled in their
+entirety.
+
+  22. WAIVER. No waiver of any of the provisions of this GPA shall be
+deemed or shall constitute a waiver of any other provision of this GPA,
+whether or not similar, nor shall such waiver constitute a continuing
+waiver unless otherwise expressly so provided in writing.  The failure
+of either party to enforce at any time any of the provisions of this
+GPA, or the failure to require at any time performance by either party
+of any of the provisions of this GPA, shall in no way be construed to be
+a present or future waiver of such provisions, nor in any way affect the
+ability of a Participant to enforce each and every such provision
+thereafter. 
+
+  23. INDEPENDENT CONTRACTORS. Nothing in this GPA shall make the
+Parties partners, joint venturers, or otherwise associated in or with
+the business of the other.  Parties are, and shall always remain,
+independent contractors.  No Participant shall be liable for any debts,
+accounts, obligations, or other liabilities of the other Participant,
+its agents or employees.  No party is authorized to incur debts or other
+obligations of any kind on the part of or as agent for the other.  This
+GPA is not a franchise agreement and does not create a franchise
+relationship between the parties, and if any provision of this GPA is
+deemed to create a franchise between the parties, then this GPA shall
+automatically terminate. 
+
+  24. CAPTIONS AND HEADINGS. The captions and headings used in this GPA
+are used for convenience only and are not to be given any legal effect.
+
+  25. EXECUTION. This GPA may be executed in counterparts, each of which
+so executed will be deemed to be an original and such counterparts
+together will constitute one and the same Agreement.  The Parties shall
+transmit to each other a signed copy of the GPA by any means that
+faithfully reproduces the GPA along with the Signature.  For purposes of
+this GPA, the term "signature" shall include digital signatures as
+defined by the jurisdiction of the Participant signing the GPA.
+
+                         Exhibit A
+
+Weight Range            Requirements 
+
+0-99                    May only be used under authorization of Owner
+
+100-199                 May only be used by the Owner's service
+                        provider, regardless of authorization.
+
+200-299                 Reserved -- do not use for e164 context.
+
+300-399                 May only be used by the owner of the code under
+                        which the Owner's number is a part of.
+
+400-499                 May be used by any entity providing access via
+                        direct connectivity to the Public Switched
+                        Telephone Network.
+
+500-599                        May be used by any entity providing access via
+                        indirect connectivity to the Public Switched
+                        Telephone Network (e.g. Via another VoIP
+                        provider)
+
+600-                    Reserved-- do not use for e164 context.                        
+
+                 Participant                         Participant
+
+Company:
+
+Address:
+
+Email:
+
+
+          _________________________           _________________________
+          Authorized Signature                Authorized Signature
+
+Name:
+
+
+END OF GENERAL PEERING AGREEMENT
+
+------------------------------------------------
+
+How to Peer using this GPA If you wish to exchange routing information
+with parties using the e164 DUNDi context, all you must do is execute
+this GPA with any member of the Peering System and you will become a
+member of the Peering System and be able to make Routes available in
+accordance with this GPA.
+
+DUNDi, IAX, Asterisk and GPA are trademarks of Digium, Inc.
diff --git a/doc/README.dundi b/doc/README.dundi
new file mode 100755 (executable)
index 0000000..89c8d24
--- /dev/null
@@ -0,0 +1,26 @@
+Distributed Universal Number Directory (DUNDi) (tm)
+===================================================
+http://www.dundi.com
+Mark Spencer, Digium, Inc.
+
+DUNDi is essentially a trusted, peer-to-peer system for being able to
+call any phone number from the Internet.  DUNDi works by creating a
+network of nodes called the "DUNDi E.164 Trust Group" which are bound by
+a common peering agreement known as the General Peering Agreement or
+GPA.  The GPA legally binds the members of the Trust Group to provide
+good-faith accurate information to the other nodes on the network, and
+provides standards by which the community can insure the integrity of
+the information on the nodes themselves.  Unlike ENUM or similar
+systems, DUNDi is explicitly designed to preclude any necessity for a
+single centralized system which could be a source of fees, regulation,
+etc.
+
+You can find the PEERING agreement in the doc directory.
+
+Much less dramatically, DUNDi can also be used within a private
+enterprise to share a dialplan efficiently between multiple nodes,
+without incuring a risk of a single point of failure.  In this way,
+administrators can locally add extensions which become immediately
+available to the other nodes in the system.
+
+For more information visit http://www.dundi.com
diff --git a/include/asterisk/dundi.h b/include/asterisk/dundi.h
new file mode 100755 (executable)
index 0000000..5780c3d
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Distributed Universal Number Discovery (DUNDi)
+ *
+ * Copyright (C) 2004, Digium Inc.
+ *
+ * Written by Mark Spencer <markster@digium.com>
+ *
+ * This program is Free Software distributed under the terms of
+ * of the GNU General Public License.
+ */
+#ifndef _ASTERISK_DUNDI_H 
+#define _ASTERISK_DUNDI_H
+
+#include <asterisk/channel.h>
+
+#define DUNDI_PORT 4520
+
+/* A DUNDi Entity ID is essentially a MAC address, brief and unique */
+struct _dundi_eid {
+       unsigned char eid[6];
+} __attribute__ ((__packed__));
+
+typedef struct _dundi_eid dundi_eid;
+
+struct dundi_hdr {
+       unsigned short strans;                  /* Source transaction */
+       unsigned short dtrans;                  /* Destination transaction */
+       unsigned char iseqno;                   /* Next expected incoming sequence number */
+       unsigned char oseqno;                   /* Outgoing sequence number */
+       unsigned char cmdresp;                  /* Command / Response */
+       unsigned char cmdflags;                 /* Command / Response specific flags*/
+       unsigned char ies[0];
+} __attribute__ ((__packed__));
+
+struct dundi_ie_hdr {
+       unsigned char ie;
+       unsigned char len;
+       unsigned char iedata[0];
+} __attribute__ ((__packed__));
+
+#define DUNDI_FLAG_RETRANS                     (1 << 16)       /* Applies to dtrans */
+#define DUNDI_FLAG_RESERVED                    (1 << 16)       /* Applies to strans */
+
+#define DUNDI_PROTO_NONE                       0       /* No answer yet */
+#define DUNDI_PROTO_IAX                                1       /* IAX version 2 */
+#define DUNDI_PROTO_SIP                                2       /* Session Initiation Protocol */
+#define DUNDI_PROTO_H323                       3       /* ITU H.323 */
+
+#define DUNDI_FLAG_NONEXISTANT         (0)                             /* Isn't and can't be a valid number */
+#define DUNDI_FLAG_EXISTS                      (1 << 0)                /* Is a valid number */
+#define DUNDI_FLAG_MATCHMORE           (1 << 1)                /* Might be valid if you add more digits */
+#define DUNDI_FLAG_CANMATCH                    (1 << 2)                /* Might be a match */
+#define DUNDI_FLAG_IGNOREPAT           (1 << 3)                /* Keep dialtone */
+#define DUNDI_FLAG_RESIDENTIAL         (1 << 4)                /* Destination known to be residential */
+#define DUNDI_FLAG_COMMERCIAL          (1 << 5)                /* Destination known to be commercial */
+#define DUNDI_FLAG_MOBILE                      (1 << 6)                /* Destination known to be cellular/mobile */
+#define DUNDI_FLAG_NOUNSOLICITED       (1 << 7)                /* No unsolicited calls of any kind through this route */
+#define DUNDI_FLAG_NOCOMUNSOLICIT      (1 << 8)                /* No commercial unsolicited calls through this route */
+
+#define DUNDI_HINT_NONE                                (0)
+#define DUNDI_HINT_TTL_EXPIRED         (1 << 0)                /* TTL Expired */
+#define DUNDI_HINT_DONT_ASK                    (1 << 1)                /* Don't ask for anything beginning with data */
+#define DUNDI_HINT_UNAFFECTED          (1 << 2)                /* Answer not affected by entity list */
+
+struct dundi_encblock {                                /* AES-128 encrypted block */
+       unsigned char iv[16];                   /* Initialization vector of random data */
+       unsigned char encdata[0];               /* Encrypted / compressed data */
+} __attribute__ ((__packed__));
+
+struct dundi_answer {
+       dundi_eid eid;                                  /* Original source of answer */
+       unsigned char protocol;                 /* Protocol (DUNDI_PROTO_*) */
+       unsigned short flags;                   /* Flags relating to answer */
+       unsigned short weight;                  /* Weight of answers */
+       unsigned char data[0];                  /* Protocol specific URI */
+} __attribute__ ((__packed__));
+
+struct dundi_hint {
+       unsigned short flags;                   /* Flags relating to answer */
+       unsigned char data[0];                  /* For data for hint */
+} __attribute__ ((__packed__));
+
+#define DUNDI_CAUSE_SUCCESS                    0       /* Success */
+#define DUNDI_CAUSE_GENERAL                    1       /* General unspecified failure */
+#define DUNDI_CAUSE_DYNAMIC                    2       /* Requested entity is dynamic */
+#define DUNDI_CAUSE_NOAUTH                     3       /* No or improper authorization */
+#define DUNDI_CAUSE_DUPLICATE          4       /* Duplicate request */
+#define DUNDI_CAUSE_TTL_EXPIRED                5       /* Expired TTL */
+#define DUNDI_CAUSE_NEEDKEY                    6       /* Need new session key to decode */
+#define DUNDI_CAUSE_BADENCRYPT         7       /* Badly encrypted data */
+
+struct dundi_cause {                   
+       unsigned char causecode;                /* Numerical cause (DUNDI_CAUSE_*) */
+       char desc[0];                                   /* Textual description */
+} __attribute__ ((__packed__));
+
+#define DUNDI_COMMAND_FINAL                    (0x80)          /* Or'd with other flags */
+
+#define DUNDI_COMMAND_ACK                      (0 | 0x40)      /* Ack a message */
+#define DUNDI_COMMAND_DPDISCOVER       1                       /* Request discovery */
+#define DUNDI_COMMAND_DPRESPONSE       (2 | 0x40)      /* Respond to a discovery request */
+#define DUNDI_COMMAND_EIDQUERY         3                       /* Request information for a peer */
+#define DUNDI_COMMAND_EIDRESPONSE      (4 | 0x40)      /* Response to a peer query */
+#define DUNDI_COMMAND_INVALID          (7 | 0x40)      /* Invalid dialog state (does not require ack) */
+#define DUNDI_COMMAND_UNKNOWN          (8 | 0x40)      /* Unknown command */
+#define DUNDI_COMMAND_NULL                     9                       /* No-op */
+#define DUNDI_COMMAND_REGREQ           (10)            /* Register Request */
+#define DUNDI_COMMAND_REGRESPONSE      (11 | 0x40)     /* Register Response */
+#define DUNDI_COMMAND_CANCEL           (12)            /* Cancel transaction entirely */
+#define DUNDI_COMMAND_ENCRYPT          (13)            /* Send an encrypted message */
+#define DUNDI_COMMAND_ENCREJ           (14 | 0x40)     /* Reject an encrypted message */
+
+/*
+ * Remember that some information elements may occur
+ * more than one time within a message
+ */
+
+#define DUNDI_IE_EID                           1       /* Entity identifier (dundi_eid) */
+#define DUNDI_IE_CALLED_CONTEXT                2       /* DUNDi Context (string) */
+#define DUNDI_IE_CALLED_NUMBER         3       /* Number of equivalent (string) */
+#define DUNDI_IE_EID_DIRECT                    4       /* Entity identifier (dundi_eid), direct connect */
+#define DUNDI_IE_ANSWER                                5       /* An answer (struct dundi_answer) */
+#define DUNDI_IE_TTL                           6       /* Max TTL for this request / Remaining TTL for the response  (short)*/
+#define DUNDI_IE_VERSION                       10      /* DUNDi version (should be 1) (short) */
+#define DUNDI_IE_EXPIRATION                    11      /* Recommended expiration (short) */
+#define DUNDI_IE_UNKNOWN                       12      /* Unknown command (byte) */
+#define DUNDI_IE_CAUSE                         14      /* Success or cause of failure */
+#define DUNDI_IE_REQEID                                15      /* EID being requested for EIDQUERY*/
+#define DUNDI_IE_ENCDATA                       16      /* AES-128 encrypted data */
+#define DUNDI_IE_SHAREDKEY                     17      /* RSA encrypted AES-128 key */
+#define DUNDI_IE_SIGNATURE                     18      /* RSA Signature of encrypted shared key */
+#define DUNDI_IE_KEYCRC32                      19      /* CRC32 of encrypted key (int) */
+#define DUNDI_IE_HINT                          20      /* Answer hints (struct ast_hint) */
+
+#define DUNDI_IE_DEPARTMENT                    21      /* Department, for EIDQUERY (string) */
+#define DUNDI_IE_ORGANIZATION          22      /* Organization, for EIDQUERY (string) */
+#define DUNDI_IE_LOCALITY                      23      /* City/Locality, for EIDQUERY (string) */
+#define DUNDI_IE_STATE_PROV                    24      /* State/Province, for EIDQUERY (string) */
+#define DUNDI_IE_COUNTRY                       25      /* Country, for EIDQUERY (string) */
+#define DUNDI_IE_EMAIL                         26      /* E-mail addy, for EIDQUERY (string) */
+#define DUNDI_IE_PHONE                         27      /* Contact Phone, for EIDQUERY (string) */
+#define DUNDI_IE_IPADDR                                28      /* IP Address, for EIDQUERY (string) */
+
+#define DUNDI_FLUFF_TIME                       2000    /* Amount of time for answer */
+#define DUNDI_TTL_TIME                         200             /* Incremental average time */
+
+#define DUNDI_DEFAULT_RETRANS          5
+#define DUNDI_DEFAULT_RETRANS_TIMER    1000
+#define DUNDI_DEFAULT_TTL                      120     /* In seconds/hops like TTL */
+#define DUNDI_DEFAULT_VERSION          1
+#define DUNDI_DEFAULT_CACHE_TIME       3600    /* In seconds */
+#define DUNDI_DEFAULT_KEY_EXPIRE       3600    /* Life of shared key In seconds */
+#define DUNDI_DEF_EMPTY_CACHE_TIME     60      /* In seconds, cache of empty answer */
+#define DUNDI_WINDOW                           1       /* Max 1 message in window */
+
+#define DEFAULT_MAXMS                          2000
+
+struct dundi_result {
+       int flags;
+       int weight;
+       int expiration;
+       int techint;
+       dundi_eid eid;
+       char eid_str[20];
+       char tech[10];
+       char dest[256];
+};
+
+struct dundi_entity_info {
+       char country[80];
+       char stateprov[80];
+       char locality[80];
+       char org[80];
+       char orgunit[80];
+       char email[80];
+       char phone[80]; 
+       char ipaddr[80];
+};
+
+/* 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.
+   returns the number of results found or -1 on a hangup of teh channel. */
+int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number);
+
+/* Retrieve information on a specific EID */
+int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid);
+#endif /* _ASTERISK_DUNDI_H */
index aa71a0e..8fb70b6 100755 (executable)
@@ -13,7 +13,7 @@
 
 
 
-PBX_LIBS=pbx_config.so pbx_spool.so     # pbx_gtkconsole.so pbx_kdeconsole.so
+PBX_LIBS=pbx_config.so pbx_spool.so pbx_dundi.so 
 
 # Add GTK console if appropriate
 #PBX_LIBS+=$(shell gtk-config --cflags >/dev/null 2>/dev/null && echo "pbx_gtkconsole.so")
@@ -55,6 +55,9 @@ pbx_kdeconsole_main.o: pbx_kdeconsole_main.cc pbx_kdeconsole.h
 pbx_kdeconsole.so: $(KDE_CONSOLE_OBJS)
        $(CC) $(SOLINK) -o $@ $(KDE_CONSOLE_OBJS) $(KDE_LIBS)
 
+pbx_dundi.so: dundi-parser.o pbx_dundi.o
+       $(CC) $(SOLINK) -o $@ pbx_dundi.o dundi-parser.o -lz
+
 %.moc : %.h
        $(MOC) $< -o $@
 
diff --git a/pbx/dundi-parser.c b/pbx/dundi-parser.c
new file mode 100755 (executable)
index 0000000..45e186c
--- /dev/null
@@ -0,0 +1,802 @@
+/*
+ * Distributed Universal Number Discovery (DUNDi)
+ *
+ * Copyright (C) 2004, Digium Inc.
+ *
+ * Written by Mark Spencer <markster@digium.com>
+ *
+ * This program is Free Software distributed under the terms of
+ * of the GNU General Public License.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <asterisk/frame.h>
+#include <asterisk/utils.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <asterisk/dundi.h>
+#include "dundi-parser.h"
+#include <asterisk/dundi.h>
+static void internaloutput(const char *str)
+{
+       printf(str);
+}
+
+static void internalerror(const char *str)
+{
+       fprintf(stderr, "WARNING: %s", str);
+}
+
+static void (*outputf)(const char *str) = internaloutput;
+static void (*errorf)(const char *str) = internalerror;
+
+char *dundi_eid_to_str(char *s, int maxlen, dundi_eid *eid)
+{
+       int x;
+       char *os = s;
+       if (maxlen < 18) {
+               if (s && (maxlen > 0))
+                       *s = '\0';
+       } else {
+               for (x=0;x<5;x++) {
+                       sprintf(s, "%02x:", eid->eid[x]);
+                       s += 3;
+               }
+               sprintf(s, "%02x", eid->eid[5]);
+       }
+       return os;
+}
+
+char *dundi_eid_to_str_short(char *s, int maxlen, dundi_eid *eid)
+{
+       int x;
+       char *os = s;
+       if (maxlen < 13) {
+               if (s && (maxlen > 0))
+                       *s = '\0';
+       } else {
+               for (x=0;x<6;x++) {
+                       sprintf(s, "%02X", eid->eid[x]);
+                       s += 2;
+               }
+       }
+       return os;
+}
+
+int dundi_str_to_eid(dundi_eid *eid, char *s)
+{
+       unsigned int eid_int[6];
+       int x;
+       if (sscanf(s, "%x:%x:%x:%x:%x:%x", &eid_int[0], &eid_int[1], &eid_int[2],
+                &eid_int[3], &eid_int[4], &eid_int[5]) != 6)
+                       return -1;
+       for (x=0;x<6;x++)
+               eid->eid[x] = eid_int[x];
+       return 0;
+}
+
+int dundi_str_short_to_eid(dundi_eid *eid, char *s)
+{
+       unsigned int eid_int[6];
+       int x;
+       if (sscanf(s, "%2x%2x%2x%2x%2x%2x", &eid_int[0], &eid_int[1], &eid_int[2],
+                &eid_int[3], &eid_int[4], &eid_int[5]) != 6)
+                       return -1;
+       for (x=0;x<6;x++)
+               eid->eid[x] = eid_int[x];
+       return 0;
+}
+int dundi_eid_zero(dundi_eid *eid)
+{
+       int x;
+       for (x=0;x<sizeof(eid->eid) / sizeof(eid->eid[0]);x++)
+               if (eid->eid[x]) return 0;
+       return 1;
+}
+
+int dundi_eid_cmp(dundi_eid *eid1, dundi_eid *eid2)
+{
+       return memcmp(eid1, eid2, sizeof(dundi_eid));
+}
+
+static void dump_string(char *output, int maxlen, void *value, int len)
+{
+       maxlen--;
+       if (maxlen > len)
+               maxlen = len;
+       strncpy(output,value, maxlen);
+       output[maxlen] = '\0';
+}
+
+static void dump_eid(char *output, int maxlen, void *value, int len)
+{
+       if (len == 6)
+               dundi_eid_to_str(output, maxlen, (dundi_eid *)value);
+       else
+               snprintf(output, maxlen, "Invalid EID len %d", len);
+}
+
+char *dundi_hint2str(char *buf, int bufsiz, int flags)
+{
+       strcpy(buf, "");
+       buf[bufsiz-1] = '\0';
+       if (flags & DUNDI_HINT_TTL_EXPIRED) {
+               strncat(buf, "TTLEXPIRED|", bufsiz - strlen(buf) - 1);
+       }
+       if (flags & DUNDI_HINT_DONT_ASK) {
+               strncat(buf, "DONTASK|", bufsiz - strlen(buf) - 1);
+       }
+       if (flags & DUNDI_HINT_UNAFFECTED) {
+               strncat(buf, "UNAFFECTED|", bufsiz - strlen(buf) - 1);
+       }
+       /* Get rid of trailing | */
+       if (ast_strlen_zero(buf))
+               strcpy(buf, "NONE|");
+       buf[strlen(buf)-1] = '\0';
+       return buf;
+}
+
+static void dump_hint(char *output, int maxlen, void *value, int len)
+{
+       unsigned short flags;
+       char tmp[512];
+       char tmp2[256];
+       if (len < 2) {
+               strncpy(output, "<invalid contents>", maxlen);
+               return;
+       }
+       memcpy(&flags, value, sizeof(flags));
+       flags = ntohs(flags);
+       memset(tmp, 0, sizeof(tmp));
+       dundi_hint2str(tmp2, sizeof(tmp2), flags);
+       snprintf(tmp, sizeof(tmp), "[%s] ", tmp2);
+       memcpy(tmp + strlen(tmp), value + 2, len - 2);
+       strncpy(output, tmp, maxlen - 1);
+}
+
+static void dump_cause(char *output, int maxlen, void *value, int len)
+{
+       static char *causes[] = {
+               "SUCCESS",
+               "GENERAL",
+               "DYNAMIC",
+               "NOAUTH" ,
+               };
+       char tmp[256];
+       char tmp2[256];
+       int mlen;
+       unsigned char cause;
+       if (len < 1) {
+               strncpy(output, "<invalid contents>", maxlen);
+               return;
+       }
+       cause = *((unsigned char *)value);
+       memset(tmp2, 0, sizeof(tmp2));
+       mlen = len - 1;
+       if (mlen > 255)
+               mlen = 255;
+       memcpy(tmp2, value + 1, mlen);
+       if (cause < sizeof(causes) / sizeof(causes[0])) {
+               if (len > 1)
+                       snprintf(tmp, sizeof(tmp), "%s: %s", causes[cause], tmp2);
+               else
+                       snprintf(tmp, sizeof(tmp), "%s", causes[cause]);
+       } else {
+               if (len > 1)
+                       snprintf(tmp, sizeof(tmp), "%d: %s", cause, tmp2);
+               else
+                       snprintf(tmp, sizeof(tmp), "%d", cause);
+       }
+       
+       strncpy(output,tmp, maxlen);
+       output[maxlen] = '\0';
+}
+
+static void dump_int(char *output, int maxlen, void *value, int len)
+{
+       if (len == (int)sizeof(unsigned int))
+               snprintf(output, maxlen, "%lu", (unsigned long)ntohl(*((unsigned int *)value)));
+       else
+               snprintf(output, maxlen, "Invalid INT");
+}
+
+static void dump_short(char *output, int maxlen, void *value, int len)
+{
+       if (len == (int)sizeof(unsigned short))
+               snprintf(output, maxlen, "%d", ntohs(*((unsigned short *)value)));
+       else
+               snprintf(output, maxlen, "Invalid SHORT");
+}
+
+static void dump_byte(char *output, int maxlen, void *value, int len)
+{
+       if (len == (int)sizeof(unsigned char))
+               snprintf(output, maxlen, "%d", *((unsigned char *)value));
+       else
+               snprintf(output, maxlen, "Invalid BYTE");
+}
+
+static char *proto2str(int proto, char *buf, int bufsiz)
+{
+       
+       switch(proto) {
+       case DUNDI_PROTO_NONE:
+               strncpy(buf, "None", bufsiz - 1);
+               break;
+       case DUNDI_PROTO_IAX:
+               strncpy(buf, "IAX", bufsiz - 1);
+               break;
+       case DUNDI_PROTO_SIP:
+               strncpy(buf, "SIP", bufsiz - 1);
+               break;
+       case DUNDI_PROTO_H323:
+               strncpy(buf, "H.323", bufsiz - 1);
+               break;
+       default:
+               snprintf(buf, bufsiz, "Unknown Proto(%d)", proto);
+       }
+       buf[bufsiz-1] = '\0';
+       return buf;
+}
+
+char *dundi_flags2str(char *buf, int bufsiz, int flags)
+{
+       strcpy(buf, "");
+       buf[bufsiz-1] = '\0';
+       if (flags & DUNDI_FLAG_EXISTS) {
+               strncat(buf, "EXISTS|", bufsiz - strlen(buf) - 1);
+       }
+       if (flags & DUNDI_FLAG_MATCHMORE) {
+               strncat(buf, "MATCHMORE|", bufsiz - strlen(buf) - 1);
+       }
+       if (flags & DUNDI_FLAG_CANMATCH) {
+               strncat(buf, "CANMATCH|", bufsiz - strlen(buf) - 1);
+       }
+       if (flags & DUNDI_FLAG_IGNOREPAT) {
+               strncat(buf, "IGNOREPAT|", bufsiz - strlen(buf) - 1);
+       }
+       if (flags & DUNDI_FLAG_RESIDENTIAL) {
+               strncat(buf, "RESIDENCE|", bufsiz - strlen(buf) - 1);
+       }
+       if (flags & DUNDI_FLAG_COMMERCIAL) {
+               strncat(buf, "COMMERCIAL|", bufsiz - strlen(buf) - 1);
+       }
+       if (flags & DUNDI_FLAG_MOBILE) {
+               strncat(buf, "MOBILE", bufsiz - strlen(buf) - 1);
+       }
+       if (flags & DUNDI_FLAG_NOUNSOLICITED) {
+               strncat(buf, "NOUNSLCTD|", bufsiz - strlen(buf) - 1);
+       }
+       if (flags & DUNDI_FLAG_NOCOMUNSOLICIT) {
+               strncat(buf, "NOCOMUNSLTD|", bufsiz - strlen(buf) - 1);
+       }
+       /* Get rid of trailing | */
+       if (ast_strlen_zero(buf))
+               strcpy(buf, "NONE|");
+       buf[strlen(buf)-1] = '\0';
+       return buf;
+}
+
+static void dump_answer(char *output, int maxlen, void *value, int len)
+{
+       struct dundi_answer *answer;
+       char proto[40];
+       char flags[40];
+       char eid_str[40];
+       char tmp[512]="";
+       if (len >= 10) {
+               answer = (struct dundi_answer *)(value);
+               memcpy(tmp, answer->data, (len >= 500) ? 500 : len - 10);
+               dundi_eid_to_str(eid_str, sizeof(eid_str), &answer->eid);
+               snprintf(output, maxlen, "[%s] %d <%s/%s> from [%s]", 
+                       dundi_flags2str(flags, sizeof(flags), ntohs(answer->flags)), 
+                       ntohs(answer->weight),
+                       proto2str(answer->protocol, proto, sizeof(proto)), 
+                               tmp, eid_str);
+       } else
+               strncpy(output, "Invalid Answer", maxlen - 1);
+}
+
+static void dump_encrypted(char *output, int maxlen, void *value, int len)
+{
+       char iv[33];
+       int x;
+       if ((len > 16) && !(len % 16)) {
+               /* Build up IV */
+               for (x=0;x<16;x++) {
+                       snprintf(iv + (x << 1), 3, "%02x", ((unsigned char *)value)[x]);
+               }
+               snprintf(output, maxlen, "[IV %s] %d encrypted blocks\n", iv, len / 16);
+       } else
+               snprintf(output, maxlen, "Invalid Encrypted Datalen %d", len);
+}
+
+static void dump_raw(char *output, int maxlen, void *value, int len)
+{
+       int x;
+       unsigned char *u = value;
+       output[maxlen - 1] = '\0';
+       strcpy(output, "[ ");
+       for (x=0;x<len;x++) {
+               snprintf(output + strlen(output), maxlen - strlen(output) - 1, "%02x ", u[x]);
+       }
+       strncat(output + strlen(output), "]", maxlen - strlen(output) - 1);
+}
+
+static struct dundi_ie {
+       int ie;
+       char *name;
+       void (*dump)(char *output, int maxlen, void *value, int len);
+} ies[] = {
+       { DUNDI_IE_EID, "ENTITY IDENT", dump_eid },
+       { DUNDI_IE_CALLED_CONTEXT, "CALLED CONTEXT", dump_string },
+       { DUNDI_IE_CALLED_NUMBER, "CALLED NUMBER", dump_string },
+       { DUNDI_IE_EID_DIRECT, "DIRECT EID", dump_eid },
+       { DUNDI_IE_ANSWER, "ANSWER", dump_answer },
+       { DUNDI_IE_TTL, "TTL", dump_short },
+       { DUNDI_IE_VERSION, "VERSION", dump_short },
+       { DUNDI_IE_EXPIRATION, "EXPIRATION", dump_short },
+       { DUNDI_IE_UNKNOWN, "UKWN DUNDI CMD", dump_byte },
+       { DUNDI_IE_CAUSE, "CAUSE", dump_cause },
+       { DUNDI_IE_REQEID, "REQUEST EID", dump_eid },
+       { DUNDI_IE_ENCDATA, "ENCDATA", dump_encrypted },
+       { DUNDI_IE_SHAREDKEY, "SHAREDKEY", dump_raw },
+       { DUNDI_IE_SIGNATURE, "SIGNATURE", dump_raw },
+       { DUNDI_IE_KEYCRC32, "KEYCRC32", dump_int },
+       { DUNDI_IE_HINT, "HINT", dump_hint },
+       { DUNDI_IE_DEPARTMENT, "DEPARTMENT", dump_string },
+       { DUNDI_IE_ORGANIZATION, "ORGANIZTN", dump_string },
+       { DUNDI_IE_LOCALITY, "LOCALITY", dump_string },
+       { DUNDI_IE_STATE_PROV, "STATEPROV", dump_string },
+       { DUNDI_IE_COUNTRY, "COUNTRY", dump_string },
+       { DUNDI_IE_EMAIL, "EMAIL", dump_string },
+       { DUNDI_IE_PHONE, "PHONE", dump_string },
+       { DUNDI_IE_IPADDR, "ADDRESS", dump_string },
+};
+
+const char *dundi_ie2str(int ie)
+{
+       int x;
+       for (x=0;x<(int)sizeof(ies) / (int)sizeof(ies[0]); x++) {
+               if (ies[x].ie == ie)
+                       return ies[x].name;
+       }
+       return "Unknown IE";
+}
+
+static void dump_ies(unsigned char *iedata, int spaces, int len)
+{
+       int ielen;
+       int ie;
+       int x;
+       int found;
+       char interp[1024];
+       char tmp[1024];
+       if (len < 2)
+               return;
+       while(len > 2) {
+               ie = iedata[0];
+               ielen = iedata[1];
+               /* Encrypted data is the remainder */
+               if (ie == DUNDI_IE_ENCDATA)
+                       ielen = len - 2;
+               if (ielen + 2> len) {
+                       snprintf(tmp, (int)sizeof(tmp), "Total IE length of %d bytes exceeds remaining frame length of %d bytes\n", ielen + 2, len);
+                       outputf(tmp);
+                       return;
+               }
+               found = 0;
+               for (x=0;x<(int)sizeof(ies) / (int)sizeof(ies[0]); x++) {
+                       if (ies[x].ie == ie) {
+                               if (ies[x].dump) {
+                                       ies[x].dump(interp, (int)sizeof(interp), iedata + 2, ielen);
+                                       snprintf(tmp, (int)sizeof(tmp), "   %s%-15.15s : %s\n", (spaces ? "     " : "" ), ies[x].name, interp);
+                                       outputf(tmp);
+                               } else {
+                                       if (ielen)
+                                               snprintf(interp, (int)sizeof(interp), "%d bytes", ielen);
+                                       else
+                                               strcpy(interp, "Present");
+                                       snprintf(tmp, (int)sizeof(tmp), "   %s%-15.15s : %s\n", (spaces ? "     " : "" ), ies[x].name, interp);
+                                       outputf(tmp);
+                               }
+                               found++;
+                       }
+               }
+               if (!found) {
+                       snprintf(tmp, (int)sizeof(tmp), "   %sUnknown IE %03d  : Present\n", (spaces ? "     " : "" ), ie);
+                       outputf(tmp);
+               }
+               iedata += (2 + ielen);
+               len -= (2 + ielen);
+       }
+       outputf("\n");
+}
+
+void dundi_showframe(struct dundi_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen)
+{
+       char *pref[] = {
+               "Tx",
+               "Rx",
+               "    ETx",
+               "    Erx" };
+       char *commands[] = {
+               "ACK         ",
+               "DPDISCOVER  ",
+               "DPRESPONSE  ",
+               "EIDQUERY    ",
+               "EIDRESPONSE ",
+               "UNKNOWN (5)?",
+               "UNKNOWN (6)?",
+               "INVALID     ",
+               "UNKNOWN CMD ",
+               "NULL        ",
+               "REQREQ      ",
+               "REGRESPONSE ",
+               "CANCEL      ",
+               "ENCRYPT     ",
+               "ENCREJ      " };
+       char class2[20];
+       char *class;
+       char subclass2[20];
+       char *subclass;
+       char tmp[256];
+       char retries[20];
+       char iabuf[INET_ADDRSTRLEN];
+       if (ntohs(fhi->dtrans) & DUNDI_FLAG_RETRANS)
+               strcpy(retries, "Yes");
+       else
+               strcpy(retries, "No");
+       if ((ntohs(fhi->strans) & DUNDI_FLAG_RESERVED)) {
+               /* Ignore frames with high bit set to 1 */
+               return;
+       }
+       if ((fhi->cmdresp & 0x3f) > (int)sizeof(commands)/(int)sizeof(char *)) {
+               snprintf(class2, (int)sizeof(class2), "(%d?)", fhi->cmdresp);
+               class = class2;
+       } else {
+               class = commands[(int)(fhi->cmdresp & 0x3f)];
+       }
+       snprintf(subclass2, (int)sizeof(subclass2), "%02x", fhi->cmdflags);
+       subclass = subclass2;
+snprintf(tmp, (int)sizeof(tmp), 
+"%s-Frame Retry[%s] -- OSeqno: %3.3d ISeqno: %3.3d Type: %s (%s)\n",
+       pref[rx],
+       retries, fhi->oseqno, fhi->iseqno, class, fhi->cmdresp & 0x40 ? "Response" : "Command");
+       outputf(tmp);
+snprintf(tmp, (int)sizeof(tmp), 
+"%s     Flags: %s STrans: %5.5d  DTrans: %5.5d [%s:%d]%s\n", (rx > 1) ? "     " : "",
+       subclass, ntohs(fhi->strans) & ~DUNDI_FLAG_RESERVED, ntohs(fhi->dtrans) & ~DUNDI_FLAG_RETRANS,
+               ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), ntohs(sin->sin_port),
+               fhi->cmdresp & 0x80 ? " (Final)" : "");
+       outputf(tmp);
+       dump_ies(fhi->ies, rx > 1, datalen);
+}
+
+int dundi_ie_append_raw(struct dundi_ie_data *ied, unsigned char ie, void *data, int datalen)
+{
+       char tmp[256];
+       if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
+               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);
+               errorf(tmp);
+               return -1;
+       }
+       ied->buf[ied->pos++] = ie;
+       ied->buf[ied->pos++] = datalen;
+       memcpy(ied->buf + ied->pos, data, datalen);
+       ied->pos += datalen;
+       return 0;
+}
+
+int dundi_ie_append_cause(struct dundi_ie_data *ied, unsigned char ie, unsigned char cause, unsigned char *data)
+{
+       char tmp[256];
+       int datalen = data ? strlen(data) + 1 : 1;
+       if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
+               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);
+               errorf(tmp);
+               return -1;
+       }
+       ied->buf[ied->pos++] = ie;
+       ied->buf[ied->pos++] = datalen;
+       ied->buf[ied->pos++] = cause;
+       memcpy(ied->buf + ied->pos, data, datalen-1);
+       ied->pos += datalen-1;
+       return 0;
+}
+
+int dundi_ie_append_hint(struct dundi_ie_data *ied, unsigned char ie, unsigned short flags, unsigned char *data)
+{
+       char tmp[256];
+       int datalen = data ? strlen(data) + 2 : 2;
+       if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
+               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);
+               errorf(tmp);
+               return -1;
+       }
+       ied->buf[ied->pos++] = ie;
+       ied->buf[ied->pos++] = datalen;
+       flags = htons(flags);
+       memcpy(ied->buf + ied->pos, &flags, sizeof(flags));
+       ied->pos += 2;
+       memcpy(ied->buf + ied->pos, data, datalen-1);
+       ied->pos += datalen-2;
+       return 0;
+}
+
+int dundi_ie_append_encdata(struct dundi_ie_data *ied, unsigned char ie, unsigned char *iv, void *data, int datalen)
+{
+       char tmp[256];
+       datalen += 16;
+       if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
+               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);
+               errorf(tmp);
+               return -1;
+       }
+       ied->buf[ied->pos++] = ie;
+       ied->buf[ied->pos++] = datalen;
+       memcpy(ied->buf + ied->pos, iv, 16);
+       ied->pos += 16;
+       if (data) {
+               memcpy(ied->buf + ied->pos, data, datalen-16);
+               ied->pos += datalen-16;
+       }
+       return 0;
+}
+
+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)
+{
+       char tmp[256];
+       int datalen = data ? strlen(data) + 11 : 11;
+       int x;
+       unsigned short myw;
+       if (datalen > ((int)sizeof(ied->buf) - ied->pos)) {
+               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);
+               errorf(tmp);
+               return -1;
+       }
+       ied->buf[ied->pos++] = ie;
+       ied->buf[ied->pos++] = datalen;
+       for (x=0;x<6;x++)
+               ied->buf[ied->pos++] = eid->eid[x];
+       ied->buf[ied->pos++] = protocol;
+       myw = htons(flags);
+       memcpy(ied->buf + ied->pos, &myw, 2);
+       ied->pos += 2;
+       myw = htons(weight);
+       memcpy(ied->buf + ied->pos, &myw, 2);
+       ied->pos += 2;
+       memcpy(ied->buf + ied->pos, data, datalen-11);
+       ied->pos += datalen-11;
+       return 0;
+}
+
+int dundi_ie_append_addr(struct dundi_ie_data *ied, unsigned char ie, struct sockaddr_in *sin)
+{
+       return dundi_ie_append_raw(ied, ie, sin, (int)sizeof(struct sockaddr_in));
+}
+
+int dundi_ie_append_int(struct dundi_ie_data *ied, unsigned char ie, unsigned int value) 
+{
+       unsigned int newval;
+       newval = htonl(value);
+       return dundi_ie_append_raw(ied, ie, &newval, (int)sizeof(newval));
+}
+
+int dundi_ie_append_short(struct dundi_ie_data *ied, unsigned char ie, unsigned short value) 
+{
+       unsigned short newval;
+       newval = htons(value);
+       return dundi_ie_append_raw(ied, ie, &newval, (int)sizeof(newval));
+}
+
+int dundi_ie_append_str(struct dundi_ie_data *ied, unsigned char ie, unsigned char *str)
+{
+       return dundi_ie_append_raw(ied, ie, str, strlen(str));
+}
+
+int dundi_ie_append_eid(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid)
+{
+       return dundi_ie_append_raw(ied, ie, (unsigned char *)eid, sizeof(dundi_eid));
+}
+
+int dundi_ie_append_byte(struct dundi_ie_data *ied, unsigned char ie, unsigned char dat)
+{
+       return dundi_ie_append_raw(ied, ie, &dat, 1);
+}
+
+int dundi_ie_append(struct dundi_ie_data *ied, unsigned char ie) 
+{
+       return dundi_ie_append_raw(ied, ie, NULL, 0);
+}
+
+void dundi_set_output(void (*func)(const char *))
+{
+       outputf = func;
+}
+
+void dundi_set_error(void (*func)(const char *))
+{
+       errorf = func;
+}
+
+int dundi_parse_ies(struct dundi_ies *ies, unsigned char *data, int datalen)
+{
+       /* Parse data into information elements */
+       int len;
+       int ie;
+       char tmp[256];
+       memset(ies, 0, (int)sizeof(struct dundi_ies));
+       ies->ttl = -1;
+       ies->expiration = -1;
+       ies->unknowncmd = -1;
+       ies->cause = -1;
+       while(datalen >= 2) {
+               ie = data[0];
+               len = data[1];
+               if (len > datalen - 2) {
+                       errorf("Information element length exceeds message size\n");
+                       return -1;
+               }
+               switch(ie) {
+               case DUNDI_IE_EID:
+               case DUNDI_IE_EID_DIRECT:
+                       if (len != (int)sizeof(dundi_eid)) {
+                               errorf("Improper entity identifer, expecting 6 bytes!\n");
+                       } else if (ies->eidcount < DUNDI_MAX_STACK) {
+                               ies->eids[ies->eidcount] = (dundi_eid *)(data + 2);
+                               ies->eid_direct[ies->eidcount] = (ie == DUNDI_IE_EID_DIRECT);
+                               ies->eidcount++;
+                       } else
+                               errorf("Too many entities in stack!\n");
+                       break;
+               case DUNDI_IE_REQEID:
+                       if (len != (int)sizeof(dundi_eid)) {
+                               errorf("Improper requested entity identifer, expecting 6 bytes!\n");
+                       } else
+                               ies->reqeid = (dundi_eid *)(data + 2);
+                       break;
+               case DUNDI_IE_CALLED_CONTEXT:
+                       ies->called_context = data + 2;
+                       break;
+               case DUNDI_IE_CALLED_NUMBER:
+                       ies->called_number = data + 2;
+                       break;
+               case DUNDI_IE_ANSWER:
+                       if (len < sizeof(struct dundi_answer)) {
+                               snprintf(tmp, (int)sizeof(tmp), "Answer expected to be >=%d bytes long but was %d\n", (int)sizeof(struct dundi_answer), len);
+                               errorf(tmp);
+                       } else {
+                               if (ies->anscount < DUNDI_MAX_ANSWERS)
+                                       ies->answers[ies->anscount++]= (struct dundi_answer *)(data + 2);
+                               else 
+                                       errorf("Ignoring extra answers!\n");
+                       }
+                       break;
+               case DUNDI_IE_TTL:
+                       if (len != (int)sizeof(unsigned short)) {
+                               snprintf(tmp, (int)sizeof(tmp), "Expecting ttl to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+                               errorf(tmp);
+                       } else
+                               ies->ttl = ntohs(*((unsigned short *)(data + 2)));
+                       break;
+               case DUNDI_IE_VERSION:
+                       if (len != (int)sizeof(unsigned short)) {
+                               snprintf(tmp, (int)sizeof(tmp),  "Expecting version to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+                               errorf(tmp);
+                       } else
+                               ies->version = ntohs(*((unsigned short *)(data + 2)));
+                       break;
+               case DUNDI_IE_EXPIRATION:
+                       if (len != (int)sizeof(unsigned short)) {
+                               snprintf(tmp, (int)sizeof(tmp),  "Expecting expiration to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+                               errorf(tmp);
+                       } else
+                               ies->expiration = ntohs(*((unsigned short *)(data + 2)));
+                       break;
+               case DUNDI_IE_KEYCRC32:
+                       if (len != (int)sizeof(unsigned int)) {
+                               snprintf(tmp, (int)sizeof(tmp),  "Expecting expiration to be %d bytes long but was %d\n", (int)sizeof(unsigned int), len);
+                               errorf(tmp);
+                       } else
+                               ies->keycrc32 = ntohl(*((unsigned int *)(data + 2)));
+                       break;
+               case DUNDI_IE_UNKNOWN:
+                       if (len == 1)
+                               ies->unknowncmd = data[2];
+                       else {
+                               snprintf(tmp, (int)sizeof(tmp), "Expected single byte Unknown command, but was %d long\n", len);
+                               errorf(tmp);
+                       }
+                       break;
+               case DUNDI_IE_CAUSE:
+                       if (len >= 1) {
+                               ies->cause = data[2];
+                               ies->causestr = data + 3;
+                       } else {
+                               snprintf(tmp, (int)sizeof(tmp), "Expected at least one byte cause, but was %d long\n", len);
+                               errorf(tmp);
+                       }
+                       break;
+               case DUNDI_IE_HINT:
+                       if (len >= 2) {
+                               ies->hint = (struct dundi_hint *)(data + 2);
+                       } else {
+                               snprintf(tmp, (int)sizeof(tmp), "Expected at least two byte hint, but was %d long\n", len);
+                               errorf(tmp);
+                       }
+                       break;
+               case DUNDI_IE_DEPARTMENT:
+                       ies->q_dept = data + 2;
+                       break;
+               case DUNDI_IE_ORGANIZATION:
+                       ies->q_org = data + 2;
+                       break;
+               case DUNDI_IE_LOCALITY:
+                       ies->q_locality = data + 2;
+                       break;
+               case DUNDI_IE_STATE_PROV:
+                       ies->q_stateprov = data + 2;
+                       break;
+               case DUNDI_IE_COUNTRY:
+                       ies->q_country = data + 2;
+                       break;
+               case DUNDI_IE_EMAIL:
+                       ies->q_email = data + 2;
+                       break;
+               case DUNDI_IE_PHONE:
+                       ies->q_phone = data + 2;
+                       break;
+               case DUNDI_IE_IPADDR:
+                       ies->q_ipaddr = data + 2;
+                       break;
+               case DUNDI_IE_ENCDATA:
+                       /* Recalculate len as the remainder of the message, regardless of
+                          theoretical length */
+                       len = datalen - 2;
+                       if ((len > 16) && !(len % 16)) {
+                               ies->encblock = (struct dundi_encblock *)(data + 2);
+                               ies->enclen = len - 16;
+                       } else {
+                               snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted data length %d\n", len);
+                               errorf(tmp);
+                       }
+                       break;
+               case DUNDI_IE_SHAREDKEY:
+                       if (len == 128) {
+                               ies->encsharedkey = (unsigned char *)(data + 2);
+                       } else {
+                               snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted shared key length %d\n", len);
+                               errorf(tmp);
+                       }
+                       break;
+               case DUNDI_IE_SIGNATURE:
+                       if (len == 128) {
+                               ies->encsig = (unsigned char *)(data + 2);
+                       } else {
+                               snprintf(tmp, (int)sizeof(tmp), "Invalid encrypted signature length %d\n", len);
+                               errorf(tmp);
+                       }
+                       break;
+               default:
+                       snprintf(tmp, (int)sizeof(tmp), "Ignoring unknown information element '%s' (%d) of length %d\n", dundi_ie2str(ie), ie, len);
+                       outputf(tmp);
+               }
+               /* Overwrite information element with 0, to null terminate previous portion */
+               data[0] = 0;
+               datalen -= (len + 2);
+               data += (len + 2);
+       }
+       /* Null-terminate last field */
+       *data = '\0';
+       if (datalen) {
+               errorf("Invalid information element contents, strange boundary\n");
+               return -1;
+       }
+       return 0;
+}
diff --git a/pbx/dundi-parser.h b/pbx/dundi-parser.h
new file mode 100755 (executable)
index 0000000..c50dd1e
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Distributed Universal Number Discovery (DUNDi)
+ *
+ * Copyright (C) 2004, Digium Inc.
+ *
+ * Written by Mark Spencer <markster@digium.com>
+ *
+ * This program is Free Software distributed under the terms of
+ * of the GNU General Public License.
+ */
+
+#ifndef _DUNDI_PARSER_H
+#define _DUNDI_PARSER_H
+
+#include <asterisk/dundi.h>
+#include <asterisk/aes.h>
+
+#define DUNDI_MAX_STACK 512
+#define DUNDI_MAX_ANSWERS      100
+
+struct dundi_ies {
+       dundi_eid *eids[DUNDI_MAX_STACK + 1];
+       int eid_direct[DUNDI_MAX_STACK + 1];
+       dundi_eid *reqeid;
+       int eidcount;
+       char *called_context;
+       char *called_number;
+       struct dundi_answer *answers[DUNDI_MAX_ANSWERS + 1];
+       struct dundi_hint *hint;
+       int anscount;
+       int ttl;
+       int version;
+       int expiration;
+       int unknowncmd;
+       unsigned char *pubkey;
+       int cause;
+       unsigned char *q_dept;
+       unsigned char *q_org;
+       unsigned char *q_locality;
+       unsigned char *q_stateprov;
+       unsigned char *q_country;
+       unsigned char *q_email;
+       unsigned char *q_phone;
+       unsigned char *q_ipaddr;
+       unsigned char *causestr;
+       unsigned char *encsharedkey;
+       unsigned char *encsig;
+       unsigned long keycrc32;
+       struct dundi_encblock *encblock;
+       int enclen;
+};
+
+struct dundi_ie_data {
+       int pos;
+       unsigned char buf[8192];
+};
+
+/* Choose a different function for output */
+extern void dundi_set_output(void (*output)(const char *data));
+/* Choose a different function for errors */
+extern void dundi_set_error(void (*output)(const char *data));
+extern void dundi_showframe(struct dundi_hdr *fhi, int rx, struct sockaddr_in *sin, int datalen);
+
+extern const char *dundi_ie2str(int ie);
+
+extern int dundi_ie_append_raw(struct dundi_ie_data *ied, unsigned char ie, void *data, int datalen);
+extern int dundi_ie_append_addr(struct dundi_ie_data *ied, unsigned char ie, struct sockaddr_in *sin);
+extern int dundi_ie_append_int(struct dundi_ie_data *ied, unsigned char ie, unsigned int value);
+extern int dundi_ie_append_short(struct dundi_ie_data *ied, unsigned char ie, unsigned short value);
+extern int dundi_ie_append_str(struct dundi_ie_data *ied, unsigned char ie, unsigned char *str);
+extern int dundi_ie_append_eid(struct dundi_ie_data *ied, unsigned char ie, dundi_eid *eid);
+extern int dundi_ie_append_cause(struct dundi_ie_data *ied, unsigned char ie, unsigned char cause, unsigned char *desc);
+extern int dundi_ie_append_hint(struct dundi_ie_data *ied, unsigned char ie, unsigned short flags, unsigned char *data);
+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);
+extern int dundi_ie_append_encdata(struct dundi_ie_data *ied, unsigned char ie, unsigned char *iv, void *data, int datalen);
+extern int dundi_ie_append_byte(struct dundi_ie_data *ied, unsigned char ie, unsigned char dat);
+extern int dundi_ie_append(struct dundi_ie_data *ied, unsigned char ie);
+extern int dundi_parse_ies(struct dundi_ies *ies, unsigned char *data, int datalen);
+extern char *dundi_eid_to_str(char *s, int maxlen, dundi_eid *eid);
+extern char *dundi_eid_to_str_short(char *s, int maxlen, dundi_eid *eid);
+extern int dundi_str_to_eid(dundi_eid *eid, char *s);
+extern int dundi_str_short_to_eid(dundi_eid *eid, char *s);
+extern int dundi_eid_zero(dundi_eid *eid);
+extern int dundi_eid_cmp(dundi_eid *eid1, dundi_eid *eid2);
+extern char *dundi_flags2str(char *s, int maxlen, int flags);
+extern char *dundi_hint2str(char *s, int maxlen, int flags);
+#endif
diff --git a/pbx/pbx_dundi.c b/pbx/pbx_dundi.c
new file mode 100755 (executable)
index 0000000..bcb1e14
--- /dev/null
@@ -0,0 +1,3827 @@
+/*
+ * Distributed Universal Number Discovery (DUNDi)
+ *
+ * Copyright (C) 2004, Digium Inc.
+ *
+ * Written by Mark Spencer <markster@digium.com>
+ *
+ * This program is Free Software distributed under the terms of
+ * of the GNU General Public License.
+ */
+
+#include <asterisk/file.h>
+#include <asterisk/logger.h>
+#include <asterisk/channel.h>
+#include <asterisk/config.h>
+#include <asterisk/options.h>
+#include <asterisk/pbx.h>
+#include <asterisk/module.h>
+#include <asterisk/frame.h>
+#include <asterisk/file.h>
+#include <asterisk/channel_pvt.h>
+#include <asterisk/cli.h>
+#include <asterisk/lock.h>
+#include <asterisk/md5.h>
+#include <asterisk/dundi.h>
+#include <asterisk/sched.h>
+#include <asterisk/io.h>
+#include <asterisk/utils.h>
+#include <asterisk/crypto.h>
+#include <asterisk/astdb.h>
+#include <asterisk/acl.h>
+#include <asterisk/aes.h>
+
+#include "dundi-parser.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/ip.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <zlib.h>
+
+#define MAX_RESULTS    64
+
+#define MAX_PACKET_SIZE 8192
+
+extern char ast_config_AST_KEY_DIR[];
+
+static char *tdesc = "Distributed Universal Number Discovery (DUNDi)";
+
+static char *app = "DUNDiLookup";
+static char *synopsis = "Look up a number with DUNDi";
+static char *descrip = 
+"DUNDiLookup(number[|context[|options]])\n"
+"      Looks up a given number in the global context specified or in\n"
+"the reserved 'e164' context if not specified.  Returns -1 if the channel\n"
+"is hungup during the lookup or 0 otherwise.  On completion, the variable\n"
+"${DUNDTECH} and ${DUNDDEST} will contain the technology and destination\n"
+"of the appropriate technology and destination to access the number. If no\n"
+"answer was found, and the priority n + 101 exists, execution will continue\n"
+"at that location.\n";
+
+#define DUNDI_MODEL_INBOUND            (1 << 0)
+#define DUNDI_MODEL_OUTBOUND   (1 << 1)
+#define DUNDI_MODEL_SYMMETRIC  (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
+
+
+#define FLAG_ISREG             (1 << 0)                /* Transaction is register request */
+#define FLAG_DEAD              (1 << 1)                /* Transaction is dead */
+#define FLAG_FINAL             (1 << 2)                /* Transaction has final message sent */
+#define FLAG_ISQUAL            (1 << 3)                /* Transaction is a qualification */
+#define FLAG_ENCRYPT   (1 << 4)                /* Transaction is encrypted wiht ECX/DCX */
+#define FLAG_SENDFULLKEY       (1 << 5)                /* Send full key on transaction */
+
+#define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
+
+#if 0
+#define DUNDI_SECRET_TIME 15   /* Testing only */
+#else
+#define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
+#endif
+
+#define KEY_OUT                        0
+#define KEY_IN                 1
+
+static struct io_context *io;
+static struct sched_context *sched;
+static int netsocket = -1;
+static pthread_t netthreadid = AST_PTHREADT_NULL;
+static int tos = 0;
+static int dundidebug = 0;
+static int authdebug = 0;
+static int dundi_ttl = DUNDI_DEFAULT_TTL;
+static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
+static int global_autokilltimeout = 0;
+static dundi_eid global_eid;
+static int default_expiration = 60;
+static char dept[80];
+static char org[80];
+static char locality[80];
+static char stateprov[80];
+static char country[80];
+static char email[80];
+static char phone[80];
+static char secretpath[80];
+static char cursecret[80];
+static char ipaddr[80];
+static time_t rotatetime;
+static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
+struct permission {
+       struct permission *next;
+       int allow;
+       char name[0];
+};
+
+struct dundi_packet {
+       struct dundi_hdr *h;
+       struct dundi_packet *next;
+       int datalen;
+       struct dundi_transaction *parent;
+       int retransid;
+       int retrans;
+       unsigned char data[0];
+};
+
+struct dundi_hint_metadata {
+       unsigned short flags;
+       char exten[AST_MAX_EXTENSION];
+};
+
+struct dundi_request;
+
+struct dundi_transaction {
+       struct sockaddr_in addr;        /* Other end of transaction */
+       dundi_eid eids[DUNDI_MAX_STACK + 1];
+       int eidcount;                           /* Number of eids in eids */
+       dundi_eid us_eid;                       /* Our EID, to them */
+       dundi_eid them_eid;                     /* Their EID, to us */
+       aes_encrypt_ctx ecx;            /* AES 128 Encryption context */
+       aes_decrypt_ctx dcx;            /* AES 128 Decryption context */
+       int flags;                                      /* Has final packet been sent */
+       int ttl;                                        /* Remaining TTL for queries on this one */
+       int thread;                                     /* We have a calling thread */
+       int autokillid;                         /* ID to kill connection if answer doesn't come back fast enough */
+       int autokilltimeout;            /* Recommended timeout for autokill */
+       unsigned short strans;          /* Our transaction identifier */
+       unsigned short dtrans;          /* Their transaction identifer */
+       unsigned char iseqno;           /* Next expected received seqno */
+       unsigned char oiseqno;          /* Last received incoming seqno */
+       unsigned char oseqno;           /* Next transmitted seqno */
+       unsigned char aseqno;           /* Last acknowledge seqno */
+       struct dundi_packet *packets;   /* Packets to be retransmitted */
+       struct dundi_packet *lasttrans; /* Last transmitted / ACK'd packet */
+       struct dundi_transaction *next; /* Next with respect to the parent */
+       struct dundi_request *parent;   /* Parent request (if there is one) */
+       struct dundi_transaction *allnext; /* Next with respect to all DUNDi transactions */
+} *alltrans;
+
+struct dundi_request {
+       char dcontext[AST_MAX_EXTENSION];
+       char number[AST_MAX_EXTENSION];
+       dundi_eid query_eid;
+       dundi_eid root_eid;
+       struct dundi_result *dr;
+       struct dundi_entity_info *dei;
+       struct dundi_hint_metadata *hmd;
+       int maxcount;
+       int respcount;
+       int expiration;
+       unsigned long crc32;                                                    /* CRC-32 of all but root EID's in avoid list */
+       struct dundi_transaction *trans;        /* Transactions */
+       struct dundi_request *next;
+} *requests;
+
+static struct dundi_mapping {
+       char dcontext[AST_MAX_EXTENSION];
+       char lcontext[AST_MAX_EXTENSION];
+       int weight;
+       int options;
+       int tech;
+       int dead;
+       char dest[AST_MAX_EXTENSION];
+       struct dundi_mapping *next;
+} *mappings = NULL;
+
+static struct dundi_peer {
+       dundi_eid eid;
+       struct sockaddr_in addr;        /* Address of DUNDi peer */
+       struct permission *permit;
+       struct permission *include;
+       dundi_eid us_eid;
+       char inkey[80];
+       char outkey[80];
+       int dead;
+       int registerid;
+       int qualifyid;
+       int sentfullkey;
+       int order;
+       unsigned char txenckey[256]; /* Transmitted encrypted key + sig */
+       unsigned char rxenckey[256]; /* Cache received encrypted key + sig */
+       unsigned long us_keycrc32;      /* CRC-32 of our key */
+       aes_encrypt_ctx us_ecx;         /* Cached AES 128 Encryption context */
+       aes_decrypt_ctx us_dcx;         /* Cached AES 128 Decryption context */
+       unsigned long them_keycrc32;/* CRC-32 of our key */
+       aes_encrypt_ctx them_ecx;       /* Cached AES 128 Encryption context */
+       aes_decrypt_ctx them_dcx;       /* Cached AES 128 Decryption context */
+       time_t keyexpire;                       /* When to expire/recreate key */
+       int registerexpire;
+       struct dundi_transaction *regtrans;     /* Registration transaction */
+       struct dundi_transaction *qualtrans;    /* Qualify transaction */
+       struct dundi_transaction *keypending;
+       int model;
+       int dynamic;                            /* Are we dynamic? */
+       int lastms;                                     /* Last measured latency */
+       int maxms;                                      /* Max permissible latency */
+       struct timeval qualtx;          /* Time of transmit */
+       struct dundi_peer *next;
+} *peers;
+
+AST_MUTEX_DEFINE_STATIC(peerlock);
+
+static int dundi_xmit(struct dundi_packet *pack);
+
+static void dundi_debug_output(const char *data)
+{
+       if (dundidebug)
+               ast_verbose(data);
+}
+
+static void dundi_error_output(const char *data)
+{
+       ast_log(LOG_WARNING, data);
+}
+
+static int has_permission(struct permission *ps, char *cont)
+{
+       int res=0;
+       while(ps) {
+               if (!strcasecmp(ps->name, "all") || !strcasecmp(ps->name, cont))
+                       res = ps->allow;
+               ps = ps->next;
+       }
+       return res;
+}
+
+static char *tech2str(int tech)
+{
+       switch(tech) {
+       case DUNDI_PROTO_NONE:
+               return "None";
+       case DUNDI_PROTO_IAX:
+               return "IAX2";
+       case DUNDI_PROTO_SIP:
+               return "SIP";
+       case DUNDI_PROTO_H323:
+               return "H323";
+       default:
+               return "Unknown";
+       }
+}
+
+static int str2tech(char *str)
+{
+       if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2")) 
+               return DUNDI_PROTO_IAX;
+       else if (!strcasecmp(str, "SIP"))
+               return DUNDI_PROTO_SIP;
+       else if (!strcasecmp(str, "H323"))
+               return DUNDI_PROTO_H323;
+       else
+               return -1;
+}
+
+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, dundi_eid *avoid[], int direct[]);
+static struct dundi_transaction *create_transaction(struct dundi_peer *p);
+static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
+{
+       /* Look for an exact match first */
+       struct dundi_transaction *trans;
+       trans = alltrans;
+       while(trans) {
+               if (!inaddrcmp(&trans->addr, sin) && 
+                    ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ ||
+                         ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) {
+                         if (hdr->strans)
+                                 trans->dtrans = ntohs(hdr->strans) & 32767;
+                         break;
+               }
+               trans = trans->allnext;
+       }
+       if (!trans) {
+               switch(hdr->cmdresp & 0x7f) {
+               case DUNDI_COMMAND_DPDISCOVER:
+               case DUNDI_COMMAND_EIDQUERY:
+               case DUNDI_COMMAND_REGREQ:
+               case DUNDI_COMMAND_NULL:
+               case DUNDI_COMMAND_ENCRYPT:
+                       if (hdr->strans) {      
+                               /* Create new transaction */
+                               trans = create_transaction(NULL);
+                               if (trans) {
+                                       memcpy(&trans->addr, sin, sizeof(trans->addr));
+                                       trans->dtrans = ntohs(hdr->strans) & 32767;
+                               } else
+                                       ast_log(LOG_WARNING, "Out of memory!\n");
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+       return trans;
+}
+
+static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
+
+static int dundi_ack(struct dundi_transaction *trans, int final)
+{
+       return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
+}
+static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
+{
+       struct {
+               struct dundi_packet pack;
+               struct dundi_hdr hdr;
+       } tmp;
+       struct dundi_transaction trans;
+       /* Never respond to an INVALID with another INVALID */
+       if (h->cmdresp == DUNDI_COMMAND_INVALID)
+               return;
+       memset(&tmp, 0, sizeof(tmp));
+       memset(&trans, 0, sizeof(trans));
+       memcpy(&trans.addr, sin, sizeof(trans.addr));
+       tmp.hdr.strans = h->dtrans;
+       tmp.hdr.dtrans = h->strans;
+       tmp.hdr.iseqno = h->oseqno;
+       tmp.hdr.oseqno = h->iseqno;
+       tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
+       tmp.hdr.cmdflags = 0;
+       tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
+       tmp.pack.datalen = sizeof(struct dundi_hdr);
+       tmp.pack.parent = &trans;
+       dundi_xmit(&tmp.pack);
+}
+
+static void reset_global_eid(void)
+{
+       int x,s;
+       char eid_str[20];
+       struct ifreq ifr;
+
+       s = socket(AF_INET, SOCK_STREAM, 0);
+       if (s > 0) {
+               x = 0;
+               for(x=0;x<10;x++) {
+                       memset(&ifr, 0, sizeof(ifr));
+                       snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
+                       if (!ioctl(s, SIOCGIFHWADDR, &ifr)) {
+                               memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
+                               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);
+                               return;
+                       }
+        }
+       }
+       ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID  You will have to set it manually.\n");
+}
+
+static int get_trans_id(void)
+{
+       struct dundi_transaction *t;
+       int stid = (rand() % 32766) + 1;
+       int tid = stid;
+       do {
+               t = alltrans;
+               while(t) {
+                       if (t->strans == tid) 
+                               break;
+                       t = t->allnext;
+               }
+               if (!t)
+                       return tid;
+               tid = (tid % 32766) + 1;
+       } while (tid != stid);
+       return 0;
+}
+
+static int reset_transaction(struct dundi_transaction *trans)
+{
+       int tid;
+       tid = get_trans_id();
+       if (tid < 1)
+               return -1;
+       trans->strans = tid;
+       trans->dtrans = 0;
+       trans->iseqno = 0;
+       trans->oiseqno = 0;
+       trans->oseqno = 0;
+       trans->aseqno = 0;
+       trans->flags &= ~FLAG_FINAL;
+       return 0;
+}
+
+static struct dundi_peer *find_peer(dundi_eid *eid)
+{
+       struct dundi_peer *cur;
+       if (!eid)
+               eid = &empty_eid;
+       cur = peers;
+       while(cur) {
+               if (!dundi_eid_cmp(&cur->eid,eid))
+                       return cur;
+               cur = cur->next;
+       }
+       return NULL;
+}
+
+static void build_iv(unsigned char *iv)
+{
+       /* XXX Would be nice to be more random XXX */
+       unsigned int *fluffy;
+       int x;
+       fluffy = (unsigned int *)(iv);
+       for (x=0;x<4;x++)
+               fluffy[x] = rand();
+}
+
+struct dundi_query_state {
+       dundi_eid *eids[DUNDI_MAX_STACK + 1]; 
+       int directs[DUNDI_MAX_STACK + 1]; 
+       dundi_eid reqeid;
+       char called_context[AST_MAX_EXTENSION];
+       char called_number[AST_MAX_EXTENSION];
+       struct dundi_mapping *maps;
+       int nummaps;
+       struct dundi_transaction *trans;
+       void *chal;
+       int challen;
+       int ttl;
+       char fluffy[0];
+};
+
+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)
+{
+       int flags;
+       int x;
+       struct ast_channel *chan=NULL;
+       if (!ast_strlen_zero(map->lcontext)) {
+               flags = 0;
+               if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
+                       flags |= DUNDI_FLAG_EXISTS;
+               if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
+                       flags |= DUNDI_FLAG_CANMATCH;
+               if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
+                       flags |= DUNDI_FLAG_MATCHMORE;
+               if (ast_ignore_pattern(map->lcontext, called_number))
+                       flags |= DUNDI_FLAG_IGNOREPAT;
+
+               /* Clearly we can't say 'don't ask' anymore if we found anything... */
+               if (flags) 
+                       hmd->flags &= ~DUNDI_HINT_DONT_ASK;
+
+               if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
+                       /* Skip partial answers */
+                       flags &= ~(DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
+               }
+               if (flags) {
+                       /* Clearly we can't say 'don't ask' anymore... */
+                       chan = ast_channel_alloc(0);
+                       if (chan) {
+                               flags |= map->options & 0xffff;
+                               dr[anscnt].flags = flags;
+                               dr[anscnt].techint = map->tech;
+                               dr[anscnt].weight = map->weight;
+                               dr[anscnt].expiration = DUNDI_DEFAULT_CACHE_TIME;
+                               strncpy(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
+                               dr[anscnt].eid = *us_eid;
+                               dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
+                               if (flags & DUNDI_FLAG_EXISTS) {
+                                       pbx_builtin_setvar_helper(chan, "NUMBER", called_number);
+                                       pbx_builtin_setvar_helper(chan, "EID", dr[anscnt].eid_str);
+                                       pbx_builtin_setvar_helper(chan, "SECRET", cursecret);
+                                       pbx_builtin_setvar_helper(chan, "IPADDR", ipaddr);
+                                       pbx_substitute_variables_helper(chan, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
+                               } else
+                                       dr[anscnt].dest[0] = '\0';
+                               anscnt++;
+                       }
+               } else {
+                       /* No answers...  Find the fewest number of digits from the
+                          number for which we have no answer. */
+                       char tmp[AST_MAX_EXTENSION]="";
+                       for (x=0;x<AST_MAX_EXTENSION;x++) {
+                               tmp[x] = called_number[x];
+                               if (!tmp[x])
+                                       break;
+                               if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
+                                       /* Oops found something we can't match.  If this is longer
+                                          than the running hint, we have to consider it */
+                                       if (strlen(tmp) > strlen(hmd->exten)) {
+                                               strncpy(hmd->exten, tmp, sizeof(hmd->exten) - 1);
+                                       }
+                                       break;
+                               }
+                       }
+               }
+       }
+       if (chan)
+               ast_hangup(chan);
+       return anscnt;
+}
+
+static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
+
+static void *dundi_lookup_thread(void *data)
+{
+       struct dundi_query_state *st = data;
+       struct dundi_result dr[MAX_RESULTS];
+       struct dundi_ie_data ied;
+       struct dundi_hint_metadata hmd;
+       char eid_str[20];
+       int res, x;
+       int ouranswers=0;
+       int max = 999999;
+       int expiration = DUNDI_DEFAULT_CACHE_TIME;
+
+       ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context, 
+               st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
+       memset(&ied, 0, sizeof(ied));
+       memset(&dr, 0, sizeof(dr));
+       memset(&hmd, 0, sizeof(hmd));
+       /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */
+       hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
+       for (x=0;x<st->nummaps;x++)
+               ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
+       if (ouranswers < 0)
+               ouranswers = 0;
+       for (x=0;x<ouranswers;x++) {
+               if (dr[x].weight < max)
+                       max = dr[x].weight;
+       }
+               
+       if (max) {
+               /* If we do not have a canonical result, keep looking */
+               res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, st->called_context, st->called_number, st->ttl, 1, &hmd, &expiration, st->eids, st->directs);
+               if (res > 0) {
+                       /* Append answer in result */
+                       ouranswers += res;
+               } else {
+                       if ((res < -1) && (!ouranswers))
+                               dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
+               }
+       }
+       ast_mutex_lock(&peerlock);
+       /* Truncate if "don't ask" isn't present */
+       if (!(hmd.flags & DUNDI_HINT_DONT_ASK))
+               hmd.exten[0] = '\0';
+       if (st->trans->flags & FLAG_DEAD) {
+               ast_log(LOG_DEBUG, "Our transaction went away!\n");
+               st->trans->thread = 0;
+               destroy_trans(st->trans, 0);
+       } else {
+               for (x=0;x<ouranswers;x++) {
+                       /* Add answers */
+                       if (dr[x].expiration && (expiration > dr[x].expiration))
+                               expiration = dr[x].expiration;
+                       dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
+               }
+               dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
+               dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
+               dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
+               st->trans->thread = 0;
+       }
+       ast_mutex_unlock(&peerlock);
+       free(st);
+       return NULL;    
+}
+
+static inline int calc_ms(struct timeval *start)
+{
+       struct timeval tv;
+       gettimeofday(&tv, NULL);
+       return ((tv.tv_sec - start->tv_sec) * 1000 + (tv.tv_usec - start->tv_usec) / 1000);
+}
+
+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[]);
+
+static void *dundi_query_thread(void *data)
+{
+       struct dundi_query_state *st = data;
+       struct dundi_entity_info dei;
+       struct dundi_ie_data ied;
+       struct dundi_hint_metadata hmd;
+       char eid_str[20];
+       int res;
+       ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context, 
+               st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) :  "ourselves");
+       memset(&ied, 0, sizeof(ied));
+       memset(&dei, 0, sizeof(dei));
+       memset(&hmd, 0, sizeof(hmd));
+       if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
+               /* Ooh, it's us! */
+               ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
+               strncpy(dei.orgunit, dept, sizeof(dei.orgunit));
+               strncpy(dei.org, org, sizeof(dei.org));
+               strncpy(dei.locality, locality, sizeof(dei.locality));
+               strncpy(dei.stateprov, stateprov, sizeof(dei.stateprov));
+               strncpy(dei.country, country, sizeof(dei.country));
+               strncpy(dei.email, email, sizeof(dei.email));
+               strncpy(dei.phone, phone, sizeof(dei.phone));
+               res = 1;
+       } else {
+               /* If we do not have a canonical result, keep looking */
+               res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
+       }
+       ast_mutex_lock(&peerlock);
+       if (st->trans->flags & FLAG_DEAD) {
+               ast_log(LOG_DEBUG, "Our transaction went away!\n");
+               st->trans->thread = 0;
+               destroy_trans(st->trans, 0);
+       } else {
+               if (res) {
+                       dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
+                       dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
+                       dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
+                       dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
+                       dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
+                       dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
+                       dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
+                       if (!ast_strlen_zero(dei.ipaddr))
+                               dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
+               }
+               dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
+               dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
+               st->trans->thread = 0;
+       }
+       ast_mutex_unlock(&peerlock);
+       free(st);
+       return NULL;    
+}
+
+static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
+{
+       struct dundi_query_state *st;
+       int totallen;
+       int x;
+       int skipfirst=0;
+       struct dundi_ie_data ied;
+       char eid_str[20];
+       char *s;
+       pthread_t lookupthread;
+       pthread_attr_t attr;
+       if (ies->eidcount > 1) {
+               /* Since it is a requirement that the first EID is the authenticating host
+                  and the last EID is the root, it is permissible that the first and last EID
+                  could be the same.  In that case, we should go ahead copy only the "root" section
+                  since we will not need it for authentication. */
+               if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
+                       skipfirst = 1;
+       }
+       totallen = sizeof(struct dundi_query_state);
+       totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
+       st = malloc(totallen);
+       if (st) {
+               memset(st, 0, totallen);
+               strncpy(st->called_context, ies->called_context, sizeof(st->called_context) - 1);
+               memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
+               st->trans = trans;
+               st->ttl = ies->ttl - 1;
+               if (st->ttl < 0)
+                       st->ttl = 0;
+               s = st->fluffy;
+               for (x=skipfirst;ies->eids[x];x++) {
+                       st->eids[x-skipfirst] = (dundi_eid *)s;
+                       *st->eids[x-skipfirst] = *ies->eids[x];
+                       s += sizeof(dundi_eid);
+               }
+               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);
+               pthread_attr_init(&attr);
+               pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+               trans->thread = 1;
+               if (ast_pthread_create(&lookupthread, &attr, dundi_query_thread, st)) {
+                       trans->thread = 0;
+                       ast_log(LOG_WARNING, "Unable to create thread!\n");
+                       free(st);
+                       memset(&ied, 0, sizeof(ied));
+                       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
+                       dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
+                       return -1;
+               }
+       } else {
+               ast_log(LOG_WARNING, "Out of memory!\n");
+               memset(&ied, 0, sizeof(ied));
+               dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
+               dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
+               return -1;
+       }
+       return 0;
+}
+
+static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
+{
+       struct dundi_query_state *st;
+       int totallen;
+       int x;
+       struct dundi_ie_data ied;
+       char *s;
+       struct dundi_mapping *cur;
+       int mapcount;
+       int skipfirst = 0;
+       
+       pthread_t lookupthread;
+       pthread_attr_t attr;
+       totallen = sizeof(struct dundi_query_state);
+       /* Count matching map entries */
+       mapcount = 0;
+       cur = mappings;
+       while(cur) {
+               if (!strcasecmp(cur->dcontext, ccontext))
+                       mapcount++;
+               cur = cur->next;
+       }
+       /* If no maps, return -1 immediately */
+       if (!mapcount)
+               return -1;
+
+       if (ies->eidcount > 1) {
+               /* Since it is a requirement that the first EID is the authenticating host
+                  and the last EID is the root, it is permissible that the first and last EID
+                  could be the same.  In that case, we should go ahead copy only the "root" section
+                  since we will not need it for authentication. */
+               if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
+                       skipfirst = 1;
+       }
+
+       totallen += mapcount * sizeof(struct dundi_mapping);
+       totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
+       st = malloc(totallen);
+       if (st) {
+               memset(st, 0, totallen);
+               strncpy(st->called_context, ies->called_context, sizeof(st->called_context) - 1);
+               strncpy(st->called_number, ies->called_number, sizeof(st->called_number) - 1);
+               st->trans = trans;
+               st->ttl = ies->ttl - 1;
+               if (st->ttl < 0)
+                       st->ttl = 0;
+               s = st->fluffy;
+               for (x=skipfirst;ies->eids[x];x++) {
+                       st->eids[x-skipfirst] = (dundi_eid *)s;
+                       *st->eids[x-skipfirst] = *ies->eids[x];
+                       st->directs[x-skipfirst] = ies->eid_direct[x];
+                       s += sizeof(dundi_eid);
+               }
+               /* Append mappings */
+               x = 0;
+               st->maps = (struct dundi_mapping *)s;
+               cur = mappings;
+               while(cur) {
+                       if (!strcasecmp(cur->dcontext, ccontext)) {
+                               if (x < mapcount) {
+                                       st->maps[x] = *cur;
+                                       st->maps[x].next = NULL;
+                                       x++;
+                               }
+                       }
+                       cur = cur->next;
+               }
+               st->nummaps = mapcount;
+               ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
+               pthread_attr_init(&attr);
+               pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+               trans->thread = 1;
+               if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) {
+                       trans->thread = 0;
+                       ast_log(LOG_WARNING, "Unable to create thread!\n");
+                       free(st);
+                       memset(&ied, 0, sizeof(ied));
+                       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
+                       dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
+                       return -1;
+               }
+       } else {
+               ast_log(LOG_WARNING, "Out of memory!\n");
+               memset(&ied, 0, sizeof(ied));
+               dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
+               dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
+               return -1;
+       }
+       return 0;
+}
+
+static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
+{
+       int unaffected;
+       char key1[256];
+       char key2[256];
+       char eidpeer_str[20];
+       char eidroot_str[20];
+       char data[80]="";
+       time_t timeout;
+
+       if (expiration < 0)
+               expiration = DUNDI_DEFAULT_CACHE_TIME;
+
+       /* Only cache hint if "don't ask" is there... */
+       if (!(ntohs(hint->flags)& DUNDI_HINT_DONT_ASK))
+               return 0;
+
+       unaffected = ntohs(hint->flags) & DUNDI_HINT_UNAFFECTED;
+
+       dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
+       dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
+       snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
+       snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
+
+       time(&timeout);
+       timeout += expiration;
+       snprintf(data, sizeof(data), "%ld|", (long)(timeout));
+       
+       ast_db_put("dundi/cache", key1, data);
+       ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
+       ast_db_put("dundi/cache", key2, data);
+       ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
+       return 0;
+}
+
+static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration)
+{
+       int x;
+       char key1[256];
+       char key2[256];
+       char data[1024]="";
+       char eidpeer_str[20];
+       char eidroot_str[20];
+       time_t timeout;
+
+       if (expiration < 1)     
+               expiration = DUNDI_DEFAULT_CACHE_TIME;
+       dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
+       dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
+       snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
+       snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
+       /* Build request string */
+       time(&timeout);
+       timeout += expiration;
+       snprintf(data, sizeof(data), "%ld|", (long)(timeout));
+       for (x=start;x<req->respcount;x++) {
+               /* Skip anything with an illegal pipe in it */
+               if (strchr(req->dr[x].dest, '|'))
+                       continue;
+               snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|", 
+                       req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest, 
+                       dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
+       }
+       ast_db_put("dundi/cache", key1, data);
+       ast_db_put("dundi/cache", key2, data);
+       return 0;
+}
+
+static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
+{
+       char data[1024]="";
+       char *ptr, *term, *src;
+       int tech;
+       int flags;
+       int weight;
+       int length;
+       int z;
+       int expiration;
+       char fs[256];
+       time_t timeout;
+       /* Build request string */
+       if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
+               ptr = data;
+               if (sscanf(ptr, "%ld|%n", &timeout, &length) == 1) {
+                       expiration = timeout - now;
+                       if (expiration > 0) {
+                               ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", (int)(timeout - now));
+                               ptr += length;
+                               while((sscanf(ptr, "%d/%d/%d/%n", &flags, &weight, &tech, &length) == 3)) {
+                                       ptr += length;
+                                       term = strchr(ptr, '|');
+                                       if (term) {
+                                               *term = '\0';
+                                               src = strrchr(ptr, '/');
+                                               if (src) {
+                                                       *src = '\0';
+                                                       src++;
+                                               } else
+                                                       src = "";
+                                               ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n", 
+                                                       tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags), eid_str_full);
+                                               /* Make sure it's not already there */
+                                               for (z=0;z<req->respcount;z++) {
+                                                       if ((req->dr[z].techint == tech) &&
+                                                           !strcmp(req->dr[z].dest, ptr)) 
+                                                                       break;
+                                               }
+                                               if (z == req->respcount) {
+                                                       /* Copy into parent responses */
+                                                       req->dr[req->respcount].flags = flags;
+                                                       req->dr[req->respcount].weight = weight;
+                                                       req->dr[req->respcount].techint = tech;
+                                                       req->dr[req->respcount].expiration = expiration;
+                                                       dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
+                                                       strncpy(req->dr[req->respcount].dest, ptr,
+                                                               sizeof(req->dr[req->respcount].dest));
+                                                       strncpy(req->dr[req->respcount].tech, tech2str(tech),
+                                                               sizeof(req->dr[req->respcount].tech));
+                                                       req->respcount++;
+                                                       req->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
+                                               } else if (req->dr[z].weight > weight)
+                                                       req->dr[z].weight = weight;
+                                               ptr = term + 1;
+                                       }
+                               }
+                               /* We found *something* cached */
+                               if (expiration < *lowexpiration)
+                                       *lowexpiration = expiration;
+                               return 1;
+                       } else 
+                               ast_db_del("dundi/cache", key);
+               } else 
+                       ast_db_del("dundi/cache", key);
+       }
+               
+       return 0;
+}
+
+static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
+{
+       char key[256];
+       char eid_str[20];
+       char eidroot_str[20];
+       time_t now;
+       int res=0;
+       int res2=0;
+       char eid_str_full[20];
+       char tmp[256]="";
+       int x;
+
+       time(&now);
+       dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
+       dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
+       dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
+       snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
+       res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
+       snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
+       res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
+       snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
+       res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
+       x = 0;
+       if (!req->respcount) {
+               while(!res2) {
+                       /* Look and see if we have a hint that would preclude us from looking at this
+                          peer for this number. */
+                       if (!(tmp[x] = req->number[x])) 
+                               break;
+                       x++;
+                       /* Check for hints */
+                       snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
+                       res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
+                       snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
+                       res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
+                       snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
+                       res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
+                       if (res2) {
+                               if (strlen(tmp) > strlen(req->hmd->exten)) {
+                                       /* Update meta data if appropriate */
+                                       strncpy(req->hmd->exten, tmp, sizeof(req->hmd->exten) - 1);
+                               }
+                       }
+               }
+               res |= res2;
+       }
+
+       return res;
+}
+
+static void qualify_peer(struct dundi_peer *peer, int schedonly);
+
+static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
+{
+       if (!trans->addr.sin_addr.s_addr)
+               memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
+       trans->us_eid = p->us_eid;
+       trans->them_eid = p->eid;
+       /* Enable encryption if appropriate */
+       if (!ast_strlen_zero(p->inkey))
+               trans->flags |= FLAG_ENCRYPT;
+       if (p->maxms)
+               trans->autokilltimeout = p->maxms;
+       else
+               trans->autokilltimeout = global_autokilltimeout;
+}
+
+static int do_register_expire(void *data)
+{
+       struct dundi_peer *peer = data;
+       char eid_str[20];
+       /* Called with peerlock already held */
+       ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+       peer->registerexpire = -1;
+       peer->lastms = 0;
+       memset(&peer->addr, 0, sizeof(peer->addr));
+       return 0;
+}
+
+static int update_key(struct dundi_peer *peer)
+{
+       unsigned char key[16];
+       struct ast_key *ekey, *skey;
+       char eid_str[20];
+       int res;
+       if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
+               build_iv(key);
+               aes_encrypt_key128(key, &peer->us_ecx);
+               aes_decrypt_key128(key, &peer->us_dcx);
+               ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
+               if (!ekey) {
+                       ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
+                               peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+                       return -1;
+               }
+               skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
+               if (!skey) {
+                       ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
+                               peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+                       return -1;
+               }
+               if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
+                       ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
+                       return -1;
+               }
+               if ((res = ast_sign_bin(skey, peer->txenckey, 128, peer->txenckey + 128))) {
+                       ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
+                       return -1;
+               }
+               peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
+               peer->sentfullkey = 0;
+               /* Looks good */
+               time(&peer->keyexpire);
+               peer->keyexpire += dundi_key_ttl;
+       }
+       return 0;
+}
+
+static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_encrypt_ctx *ecx) 
+{
+       unsigned char curblock[16];
+       int x;
+       memcpy(curblock, iv, sizeof(curblock));
+       while(len > 0) {
+               for (x=0;x<16;x++)
+                       curblock[x] ^= src[x];
+               aes_encrypt(curblock, dst, ecx);
+               memcpy(curblock, dst, sizeof(curblock)); 
+               dst += 16;
+               src += 16;
+               len -= 16;
+       }
+       return 0;
+}
+static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_decrypt_ctx *dcx) 
+{
+       unsigned char lastblock[16];
+       int x;
+       memcpy(lastblock, iv, sizeof(lastblock));
+       while(len > 0) {
+               aes_decrypt(src, dst, dcx);
+               for (x=0;x<16;x++)
+                       dst[x] ^= lastblock[x];
+               memcpy(lastblock, src, sizeof(lastblock));
+               dst += 16;
+               src += 16;
+               len -= 16;
+       }
+       return 0;
+}
+
+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)
+{
+       int space = *dstlen;
+       unsigned long bytes;
+       struct dundi_hdr *h;
+       unsigned char *decrypt_space;
+       decrypt_space = alloca(srclen);
+       if (!decrypt_space)
+               return NULL;
+       decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
+       /* Setup header */
+       h = (struct dundi_hdr *)dst;
+       *h = *ohdr;
+       bytes = space - 6;
+       if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
+               ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n");
+               return NULL;
+       }
+       /* Update length */
+       *dstlen = bytes + 6;
+       /* Return new header */
+       return h;
+}
+
+static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
+{
+       unsigned char *compress_space;
+       int len;
+       int res;
+       unsigned long bytes;
+       struct dundi_ie_data ied;
+       struct dundi_peer *peer;
+       unsigned char iv[16];
+       len = pack->datalen + pack->datalen / 100 + 42;
+       compress_space = alloca(len);
+       if (compress_space) {
+               memset(compress_space, 0, len);
+               /* We care about everthing save the first 6 bytes of header */
+               bytes = len;
+               res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
+               if (res != Z_OK) {
+                       ast_log(LOG_DEBUG, "Ouch, compression failed!\n");
+                       return -1;
+               }
+               memset(&ied, 0, sizeof(ied));
+               /* Say who we are */
+               if (!pack->h->iseqno && !pack->h->oseqno) {
+                       /* Need the key in the first copy */
+                       if (!(peer = find_peer(&trans->them_eid))) 
+                               return -1;
+                       if (update_key(peer))
+                               return -1;
+                       if (!peer->sentfullkey)
+                               trans->flags |= FLAG_SENDFULLKEY;
+                       /* Append key data */
+                       dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
+                       if (trans->flags & FLAG_SENDFULLKEY) {
+                               dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
+                               dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
+                       } else {
+                               dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
+                       }
+                       /* Setup contexts */
+                       trans->ecx = peer->us_ecx;
+                       trans->dcx = peer->us_dcx;
+
+                       /* We've sent the full key */
+                       peer->sentfullkey = 1;
+               }
+               /* Build initialization vector */
+               build_iv(iv);
+               /* Add the field, rounded up to 16 bytes */
+               dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
+               /* Copy the data */
+               if ((ied.pos + bytes) >= sizeof(ied.buf)) {
+                       ast_log(LOG_NOTICE, "Final packet too large!\n");
+                       return -1;
+               }
+               encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
+               ied.pos += ((bytes + 15) / 16) * 16;
+               /* Reconstruct header */
+               pack->datalen = sizeof(struct dundi_hdr);
+               pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
+               pack->h->cmdflags = 0;
+               memcpy(pack->h->ies, ied.buf, ied.pos);
+               pack->datalen += ied.pos;
+               return 0;
+       }
+       return -1;
+}
+
+static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
+{
+       unsigned char dst[128];
+       int res;
+       struct ast_key *key, *skey;
+       char eid_str[20];
+       if (option_debug)
+               ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
+       if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
+               /* A match */
+               return 1;
+       } else if (!newkey || !newsig)
+               return 0;
+       if (!memcmp(peer->rxenckey, newkey, 128) &&
+           !memcmp(peer->rxenckey + 128, newsig, 128)) {
+               /* By definition, a match */
+               return 1;
+       }
+       /* Decrypt key */
+       key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
+       if (!key) {
+               ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
+                       peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+               return -1;
+       }
+
+       skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
+       if (!skey) {
+               ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
+                       peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+               return -1;
+       }
+
+       /* First check signature */
+       res = ast_check_signature_bin(skey, newkey, 128, newsig);
+       if (res) 
+               return 0;
+
+       res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
+       if (res != 16) {
+               if (res >= 0)
+                       ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
+               return 0;
+       }
+       /* Decrypted, passes signature */
+       ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n");
+       memcpy(peer->rxenckey, newkey, 128);
+       memcpy(peer->rxenckey + 128, newsig, 128);
+       peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
+       aes_decrypt_key128(dst, &peer->them_dcx);
+       aes_encrypt_key128(dst, &peer->them_ecx);
+       return 1;
+}
+
+static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
+{
+       /* Handle canonical command / response */
+       int final = hdr->cmdresp & 0x80;
+       int cmd = hdr->cmdresp & 0x7f;
+       int x,y,z;
+       int resp;
+       int res;
+       unsigned char *bufcpy;
+       struct dundi_ie_data ied;
+       struct dundi_ies ies;
+       struct dundi_peer *peer;
+       char eid_str[20];
+       char eid_str2[20];
+       memset(&ied, 0, sizeof(ied));
+       memset(&ies, 0, sizeof(ies));
+       if (datalen) {
+               bufcpy = alloca(datalen);
+               if (!bufcpy)
+                       return -1;
+               /* Make a copy for parsing */
+               memcpy(bufcpy, hdr->ies, datalen);
+               ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
+               if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
+                       ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
+                       return -1;
+               }
+       }
+       switch(cmd) {
+       case DUNDI_COMMAND_DPDISCOVER:
+       case DUNDI_COMMAND_EIDQUERY:
+               if (cmd == DUNDI_COMMAND_EIDQUERY)
+                       resp = DUNDI_COMMAND_EIDRESPONSE;
+               else
+                       resp = DUNDI_COMMAND_DPRESPONSE;
+               /* A dialplan or entity discover -- qualify by highest level entity */
+               peer = find_peer(ies.eids[0]);
+               if (!peer) {
+                       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
+                       dundi_send(trans, resp, 0, 1, &ied);
+               } else {
+                       int hasauth = 0;
+                       trans->us_eid = peer->us_eid;
+                       if (strlen(peer->inkey)) {
+                               hasauth = encrypted;
+                       } else 
+                               hasauth = 1;
+                       if (hasauth) {
+                               /* Okay we're authentiated and all, now we check if they're authorized */
+                               if (!ies.called_context)
+                                       ies.called_context = "e164";
+                               if (cmd == DUNDI_COMMAND_EIDQUERY) {
+                                       res = dundi_answer_entity(trans, &ies, ies.called_context);
+                               } else {
+                                       if (!ies.called_number || ast_strlen_zero(ies.called_number)) {
+                                               /* They're not permitted to access that context */
+                                               dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
+                                               dundi_send(trans, resp, 0, 1, &ied);
+                                       } else if (has_permission(peer->permit, ies.called_context)) {
+                                               res = dundi_answer_query(trans, &ies, ies.called_context);
+                                               if (res < 0) {
+                                                       /* There is no such dundi context */
+                                                       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
+                                                       dundi_send(trans, resp, 0, 1, &ied);
+                                               }
+                               
+                                       } else {
+                                               /* They're not permitted to access that context */
+                                               dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
+                                               dundi_send(trans, resp, 0, 1, &ied);
+                                       }
+                               }
+                       } else {
+                               /* They're not permitted to access that context */
+                               dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
+                               dundi_send(trans, resp, 0, 1, &ied);
+                       }
+               }
+               break;
+       case DUNDI_COMMAND_REGREQ:
+               /* A register request -- should only have one entity */
+               peer = find_peer(ies.eids[0]);
+               if (!peer || !peer->dynamic) {
+                       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
+                       dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
+               } else {
+                       int hasauth = 0;
+                       trans->us_eid = peer->us_eid;
+                       if (!ast_strlen_zero(peer->inkey)) {
+                               hasauth = encrypted;
+                       } else
+                               hasauth = 1;
+                       if (hasauth) {
+                               int expire = default_expiration;
+                               char iabuf[INET_ADDRSTRLEN];
+                               char data[256];
+                               int needqual = 0;
+                               if (peer->registerexpire > -1)
+                                       ast_sched_del(sched, peer->registerexpire);
+                               peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
+                               ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr);
+                               snprintf(data, sizeof(data), "%s:%d:%d", iabuf, ntohs(trans->addr.sin_port), expire);
+                               ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
+                               if (inaddrcmp(&peer->addr, &trans->addr)) {
+                                       if (option_verbose > 2)
+                                               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));
+                                       needqual = 1;
+                               }
+                                       
+                               memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
+                               dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
+                               dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
+                               if (needqual)
+                                       qualify_peer(peer, 1);
+                       }
+               }
+               break;
+       case DUNDI_COMMAND_DPRESPONSE:
+               /* A dialplan response, lets see what we got... */
+               if (ies.cause < 1) {
+                       int authpass=0;
+                       /* Success of some sort */
+                       ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
+                       if (trans->flags & FLAG_ENCRYPT) {
+                               authpass = encrypted;
+                       } else 
+                               authpass = 1;
+                       if (authpass) {
+                               /* Pass back up answers */
+                               if (trans->parent && trans->parent->dr) {
+                                       y = trans->parent->respcount;
+                                       for (x=0;x<ies.anscount;x++) {
+                                               if (trans->parent->respcount < trans->parent->maxcount) {
+                                                       /* Make sure it's not already there */
+                                                       for (z=0;z<trans->parent->respcount;z++) {
+                                                               if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
+                                                                   !strcmp(trans->parent->dr[z].dest, ies.answers[x]->data)) 
+                                                                               break;
+                                                       }
+                                                       if (z == trans->parent->respcount) {
+                                                               /* Copy into parent responses */
+                                                               trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
+                                                               trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
+                                                               trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
+                                                               trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
+                                                               if (ies.expiration > 0)
+                                                                       trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
+                                                               else
+                                                                       trans->parent->dr[trans->parent->respcount].expiration = DUNDI_DEFAULT_CACHE_TIME;
+                                                               dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str, 
+                                                                       sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
+                                                                       &ies.answers[x]->eid);
+                                                               strncpy(trans->parent->dr[trans->parent->respcount].dest, ies.answers[x]->data,
+                                                                       sizeof(trans->parent->dr[trans->parent->respcount].dest));
+                                                               strncpy(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
+                                                                       sizeof(trans->parent->dr[trans->parent->respcount].tech));
+                                                               trans->parent->respcount++;
+                                                               trans->parent->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
+                                                       } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
+                                                               /* Update weight if appropriate */
+                                                               trans->parent->dr[z].weight = ies.answers[x]->weight;
+                                                       }
+                                               } else
+                                                       ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
+                                                               trans->parent->number, trans->parent->dcontext);
+                                       }
+                                       /* Save all the results (if any) we had.  Even if no results, still cache lookup.  Let
+                                          the cache know if this request was unaffected by our entity list. */
+                                       cache_save(&trans->them_eid, trans->parent, y, 
+                                                       ies.hint ? ntohs(ies.hint->flags) & DUNDI_HINT_UNAFFECTED : 0, ies.expiration);
+                                       if (ies.hint) {
+                                               cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
+                                               if (ntohs(ies.hint->flags) & DUNDI_HINT_TTL_EXPIRED)
+                                                       trans->parent->hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
+                                               if (ntohs(ies.hint->flags) & DUNDI_HINT_DONT_ASK) { 
+                                                       if (strlen(ies.hint->data) > strlen(trans->parent->hmd->exten)) {
+                                                               strncpy(trans->parent->hmd->exten, ies.hint->data, 
+                                                                       sizeof(trans->parent->hmd->exten) - 1);
+                                                       }
+                                               } else {
+                                                       trans->parent->hmd->flags &= ~DUNDI_HINT_DONT_ASK;
+                                               }
+                                       }
+                                       if (ies.expiration > 0) {
+                                               if (trans->parent->expiration > ies.expiration) {
+                                                       trans->parent->expiration = ies.expiration;
+                                               }
+                                       }
+                               }
+                               /* Close connection if not final */
+                               if (!final) 
+                                       dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
+                       }
+                       
+               } else {
+                       /* Auth failure, check for data */
+                       if (!final) {
+                               /* Cancel if they didn't already */
+                               dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
+                       }
+               }
+               break;
+       case DUNDI_COMMAND_EIDRESPONSE:
+               /* A dialplan response, lets see what we got... */
+               if (ies.cause < 1) {
+                       int authpass=0;
+                       /* Success of some sort */
+                       ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
+                       if (trans->flags & FLAG_ENCRYPT) {
+                               authpass = encrypted;
+                       } else 
+                               authpass = 1;
+                       if (authpass) {
+                               /* Pass back up answers */
+                               if (trans->parent && trans->parent->dei && ies.q_org) {
+                                       if (!trans->parent->respcount) {
+                                               trans->parent->respcount++;
+                                               if (ies.q_dept)
+                                                       strncpy(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit) - 1);
+                                               if (ies.q_org)
+                                                       strncpy(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org) - 1);
+                                               if (ies.q_locality)
+                                                       strncpy(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality) - 1);
+                                               if (ies.q_stateprov)
+                                                       strncpy(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov) - 1);
+                                               if (ies.q_country)
+                                                       strncpy(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country) - 1);
+                                               if (ies.q_email)
+                                                       strncpy(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email) - 1);
+                                               if (ies.q_phone)
+                                                       strncpy(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone) - 1);
+                                               if (ies.q_ipaddr)
+                                                       strncpy(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr) - 1);
+                                               if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
+                                                       /* If it's them, update our address */
+                                                       ast_inet_ntoa(trans->parent->dei->ipaddr, sizeof(trans->parent->dei->ipaddr),
+                                                               trans->addr.sin_addr);
+                                               }
+                                       }
+                                       if (ies.hint) {
+                                               if (ntohs(ies.hint->flags) & DUNDI_HINT_TTL_EXPIRED)
+                                                       trans->parent->hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
+                                       }
+                               }
+                               /* Close connection if not final */
+                               if (!final) 
+                                       dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
+                       }
+                       
+               } else {
+                       /* Auth failure, check for data */
+                       if (!final) {
+                               /* Cancel if they didn't already */
+                               dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
+                       }
+               }
+               break;
+       case DUNDI_COMMAND_REGRESPONSE:
+               /* A dialplan response, lets see what we got... */
+               if (ies.cause < 1) {
+                       int hasauth;
+                       /* Success of some sort */
+                       if (trans->flags & FLAG_ENCRYPT) {
+                               hasauth = encrypted;
+                       } else 
+                               hasauth = 1;
+                       
+                       if (!hasauth) {
+                               ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
+                               if (!final) {
+                                       dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
+                                       dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
+                               }
+                       } else {
+                               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),
+                                                       dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
+                               /* Close connection if not final */
+                               if (!final) 
+                                       dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
+                       }
+               } else {
+                       /* Auth failure, cancel if they didn't for some reason */
+                       if (!final) {
+                               dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
+                       }
+               }
+               break;
+       case DUNDI_COMMAND_INVALID:
+       case DUNDI_COMMAND_NULL:
+               /* Do nothing special */
+               if (!final) 
+                       dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
+               break;
+       case DUNDI_COMMAND_ENCREJ:
+               if ((trans->flags & FLAG_SENDFULLKEY) || !trans->lasttrans || !(peer = find_peer(&trans->them_eid))) {
+                       /* No really, it's over at this point */
+                       if (!final) 
+                               dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
+               } else {
+                       /* Send with full key */
+                       trans->flags |= FLAG_SENDFULLKEY;
+                       if (final) {
+                               /* Ooops, we got a final message, start by sending ACK... */
+                               dundi_ack(trans, hdr->cmdresp & 0x80);
+                               trans->aseqno = trans->iseqno;
+                               /* Now, we gotta create a new transaction */
+                               if (!reset_transaction(trans)) {
+                                       /* Make sure handle_frame doesn't destroy us */
+                                       hdr->cmdresp &= 0x7f;
+                                       /* Parse the message we transmitted */
+                                       memset(&ies, 0, sizeof(ies));
+                                       dundi_parse_ies(&ies, trans->lasttrans->h->ies, trans->lasttrans->datalen - sizeof(struct dundi_hdr));
+                                       /* Reconstruct outgoing encrypted packet */
+                                       memset(&ied, 0, sizeof(ied));
+                                       dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
+                                       dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
+                                       dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
+                                       if (ies.encblock) 
+                                               dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
+                                       dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, trans->lasttrans->h->cmdresp & 0x80, &ied);
+                                       peer->sentfullkey = 1;
+                               }
+                       }
+               }
+               break;
+       case DUNDI_COMMAND_ENCRYPT:
+               if (!encrypted) {
+                       /* No nested encryption! */
+                       if ((trans->iseqno == 1) && !trans->oseqno) {
+                               if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) || 
+                                       ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) || 
+                                       (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
+                                       if (!final) {
+                                               dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
+                                       }
+                                       break;
+                               }
+                               apply_peer(trans, peer);
+                               /* Key passed, use new contexts for this session */
+                               trans->ecx = peer->them_ecx;
+                               trans->dcx = peer->them_dcx;
+                       }
+                       if ((trans->flags & FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
+                               struct dundi_hdr *dhdr;
+                               unsigned char decoded[MAX_PACKET_SIZE];
+                               int ddatalen;
+                               ddatalen = sizeof(decoded);
+                               dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
+                               if (dhdr) {
+                                       /* Handle decrypted response */
+                                       if (dundidebug)
+                                               dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
+                                       handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
+                                       /* Carry back final flag */
+                                       hdr->cmdresp |= dhdr->cmdresp & 0x80;
+                                       break;
+                               } else
+                                       ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
+                       }
+               }
+               if (!final) {
+                       /* Turn off encryption */
+                       trans->flags &= ~FLAG_ENCRYPT;
+                       dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
+               }
+               break;
+       default:
+               /* Send unknown command if we don't know it, with final flag IFF it's the
+                  first command in the dialog and only if we haven't recieved final notification */
+               if (!final) {
+                       dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
+                       dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
+               }
+       }
+       return 0;
+}
+
+static void destroy_packet(struct dundi_packet *pack, int needfree);
+static void destroy_packets(struct dundi_packet *p)
+{
+       struct dundi_packet *prev;
+       while(p) {
+               prev = p;
+               p = p->next;
+               if (prev->retransid > -1)
+                       ast_sched_del(sched, prev->retransid);
+               free(prev);
+       }
+}
+
+
+static int ack_trans(struct dundi_transaction *trans, int iseqno)
+{
+       /* Ack transmitted packet corresponding to iseqno */
+       struct dundi_packet *pack;
+       pack = trans->packets;
+       while(pack) {
+               if ((pack->h->oseqno + 1) % 255 == iseqno) {
+                       destroy_packet(pack, 0);
+                       if (trans->lasttrans) {
+                               ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
+                               destroy_packets(trans->lasttrans);
+                       }
+                       trans->lasttrans = pack;
+                       if (trans->autokillid > -1)
+                               ast_sched_del(sched, trans->autokillid);
+                       trans->autokillid = -1;
+                       return 1;
+               }
+               pack = pack->next;
+       }
+       return 0;
+}
+
+static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
+{
+       struct dundi_transaction *trans;
+       trans = find_transaction(h, sin);
+       if (!trans) {
+               dundi_reject(h, sin);
+               return 0;
+       }
+       /* Got a transaction, see where this header fits in */
+       if (h->oseqno == trans->iseqno) {
+               /* Just what we were looking for...  Anything but ack increments iseqno */
+               if (ack_trans(trans, h->iseqno) && (trans->flags & FLAG_FINAL)) {
+                       /* If final, we're done */
+                       destroy_trans(trans, 0);
+                       return 0;
+               }
+               if (h->cmdresp != DUNDI_COMMAND_ACK) {
+                       trans->oiseqno = trans->iseqno;
+                       trans->iseqno++;
+                       handle_command_response(trans, h, datalen, 0);
+               }
+               if (trans->aseqno != trans->iseqno) {
+                       dundi_ack(trans, h->cmdresp & 0x80);
+                       trans->aseqno = trans->iseqno;
+               }
+               /* Delete any saved last transmissions */
+               destroy_packets(trans->lasttrans);
+               trans->lasttrans = NULL;
+               if (h->cmdresp & 0x80) {
+                       /* Final -- destroy now */
+                       destroy_trans(trans, 0);
+               }
+       } else if (h->oseqno == trans->oiseqno) {
+               /* Last incoming sequence number -- send ACK without processing */
+               dundi_ack(trans, 0);
+       } else {
+               /* Out of window -- simply drop */
+               ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
+       }
+       return 0;
+}
+
+static int socket_read(int *id, int fd, short events, void *cbdata)
+{
+       struct sockaddr_in sin;
+       int res;
+       struct dundi_hdr *h;
+       unsigned char buf[MAX_PACKET_SIZE];
+       int len;
+       len = sizeof(sin);
+       res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
+       if (res < 0) {
+               if (errno != ECONNREFUSED)
+                       ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
+               return 1;
+       }
+       if (res < sizeof(struct dundi_hdr)) {
+               ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
+               return 1;
+       }
+       buf[res] = '\0';
+       h = (struct dundi_hdr *)buf;
+       if (dundidebug)
+               dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
+       ast_mutex_lock(&peerlock);
+       handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
+       ast_mutex_unlock(&peerlock);
+       return 1;
+}
+
+static void build_secret(char *secret, int seclen)
+{
+       char tmp[16];
+       char *s;
+       build_iv(tmp);
+       secret[0] = '\0';
+       ast_base64encode(secret ,tmp, sizeof(tmp), seclen);
+       /* Eliminate potential bad characters */
+       while((s = strchr(secret, ';'))) *s = '+';
+       while((s = strchr(secret, '/'))) *s = '+';
+       while((s = strchr(secret, ':'))) *s = '+';
+       while((s = strchr(secret, '@'))) *s = '+';
+}
+
+
+static void save_secret(const char *newkey, const char *oldkey)
+{
+       char tmp[256];
+       if (oldkey)
+               snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
+       else
+               snprintf(tmp, sizeof(tmp), "%s", newkey);
+       rotatetime = time(NULL) + DUNDI_SECRET_TIME;
+       ast_db_put(secretpath, "secret", tmp);
+       snprintf(tmp, sizeof(tmp), "%ld", rotatetime);
+       ast_db_put(secretpath, "secretexpiry", tmp);
+}
+
+static void load_password(void)
+{
+       char *current=NULL;
+       char *last=NULL;
+       char tmp[256];
+       time_t expired;
+       
+       ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
+       if (sscanf(tmp, "%ld", &expired) == 1) {
+               ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
+               current = strchr(tmp, ';');
+               if (!current)
+                       current = tmp;
+               else {
+                       *current = '\0';
+                       current++;
+               };
+               if ((time(NULL) - expired) < 0) {
+                       if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
+                               expired = time(NULL) + DUNDI_SECRET_TIME;
+               } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
+                       last = current;
+                       current = NULL;
+               } else {
+                       last = NULL;
+                       current = NULL;
+               }
+       }
+       if (current) {
+               /* Current key is still valid, just setup rotatation properly */
+               strncpy(cursecret, current, sizeof(cursecret) - 1);
+               rotatetime = expired;
+       } else {
+               /* Current key is out of date, rotate or eliminate all together */
+               build_secret(cursecret, sizeof(cursecret));
+               save_secret(cursecret, last);
+       }
+}
+
+static void check_password(void)
+{
+       char oldsecret[80];
+       time_t now;
+       
+       time(&now);     
+#if 0
+       printf("%ld/%ld\n", now, rotatetime);
+#endif
+       if ((now - rotatetime) >= 0) {
+               /* Time to rotate keys */
+               strncpy(oldsecret, cursecret, sizeof(oldsecret) - 1);
+               build_secret(cursecret, sizeof(cursecret));
+               save_secret(cursecret, oldsecret);
+       }
+}
+
+static void *network_thread(void *ignore)
+{
+       /* Our job is simple: Send queued messages, retrying if necessary.  Read frames 
+          from the network, and queue them for delivery to the channels */
+       int res;
+       /* Establish I/O callback for socket read */
+       ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
+       for(;;) {
+               res = ast_sched_wait(sched);
+               if ((res > 1000) || (res < 0))
+                       res = 1000;
+               res = ast_io_wait(io, res);
+               if (res >= 0) {
+                       ast_mutex_lock(&peerlock);
+                       ast_sched_runq(sched);
+                       ast_mutex_unlock(&peerlock);
+               }
+               check_password();
+       }
+       return NULL;
+}
+
+static int start_network_thread(void)
+{
+       return ast_pthread_create(&netthreadid, NULL, network_thread, NULL);
+}
+
+static int dundi_do_debug(int fd, int argc, char *argv[])
+{
+       if (argc != 2)
+               return RESULT_SHOWUSAGE;
+       dundidebug = 1;
+       ast_cli(fd, "DUNDi Debugging Enabled\n");
+       return RESULT_SUCCESS;
+}
+
+static int dundi_flush(int fd, int argc, char *argv[])
+{
+       if (argc != 2)
+               return RESULT_SHOWUSAGE;
+       ast_db_deltree("dundi/cache", NULL);
+       ast_cli(fd, "DUNDi Cache Flushed\n");
+       return RESULT_SUCCESS;
+}
+
+static int dundi_no_debug(int fd, int argc, char *argv[])
+{
+       if (argc != 3)
+               return RESULT_SHOWUSAGE;
+       dundidebug = 0;
+       ast_cli(fd, "DUNDi Debugging Disabled\n");
+       return RESULT_SUCCESS;
+}
+
+static char *model2str(int model)
+{
+       switch(model) {
+       case DUNDI_MODEL_INBOUND:
+               return "Inbound";
+       case DUNDI_MODEL_OUTBOUND:
+               return "Outbound";
+       case DUNDI_MODEL_SYMMETRIC:
+               return "Symmetric";
+       default:
+               return "Unknown";
+       }
+}
+
+static char *complete_peer_helper(char *line, char *word, int pos, int state, int rpos)
+{
+       int which=0;
+       char *ret;
+       struct dundi_peer *p;
+       char eid_str[20];
+       if (pos != rpos)
+               return NULL;
+       ast_mutex_lock(&peerlock);
+       p = peers;
+       while(p) {
+               if (!strncasecmp(word, dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), strlen(word))) {
+                       if (++which > state)
+                               break;
+               }
+               p = p->next;
+       }
+       if (p) {
+               ret = strdup(dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid));
+       } else
+               ret = NULL;
+       ast_mutex_unlock(&peerlock);
+       return ret;
+}
+
+static char *complete_peer_4(char *line, char *word, int pos, int state)
+{
+       return complete_peer_helper(line, word, pos, state, 3);
+}
+
+static int rescomp(const void *a, const void *b)
+{
+       const struct dundi_result *resa, *resb;
+       resa = a;
+       resb = b;
+       if (resa->weight < resb->weight)
+               return -1;
+       if (resa->weight > resb->weight)
+               return 1;
+       return 0;
+}
+
+static void sort_results(struct dundi_result *results, int count)
+{
+       qsort(results, count, sizeof(results[0]), rescomp);
+}
+
+static int dundi_do_lookup(int fd, int argc, char *argv[])
+{
+       int res;
+       char tmp[256] = "";
+       char fs[80] = "";
+       char *context;
+       int x;
+       struct dundi_result dr[MAX_RESULTS];
+       if ((argc < 3) || (argc > 3))
+               return RESULT_SHOWUSAGE;
+       strncpy(tmp, argv[2], sizeof(tmp) - 1);
+       context = strchr(tmp, '@');
+       if (context) {
+               *context = '\0';
+               context++;
+       }
+       res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp);
+       if (res < 0) 
+               ast_cli(fd, "DUNDi lookup returned error.\n");
+       else if (!res) 
+               ast_cli(fd, "DUNDi lookup returned no results.\n");
+       else
+               sort_results(dr, res);
+       for (x=0;x<res;x++) {
+               ast_cli(fd, "%d. %5d %s/%s (%s)\n", x + 1, dr[x].weight, dr[x].tech, dr[x].dest, dundi_flags2str(fs, sizeof(fs), dr[x].flags));
+       }
+       return RESULT_SUCCESS;
+}
+
+static int dundi_do_query(int fd, int argc, char *argv[])
+{
+       int res;
+       char tmp[256] = "";
+       char *context;
+       dundi_eid eid;
+       struct dundi_entity_info dei;
+       if ((argc < 3) || (argc > 3))
+               return RESULT_SHOWUSAGE;
+       if (dundi_str_to_eid(&eid, argv[2])) {
+               ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
+               return RESULT_SHOWUSAGE;
+       }
+       strncpy(tmp, argv[2], sizeof(tmp) - 1);
+       context = strchr(tmp, '@');
+       if (context) {
+               *context = '\0';
+               context++;
+       }
+       res = dundi_query_eid(&dei, context, eid);
+       if (res < 0) 
+               ast_cli(fd, "DUNDi Query EID returned error.\n");
+       else if (!res) 
+               ast_cli(fd, "DUNDi Query EID returned no results.\n");
+       else {
+               ast_cli(fd, "DUNDi Query EID succeeded:\n");
+               ast_cli(fd, "Department:      %s\n", dei.orgunit);
+               ast_cli(fd, "Organization:    %s\n", dei.org);
+               ast_cli(fd, "City/Locality:   %s\n", dei.locality);
+               ast_cli(fd, "State/Province:  %s\n", dei.stateprov);
+               ast_cli(fd, "Country:         %s\n", dei.country);
+               ast_cli(fd, "E-mail:          %s\n", dei.email);
+               ast_cli(fd, "Phone:           %s\n", dei.phone);
+               ast_cli(fd, "IP Address:      %s\n", dei.ipaddr);
+       }
+       return RESULT_SUCCESS;
+}
+
+static int dundi_show_peer(int fd, int argc, char *argv[])
+{
+       struct dundi_peer *peer;
+       struct permission *p;
+       char *order;
+       char iabuf[INET_ADDRSTRLEN];
+       char eid_str[20];
+       
+       if (argc != 4)
+               return RESULT_SHOWUSAGE;
+       ast_mutex_lock(&peerlock);
+       peer = peers;
+       while(peer) {
+               if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
+                       break;
+               peer = peer->next;
+       }
+       if (peer) {
+               switch(peer->order) {
+               case 0:
+                       order = "Primary";
+                       break;
+               case 1:
+                       order = "Secondary";
+                       break;
+               case 2:
+                       order = "Tertiary";
+                       break;
+               case 3:
+                       order = "Quartiary";
+                       break;
+               default:
+                       order = "Unknown";
+               }
+               ast_cli(fd, "Peer:    %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+               ast_cli(fd, "Model:   %s\n", model2str(peer->model));
+               ast_cli(fd, "Host:    %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "<Unspecified>");
+               ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
+               ast_cli(fd, "KeyPend: %s\n", peer->keypending ? "yes" : "no");
+               ast_cli(fd, "Reg:     %s\n", peer->registerid < 0 ? "No" : "Yes");
+               ast_cli(fd, "In Key:  %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
+               ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
+               if (peer->include) {
+                       ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
+               }
+               p = peer->include;
+               while(p) {
+                       ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
+                       p = p->next;
+               }
+               if (peer->permit) {
+                       ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
+               }
+               p = peer->permit;
+               while(p) {
+                       ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
+                       p = p->next;
+               }
+               
+       } else
+               ast_cli(fd, "No such peer '%s'\n", argv[3]);
+       ast_mutex_unlock(&peerlock);
+       return RESULT_SUCCESS;
+}
+
+static int dundi_show_peers(int fd, int argc, char *argv[])
+{
+#define FORMAT2 "%-20.20s %-15.15s     %-15.15s %-15.15s\n"
+#define FORMAT "%-20.20s %-15.15s %s %-15.15s %-15.15s\n"
+       struct dundi_peer *peer;
+       char iabuf[INET_ADDRSTRLEN];
+       int registeredonly=0;
+       char eid_str[20];
+       if ((argc != 3) && (argc != 4) && (argc != 5))
+               return RESULT_SHOWUSAGE;
+       if ((argc == 4)) {
+               if (!strcasecmp(argv[3], "registered")) {
+                       registeredonly = 1;
+               } else
+                       return RESULT_SHOWUSAGE;
+       }
+       ast_mutex_lock(&peerlock);
+       ast_cli(fd, FORMAT2, "EID", "Host", "Model", "Status");
+       for (peer = peers;peer;peer = peer->next) {
+               char status[20] = "";
+        int print_line = -1;
+               char srch[2000] = "";
+               if (registeredonly && !peer->addr.sin_addr.s_addr)
+                       continue;
+               if (peer->maxms) {
+                       if (peer->lastms < 0)
+                               strncpy(status, "UNREACHABLE", sizeof(status) - 1);
+                       else if (peer->lastms > peer->maxms) 
+                               snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
+                       else if (peer->lastms) 
+                               snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
+                       else 
+                               strncpy(status, "UNKNOWN", sizeof(status) - 1);
+               } else 
+                       strncpy(status, "Unmonitored", sizeof(status) - 1);
+
+               snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
+                                       peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
+                                       peer->dynamic ? "(D)" : "(S)", model2str(peer->model), status);
+
+                if (argc == 5) {
+                  if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
+                        print_line = -1;
+                   } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
+                        print_line = 1;
+                   } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
+                        print_line = -1;
+                   } else {
+                        print_line = 0;
+                  }
+                }
+               
+        if (print_line) {
+                       ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 
+                                       peer->addr.sin_addr.s_addr ? ast_inet_ntoa(iabuf, sizeof(iabuf), peer->addr.sin_addr) : "(Unspecified)",
+                                       peer->dynamic ? "(D)" : "(S)", model2str(peer->model), status);
+               }
+       }
+       ast_mutex_unlock(&peerlock);
+       return RESULT_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+static int dundi_show_trans(int fd, int argc, char *argv[])
+{
+#define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
+#define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
+       struct dundi_transaction *trans;
+       char iabuf[INET_ADDRSTRLEN];
+       if (argc != 3)
+               return RESULT_SHOWUSAGE;
+       ast_mutex_lock(&peerlock);
+       ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
+       for (trans = alltrans;trans;trans = trans->allnext) {
+                       ast_cli(fd, FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), trans->addr.sin_addr), 
+                                       ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
+       }
+       ast_mutex_unlock(&peerlock);
+       return RESULT_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+static int dundi_show_entityid(int fd, int argc, char *argv[])
+{
+       char eid_str[20];
+       if (argc != 3)
+               return RESULT_SHOWUSAGE;
+       ast_mutex_lock(&peerlock);
+       dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
+       ast_mutex_unlock(&peerlock);
+       ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
+       return RESULT_SUCCESS;
+}
+
+static int dundi_show_requests(int fd, int argc, char *argv[])
+{
+#define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
+#define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
+       struct dundi_request *req;
+       char eidstr[20];
+       if (argc != 3)
+               return RESULT_SHOWUSAGE;
+       ast_mutex_lock(&peerlock);
+       ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
+       for (req = requests;req;req = req->next) {
+                       ast_cli(fd, FORMAT, req->number, req->dcontext,
+                                               dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
+       }
+       ast_mutex_unlock(&peerlock);
+       return RESULT_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+/* Grok-a-dial DUNDi */
+
+static int dundi_show_mappings(int fd, int argc, char *argv[])
+{
+#define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
+#define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
+       struct dundi_mapping *map;
+       char fs[256];
+       if (argc != 3)
+               return RESULT_SHOWUSAGE;
+       ast_mutex_lock(&peerlock);
+       ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
+       for (map = mappings;map;map = map->next) {
+                       ast_cli(fd, FORMAT, map->dcontext, map->weight, 
+                                           ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext, 
+                                                               dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
+       }
+       ast_mutex_unlock(&peerlock);
+       return RESULT_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
+}
+
+static char debug_usage[] = 
+"Usage: dundi debug\n"
+"       Enables dumping of DUNDi packets for debugging purposes\n";
+
+static char no_debug_usage[] = 
+"Usage: dundi no debug\n"
+"       Disables dumping of DUNDi packets for debugging purposes\n";
+
+static char show_peers_usage[] = 
+"Usage: dundi show peers\n"
+"       Lists all known DUNDi peers.\n";
+
+static char show_trans_usage[] = 
+"Usage: dundi show trans\n"
+"       Lists all known DUNDi transactions.\n";
+
+static char show_mappings_usage[] = 
+"Usage: dundi show mappings\n"
+"       Lists all known DUNDi mappings.\n";
+
+static char show_entityid_usage[] = 
+"Usage: dundi show entityid\n"
+"       Displays the global entityid for this host.\n";
+
+static char show_peer_usage[] = 
+"Usage: dundi show peer [peer]\n"
+"       Provide a detailed description of a specifid DUNDi peer.\n";
+
+static char show_requests_usage[] = 
+"Usage: dundi show requests\n"
+"       Lists all known pending DUNDi requests.\n";
+
+static char lookup_usage[] =
+"Usage: dundi lookup <number>[@context]\n"
+"       Lookup the given number within the given DUNDi context\n"
+"(or e164 if none is specified).\n"
+"if specified.\n";
+
+static char query_usage[] =
+"Usage: dundi query <entity>[@context]\n"
+"       Attempts to retrieve contact information for a specific\n"
+"DUNDi entity identifier (EID) within a given DUNDi context (or\n"
+"e164 if none is specified).\n";
+
+static char flush_usage[] =
+"Usage: dundi flush\n"
+"       Flushes DUNDi answer cache, used primarily for debug.\n";
+
+static struct ast_cli_entry  cli_debug =
+       { { "dundi", "debug", NULL }, dundi_do_debug, "Enable DUNDi debugging", debug_usage };
+
+static struct ast_cli_entry  cli_flush =
+       { { "dundi", "flush", NULL }, dundi_flush, "Flush DUNDi cache", flush_usage };
+
+static struct ast_cli_entry  cli_no_debug =
+       { { "dundi", "no", "debug", NULL }, dundi_no_debug, "Disable DUNDi debugging", no_debug_usage };
+
+static struct ast_cli_entry  cli_show_peers =
+       { { "dundi", "show", "peers", NULL }, dundi_show_peers, "Show defined DUNDi peers", show_peers_usage };
+
+static struct ast_cli_entry  cli_show_trans =
+       { { "dundi", "show", "trans", NULL }, dundi_show_trans, "Show active DUNDi transactions", show_trans_usage };
+
+static struct ast_cli_entry  cli_show_entityid =
+       { { "dundi", "show", "entityid", NULL }, dundi_show_entityid, "Display Global Entity ID", show_entityid_usage };
+
+static struct ast_cli_entry  cli_show_mappings =
+       { { "dundi", "show", "mappings", NULL }, dundi_show_mappings, "Show DUNDi mappings", show_mappings_usage };
+
+static struct ast_cli_entry  cli_show_requests =
+       { { "dundi", "show", "requests", NULL }, dundi_show_requests, "Show DUNDi requests", show_requests_usage };
+
+static struct ast_cli_entry  cli_show_peer =
+       { { "dundi", "show", "peer", NULL }, dundi_show_peer, "Show info on a specific DUNDi peer", show_peer_usage, complete_peer_4 };
+
+static struct ast_cli_entry  cli_lookup =
+       { { "dundi", "lookup", NULL }, dundi_do_lookup, "Lookup a number in DUNDi", lookup_usage };
+
+static struct ast_cli_entry  cli_queryeid =
+       { { "dundi", "query", NULL }, dundi_do_query, "Query a DUNDi EID", query_usage };
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+static struct dundi_transaction *create_transaction(struct dundi_peer *p)
+{
+       struct dundi_transaction *trans;
+       int tid;
+       
+       /* Don't allow creation of transactions to non-registered peers */
+       if (p && !p->addr.sin_addr.s_addr)
+               return NULL;
+       tid = get_trans_id();
+       if (tid < 1)
+               return NULL;
+       trans = malloc(sizeof(struct dundi_transaction));
+       if (trans) {
+               memset(trans, 0, sizeof(struct dundi_transaction));
+               trans->autokillid = -1;
+               if (p) {
+                       apply_peer(trans, p);
+                       if (!p->sentfullkey)
+                               trans->flags |= FLAG_SENDFULLKEY;
+               }
+               trans->strans = tid;
+               trans->allnext = alltrans;
+               alltrans = trans;
+       }
+       return trans;
+}
+
+static int dundi_xmit(struct dundi_packet *pack)
+{
+       int res;
+       char iabuf[INET_ADDRSTRLEN];
+       if (dundidebug)
+               dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
+       res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
+       if (res < 0) {
+               ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n", 
+                       ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr),
+                       ntohs(pack->parent->addr.sin_port), strerror(errno));
+       }
+       if (res > 0)
+               res = 0;
+       return res;
+}
+
+static void destroy_packet(struct dundi_packet *pack, int needfree)
+{
+       struct dundi_packet *prev, *cur;
+       if (pack->parent) {
+               prev = NULL;
+               cur = pack->parent->packets;
+               while(cur) {
+                       if (cur == pack) {
+                               if (prev)
+                                       prev->next = cur->next;
+                               else
+                                       pack->parent->packets = cur->next;
+                               break;
+                       }
+                       prev = cur;
+                       cur = cur->next;
+               }
+       }
+       if (pack->retransid > -1)
+               ast_sched_del(sched, pack->retransid);
+       if (needfree)
+               free(pack);
+       else {
+               pack->retransid = -1;
+               pack->next = NULL;
+       }
+}
+
+static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
+{
+       struct dundi_transaction *cur, *prev;
+       struct dundi_peer *peer;
+       struct timeval tv;
+       int ms;
+       char eid_str[20];
+       if (trans->flags & (FLAG_ISREG | FLAG_ISQUAL)) {
+               peer = peers;
+               while (peer) {
+                       if (peer->regtrans == trans)
+                               peer->regtrans = NULL;
+                       if (peer->keypending == trans)
+                               peer->keypending = NULL;
+                       if (peer->qualtrans == trans) {
+                               if (fromtimeout) {
+                                       if (peer->lastms > -1)
+                                               ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+                                       peer->lastms = -1;
+                               } else {
+                                       gettimeofday(&tv, NULL);
+                                       ms = (tv.tv_sec - peer->qualtx.tv_sec) * 1000 + 
+                                                       (tv.tv_usec - peer->qualtx.tv_usec) / 1000;
+                                       if (ms < 1)
+                                               ms = 1;
+                                       if (ms < peer->maxms) {
+                                               if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
+                                                       ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+                                       } else if (peer->lastms < peer->maxms) {
+                                               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);
+                                       }
+                                       peer->lastms = ms;
+                               }
+                               peer->qualtrans = NULL;
+                       }
+                       peer = peer->next;
+               }
+       }
+       if (trans->parent) {
+               /* Unlink from parent if appropriate */
+               prev = NULL;
+               cur = trans->parent->trans;
+               while(cur) {
+                       if (cur == trans) {
+                               if (prev)
+                                       prev->next = trans->next;
+                               else
+                                       trans->parent->trans = trans->next;
+                               break;
+                       }
+                       prev = cur;
+                       cur = cur->next;
+               }
+       }
+       /* Unlink from all trans */
+       prev = NULL;
+       cur = alltrans;
+       while(cur) {
+               if (cur == trans) {
+                       if (prev)
+                               prev->allnext = trans->allnext;
+                       else
+                               alltrans = trans->allnext;
+                       break;
+               }
+               prev = cur;
+               cur = cur->allnext;
+       }
+       destroy_packets(trans->packets);
+       destroy_packets(trans->lasttrans);
+       trans->packets = NULL;
+       if (trans->autokillid > -1)
+               ast_sched_del(sched, trans->autokillid);
+       trans->autokillid = -1;
+       if (trans->thread) {
+               /* If used by a thread, mark as dead and be done */
+               trans->flags |= FLAG_DEAD;
+       } else
+               free(trans);
+}
+
+static int dundi_rexmit(void *data)
+{
+       struct dundi_packet *pack;
+       char iabuf[INET_ADDRSTRLEN];
+       int res;
+       ast_mutex_lock(&peerlock);
+       pack = data;
+       if (pack->retrans < 1) {
+               pack->retransid = -1;
+               if (!(pack->parent->flags & FLAG_ISQUAL))
+                       ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n", 
+                               ast_inet_ntoa(iabuf, sizeof(iabuf), pack->parent->addr.sin_addr), 
+                               ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
+               destroy_trans(pack->parent, 1);
+               res = 0;
+       } else {
+               /* Decrement retransmission, try again */
+               pack->retrans--;
+               dundi_xmit(pack);
+               res = 1;
+       }
+       ast_mutex_unlock(&peerlock);
+       return res;
+}
+
+static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
+{
+       struct dundi_packet *pack;
+       int res;
+       int len;
+       char eid_str[20];
+       len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
+       /* Reserve enough space for encryption */
+       if (trans->flags & FLAG_ENCRYPT)
+               len += 384;
+       pack = malloc(len);
+       if (pack) {
+               memset(pack, 0, len);
+               pack->h = (struct dundi_hdr *)(pack->data);
+               if (cmdresp != DUNDI_COMMAND_ACK) {
+                       pack->retransid = ast_sched_add(sched, DUNDI_DEFAULT_RETRANS_TIMER, dundi_rexmit, pack);
+                       pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
+                       pack->next = trans->packets;
+                       trans->packets = pack;
+               }
+               pack->parent = trans;
+               pack->h->strans = htons(trans->strans);
+               pack->h->dtrans = htons(trans->dtrans);
+               pack->h->iseqno = trans->iseqno;
+               pack->h->oseqno = trans->oseqno;
+               pack->h->cmdresp = cmdresp;
+               pack->datalen = sizeof(struct dundi_hdr);
+               if (ied) {
+                       memcpy(pack->h->ies, ied->buf, ied->pos);
+                       pack->datalen += ied->pos;
+               } 
+               if (final) {
+                       pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
+                       trans->flags |= FLAG_FINAL;
+               }
+               pack->h->cmdflags = flags;
+               if (cmdresp != DUNDI_COMMAND_ACK) {
+                       trans->oseqno++;
+                       trans->oseqno = trans->oseqno % 256;
+               }
+               trans->aseqno = trans->iseqno;
+               /* If we have their public key, encrypt */
+               if (trans->flags & FLAG_ENCRYPT) {
+                       switch(cmdresp) {
+                       case DUNDI_COMMAND_REGREQ:
+                       case DUNDI_COMMAND_REGRESPONSE:
+                       case DUNDI_COMMAND_DPDISCOVER:
+                       case DUNDI_COMMAND_DPRESPONSE:
+                       case DUNDI_COMMAND_EIDQUERY:
+                       case DUNDI_COMMAND_EIDRESPONSE:
+                               if (dundidebug)
+                                       dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
+                               res = dundi_encrypt(trans, pack);
+                               break;
+                       default:
+                               res = 0;
+                       }
+               } else 
+                       res = 0;
+               if (!res) 
+                       res = dundi_xmit(pack);
+               if (res)
+                       ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
+                               
+               if (cmdresp == DUNDI_COMMAND_ACK)
+                       free(pack);
+               return res;
+       }
+       return -1;
+}
+
+static int do_autokill(void *data)
+{
+       struct dundi_transaction *trans = data;
+       char eid_str[20];
+       ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n", 
+               dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
+       trans->autokillid = -1;
+       destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */
+       return 0;
+}
+
+static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
+{
+       struct dundi_peer *p;
+       if (!dundi_eid_cmp(eid, us)) {
+               dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
+               return;
+       }
+       ast_mutex_lock(&peerlock);
+       p = peers;
+       while(p) {
+               if (!dundi_eid_cmp(&p->eid, eid)) {
+                       if (has_permission(p->include, context))
+                               dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
+                       else
+                               dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
+                       break;
+               }
+               p = p->next;
+       }
+       if (!p)
+               dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
+       ast_mutex_unlock(&peerlock);
+}
+
+static int dundi_discover(struct dundi_transaction *trans)
+{
+       struct dundi_ie_data ied;
+       int x;
+       if (!trans->parent) {
+               ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
+               return -1;
+       }
+       memset(&ied, 0, sizeof(ied));
+       dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
+       if (!dundi_eid_zero(&trans->us_eid))
+               dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
+       for (x=0;x<trans->eidcount;x++)
+               dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
+       dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
+       dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
+       dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
+       if (trans->autokilltimeout)
+               trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
+       return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
+}
+
+static int dundi_query(struct dundi_transaction *trans)
+{
+       struct dundi_ie_data ied;
+       int x;
+       if (!trans->parent) {
+               ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
+               return -1;
+       }
+       memset(&ied, 0, sizeof(ied));
+       dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
+       if (!dundi_eid_zero(&trans->us_eid))
+               dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
+       for (x=0;x<trans->eidcount;x++)
+               dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
+       dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
+       dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
+       dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
+       if (trans->autokilltimeout)
+               trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
+       return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
+}
+
+static int discover_transactions(struct dundi_request *dr)
+{
+       struct dundi_transaction *trans;
+       ast_mutex_lock(&peerlock);
+       trans = dr->trans;
+       while(trans) {
+               dundi_discover(trans);
+               trans = trans->next;
+       }
+       ast_mutex_unlock(&peerlock);
+       return 0;
+}
+
+static int query_transactions(struct dundi_request *dr)
+{
+       struct dundi_transaction *trans;
+       ast_mutex_lock(&peerlock);
+       trans = dr->trans;
+       while(trans) {
+               dundi_query(trans);
+               trans = trans->next;
+       }
+       ast_mutex_unlock(&peerlock);
+       return 0;
+}
+
+static int optimize_transactions(struct dundi_request *dr, int order)
+{
+       /* Minimize the message propagation through DUNDi by
+          alerting the network to hops which should be not be considered */
+       struct dundi_transaction *trans;
+       struct dundi_peer *peer;
+       dundi_eid tmp;
+       int x;
+       int needpush;
+       ast_mutex_lock(&peerlock);
+       trans = dr->trans;
+       while(trans) {
+               /* Pop off the true root */
+               if (trans->eidcount) {
+                       tmp = trans->eids[--trans->eidcount];
+                       needpush = 1;
+               } else {
+                       tmp = trans->us_eid;
+                       needpush = 0;
+               }
+
+               peer = peers;
+               while(peer) {
+                       if (has_permission(peer->include, dr->dcontext) && 
+                           dundi_eid_cmp(&peer->eid, &trans->them_eid) &&
+                               (peer->order <= order)) {
+                               /* For each other transaction, make sure we don't
+                                  ask this EID about the others if they're not
+                                  already in the list */
+                               if (!dundi_eid_cmp(&tmp, &peer->eid)) 
+                                       x = -1;
+                               else {
+                                       for (x=0;x<trans->eidcount;x++) {
+                                               if (!dundi_eid_cmp(&trans->eids[x], &peer->eid))
+                                                       break;
+                                       }
+                               }
+                               if (x == trans->eidcount) {
+                                       /* Nope not in the list, if needed, add us at the end since we're the source */
+                                       if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
+                                               trans->eids[trans->eidcount++] = peer->eid;
+                                               /* Need to insert the real root (or us) at the bottom now as
+                                                  a requirement now.  */
+                                               needpush = 1;
+                                       }
+                               }
+                       }
+                       peer = peer->next;
+               }
+               /* If necessary, push the true root back on the end */
+               if (needpush)
+                       trans->eids[trans->eidcount++] = tmp;
+               trans = trans->next;
+       }
+       ast_mutex_unlock(&peerlock);
+       return 0;
+}
+
+static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
+{
+       struct dundi_transaction *trans;
+       int x;
+       char eid_str[20];
+       char eid_str2[20];
+       /* Ignore if not registered */
+       if (!p->addr.sin_addr.s_addr)
+               return 0;
+       if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
+               return 0;
+       if (ast_strlen_zero(dr->number))
+               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);
+       else
+               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);
+       trans = create_transaction(p);
+       if (!trans)
+               return -1;
+       trans->next = dr->trans;
+       trans->parent = dr;
+       trans->ttl = ttl;
+       for (x=0;avoid[x] && (x <DUNDI_MAX_STACK);x++)
+               trans->eids[x] = *avoid[x];
+       trans->eidcount = x;
+       dr->trans = trans;
+       return 0;
+}
+
+static void cancel_request(struct dundi_request *dr)
+{
+       struct dundi_transaction *trans, *next;
+
+       ast_mutex_lock(&peerlock);
+       trans = dr->trans;
+       
+       while(trans) {
+               next = trans->next;
+               /* Orphan transaction from request */
+               trans->parent = NULL;
+               trans->next = NULL;
+               /* Send final cancel */
+               dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
+               trans = next;
+       }
+       ast_mutex_unlock(&peerlock);
+}
+
+static void abort_request(struct dundi_request *dr)
+{
+       ast_mutex_lock(&peerlock);
+       while(dr->trans) 
+               destroy_trans(dr->trans, 0);
+       ast_mutex_unlock(&peerlock);
+}
+
+static void build_transactions(struct dundi_request *dr, int ttl, int order, int *foundcache, int *skipped, int blockempty, dundi_eid *avoid[], int directs[])
+{
+       struct dundi_peer *p;
+       int x;
+       int res;
+       char eid_str[20];
+       ast_mutex_lock(&peerlock);
+       p = peers;
+       while(p) {
+               if (has_permission(p->include, dr->dcontext)) {
+                       if (p->order <= order) {
+                               /* Check order first, then check cache, regardless of
+                                  omissions, this gets us more likely to not have an
+                                  affected answer. */
+                               if (!(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration))) {
+                                       /* Make sure we haven't already seen it and that it won't
+                                          affect our answer */
+                                       for (x=0;avoid[x];x++) {
+                                               if (!dundi_eid_cmp(avoid[x], &p->eid) || !dundi_eid_cmp(avoid[x], &p->us_eid)) {
+                                                       /* If not a direct connection, it affects our answer */
+                                                       if (directs && !directs[x]) 
+                                                               dr->hmd->flags &= ~DUNDI_HINT_UNAFFECTED;
+                                                       break;
+                                               }
+                                       }
+                                       /* Make sure we can ask */
+                                       if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) {
+                                               /* Check for a matching or 0 cache entry */
+                                               append_transaction(dr, p, ttl, avoid);
+                                       } else
+                                               ast_log(LOG_DEBUG, "Avoiding '%s' in transaction\n", dundi_eid_to_str(eid_str, sizeof(eid_str), avoid[x]));
+                               }
+                               *foundcache |= res;
+                       } else if (!*skipped || (p->order < *skipped))
+                               *skipped = p->order;
+               }
+               p = p->next;
+       }
+       ast_mutex_unlock(&peerlock);
+}
+
+static int register_request(struct dundi_request *dr, struct dundi_request **pending)
+{
+       struct dundi_request *cur;
+       int res=0;
+       char eid_str[20];
+       ast_mutex_lock(&peerlock);
+       cur = requests;
+       while(cur) {
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "Checking '%s@%s' vs '%s@%s'\n", cur->dcontext, cur->number,
+                               dr->dcontext, dr->number);
+               if (!strcasecmp(cur->dcontext, dr->dcontext) &&
+                   !strcasecmp(cur->number, dr->number) &&
+                       (!dundi_eid_cmp(&cur->root_eid, &dr->root_eid) || (cur->crc32 == dr->crc32))) {
+                               ast_log(LOG_DEBUG, "Found existing query for '%s@%s' for '%s' crc '%08lx'\n", 
+                                       cur->dcontext, cur->number, dundi_eid_to_str(eid_str, sizeof(eid_str), &cur->root_eid), cur->crc32);
+                               *pending = cur;
+                       res = 1;
+                       break;
+               }
+               cur = cur->next;
+       }
+       if (!res) {
+               ast_log(LOG_DEBUG, "Registering request for '%s@%s' on behalf of '%s' crc '%08lx'\n", 
+                               dr->number, dr->dcontext, dundi_eid_to_str(eid_str, sizeof(eid_str), &dr->root_eid), dr->crc32);
+               /* Go ahead and link us in since nobody else is searching for this */
+               dr->next = requests;
+               requests = dr;
+               *pending = NULL;
+       }
+       ast_mutex_unlock(&peerlock);
+       return res;
+}
+
+static void unregister_request(struct dundi_request *dr)
+{
+       struct dundi_request *cur, *prev;
+       ast_mutex_lock(&peerlock);
+       prev = NULL;
+       cur = requests;
+       while(cur) {
+               if (cur == dr) {
+                       if (prev)
+                               prev->next = cur->next;
+                       else
+                               requests = cur->next;
+                       break;
+               }
+               prev = cur;
+               cur = cur->next;
+       }
+       ast_mutex_unlock(&peerlock);
+}
+
+static int check_request(struct dundi_request *dr)
+{
+       struct dundi_request *cur;
+       int res = 0;
+       ast_mutex_lock(&peerlock);
+       cur = requests;
+       while(cur) {
+               if (cur == dr) {
+                       res = 1;
+                       break;
+               }
+               cur = cur->next;
+       }
+       ast_mutex_unlock(&peerlock);
+       return res;
+}
+
+static unsigned long avoid_crc32(dundi_eid *avoid[])
+{
+       /* Idea is that we're calculating a checksum which is independent of
+          the order that the EID's are listed in */
+       unsigned long acrc32 = 0;
+       int x;
+       for (x=0;avoid[x];x++) {
+               /* Order doesn't matter */
+               if (avoid[x+1]) {
+                       acrc32 ^= crc32(0L, (unsigned char *)avoid[x], sizeof(dundi_eid));
+               }
+       }
+       return acrc32;
+}
+
+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, dundi_eid *avoid[], int direct[])
+{
+       int res;
+       struct dundi_request dr, *pending;
+       dundi_eid *rooteid=NULL;
+       int x;
+       int ttlms;
+       int foundcache;
+       int skipped=0;
+       int order=0;
+       char eid_str[20];
+       struct timeval start;
+       
+       /* Don't do anthing for a hungup channel */
+       if (chan && chan->_softhangup)
+               return 0;
+
+       ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
+
+       for (x=0;avoid[x];x++)
+               rooteid = avoid[x];
+       /* Now perform real check */
+       memset(&dr, 0, sizeof(dr));
+       dr.dr = result;
+       dr.hmd = hmd;
+       dr.maxcount = maxret;
+       dr.expiration = *expiration;
+       dr.crc32 = avoid_crc32(avoid);
+       strncpy(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext) - 1);
+       strncpy(dr.number, number, sizeof(dr.number) - 1);
+       if (rooteid)
+               dr.root_eid = *rooteid;
+       res = register_request(&dr, &pending);
+       if (res) {
+               /* Already a request */
+               if (rooteid && !dundi_eid_cmp(&dr.root_eid, &pending->root_eid)) {
+                       /* This is on behalf of someone else.  Go ahead and close this out since
+                          they'll get their answer anyway. */
+                       ast_log(LOG_DEBUG, "Oooh, duplicate request for '%s@%s' for '%s'\n",
+                               dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &dr.root_eid));
+                       return -2;
+               } else {
+                       /* Wait for the cache to populate */
+                       ast_log(LOG_DEBUG, "Waiting for similar request for '%s@%s' for '%s'\n",
+                               dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &pending->root_eid));
+                       gettimeofday(&start, NULL);
+                       while(check_request(pending) && (calc_ms(&start) < ttlms) && (!chan || !chan->_softhangup))
+                               usleep(1);
+                       /* Continue on as normal, our cache should kick in */
+               }
+       }
+       /* Create transactions */
+       do {
+               order = skipped;
+               skipped = 0;
+               foundcache = 0;
+               build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, avoid, direct);
+       } while (skipped && !foundcache && !dr.trans);
+       /* If no TTL, abort and return 0 now after setting TTL expired hint.  Couldn't
+          do this earlier because we didn't know if we were going to have transactions
+          or not. */
+       if (!ttl) {
+               hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
+               abort_request(&dr);
+               unregister_request(&dr);
+               return 0;
+       }
+               
+       /* Optimize transactions */
+       optimize_transactions(&dr, order);
+       /* Actually perform transactions */
+       discover_transactions(&dr);
+       /* Wait for transaction to come back */
+       gettimeofday(&start, NULL);
+       while(dr.trans && (calc_ms(&start) < ttlms) && (!chan || !chan->_softhangup))
+               usleep(1);
+       if (chan && chan->_softhangup)
+               ast_log(LOG_DEBUG, "Hrm, '%s' hungup before their query for %s@%s finished\n", chan->name, dr.number, dr.dcontext);
+       cancel_request(&dr);
+       unregister_request(&dr);
+       res = dr.respcount;
+       *expiration = dr.expiration;
+       return res;
+}
+
+int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number)
+{
+       struct dundi_hint_metadata hmd;
+       dundi_eid *avoid[1] = { NULL, };
+       int direct[1] = { 0, };
+       int expiration = DUNDI_DEFAULT_CACHE_TIME;
+       memset(&hmd, 0, sizeof(hmd));
+       hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
+       return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, avoid, direct);
+}
+
+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[])
+{
+       int res;
+       struct dundi_request dr;
+       dundi_eid *rooteid=NULL;
+       int x;
+       int ttlms;
+       int skipped=0;
+       int foundcache=0;
+       struct timeval start;
+       
+       ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
+
+       for (x=0;avoid[x];x++)
+               rooteid = avoid[x];
+       /* Now perform real check */
+       memset(&dr, 0, sizeof(dr));
+       dr.hmd = hmd;
+       dr.dei = dei;
+       strncpy(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext) - 1);
+       memcpy(&dr.query_eid, eid, sizeof(dr.query_eid));
+       if (rooteid)
+               dr.root_eid = *rooteid;
+       /* Create transactions */
+       build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, avoid, NULL);
+
+       /* If no TTL, abort and return 0 now after setting TTL expired hint.  Couldn't
+          do this earlier because we didn't know if we were going to have transactions
+          or not. */
+       if (!ttl) {
+               hmd->flags |= DUNDI_HINT_TTL_EXPIRED;
+               return 0;
+       }
+               
+       /* Optimize transactions */
+       optimize_transactions(&dr, 9999);
+       /* Actually perform transactions */
+       query_transactions(&dr);
+       /* Wait for transaction to come back */
+       gettimeofday(&start, NULL);
+       while(dr.trans && (calc_ms(&start) < ttlms))
+               usleep(1);
+       res = dr.respcount;
+       return res;
+}
+
+int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid)
+{
+       dundi_eid *avoid[1] = { NULL, };
+       struct dundi_hint_metadata hmd;
+       memset(&hmd, 0, sizeof(hmd));
+       return dundi_query_eid_internal(dei, dcontext, &eid, &hmd, dundi_ttl, 0, avoid);
+}
+
+static int dundi_lookup_exec(struct ast_channel *chan, void *data)
+{
+       char *tmp;
+       char *context;
+       char *opts;
+       int res = -1;
+       struct localuser *u;
+
+       if (!data || !strlen(data)) {
+               ast_log(LOG_WARNING, "DUNDiLookup requires an argument (number)\n");
+               return 0;
+       }
+       LOCAL_USER_ADD(u);
+       tmp = ast_strdupa(data);
+       if (tmp) {
+               context = strchr(tmp, '|');
+               if (context) {
+                       *context = '\0';
+                       context++;
+                       opts = strchr(context, '|');
+                       if (opts) {
+                               *opts = '\0';
+                               opts++;
+                       }
+               } else
+                       opts = NULL;
+               if (!context || !strlen(context))
+                       context = "e164";
+               if (!opts)
+                       opts = "";
+               
+       }
+       LOCAL_USER_REMOVE(u);
+       return res;
+}
+
+
+static void mark_peers(void)
+{
+       struct dundi_peer *peer;
+       ast_mutex_lock(&peerlock);
+       peer = peers;
+       while(peer) {
+               peer->dead = 1;
+               peer = peer->next;
+       }
+       ast_mutex_unlock(&peerlock);
+}
+
+static void mark_mappings(void)
+{
+       struct dundi_mapping *map;
+       ast_mutex_lock(&peerlock);
+       map = mappings;
+       while(map) {
+               map->dead = 1;
+               map = map->next;
+       }
+       ast_mutex_unlock(&peerlock);
+}
+
+static void destroy_permissions(struct permission *p)
+{
+       struct permission *prev;
+       while(p) {
+               prev = p;
+               p = p->next;
+               free(prev);
+       }
+}
+
+static void destroy_peer(struct dundi_peer *peer)
+{
+       if (peer->registerid > -1)
+               ast_sched_del(sched, peer->registerid);
+       if (peer->regtrans)
+               destroy_trans(peer->regtrans, 0);
+       if (peer->keypending)
+               destroy_trans(peer->keypending, 0);
+       if (peer->qualifyid > -1)
+               ast_sched_del(sched, peer->qualifyid);
+       destroy_permissions(peer->permit);
+       destroy_permissions(peer->include);
+       free(peer);
+}
+
+static void destroy_map(struct dundi_mapping *map)
+{
+       free(map);
+}
+
+static void prune_peers(void)
+{
+       struct dundi_peer *peer, *prev, *next;
+       ast_mutex_lock(&peerlock);
+       peer = peers;
+       prev = NULL;
+       while(peer) {
+               next = peer->next;
+               if (peer->dead) {
+                       if (prev)
+                               prev->next = peer->next;
+                       else
+                               peers = peer->next;
+                       destroy_peer(peer);
+               } else
+                       prev = peer;
+               peer = next;
+       }
+       ast_mutex_unlock(&peerlock);
+}
+
+static void prune_mappings(void)
+{
+       struct dundi_mapping *map, *prev, *next;
+       ast_mutex_lock(&peerlock);
+       map = mappings;
+       prev = NULL;
+       while(map) {
+               next = map->next;
+               if (map->dead) {
+                       if (prev)
+                               prev->next = map->next;
+                       else
+                               mappings = map->next;
+                       destroy_map(map);
+               } else
+                       prev = map;
+               map = next;
+       }
+       ast_mutex_unlock(&peerlock);
+}
+
+static struct permission *append_permission(struct permission *p, char *s, int allow)
+{
+       struct permission *start;
+       start = p;
+       if (p) {
+               while(p->next)
+                       p = p->next;
+       }
+       if (p) {
+               p->next = malloc(sizeof(struct permission) + strlen(s) + 1);
+               p = p->next;
+       } else {
+               p = malloc(sizeof(struct permission) + strlen(s) + 1);
+       }
+       if (p) {
+               memset(p, 0, sizeof(struct permission));
+               memcpy(p->name, s, strlen(s) + 1);
+               p->allow = allow;
+       }
+       return start ? start : p;
+}
+
+#define MAX_OPTS 128
+
+static void build_mapping(char *name, char *value)
+{
+       char *t, *fields[MAX_OPTS];
+       struct dundi_mapping *map;
+       int x;
+       int y;
+       t = strdupa(value);
+       if (t) {
+               map = mappings;
+               while(map) {
+                       /* Find a double match */
+                       if (!strcasecmp(map->dcontext, name) && 
+                               (!strncasecmp(map->lcontext, value, strlen(map->lcontext)) && 
+                                 (!value[strlen(map->lcontext)] || 
+                                  (value[strlen(map->lcontext)] == ','))))
+                               break;
+                       map = map->next;
+               }
+               if (!map) {
+                       map = malloc(sizeof(struct dundi_mapping));
+                       if (map) {
+                               memset(map, 0, sizeof(struct dundi_mapping));
+                               map->next = mappings;
+                               mappings = map;
+                               map->dead = 1;
+                       }
+               }
+               if (map) {
+                       map->options = 0;
+                       memset(fields, 0, sizeof(fields));
+                       x = 0;
+                       while(t && x < MAX_OPTS) {
+                               fields[x++] = t;
+                               t = strchr(t, ',');
+                               if (t) {
+                                       *t = '\0';
+                                       t++;
+                               }
+                       } /* Russell was here, arrrr! */
+                       if ((x == 1) && ast_strlen_zero(fields[0])) {
+                               /* Placeholder mapping */
+                               strncpy(map->dcontext, name, sizeof(map->dcontext) - 1);
+                               map->dead = 0;
+                       } else if (x >= 4) {
+                               strncpy(map->dcontext, name, sizeof(map->dcontext) - 1);
+                               strncpy(map->lcontext, fields[0], sizeof(map->lcontext) - 1);
+                               if ((sscanf(fields[1], "%i", &map->weight) == 1) && (map->weight >= 0) && (map->weight < 60000)) {
+                                       strncpy(map->dest, fields[3], sizeof(map->dest) - 1);
+                                       if ((map->tech = str2tech(fields[2]))) {
+                                               map->dead = 0;
+                                       }
+                               } else {
+                                       ast_log(LOG_WARNING, "Invalid weight '%s' specified, deleting entry '%s/%s'\n", fields[1], map->dcontext, map->lcontext);
+                               }
+                               for (y=4;y<x;y++) {
+                                       if (!strcasecmp(fields[y], "nounsolicited"))
+                                               map->options |= DUNDI_FLAG_NOUNSOLICITED;
+                                       else if (!strcasecmp(fields[y], "nocomunsolicit"))
+                                               map->options |= DUNDI_FLAG_NOCOMUNSOLICIT;
+                                       else if (!strcasecmp(fields[y], "residential"))
+                                               map->options |= DUNDI_FLAG_RESIDENTIAL;
+                                       else if (!strcasecmp(fields[y], "commercial"))
+                                               map->options |= DUNDI_FLAG_COMMERCIAL;
+                                       else if (!strcasecmp(fields[y], "mobile"))
+                                               map->options |= DUNDI_FLAG_MOBILE;
+                                       else if (!strcasecmp(fields[y], "nopartial"))
+                                               map->options |= DUNDI_FLAG_INTERNAL_NOPARTIAL;
+                                       else
+                                               ast_log(LOG_WARNING, "Don't know anything about option '%s'\n", fields[y]);
+                               }
+                       } else 
+                               ast_log(LOG_WARNING, "Expected at least %d arguments in map, but got only %d\n", 4, x);
+               }
+       }
+}
+
+static int do_register(void *data)
+{
+       struct dundi_ie_data ied;
+       struct dundi_peer *peer = data;
+       char eid_str[20];
+       char eid_str2[20];
+       /* Called with peerlock already held */
+       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));
+       peer->registerid = ast_sched_add(sched, default_expiration * 1000, do_register, data);
+       /* Destroy old transaction if there is one */
+       if (peer->regtrans)
+               destroy_trans(peer->regtrans, 0);
+       peer->regtrans = create_transaction(peer);
+       if (peer->regtrans) {
+               peer->regtrans->flags |= FLAG_ISREG;
+               memset(&ied, 0, sizeof(ied));
+               dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
+               dundi_ie_append_eid(&ied, DUNDI_IE_EID, &peer->regtrans->us_eid);
+               dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
+               dundi_send(peer->regtrans, DUNDI_COMMAND_REGREQ, 0, 0, &ied);
+               
+       } else
+               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));
+
+       return 0;
+}
+
+static int do_qualify(void *data)
+{
+       struct dundi_peer *peer;
+       peer = data;
+       peer->qualifyid = -1;
+       qualify_peer(peer, 0);
+       return 0;
+}
+
+static void qualify_peer(struct dundi_peer *peer, int schedonly)
+{
+       int when;
+       if (peer->qualifyid > -1)
+               ast_sched_del(sched, peer->qualifyid);
+       peer->qualifyid = -1;
+       if (peer->qualtrans)
+               destroy_trans(peer->qualtrans, 0);
+       peer->qualtrans = NULL;
+       if (peer->maxms > 0) {
+               when = 60000;
+               if (peer->lastms < 0)
+                       when = 10000;
+               if (schedonly)
+                       when = 5000;
+               peer->qualifyid = ast_sched_add(sched, when, do_qualify, peer);
+               if (!schedonly)
+                       peer->qualtrans = create_transaction(peer);
+               if (peer->qualtrans) {
+                       gettimeofday(&peer->qualtx, NULL);
+                       peer->qualtrans->flags |= FLAG_ISQUAL;
+                       dundi_send(peer->qualtrans, DUNDI_COMMAND_NULL, 0, 1, NULL);
+               }
+       }
+}
+static void populate_addr(struct dundi_peer *peer, dundi_eid *eid)
+{
+       char data[256];
+       char *c;
+       int port, expire;
+       char eid_str[20];
+       dundi_eid_to_str(eid_str, sizeof(eid_str), eid);
+       if (!ast_db_get("dundi/dpeers", eid_str, data, sizeof(data))) {
+               c = strchr(data, ':');
+               if (c) {
+                       *c = '\0';
+                       c++;
+                       if (sscanf(c, "%d:%d", &port, &expire) == 2) {
+                               /* Got it! */
+                               inet_aton(data, &peer->addr.sin_addr);
+                               peer->addr.sin_family = AF_INET;
+                               peer->addr.sin_port = htons(port);
+                               peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
+                       }
+               }
+       }
+}
+
+
+static void build_peer(dundi_eid *eid, struct ast_variable *v)
+{
+       struct dundi_peer *peer;
+       struct ast_hostent he;
+       struct hostent *hp;
+       dundi_eid testeid;
+       int needregister=0;
+       char eid_str[20];
+
+       ast_mutex_lock(&peerlock);
+       peer = peers;
+       while(peer) {
+               if (!dundi_eid_cmp(&peer->eid, eid)) {  
+                       break;
+               }
+               peer = peer->next;
+       }
+       if (!peer) {
+               /* Add us into the list */
+               peer = malloc(sizeof(struct dundi_peer));
+               if (peer) {
+                       memset(peer, 0, sizeof(struct dundi_peer));
+                       peer->registerid = -1;
+                       peer->registerexpire = -1;
+                       peer->qualifyid = -1;
+                       peer->addr.sin_family = AF_INET;
+                       peer->addr.sin_port = htons(DUNDI_PORT);
+                       populate_addr(peer, eid);
+                       peer->next = peers;
+                       peers = peer;
+               }
+       }
+       if (peer) {
+               peer->dead = 0;
+               peer->eid = *eid;
+               peer->us_eid = global_eid;
+               destroy_permissions(peer->permit);
+               destroy_permissions(peer->include);
+               peer->permit = NULL;
+               peer->include = NULL;
+               if (peer->registerid > -1)
+                       ast_sched_del(sched, peer->registerid);
+               peer->registerid = -1;
+               while(v) {
+                       if (!strcasecmp(v->name, "inkey")) {
+                               strncpy(peer->inkey, v->value, sizeof(peer->inkey) - 1);
+                       } else if (!strcasecmp(v->name, "outkey")) {
+                               strncpy(peer->outkey, v->value, sizeof(peer->outkey) - 1);
+                       } else if (!strcasecmp(v->name, "host")) {
+                               if (!strcasecmp(v->value, "dynamic")) {
+                                       peer->dynamic = 1;
+                               } else {
+                                       hp = ast_gethostbyname(v->value, &he);
+                                       if (hp) {
+                                               memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr));
+                                               peer->dynamic = 0;
+                                       } else {
+                                               ast_log(LOG_WARNING, "Unable to find host '%s' at line %d\n", v->value, v->lineno);
+                                               peer->dead = 1;
+                                       }
+                               }
+                       } else if (!strcasecmp(v->name, "ustothem")) {
+                               if (!dundi_str_to_eid(&testeid, v->value))
+                                       peer->us_eid = testeid;
+                               else
+                                       ast_log(LOG_WARNING, "'%s' is not a valid DUNDi Entity Identifier at line %d\n", v->value, v->lineno);
+                       } else if (!strcasecmp(v->name, "include")) {
+                               peer->include = append_permission(peer->include, v->value, 1);
+                       } else if (!strcasecmp(v->name, "permit")) {
+                               peer->permit = append_permission(peer->permit, v->value, 1);
+                       } else if (!strcasecmp(v->name, "noinclude")) {
+                               peer->include = append_permission(peer->include, v->value, 0);
+                       } else if (!strcasecmp(v->name, "deny")) {
+                               peer->permit = append_permission(peer->permit, v->value, 0);
+                       } else if (!strcasecmp(v->name, "register")) {
+                               needregister = ast_true(v->value);
+                       } else if (!strcasecmp(v->name, "order")) {
+                               if (!strcasecmp(v->value, "primary"))
+                                       peer->order = 0;
+                               else if (!strcasecmp(v->value, "secondary"))
+                                       peer->order = 1;
+                               else if (!strcasecmp(v->value, "tertiary"))
+                                       peer->order = 2;
+                               else if (!strcasecmp(v->value, "quartiary"))
+                                       peer->order = 3;
+                               else {
+                                       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);
+                               }
+                       } else if (!strcasecmp(v->name, "qualify")) {
+                               if (!strcasecmp(v->value, "no")) {
+                                       peer->maxms = 0;
+                               } else if (!strcasecmp(v->value, "yes")) {
+                                       peer->maxms = DEFAULT_MAXMS;
+                               } else if (sscanf(v->value, "%d", &peer->maxms) != 1) {
+                                       ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of dundi.conf\n", 
+                                               dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), v->lineno);
+                                       peer->maxms = 0;
+                               }
+                       } else if (!strcasecmp(v->name, "model")) {
+                               if (!strcasecmp(v->value, "inbound"))
+                                       peer->model = DUNDI_MODEL_INBOUND;
+                               else if (!strcasecmp(v->value, "outbound")) 
+                                       peer->model = DUNDI_MODEL_OUTBOUND;
+                               else if (!strcasecmp(v->value, "symmetric"))
+                                       peer->model = DUNDI_MODEL_SYMMETRIC;
+                               else {
+                                       ast_log(LOG_WARNING, "Unknown model '%s', should be 'outbound', 'inbound', or 'symmetric' at line %d\n", 
+                                               v->value, v->lineno);
+                               }
+                       }
+                       v = v->next;
+               }
+               if (!peer->model) {
+                       ast_log(LOG_WARNING, "Peer '%s' lacks a model, discarding!\n", 
+                               dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+                       peer->dead = 1;
+               } else if (peer->include && !(peer->model & DUNDI_MODEL_OUTBOUND)) {
+                       ast_log(LOG_WARNING, "Peer '%s' is supposed to be included in outbound searches but isn't an outbound peer!\n", 
+                               dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+               } else if (peer->permit && !(peer->model & DUNDI_MODEL_INBOUND)) {
+                       ast_log(LOG_WARNING, "Peer '%s' is supposed to have permission for some inbound searches but isn't an inbound peer!\n", 
+                               dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
+               } else { 
+                       if (needregister) {
+                               peer->registerid = ast_sched_add(sched, 2000, do_register, peer);
+                       }
+                       qualify_peer(peer, 1);
+               }
+       }
+       ast_mutex_unlock(&peerlock);
+}
+
+static int dundi_helper(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *data, int flag)
+{
+       struct dundi_result results[MAX_RESULTS];
+       int res;
+       int x;
+       int found = 0;
+       if (!strncasecmp(context, "macro-", 6)) {
+               if (!chan) {    
+                       ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
+                       return -1;
+               }
+               /* If done as a macro, use macro extension */
+               if (!strcasecmp(exten, "s")) {
+                       exten = pbx_builtin_getvar_helper(chan, "ARG1");
+                       if (!exten || ast_strlen_zero(exten))
+                               exten = chan->macroexten;
+                       if (!exten || ast_strlen_zero(exten))
+                               exten = chan->exten;
+                       if (!exten || ast_strlen_zero(exten)) { 
+                               ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
+                               return -1;
+                       }
+               }
+               if (!data || ast_strlen_zero(data))
+                       data = "e164";
+       } else {
+               if (!data || ast_strlen_zero(data))
+                       data = context;
+       }
+       res = dundi_lookup(results, MAX_RESULTS, chan, data, exten);
+       for (x=0;x<res;x++) {
+               if (results[x].flags & flag)
+                       found++;
+       }
+       if (found >= priority)
+               return 1;
+       return 0;
+}
+
+static int dundi_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+       return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_EXISTS);
+}
+
+static int dundi_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+       return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_CANMATCH);
+}
+
+static int dundi_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, int newstack, const char *data)
+{
+       struct dundi_result results[MAX_RESULTS];
+       int res;
+       int x=0;
+       char req[1024];
+       struct ast_app *dial;
+       
+       if (!strncasecmp(context, "macro-", 6)) {
+               if (!chan) {    
+                       ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
+                       return -1;
+               }
+               /* If done as a macro, use macro extension */
+               if (!strcasecmp(exten, "s")) {
+                       exten = pbx_builtin_getvar_helper(chan, "ARG1");
+                       if (!exten || ast_strlen_zero(exten))
+                               exten = chan->macroexten;
+                       if (!exten || ast_strlen_zero(exten))
+                               exten = chan->exten;
+                       if (!exten || ast_strlen_zero(exten)) { 
+                               ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
+                               return -1;
+                       }
+               }
+               if (!data || ast_strlen_zero(data))
+                       data = "e164";
+       } else {
+               if (!data || ast_strlen_zero(data))
+                       data = context;
+       }
+       res = dundi_lookup(results, MAX_RESULTS, chan, data, exten);
+       if (res > 0) {
+               sort_results(results, res);
+               for (x=0;x<res;x++) {
+                       if (results[x].flags & DUNDI_FLAG_EXISTS) {
+                               if (!--priority)
+                                       break;
+                       }
+               }
+       }
+       if (x < res) {
+               /* Got a hit! */
+               snprintf(req, sizeof(req), "%s/%s", results[x].tech, results[x].dest);
+               dial = pbx_findapp("Dial");
+               if (dial)
+                       res = pbx_exec(chan, dial, req, newstack);
+       } else
+               res = -1;
+       return res;
+}
+
+static int dundi_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
+{
+       return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_MATCHMORE);
+}
+
+static struct ast_switch dundi_switch =
+{
+        name:                   "DUNDi",
+        description:                   "DUNDi Discovered Dialplan Switch",
+        exists:                 dundi_exists,
+        canmatch:               dundi_canmatch,
+        exec:                   dundi_exec,
+        matchmore:              dundi_matchmore,
+};
+
+static int set_config(char *config_file, struct sockaddr_in* sin)
+{
+       struct ast_config *cfg;
+       struct ast_variable *v;
+       char *cat;
+       int format;
+       int x;
+       char hn[256];
+       struct ast_hostent he;
+       struct hostent *hp;
+       struct sockaddr_in sin2;
+       static int last_port = 0;
+       dundi_eid testeid;
+
+       dundi_ttl = DUNDI_DEFAULT_TTL;
+       cfg = ast_load(config_file);
+       
+       
+       if (!cfg) {
+               ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
+               return -1;
+       }
+       ipaddr[0] = '\0';
+       if (!gethostname(hn, sizeof(hn))) {
+               hp = ast_gethostbyname(hn, &he);
+               if (hp) {
+                       memcpy(&sin2.sin_addr, hp->h_addr, sizeof(sin2.sin_addr));
+                       ast_inet_ntoa(ipaddr, sizeof(ipaddr), sin2.sin_addr);
+               } else
+                       ast_log(LOG_WARNING, "Unable to look up host '%s'\n", hn);
+       } else
+               ast_log(LOG_WARNING, "Unable to get host name!\n");
+       ast_mutex_lock(&peerlock);
+       reset_global_eid();
+       strncpy(secretpath, "dundi", sizeof(secretpath) - 1);
+       v = ast_variable_browse(cfg, "general");
+       while(v) {
+               if (!strcasecmp(v->name, "port")){ 
+                       sin->sin_port = ntohs(atoi(v->value));
+                       if(last_port==0){
+                               last_port=sin->sin_port;
+                       } else if(sin->sin_port != last_port)
+                               ast_log(LOG_WARNING, "change to port ignored until next asterisk re-start\n");
+               } else if (!strcasecmp(v->name, "bindaddr")) {
+                       struct hostent *hp;
+                       struct ast_hostent he;
+                       hp = ast_gethostbyname(v->value, &he);
+                       if (hp) {
+                               memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
+                       } else
+                               ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
+               } else if (!strcasecmp(v->name, "authdebug")) {
+                       authdebug = ast_true(v->value);
+               } else if (!strcasecmp(v->name, "ttl")) {
+                       if ((sscanf(v->value, "%i", &x) == 1) && (x > 0) && (x < DUNDI_DEFAULT_TTL)) {
+                               dundi_ttl = x;
+                       } else {
+                               ast_log(LOG_WARNING, "'%s' is not a valid TTL at line %d, must be number from 1 to %d\n",
+                                       v->value, v->lineno, DUNDI_DEFAULT_TTL);
+                       }
+               } else if (!strcasecmp(v->name, "autokill")) {
+                       if (sscanf(v->value, "%i", &x) == 1) {
+                               if (x >= 0)
+                                       global_autokilltimeout = x;
+                               else
+                                       ast_log(LOG_NOTICE, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno);
+                       } else if (ast_true(v->value)) {
+                               global_autokilltimeout = DEFAULT_MAXMS;
+                       } else {
+                               global_autokilltimeout = 0;
+                       }
+               } else if (!strcasecmp(v->name, "entityid")) {
+                       if (!dundi_str_to_eid(&testeid, v->value))
+                               global_eid = testeid;
+                       else
+                               ast_log(LOG_WARNING, "Invalid global endpoint identifier '%s' at line %d\n", v->value, v->lineno);
+               } else if (!strcasecmp(v->name, "tos")) {
+                       if (sscanf(v->value, "%i", &format) == 1)
+                               tos = format & 0xff;
+                       else if (!strcasecmp(v->value, "lowdelay"))
+                               tos = IPTOS_LOWDELAY;
+                       else if (!strcasecmp(v->value, "throughput"))
+                               tos = IPTOS_THROUGHPUT;
+                       else if (!strcasecmp(v->value, "reliability"))
+                               tos = IPTOS_RELIABILITY;
+                       else if (!strcasecmp(v->value, "mincost"))
+                               tos = IPTOS_MINCOST;
+                       else if (!strcasecmp(v->value, "none"))
+                               tos = 0;
+                       else
+                               ast_log(LOG_WARNING, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', 'mincost', or 'none'\n", v->lineno);
+               } else if (!strcasecmp(v->name, "department")) {
+                       strncpy(dept, v->value, sizeof(dept) - 1);
+               } else if (!strcasecmp(v->name, "organization")) {
+                       strncpy(org, v->value, sizeof(org) - 1);
+               } else if (!strcasecmp(v->name, "locality")) {
+                       strncpy(locality, v->value, sizeof(locality) - 1);
+               } else if (!strcasecmp(v->name, "stateprov")) {
+                       strncpy(stateprov, v->value, sizeof(stateprov) - 1);
+               } else if (!strcasecmp(v->name, "country")) {
+                       strncpy(country, v->value, sizeof(country) - 1);
+               } else if (!strcasecmp(v->name, "email")) {
+                       strncpy(email, v->value, sizeof(email) - 1);
+               } else if (!strcasecmp(v->name, "phone")) {
+                       strncpy(phone, v->value, sizeof(phone) - 1);
+               }
+               v = v->next;
+       }
+       ast_mutex_unlock(&peerlock);
+       mark_mappings();
+       v = ast_variable_browse(cfg, "mappings");
+       while(v) {
+               build_mapping(v->name, v->value);
+               v = v->next;
+       }
+       prune_mappings();
+       mark_peers();
+       cat = ast_category_browse(cfg, NULL);
+       while(cat) {
+               if (strcasecmp(cat, "general") && strcasecmp(cat, "mappings")) {
+                       /* Entries */
+                       if (!dundi_str_to_eid(&testeid, cat))
+                               build_peer(&testeid, ast_variable_browse(cfg, cat));
+                       else
+                               ast_log(LOG_NOTICE, "Ignoring invalid EID entry '%s'\n", cat);
+               }
+               cat = ast_category_browse(cfg, cat);
+       }
+       prune_peers();
+       ast_destroy(cfg);
+       load_password();
+       return 0;
+}
+
+int unload_module(void)
+{
+       int res;
+       STANDARD_HANGUP_LOCALUSERS;
+       ast_cli_unregister(&cli_debug);
+       ast_cli_unregister(&cli_flush);
+       ast_cli_unregister(&cli_no_debug);
+       ast_cli_unregister(&cli_show_peers);
+       ast_cli_unregister(&cli_show_entityid);
+       ast_cli_unregister(&cli_show_trans);
+       ast_cli_unregister(&cli_show_requests);
+       ast_cli_unregister(&cli_show_mappings);
+       ast_cli_unregister(&cli_show_peer);
+       ast_cli_unregister(&cli_lookup);
+       ast_cli_unregister(&cli_queryeid);
+       ast_unregister_switch(&dundi_switch);
+       res = ast_unregister_application(app);
+       return res;
+}
+
+int reload(void)
+{
+       struct sockaddr_in sin;
+       set_config("dundi.conf",&sin);
+       return 0;
+}
+
+int load_module(void)
+{
+       int res=0;
+       struct sockaddr_in sin;
+       char iabuf[INET_ADDRSTRLEN];
+       
+       dundi_set_output(dundi_debug_output);
+       dundi_set_error(dundi_error_output);
+       
+       /* Seed random number generator */
+       srand(time(NULL));
+       
+       sin.sin_family = AF_INET;
+       sin.sin_port = ntohs(DUNDI_PORT);
+       sin.sin_addr.s_addr = INADDR_ANY;
+
+       /* Make a UDP socket */
+       io = io_context_create();
+       sched = sched_context_create();
+       
+       if (!io || !sched) {
+               ast_log(LOG_ERROR, "Out of memory\n");
+               return -1;
+       }
+
+       ast_cli_register(&cli_debug);
+       ast_cli_register(&cli_flush);
+       ast_cli_register(&cli_no_debug);
+       ast_cli_register(&cli_show_peers);
+       ast_cli_register(&cli_show_entityid);
+       ast_cli_register(&cli_show_trans);
+       ast_cli_register(&cli_show_requests);
+       ast_cli_register(&cli_show_mappings);
+       ast_cli_register(&cli_show_peer);
+       ast_cli_register(&cli_lookup);
+       ast_cli_register(&cli_queryeid);
+       if (ast_register_switch(&dundi_switch))
+               ast_log(LOG_ERROR, "Unable to register DUNDi switch\n");
+
+       set_config("dundi.conf",&sin);
+
+       netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+       
+       if (netsocket < 0) {
+               ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
+               return -1;
+       }
+       if (bind(netsocket,(struct sockaddr *)&sin, sizeof(sin))) {
+               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));
+               return -1;
+       }
+
+       if (option_verbose > 1)
+               ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", tos);
+
+       if (setsockopt(netsocket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos))) 
+               ast_log(LOG_WARNING, "Unable to set TOS to %d\n", tos);
+       
+       if (!res) {
+               res = start_network_thread();
+               if (option_verbose > 1) 
+                       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));
+       } else {
+               ast_log(LOG_ERROR, "Unable to start network thread\n");
+               close(netsocket);
+       }
+       res = ast_register_application(app, dundi_lookup_exec, synopsis, descrip);
+       return 0;
+}
+
+char *description(void)
+{
+       return tdesc;
+}
+
+int usecount(void)
+{
+       int res;
+       /* XXX DUNDi cannot be unloaded XXX */
+       return 1;
+       STANDARD_USECOUNT(res);
+       return res;
+}
+
+char *key()
+{
+       return ASTERISK_GPL_KEY;
+}