[asterisk-scf-commits] asterisk-scf/integration/sip.git branch "nat-support" created.

Commits to the Asterisk SCF project code repositories asterisk-scf-commits at lists.digium.com
Wed Jun 29 12:06:47 CDT 2011


branch "nat-support" has been created
        at  ab5d3518d166da492bae4a81b321a224419c39c2 (commit)

- Log -----------------------------------------------------------------
commit ab5d3518d166da492bae4a81b321a224419c39c2
Author: Brent Eagles <beagles at digium.com>
Date:   Wed Jun 29 12:55:04 2011 -0230

    - Added transport and module hashtables to PJSipManager along with
      modifiers and accessors for same. This allows functionality to be
      added without having to propogate additional arguments around that
      might never be used.
      NOTE: While I am not a fan of this approach, it seemed the most direct
      route to take. An improvement would be take that stuff out of PJSipManager
      and put it in a separate class and pass that around instead.
    
    - Added STUNModule to encapsulate initialization of pj_nath library.
    
    - Added a Transport base class that encapsulates some common
      functions.
    
    - Moved TLS, UDP, TCP transport code out of PJSipManager into subsclasses of
      Transport. With the exception of a default UDP transport, all other transports are constructed
      through configuration.
    
    - Added a STUN transport, also a subclass of transport. Default transport
      behavior is modified to return a local address provided by the STUN
      binding request.
    
    - Added configuration handler for STUNTransport.
    
    - Added transport selection code in SipSession for STUN. This
      can be extended in the future to force selection of different transports
      based on session/endpoint configuration. By default pjsip is
      supposed to select the transport based on details of the dialog
      setup, but this just allows you to force it based on properties.
    
    - Modified dialog creation to build a contact URI with the external
      address for the SIP signalling through a NAT.
    
    - Added media_sdp modifier code if RTP over ICE is turned on which
      gets the InteractiveConnection facet off of the MediaSession.
      NOTE: I'm not completely sold on using a facet here, but where
      we are talking about conditionally available features it seemed
      like a good fit.
    
    - PJSipManager is now being passed along as a boost::shared_ptr based
      PJSipManagerPtr. This seems to work ok, but did change a lot of
      method signatures.

diff --git a/config/test_sip.conf b/config/test_sip.conf
index b96ee9c..53416f1 100644
--- a/config/test_sip.conf
+++ b/config/test_sip.conf
@@ -39,6 +39,8 @@ Sip.StateReplicatorListener=no
 # Endpoints that we know about
 Sip.Endpoints=cisco 18005558355
 
+Sip.Standalone=true
+
 # This is Josh's phone
 Sip.Endpoint.cisco.Session.CallDirection=Both
 
diff --git a/slice/AsteriskSCF/Configuration/SipSessionManager/SipConfigurationIf.ice b/slice/AsteriskSCF/Configuration/SipSessionManager/SipConfigurationIf.ice
index 849992d..c8855d3 100644
--- a/slice/AsteriskSCF/Configuration/SipSessionManager/SipConfigurationIf.ice
+++ b/slice/AsteriskSCF/Configuration/SipSessionManager/SipConfigurationIf.ice
@@ -32,382 +32,396 @@ module SipSessionManager
 ["suppress"]
 module V1
 {
-   /**
-    * Service locator category for finding the configuration service
-    */
-   const string ConfigurationDiscoveryCategory = "SipConfiguration";
-
-   /**
-    * Service locator parameters class for discovering the configuration service
-    */
-   unsliceable class SipConfigurationParams extends AsteriskSCF::Core::Discovery::V1::ServiceLocatorParams
-   {
-       /**
-	* Unique name for the configuration service
-	*/
-       string name;
-   };
-
-   /**
-    * Local visitor class for visiting SIP configuration groups
-    */
-   local class SipConfigurationGroupVisitor extends AsteriskSCF::System::Configuration::V1::ConfigurationGroupVisitor
-   {
-   };
-
-   /**
-    * Generic SIP configuration group
-    */
-   ["visitor:SipConfigurationGroupVisitor"] class SipConfigurationGroup extends AsteriskSCF::System::Configuration::V1::ConfigurationGroup
-   {
-   };
-
-   /**
-    * General SIP configuration group that contains general items related to the SIP component as a whole
-    */
-   class SipGeneralGroup extends SipConfigurationGroup
-   {
-   };
-
-   /**
-    * Local visitor class for visiting SIP configuration items
-    */
-   local class SipConfigurationItemVisitor extends AsteriskSCF::System::Configuration::V1::ConfigurationItemVisitor
-   {
-   };
-
-   /**
-    * Generic SIP configuration item
-    */
-   ["visitor:SipConfigurationItemVisitor"] class SipConfigurationItem extends AsteriskSCF::System::Configuration::V1::ConfigurationItem
-   {
-   };
-
-   /**
-    * SIP Endpoint group, used to configure an endpoint
-    */
-   class SipEndpointGroup extends SipConfigurationGroup
-   {
-      /**
-       * Name of the endpoint being configured
-       */
-      string name;
-   };
-
-   /**
-    * SIP Domain group, used to configure a specific domain
-    */
-   class SipDomainGroup extends SipConfigurationGroup
-   {
-      /**
-       * Name of the domain being configured
-       */
-      string domain;
-   };
-
-   /**
-    * Domain item for bindings
-    */
-   class SipBindingsItem extends SipConfigurationItem
-   {
-      /**
-       * Addresses the domain is reachable over
-       */
-      Ice::StringSeq bindings;
-   };
-
-   /**
-    * Generic SIP transport group that others derive from, primarily created since each one has
-    * the requirement of a name
-    */
-   ["visitor:SipConfigurationGroupVisitor"] class SipTransportGroup extends SipConfigurationGroup
-   {
-      /**
-       * Name of the specific transport. Since each one may have multiple transports this differentiates
-       * them.
-       */
-      string name;
-   };
-
-   /**
-    * Host information configuration item
-    */
-   ["visitor:SipConfigurationItemVisitor"] class SipHostItem extends SipConfigurationItem
-   {
-      /**
-       * String containing the IP address or string address
-       */
-      string host;
-
-      /**
-       * Port number
-       */
-      int port;
-   };
-
-   /**
-    * Source transport address configuration item
-    */
-   ["visitor:SipConfigurationItemVisitor"] class SipSourceTransportAddressItem extends SipHostItem
-   {
-   };
-
-   /**
-    * Target destination address configuration item
-    */
-   ["visitor:SipConfigurationItemVisitor"] class SipTargetDestinationAddressItem extends SipHostItem
-   {
-   };
-
-   /**
-    * Crypto certificate configuration item
-    */
-   class SipCryptoCertificateItem extends SipConfigurationItem
-   {
-      /**
-       * Full location of certificate authority file
-       */
-      string certificateAuthority;
-
-      /**
-       * Full location of certificate file
-       */
-      string certificate;
-
-      /**
-       * Full location of private key file
-       */
-      string privateKey;
-
-      /**
-       * Password to open private key file
-       */
-      string privateKeyPassword;
-   };
-
-   /**
-    * Crypto requirement configuration item
-    */
-   class SipCryptoRequirementsItem extends SipConfigurationItem
-   {
-       /**
-	* Require verification of server certificate
-	*/
-       bool requireVerifiedServer = false;
-
-       /**
-	* Require verification of client certificate
-	*/
-       bool requireVerifiedClient = false;
-
-       /**
-	* Require client certificate be present
-	*/
-       bool requireClientCertificate = false;
-   };
-
-   /**
-    * TLS protocol methods
-    */
-   enum TLSProtocolMethod
-   {
-       PROTOCOLMETHODUNSPECIFIED,
-       PROTOCOLMETHODTLSV1,
-       PROTOCOLMETHODSSLV2,
-       PROTOCOLMETHODSSLV3,
-       PROTOCOLMETHODSSLV23,
-   };
-
-   /**
-    * General crypto configuration item
-    */
-   class SipCryptoItem extends SipConfigurationItem
-   {
-       /**
-	* TLS protocol method to use
-	*/
-       TLSProtocolMethod protocolMethod = PROTOCOLMETHODUNSPECIFIED;
-
-       /**
-	* Supported ciphers (OpenSSL format)
-	*/
-       string supportedCiphers;
-
-       /**
-	* Server name
-	*/
-       string serverName;
-
-       /**
-	* TLS negotiation timeout in seconds
-	*/
-       int timeout = 0;
-   };
-
-   /**
-    * Routing service configuration item
-    */
-   class SipRoutingItem extends SipConfigurationItem
-   {
-      /**
-       * Name of the routing service to use
-       */
-      string routingServiceName;
-   };
-
-   /**
-    * RTP Media service configuration item
-    */
-   class SipRTPMediaServiceItem extends SipConfigurationItem
-   {
-      /**
-       * Name of the RTP media service to use
-       */
-      string mediaServiceName;
-
-      /**
-       * Whether to choose an IPv6 RTP media service or not
-       */
-      bool requireIPv6 = false;
-   };
-
-   /**
-    * Signaling NAT configuration item
-    */
-   class SipSignalingNATItem extends SipConfigurationItem
-   {
-      /**
-       * Boolean for whether STUN is enabled
-       */
-      bool stun;
-   };
-
-   /**
-    * Media NAT configuration item
-    */
-   class SipMediaNATItem extends SipConfigurationItem
-   {
-      /**
-       * Boolean for whether symmetric RTP is enabled
-       */
-      bool symmetricRTP;
-
-      /**
-       * Boolean for whether STUN is enabled
-       */
-      bool stun;
-
-      /**
-       * Boolean for whether ICE is enabled
-       */
-      bool interactiveconnectivityestablishment;
-
-      /**
-       * Boolean for whether TURN is enabled
-       */
-      bool turn;
-   };
-
-   /**
-    * Access control lists item
-    */
-   class SipACLItem extends SipConfigurationItem
-   {
-      /**
-       * Name of this ACL item. A user may want to have multiple.
-       */
-      string name;
-
-      /**
-       * Ranges of allowed hosts
-       */
-      Ice::StringSeq allowRanges;
-
-      /**
-       * Specific allowed hosts
-       */
-      Ice::StringSeq allowHosts;
-
-      /**
-       * Ranges of denied hosts
-       */
-      Ice::StringSeq denyRanges;
-
-      /**
-       * Specific denied hosts
-       */
-      Ice::StringSeq denyHosts;
-   };
-
-   /**
-    * Allowable call directions
-    */
-   enum SipAllowableCallDirection
-   {
-      Disabled,
-      Inbound,
-      Outbound,
-      Both,
-   };
-
-   /**
-    * Allowable call direction item
-    */
-   class SipAllowableCallDirectionItem extends SipConfigurationItem
-   {
-      /**
-       * What direction is permitted for this endpoint
-       */
-      SipAllowableCallDirection callDirection = Both;
-   };
-
-   /**
-    * User agent presentation configuration item
-    */
-   class SipUserAgentItem extends SipConfigurationItem
-   {
-      /**
-       * String that our user agent should appear as
-       */
-      string userAgent;
-   };
-
-   /**
-    * Transport information configuration item
-    */
-   class SipEndpointTransportItem extends SipConfigurationItem
-   {
-       /**
-	* What directions to require secure transport in
-	*/
-       SipAllowableCallDirection secureTransport = Disabled;
-   };
-
-   /**
-    * Group of configuration items related to a UDP transport
-   */
-   ["visitor:SipConfigurationGroupVisitor"] class SipUDPTransportGroup extends SipTransportGroup
-   {
-   };
-
-   /**
-    * Group of configuration items related to a TCP transport
-    */
-   ["visitor:SipConfigurationGroupVisitor"] class SipTCPTransportGroup extends SipTransportGroup
-   {
-   };
-
-   /**
-    * Group of configuration items related to a TLS transport
-    */
-   ["visitor:SipConfigurationGroupVisitor"] class SipTLSTransportGroup extends SipTransportGroup
-   {
-   };
+
+/**
+ * Service locator category for finding the configuration service
+ */
+const string ConfigurationDiscoveryCategory = "SipConfiguration";
+
+/**
+ * Service locator parameters class for discovering the configuration service
+ */
+unsliceable class SipConfigurationParams extends AsteriskSCF::Core::Discovery::V1::ServiceLocatorParams
+{
+    /**
+     * Unique name for the configuration service
+     */
+    string name;
+};
+
+/**
+ * Local visitor class for visiting SIP configuration groups
+ */
+local class SipConfigurationGroupVisitor extends AsteriskSCF::System::Configuration::V1::ConfigurationGroupVisitor
+{
+};
+
+/**
+ * Generic SIP configuration group
+ */
+["visitor:SipConfigurationGroupVisitor"] class SipConfigurationGroup extends AsteriskSCF::System::Configuration::V1::ConfigurationGroup
+{
+};
+
+/**
+ * General SIP configuration group that contains general items related to the SIP component as a whole
+ */
+class SipGeneralGroup extends SipConfigurationGroup
+{
+};
+
+/**
+ * Local visitor class for visiting SIP configuration items
+ */
+local class SipConfigurationItemVisitor extends AsteriskSCF::System::Configuration::V1::ConfigurationItemVisitor
+{
+};
+
+/**
+ * Generic SIP configuration item
+ */
+["visitor:SipConfigurationItemVisitor"] class SipConfigurationItem extends AsteriskSCF::System::Configuration::V1::ConfigurationItem
+{
+};
+
+/**
+ * SIP Endpoint group, used to configure an endpoint
+ */
+class SipEndpointGroup extends SipConfigurationGroup
+{
+    /**
+     * Name of the endpoint being configured
+     */
+    string name;
+};
+
+/**
+ * SIP Domain group, used to configure a specific domain
+ */
+class SipDomainGroup extends SipConfigurationGroup
+{
+    /**
+     * Name of the domain being configured
+     */
+    string domain;
+};
+
+/**
+ * Domain item for bindings
+ */
+class SipBindingsItem extends SipConfigurationItem
+{
+    /**
+     * Addresses the domain is reachable over
+     */
+    Ice::StringSeq bindings;
+};
+
+/**
+ * Generic SIP transport group that others derive from, primarily created since each one has
+ * the requirement of a name
+ */
+["visitor:SipConfigurationGroupVisitor"] class SipTransportGroup extends SipConfigurationGroup
+{
+    /**
+     * Name of the specific transport. Since each one may have multiple transports this differentiates
+     * them.
+     */
+    string name;
+};
+
+/**
+ * Host information configuration item
+ */
+["visitor:SipConfigurationItemVisitor"] class SipHostItem extends SipConfigurationItem
+{
+    /**
+     * String containing the IP address or string address
+     */
+    string host;
+
+    /**
+     * Port number
+     */
+    int port;
+};
+
+/**
+ * Source transport address configuration item
+ */
+["visitor:SipConfigurationItemVisitor"] class SipSourceTransportAddressItem extends SipHostItem
+{
+};
+
+/**
+ * Target destination address configuration item
+ */
+["visitor:SipConfigurationItemVisitor"] class SipTargetDestinationAddressItem extends SipHostItem
+{
+};
+
+/**
+ * Crypto certificate configuration item
+ */
+class SipCryptoCertificateItem extends SipConfigurationItem
+{
+    /**
+     * Full location of certificate authority file
+     */
+    string certificateAuthority;
+
+    /**
+     * Full location of certificate file
+     */
+    string certificate;
+
+    /**
+     * Full location of private key file
+     */
+    string privateKey;
+
+    /**
+     * Password to open private key file
+     */
+    string privateKeyPassword;
+};
+
+/**
+ * Crypto requirement configuration item
+ */
+class SipCryptoRequirementsItem extends SipConfigurationItem
+{
+    /**
+     * Require verification of server certificate
+     */
+    bool requireVerifiedServer = false;
+
+    /**
+     * Require verification of client certificate
+     */
+    bool requireVerifiedClient = false;
+
+    /**
+     * Require client certificate be present
+     */
+    bool requireClientCertificate = false;
+};
+
+/**
+ * TLS protocol methods
+ */
+enum TLSProtocolMethod
+{
+    PROTOCOLMETHODUNSPECIFIED,
+    PROTOCOLMETHODTLSV1,
+    PROTOCOLMETHODSSLV2,
+    PROTOCOLMETHODSSLV3,
+    PROTOCOLMETHODSSLV23,
+};
+
+/**
+ * General crypto configuration item
+ */
+class SipCryptoItem extends SipConfigurationItem
+{
+    /**
+     * TLS protocol method to use
+     */
+    TLSProtocolMethod protocolMethod = PROTOCOLMETHODUNSPECIFIED;
+
+    /**
+     * Supported ciphers (OpenSSL format)
+     */
+    string supportedCiphers;
+
+    /**
+     * Server name
+     */
+    string serverName;
+
+    /**
+     * TLS negotiation timeout in seconds
+     */
+    int timeout = 0;
+};
+
+/**
+ * Routing service configuration item
+ */
+class SipRoutingItem extends SipConfigurationItem
+{
+    /**
+     * Name of the routing service to use
+     */
+    string routingServiceName;
+};
+
+/**
+ * RTP Media service configuration item
+ */
+class SipRTPMediaServiceItem extends SipConfigurationItem
+{
+    /**
+     * Name of the RTP media service to use
+     */
+    string mediaServiceName;
+
+    /**
+     * Whether to choose an IPv6 RTP media service or not
+     */
+    bool requireIPv6 = false;
+};
+
+/**
+ * Signaling NAT configuration item
+ */
+class SipSignalingNATItem extends SipConfigurationItem
+{
+    /**
+     * Boolean for whether STUN is enabled
+     */
+    bool stun;
+};
+
+/**
+ * STUN server to be used by Sip signalling.
+ */
+class SipSignalingSTUNHostItem extends SipConfigurationItem
+{
+    /**
+     * Address for the STUN server.
+     **/
+    string address;
+
+    /**
+     * Port for the STUN host.
+     */
+    int port;
+};
+
+/**
+ * Media NAT configuration item
+ */
+class SipMediaNATItem extends SipConfigurationItem
+{
+    /**
+     * Boolean for whether ICE is enabled
+     */
+    bool enableICE;
+
+    /**
+     * Boolean for whether TURN is enabled
+     */
+    bool enableTURN;
+};
+
+/**
+ * Access control lists item
+ */
+class SipACLItem extends SipConfigurationItem
+{
+    /**
+     * Name of this ACL item. A user may want to have multiple.
+     */
+    string name;
+
+    /**
+     * Ranges of allowed hosts
+     */
+    Ice::StringSeq allowRanges;
+
+    /**
+     * Specific allowed hosts
+     */
+    Ice::StringSeq allowHosts;
+
+    /**
+     * Ranges of denied hosts
+     */
+    Ice::StringSeq denyRanges;
+
+    /**
+     * Specific denied hosts
+     */
+    Ice::StringSeq denyHosts;
+};
+
+/**
+ * Allowable call directions
+ */
+enum SipAllowableCallDirection
+{
+    Disabled,
+    Inbound,
+    Outbound,
+    Both,
+};
+
+/**
+ * Allowable call direction item
+ */
+class SipAllowableCallDirectionItem extends SipConfigurationItem
+{
+    /**
+     * What direction is permitted for this endpoint
+     */
+    SipAllowableCallDirection callDirection = Both;
+};
+
+/**
+ * User agent presentation configuration item
+ */
+class SipUserAgentItem extends SipConfigurationItem
+{
+    /**
+     * String that our user agent should appear as
+     */
+    string userAgent;
+};
+
+/**
+ * Transport information configuration item
+ */
+class SipEndpointTransportItem extends SipConfigurationItem
+{
+    /**
+     * What directions to require secure transport in
+     */
+    SipAllowableCallDirection secureTransport = Disabled;
+};
+
+/**
+ * Group of configuration items related to a UDP transport
+ */
+["visitor:SipConfigurationGroupVisitor"] class SipUDPTransportGroup extends SipTransportGroup
+{
+};
+
+/**
+ * Group of configuration items related to a TCP transport
+ */
+["visitor:SipConfigurationGroupVisitor"] class SipTCPTransportGroup extends SipTransportGroup
+{
+};
+
+/**
+ * Group of configuration items related to a TLS transport
+ */
+["visitor:SipConfigurationGroupVisitor"] class SipTLSTransportGroup extends SipTransportGroup
+{
+};
+
+/**
+ * Group of configuration items related to a STUN enabled transport
+ */
+["visitor:SipConfigurationGroupVisitor"] class SipSTUNTransportGroup extends SipTransportGroup
+{
+};
 
 }; /* module V1 */
 
 }; /* module SipSessionManager */
 
-}; /* module Replication */
+}; /* module Configuration */
 
 }; /* module AsteriskSCF */
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2412342..3086153 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -33,6 +33,24 @@ astscf_component_add_files(SipSessionManager SipConfiguration.cpp)
 astscf_component_add_files(SipSessionManager SipConfiguration.h)
 astscf_component_add_files(SipSessionManager SipStateReplicatorListener.cpp)
 astscf_component_add_files(SipSessionManager SipStateReplicator.h)
+astscf_component_add_files(SipSessionManager Transports.cpp)
+astscf_component_add_files(SipSessionManager Transports.h)
+astscf_component_add_files(SipSessionManager UDPTransport.cpp)
+astscf_component_add_files(SipSessionManager UDPTransport.h)
+astscf_component_add_files(SipSessionManager TCPTransport.cpp)
+astscf_component_add_files(SipSessionManager TCPTransport.h)
+astscf_component_add_files(SipSessionManager TLSTransport.cpp)
+astscf_component_add_files(SipSessionManager TLSTransport.h)
+astscf_component_add_files(SipSessionManager PJUtil.h)
+astscf_component_add_files(SipSessionManager DebugUtil.h)
+astscf_component_add_files(SipSessionManager SipModule.h)
+astscf_component_add_files(SipSessionManager STUNModule.cpp)
+astscf_component_add_files(SipSessionManager STUNModule.h)
+astscf_component_add_files(SipSessionManager STUNTransport.cpp)
+astscf_component_add_files(SipSessionManager STUNTransport.h)
+astscf_component_add_files(SipSessionManager STUNTransportConfig.cpp)
+astscf_component_add_files(SipSessionManager STUNTransportConfig.h)
+astscf_component_add_files(SipSessionManager NATOptions.h)
 astscf_component_add_slices(SipSessionManager PROJECT SipIf.ice)
 astscf_component_add_slices(SipSessionManager PROJECT AsteriskSCF/Replication/SipSessionManager/SipStateReplicationIf.ice)
 astscf_component_add_slices(SipSessionManager PROJECT AsteriskSCF/Configuration/SipSessionManager/SipConfigurationIf.ice)
@@ -41,8 +59,10 @@ astscf_component_add_boost_libraries(SipSessionManager core)
 astscf_component_add_slice_collection_libraries(SipSessionManager ASTSCF)
 astscf_component_build_icebox(SipSessionManager)
 target_link_libraries(SipSessionManager logging-client astscf-ice-util-cpp ${OPENSSL_LIBRARIES})
+
 pjproject_link(SipSessionManager pjsip)
 pjproject_link(SipSessionManager pjmedia)
+pjproject_link(SipSessionManager pjnath)
 pjproject_link(SipSessionManager pjlib-util)
 pjproject_link(SipSessionManager pjlib)
 astscf_component_install(SipSessionManager)
diff --git a/src/DebugUtil.h b/src/DebugUtil.h
new file mode 100644
index 0000000..cf65974
--- /dev/null
+++ b/src/DebugUtil.h
@@ -0,0 +1,33 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2011, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk SCF project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE.txt file
+ * at the top of the source tree.
+ */
+#pragma once
+
+#include <Ice/Ice.h>
+#include <boost/current_function.hpp>
+#include <string>
+
+namespace AsteriskSCF
+{
+
+#define FUNLOG \
+    __FILE__ << ':' << __LINE__ << '(' << BOOST_CURRENT_FUNCTION << ')'
+
+inline std::string objectIdFromCurrent(const Ice::Current& current)
+{
+    return current.adapter->getCommunicator()->identityToString(current.id);
+}
+
+} /* End of namespace AsteriskSCF */
diff --git a/src/NATOptions.h b/src/NATOptions.h
new file mode 100644
index 0000000..2319c50
--- /dev/null
+++ b/src/NATOptions.h
@@ -0,0 +1,37 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2010, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk SCF project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE.txt file
+ * at the top of the source tree.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+namespace AsteriskSCF
+{
+namespace SipSessionManager
+{
+struct NATEndpointOptions 
+{
+    bool enableICE;
+    bool enableTURN;
+    bool enableSIPSTUN;
+
+    NATEndpointOptions(bool ice, bool turn, bool sip) :
+        enableICE(ice), enableTURN(turn), enableSIPSTUN(sip) 
+    {
+    }
+};
+} /* End of namespace SipSessionManager */
+} /* End of namespace AsteriskSCF */
diff --git a/src/PJSipLoggingModule.cpp b/src/PJSipLoggingModule.cpp
index acfe04f..214e7f2 100644
--- a/src/PJSipLoggingModule.cpp
+++ b/src/PJSipLoggingModule.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "PJSipLoggingModule.h"
+#include <AsteriskSCF/logger.h>
 
 #include <AsteriskSCF/logger.h>
 
diff --git a/src/PJSipManager.cpp b/src/PJSipManager.cpp
index 928d47a..859b35b 100644
--- a/src/PJSipManager.cpp
+++ b/src/PJSipManager.cpp
@@ -16,14 +16,25 @@
 
 #include "PJSipManager.h"
 #include <AsteriskSCF/logger.h>
+#include <AsteriskSCF/System/ExceptionsIf.h>
 
 #include <boost/lexical_cast.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/thread/locks.hpp>
+#include <vector>
+
+#include "PJUtil.h"
+#include "STUNModule.h"
+#include "UDPTransport.h"
+#include <boost/lexical_cast.hpp>
 
 using namespace AsteriskSCF::System::Logging;
+using namespace AsteriskSCF::System::V1;
+using namespace std;
 
 namespace
 {
-Logger lg = getLoggerFactory().getLogger("AsteriskSCF.SipSessionManager");
+Logger logger = getLoggerFactory().getLogger("AsteriskSCF.SipSessionManager");
 }
 
 
@@ -33,47 +44,53 @@ namespace AsteriskSCF
 namespace SipSessionManager
 {
 
+//
+// TODO: The Transport/TransportFactory classes could be extended to allow multiple transports to
+// be configured on this endpoint. 
+// 
+
 static void *monitorThread(void *endpt)
 {
-    pjsip_endpoint *endpoint = static_cast<pjsip_endpoint *> (endpt);
+    PJSipManager* endpoint = reinterpret_cast<PJSipManager*> (endpt);
     for (;;)
     {
-        // No timeout is a bad idea in the long run, but for now,
-        // let's just go with it.
-	    pj_time_val timeout = {0, 10};
-        pjsip_endpt_handle_events(endpoint, &timeout);
+        endpoint->handleEvents();
     }
 
     return NULL;
 }
 
-PJSipManager::PJSipManager(const Ice::PropertiesPtr& props)
+PJSipManagerPtr AsteriskSCF::SipSessionManager::PJSipManager::create(const Ice::PropertiesPtr& properties)
 {
-    pj_status_t status = pj_init();
-    if (status != PJ_SUCCESS)
+    PJSipManagerPtr result(new PJSipManager);
+    result->initializeDefaultTransports(properties);
+    STUNModulePtr stunModule = STUNModule::create(result, properties, logger);
+    result->addModule("STUN", stunModule);
+    return result;
+}
+
+
+PJSipManager::~PJSipManager()
+{
+    for (SipModuleMap::iterator i = mModules.begin(); i != mModules.end(); ++i)
     {
-        lg(Error) << "Failed to Initialize PJSIP";
+        i->second->destroy();
+        i->second.reset();
     }
-    // The third parameter is just copied from
-    // example code from PJLIB. This can be adjusted
-    // if necessary.
-    pj_caching_pool_init(&mCachingPool, NULL, 1024 * 1024);
-    pjsip_endpt_create(&mCachingPool.factory, "SIP", &mEndpoint);
-    setTransports(mEndpoint, props);
-    mMemoryPool = pj_pool_create(&mCachingPool.factory, "SIP", 1024, 1024, NULL);
-    if (!mMemoryPool)
+    //
+    // It's pretty important that the transports don't take a reference to the PJSipManager,
+    // otherwise we'd never get here.
+    //
+    for (map<string, TransportPtr>::iterator i = mTransports.begin(); i != mTransports.end(); ++i)
     {
-        lg(Error) << "Failed to create a memory pool";
+        i->second->destroy();
+        i->second.reset();
     }
+}
 
-    // The default stack size might be set in config_site.h for
-    // specific platforms. 
-    status = pj_thread_create(mMemoryPool, "SIP", (pj_thread_proc *) &monitorThread,
-        mEndpoint, PJ_THREAD_DEFAULT_STACK_SIZE, 0, &mPjThread);
-    if (status != PJ_SUCCESS)
-    {
-        lg(Error) << "Failed to create SIP maintenance thread";
-    }
+pjsip_endpoint* PJSipManager::getEndpoint()
+{
+    return mEndpoint;
 }
 
 void PJSipManager::registerSessionModule(const boost::shared_ptr<SipEndpointFactory>& endpointFactoryPtr,
@@ -93,122 +110,159 @@ void PJSipManager::registerLoggingModule()
     mLoggingModule = new PJSipLoggingModule(mEndpoint);
 }
 
-PJSipSessionModulePtr PJSipManager::getSessionModule()
-{
-    return mSessionModule;
-}
-
-PJSipLoggingModulePtr PJSipManager::getLoggingModule()
-{
-    return mLoggingModule;
-}
-
-bool PJSipManager::setTransports(pjsip_endpoint *endpoint, const Ice::PropertiesPtr& props)
+void PJSipManager::addTransport(const string& id, const TransportPtr& transport)
 {
-    //XXX We'll also want to allow for TCP and TLS-specific
-    //addresses to be specified.
-    pj_sockaddr udpAddr;
-    pj_status_t status;
-    std::string udpBindAddr = props->getProperty("Sip.Transport.UdpBindAddr");
-
-    if (udpBindAddr.empty())
+    //
+    // Reap destroyed transports and add the new one.
+    // 
+    boost::unique_lock<boost::shared_mutex> lock(mLock);
+    map<string, TransportPtr>::iterator i = mTransports.begin();
+    while (i != mTransports.end())
     {
-	return true;
+        if (i->second->isDestroyed())
+        {
+            mTransports.erase(i);
+        }
+        else
+        {
+            ++i;
+        }
     }
+    mTransports.insert(make_pair(id, transport));
+}
 
-    pj_str_t udpString;
-    pj_cstr(&udpString, udpBindAddr.c_str());
-    //UNSPEC family means "you figure out the address family, pjlib!"
-    pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &udpString, &udpAddr);
-    if (udpAddr.addr.sa_family == pj_AF_INET())
+TransportPtr PJSipManager::getTransport(const string& id)
+{
+    boost::unique_lock<boost::shared_mutex> lock(mLock);
+    map<string, TransportPtr>::iterator i = mTransports.begin();
+    while (i != mTransports.end())
     {
-        //XXX The fourth parameter should probably be controlled through
-        //configuration. PJSIP reference says it should be the number of
-        //processors on the machine.
-        //
-        status = pjsip_udp_transport_start(endpoint, &udpAddr.ipv4, NULL, 2, &mUdpTransport);
-        if (status != PJ_SUCCESS)
+        if (i->second->isDestroyed())
         {
-            lg(Error) << "Failed to create IPv4 UDP transport. DANG!";
+            mTransports.erase(i);
         }
-    }
-    else if (udpAddr.addr.sa_family == pj_AF_INET6())
-    {
-        status = pjsip_udp_transport_start6(endpoint, &udpAddr.ipv6, NULL, 2, &mUdpTransport);
-        if (status != PJ_SUCCESS)
+        else
         {
-            lg(Error) << "Failed to create IPv4 UDP transport. DANG!";
+            ++i;
         }
     }
-    //XXX Note that TCP and TLS stuff should be done here. Also note that
-    //currently PJSIP does not have functions for starting IPv6 TCP or
-    //TLS transports, so we'll have to modify the code to allow for it.
-    return true;
+    i = mTransports.find(id);
+    if (i != mTransports.end())
+    {
+        return i->second;
+    }
+    return TransportPtr();
 }
 
-pjsip_transport *PJSipManager::createUDPTransport(std::string address, int port)
+void PJSipManager::addModule(const string& moduleName, const SipModulePtr& module)
 {
-    pj_sockaddr udpAddr;
-    pj_status_t status;
-    pj_str_t udpString;
-    pj_cstr(&udpString, address.c_str());
-    pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &udpString, &udpAddr);
-    pj_sockaddr_set_port(&udpAddr, boost::lexical_cast<pj_uint16_t>(port));
-
-    pjsip_transport *udpTransport = NULL;
-    if (udpAddr.addr.sa_family == pj_AF_INET())
+    boost::unique_lock<boost::shared_mutex> lock(mLock);
+    SipModuleMap::const_iterator i = mModules.find(moduleName);
+    if (i != mModules.end())
     {
-	status = pjsip_udp_transport_start(mEndpoint, &udpAddr.ipv4, NULL, 2, &udpTransport);
-    }
-    else if (udpAddr.addr.sa_family == pj_AF_INET6())
-    {
-	status = pjsip_udp_transport_start6(mEndpoint, &udpAddr.ipv6, NULL, 2, &udpTransport);
+        mModules.erase(i);
     }
+    mModules.insert(make_pair(moduleName, module));
+}
 
-    if (status != PJ_SUCCESS)
+SipModulePtr PJSipManager::getModule(const string& moduleName)
+{
+    boost::shared_lock<boost::shared_mutex> lock(mLock);
+    SipModuleMap::const_iterator i = mModules.find(moduleName);
+    if (i != mModules.end())
     {
-	lg(Error) << "Failed to create UDP transport.";
+        return i->second;
     }
+    return SipModulePtr();
+}
 
-    return udpTransport;
+void PJSipManager::handleEvents()
+{
+    onHandleEvents(mModules);
+    const pj_time_val delay = {0, 10};
+    pjsip_endpt_handle_events(mEndpoint, &delay);
 }
 
-pjsip_tpfactory *PJSipManager::createTCPTransport(std::string address, int port)
+PJSipSessionModulePtr PJSipManager::getSessionModule()
 {
-    pj_sockaddr tcpAddr;
-    pj_str_t tcpString;
-    pj_cstr(&tcpString, address.c_str());
-    pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &tcpString, &tcpAddr);
-    pj_sockaddr_set_port(&tcpAddr, boost::lexical_cast<pj_uint16_t>(port));
+    return mSessionModule;
+}
 
-    pjsip_tpfactory *tcpTransport = NULL;
+PJSipLoggingModulePtr PJSipManager::getLoggingModule()
+{
+    return mLoggingModule;
+}
 
-    if (pjsip_tcp_transport_start(mEndpoint, &tcpAddr, 2, &tcpTransport) != PJ_SUCCESS)
-    {
-	lg(Error) << "Failed to create TCP transport.";
-    }
+pj_caching_pool* PJSipManager::getCachingPool()
+{
+    return &mCachingPool;
+}
 
-    return tcpTransport;
+pj_pool_t* PJSipManager::getMemoryPool()
+{
+    return mMemoryPool;
 }
 
-pjsip_tpfactory *PJSipManager::createTLSTransport(std::string address, int port, pjsip_tls_setting *)
+void PJSipManager::initializeDefaultTransports(const Ice::PropertiesPtr& properties)
 {
-    pj_sockaddr tlsAddr;
-    pj_str_t tlsString;
-    pj_cstr(&tlsString, address.c_str());
-    pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &tlsString, &tlsAddr);
-    pj_sockaddr_set_port(&tlsAddr, boost::lexical_cast<pj_uint16_t>(port));
+    string bindAddress = properties->getPropertyWithDefault("Sip.Transport.UdpBindAddr", "0.0.0.0:5060");
+    string addressPart;
+    int portPart = 5060;
+    size_t colon = bindAddress.rfind(':');
+    if (colon != string::npos)
+    {
+        addressPart = bindAddress.substr(0, colon);
+        portPart = boost::lexical_cast<int>(bindAddress.substr(colon+1));
+    }
+    else
+    {
+        addressPart = bindAddress;
+    }
+    TransportPtr defaultUDPTransport = createUDPTransport(shared_from_this(), addressPart, portPart);
+    mTransports.insert(make_pair(defaultUDPTransport->id(), defaultUDPTransport));
+}
 
-    pjsip_tpfactory *tlsTransport = NULL;
+PJSipManager::PJSipManager() :
+    mEndpoint(0),
+    mSessionModule(0),
+    mLoggingModule(0),
+    mPjThread(0),
+    mMemoryPool(0)
+{
+    memset(&mCachingPool, 0, sizeof(mCachingPool));
 
-#if PJSIP_HAS_TLS_TRANSPORT
-    if (pjsip_tls_transport_start(mEndpoint, tlsSettings, &tlsAddr, NULL, 2, &tlsTransport) != PJ_SUCCESS)
-#endif
+    pj_status_t status = pj_init();
+    if (fail(status))
     {
-        lg(Error) << "Failed to create TLS transport.";
+        const char* message = "Failed to Initialize PJSIP";
+        logger(Error) << message;
+        throw InternalInitializationException(message);
     }
+    // The third parameter is just copied from
+    // example code from PJLIB. This can be adjusted
+    // if necessary.
+    pj_caching_pool_init(&mCachingPool, NULL, 1024 * 1024);
+    pjsip_endpt_create(&mCachingPool.factory, "SIP", &mEndpoint);
 
-    return tlsTransport;
+    //
+    // Careful! This was after the setTransports call, so reordering it might cause issues.
+    //
+    mMemoryPool = pj_pool_create(&mCachingPool.factory, "SIP", 1024, 1024, NULL);
+    if (!mMemoryPool)
+    {
+        const char* message = "Failed to create a memory pool";
+        logger(Error) << message;
+        throw InternalInitializationException(message);
+    }
+    
+    status = pj_thread_create(mMemoryPool, "SIP", (pj_thread_proc *) &monitorThread,
+        this, PJ_THREAD_DEFAULT_STACK_SIZE * 2, 0, &mPjThread);
+    if (fail(status))
+    {
+        const char* message = "Failed to create SIP maintenance thread";
+        logger(Error) << message;
+        throw InternalInitializationException(message);
+    }
 }
 
 }; //End namespace SipSessionManager
diff --git a/src/PJSipManager.h b/src/PJSipManager.h
index d82829e..7975fad 100644
--- a/src/PJSipManager.h
+++ b/src/PJSipManager.h
@@ -30,6 +30,8 @@
 
 #include "PJSipSessionModule.h"
 #include "PJSipLoggingModule.h"
+#include "Transports.h"
+#include "SipModule.h"
 
 namespace AsteriskSCF
 {
@@ -37,18 +39,22 @@ namespace AsteriskSCF
 namespace SipSessionManager
 {
 
-/**
- * This class is responsible for providing
- * access to the pjsip_endpt for the Asterisk
- * SCF SIP component.
+class PJSipManager;
+typedef boost::shared_ptr<PJSipManager> PJSipManagerPtr;
+
+/*
+ * This class is responsible for providing access to the pjsip_endpoint for the Asterisk SCF SIP component.
  *
- * In addition, it provides some common functions
- * that many SIP services will use.
+ * In addition, it provides some common functions that many SIP services will use.
  */
-class PJSipManager
+class PJSipManager : public boost::enable_shared_from_this<PJSipManager>
 {
 public:
-    PJSipManager(const Ice::PropertiesPtr& props);
+
+    static PJSipManagerPtr create(const Ice::PropertiesPtr& properties);
+
+    virtual ~PJSipManager();
+
     /**
      * Get a handle to the PJSipEndpoint for operations
      * that may require it
@@ -65,6 +71,10 @@ public:
      */
     PJSipLoggingModulePtr getLoggingModule();
 
+    pj_caching_pool* getCachingPool();
+    
+    pj_pool_t* getMemoryPool();
+
     /**
      * Register the PJSipSessionModule, responsible
      * for basic call handling
@@ -83,34 +93,38 @@ public:
      */
     void registerLoggingModule();
 
-    /**
-     * Create a UDP transport.
-     */
-    pjsip_transport *createUDPTransport(std::string, int);
+    //
+    // TODO: move this transport collection stuff to a transport manager or something.
+    // PJSipManager does not need it.
+    //
 
     /**
-     * Create a TCP transport.
-     */
-    pjsip_tpfactory *createTCPTransport(std::string, int);
+     * Add a transport the module. 
+     **/
+    void addTransport(const std::string& id, const TransportPtr& transport);
+    TransportPtr getTransport(const std::string& id);
+
+    void addModule(const std::string& id, const SipModulePtr& module);
+    SipModulePtr getModule(const std::string& moduleName);
+
+    void handleEvents();
 
-    /**
-     * Create a TLS transport.
-     */
-    pjsip_tpfactory *createTLSTransport(std::string, int, pjsip_tls_setting*);
 private:
-    static PJSipManager *mInstance;
     pjsip_endpoint *mEndpoint;
     PJSipSessionModulePtr mSessionModule;
     PJSipLoggingModulePtr mLoggingModule;
     pj_thread_t *mPjThread;
     pj_caching_pool mCachingPool;
     pj_pool_t *mMemoryPool;
-    pjsip_transport *mUdpTransport;
 
-    bool setTransports(pjsip_endpoint *endpoint, const Ice::PropertiesPtr& props);
-};
+    boost::shared_mutex mLock;
+    SipModuleMap mModules;
+    std::map<std::string, TransportPtr> mTransports;
 
-typedef boost::shared_ptr<PJSipManager> PJSipManagerPtr;
+    void initializeDefaultTransports(const Ice::PropertiesPtr& properties);
+
+    PJSipManager();
+};
 
 /**
  * Wrapper class around pj_thread_desc.
diff --git a/src/PJSipModule.h b/src/PJSipModule.h
index 354f5ba..175dd33 100644
--- a/src/PJSipModule.h
+++ b/src/PJSipModule.h
@@ -16,11 +16,10 @@
 
 #pragma once
 
+#include <pjlib.h>
 #include <string>
-
 #include <pjsip.h>
 #include <pjsip_ua.h>
-#include <pjlib.h>
 
 #include <SipStateReplicationIf.h>
 
diff --git a/src/PJUtil.h b/src/PJUtil.h
new file mode 100644
index 0000000..c30a1a6
--- /dev/null
+++ b/src/PJUtil.h
@@ -0,0 +1,37 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2011, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk SCF project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE.txt file
+ * at the top of the source tree.
+ */
+
+#pragma once
+
+#include <pjlib.h>
+
+namespace AsteriskSCF
+{
+namespace SipSessionManager
+{
+
+inline bool success(pj_status_t r)
+{
+    return r == PJ_SUCCESS;
+}
+
+inline bool fail(pj_status_t r)
+{
+    return !success(r);
+}
+
+} /* End of namespace SipSessionManager */
+} /* End of namespace AsteriskSCF */
diff --git a/src/STUNModule.cpp b/src/STUNModule.cpp
new file mode 100644
index 0000000..3678e5e
--- /dev/null
+++ b/src/STUNModule.cpp
@@ -0,0 +1,157 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2011, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk SCF project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE.txt file
+ * at the top of the source tree.
+ */
+
+#include "STUNModule.h"
+#include <pjlib.h>
+#include <pjnath.h>
+#include <Ice/Properties.h>
+#include <AsteriskSCF/logger.h>
+#include "PJSipManager.h"
+
+#include <pjnath/stun_sock.h>
+#include <pjnath/stun_session.h>
+#include <AsteriskSCF/System/ExceptionsIf.h>
+#include "DebugUtil.h"
+#include "PJUtil.h"
+
+using namespace AsteriskSCF::System::V1;
+using namespace AsteriskSCF::SipSessionManager;
+using namespace AsteriskSCF::System::Logging;
+
+namespace AsteriskSCF
+{
+namespace SipSessionManager
+{
+
+const pj_time_val DEFAULT_PROCESSING_TIMEOUT = { 0 /* seconds */, 100 /* milliseconds */ };
+
+//
+// Public methods.
+//
+STUNModule::~STUNModule()
+{
+    destroyImpl();
+}
+
+void STUNModule::destroy()
+{
+    destroyImpl();
+}
+
+void STUNModule::onHandleEvents()
+{
+    //
+    // Since the STUN module currently has it's own ioqueue and timer heap, we need to run them in a timely
+    // fashion. Every poll of the pjsip library seems appropriate.
+    // TODO: It may be possible to simply use the SIP endpoint's ioqueue for STUN transports, but
+    // this seems safter for the time being.
+    //
+    boost::shared_lock<boost::shared_mutex> lock(mLock);
+    pj_ioqueue_poll(mConfig->ioqueue, mTimeout.get());
+    pj_timer_heap_poll(mConfig->timer_heap, 0);
+}
+
+pj_stun_config* STUNModule::getConfig()
+{
+    return mConfig.get();
+}
+
+pj_pool_t* STUNModule::getPool()
+{
+    return mPool;
+}
+
+//
+// Static public
+//
+boost::shared_ptr<STUNModule> STUNModule::create(const PJSipManagerPtr& sipManager,
+        const Ice::PropertiesPtr& properties, const Logger& logger)
+{
+    logger(Debug) << FUNLOG << " : initializing STUN module";
+    pj_status_t status = pjnath_init();
+    if (fail(status))
+    {
+        const char* message = "Unable to initialize PJNATH library.";
+        logger(Error) << message;
+        throw InternalInitializationException(message);
+    }
+
+    boost::shared_ptr<pj_stun_config> config(new pj_stun_config);
+    
+    //
+    // We set the pool, queue, etc. to 0 because they are initialized later on.
+    // TODO: We may be able to use the main SIP ioqueue and timer heap. If so
+    // we can remove the poll method on this object.
+    //
+    pj_stun_config_init(config.get(), &sipManager->getCachingPool()->factory, 0, 0, 0);
+
+    status = pj_timer_heap_create(sipManager->getMemoryPool(), 
+            properties->getPropertyAsIntWithDefault("Sip.PJSip.TimerHeap.Size", 1000), 
+            &(config->timer_heap));
+    
+    if (fail(status))
+    {
+        //
+        // No far we haven't allocated anything that won't clean up after itself, so we can just throw here.
+        //
+        const char* message = "Unable to initialize timer heap.";
+        logger(Error) << message;
+        throw InternalInitializationException(message);
+    }
+
+    status = pj_ioqueue_create(sipManager->getMemoryPool(), properties->getPropertyAsIntWithDefault("Sip.PJSip.IOQueue.MaxSize", 16), 
+            &(config->ioqueue));
+    if (fail(status))
+    {
+        //
+        // Now we should probably clean up that timer heap!
+        //
+        pj_timer_heap_destroy(config->timer_heap);
+        const char* message = "Unable to initialize IO queue.";
+        logger(Error) << message;
+        throw InternalInitializationException(message);
+    }
+    return STUNModulePtr(new STUNModule(config));
+}
+
+//
+// Protected methods.
+//
+
+/**
+ * Construct the STUNModule object to "hold" the PJNATH STUN resources.
+ * TODO: we might want to make that processing timeout configurable.
+ **/
+STUNModule::STUNModule(const boost::shared_ptr<pj_stun_config>& config) :
+    mConfig(config),
+    mTimeout(new pj_time_val(DEFAULT_PROCESSING_TIMEOUT)),
+    mPool(pj_pool_create(mConfig->pf, "STUNMEMPL", 16 * 1024, 1024, 0))
+{
+}
+
+//
+// Private methods.
+//
+void STUNModule::destroyImpl()
+{
+    boost::unique_lock<boost::shared_mutex> lock(mLock);
+    pj_ioqueue_destroy(mConfig->ioqueue);
+    pj_timer_heap_destroy(mConfig->timer_heap);
+    pj_pool_release(mPool);
+    mConfig.reset();
+}
+}
+}
diff --git a/src/STUNModule.h b/src/STUNModule.h
new file mode 100644
index 0000000..492b213
--- /dev/null
+++ b/src/STUNModule.h
@@ -0,0 +1,101 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2011, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk SCF project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE.txt file
+ * at the top of the source tree.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/thread/shared_mutex.hpp>
+#include "SipModule.h"
+#include <Ice/PropertiesF.h>
+
+//
+// Forward declarations.
+//
+struct pj_stun_config;
+struct pj_caching_pool;
+struct pj_time_val;
+struct pj_pool_t;
+
+namespace AsteriskSCF
+{
+namespace System
+{
+namespace Logging
+{
+class Logger;
+}
+}
+
+namespace SipSessionManager
+{
+
+// 
+// forward declarations.
+//
+class PJSipManager;
+typedef boost::shared_ptr<PJSipManager> PJSipManagerPtr;
+
+//
+// TODO: there currently isn't a precedent for low level pjlib-type
+// configuration.  In the future, we may want to add configuration support
+// for this kidn of thing.
+//
+
+class STUNModule : public SipModule
+{
+public:
+    ~STUNModule();
+
+    /**
+     * SipModule interface.
+     **/
+    void destroy();
+    void onHandleEvents();
+
+    /**
+     * Accessors required to create STUN enable transports, etc. These are dangerous because they take the pointers out
+     * of context and it may not be safe to use them in multiple threads. It's basically only safe to use these during
+     * initialization.
+     **/
+    pj_stun_config* getConfig();
+    pj_pool_t* getPool();
+
+    //
+    //
+    static boost::shared_ptr<STUNModule> create(const PJSipManagerPtr& sipManager,
+            const Ice::PropertiesPtr& properties, const AsteriskSCF::System::Logging::Logger& logger);
+protected:
+    STUNModule(const boost::shared_ptr<pj_stun_config>& config);
+
+private:
+    boost::shared_mutex mLock;
+    boost::shared_ptr<pj_stun_config> mConfig;
+    boost::shared_ptr<pj_time_val> mTimeout;
+    pj_pool_t* mPool;
+
+    void destroyImpl();
+
+    //
+    // Unimplemented.
+    //
+    STUNModule(const STUNModule&);
+    void operator=(const STUNModule&);
+};
+typedef boost::shared_ptr<STUNModule> STUNModulePtr;
+
+
+} /* End of namespace SipSessionManager */
+} /* End of namespace AsteriskSCF */
diff --git a/src/STUNTransport.cpp b/src/STUNTransport.cpp
new file mode 100644
index 0000000..b0942fd
--- /dev/null
+++ b/src/STUNTransport.cpp
@@ -0,0 +1,397 @@
+/*
+ * Asterisk SCF -- An open-source communications framework.
+ *
+ * Copyright (C) 2011, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk SCF project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE.txt file
+ * at the top of the source tree.
+ */
+
+#include "STUNTransport.h"
+#include "STUNModule.h"
+#include "PJSipManager.h"
+
+#include <pjlib.h>
+#include <pjnath.h>
+#include <AsteriskSCF/logger.h>
+#include <AsteriskSCF/System/ExceptionsIf.h>
+
+#include "DebugUtil.h"
+#include "PJUtil.h"
+
+#include <boost/thread.hpp>
+#include <boost/thread/locks.hpp>
+#include <pjsip.h>
+
+using namespace AsteriskSCF::System::Logging;
+using namespace AsteriskSCF::System::V1;
+using namespace std;
+
+namespace
+{
+Logger logger = getLoggerFactory().getLogger("AsteriskSCF.SipSessionManager");
+}
+
+namespace AsteriskSCF
+{
+namespace SipSessionManager
+{
+
+class STUNTransport : public Transport
+{
+private:
+    /**
+     * Callback methods for the STUN socket callback data structure.
+     **/
+    static pj_bool_t onRxData(pj_stun_sock* stunSock, void* packet, unsigned packetLength, 
+        const pj_sockaddr_t* sourceAddress, unsigned addressLength)
+    {
+        STUNTransport* t = reinterpret_cast<STUNTransport*>(pj_stun_sock_get_user_data(stunSock));
+        t->dataReceived(packet, packetLength, sourceAddress, addressLength);
+        return PJ_TRUE;
+    }
+
+    static pj_bool_t onDataSent(pj_stun_sock*, pj_ioqueue_op_key_t*, pj_ssize_t)
+    {
+        //
+        // TODO. We could rig this up to allow asynchronous replies to the 
+        // PJSIP transport manager.
+        //
+        return PJ_TRUE;
+    }
+
+    static pj_bool_t onStatus(pj_stun_sock* stunSock, pj_stun_sock_op operation, pj_status_t)
+    {
+        //
+        // Respond to binding update.
+        // TODO: We should allow this to change over time, so we need to have some thread
+        // safety around initTransport.
+        //
+        if (operation == PJ_STUN_SOCK_BINDING_OP || operation == PJ_STUN_SOCK_MAPPED_ADDR_CHANGE)
+        {
+            //
+            // NOTE: If you watch the PJNATH tracing, you will see binding requests occurring periodically, but 
+            // it doesn't seem to enter here for each request. I suspect PJNATH checks to see if the mapped
+            // address has changed or not before firing a PJ_STUN_SOCK_BINDING_OP notification.
+            //
+            STUNTransport* t = reinterpret_cast<STUNTransport*>(pj_stun_sock_get_user_data(stunSock));
+            if (t)
+            {
+                pj_stun_sock_info socketInfo;
+                pj_status_t result = pj_stun_sock_get_info(stunSock, &socketInfo);
+                if (success(result))
+                {
+                    t->updateBinding(socketInfo);
+                }
+            }
+        }
+        return PJ_TRUE;
+    }
+
+    //
+    // Integration methods for STUN transport.
+    //
+    static pj_status_t sendMsgImplCB(pjsip_transport* transport, pjsip_tx_data* data, const pj_sockaddr_t* remoteAddress,
+        int remoteAddressLength, void* token, pjsip_transport_callback callback)
+    {
+        STUNTransport* t = reinterpret_cast<STUNTransport*>(transport->data);
+        return t->sendData(data, remoteAddress, remoteAddressLength, token, callback);
+    }
+
+    static pj_status_t shutdownImplCB(pjsip_transport*)
+    {
+        //
+        // The STUN transport doesn't have a distinct shutdown.
+        //
+        return PJ_SUCCESS;
+    }
+
+    static pj_status_t destroyImplCB(pjsip_transport* transport)
+    {
+        STUNTransport* t = reinterpret_cast<STUNTransport*>(transport->data);
+        t->destroy();
+        return PJ_SUCCESS;
+    }
+
+public:
+    STUNTransport(pjsip_endpoint* endpoint, const AsteriskSCF::Helpers::AddressPtr& stunServer, 
+        const AsteriskSCF::Helpers::AddressPtr& address, const STUNModulePtr& stun) : 
+        Transport(address->address(), address->port()),
+        mDestroyed(false),
+        mEndpoint(endpoint),
+        mModule(stun),
+        mSocket(0)
+    {
+        assert(stun);
+        logger(Debug) << FUNLOG << " : initializing STUN transport";
+
+        //
+        // Get default configuration from library.
+        //
+        pj_stun_sock_cfg socketConfig;
+        pj_stun_sock_cfg_default(&socketConfig);
+
+        //
+        // Setup the callback structure. PJNATH will call these functions when the relevant events occur
+        // on the STUN socket. These are hooked up as basically NO-OPs for the moment. If it turns out
+        // that they are not necessary, the callbacks should be removed to reduce that slight bit of 
+        // overhead.
+        //
+        pj_stun_sock_cb stunSockCB;
+        memset(&stunSockCB, 0, sizeof stunSockCB);
+        stunSockCB.on_status = &onStatus;
+        stunSockCB.on_rx_data = &onRxData;
+        stunSockCB.on_data_sent = &onDataSent;
+
+        pj_sockaddr udpAddr;
+        pj_str_t pjT;
+        if (address)
+        {
+            int af = pj_AF_INET();
+            if (address->isIPV6())
+            {
+                af = pj_AF_INET6();
+            }
+            pj_sockaddr_init(af, &udpAddr, pj_cstr(&pjT, address->address().c_str()), 
+                static_cast<pj_uint16_t>(address->port()));
+        }
+        else
+        {
+            pj_sockaddr_init(pj_AF_INET(), &udpAddr, pj_cstr(&pjT, "0.0.0.0"), 5060);
+        }
+
+        pj_status_t result = pj_stun_sock_create(mModule->getConfig(), 0, udpAddr.addr.sa_family, &stunSockCB, 
+            &socketConfig, this, &mSocket);
+        if (fail(result))
+        {
+            const char* message = "Failed to initalize STUN socket.";
+            logger(Error) << message;
+            throw InternalInitializationException(message);
+        }
+
+        result = pj_stun_sock_start(mSocket, pj_cstr(&pjT, stunServer->address().c_str()), 
+            static_cast<pj_uint16_t>(stunServer->port()), 0);
+        if (fail(result))
+        {
+            const char* message = "Failed to start STUN socket."; 
+            logger(Error) << message;
+            throw InternalInitializationException(message);
+        }
+        pj_stun_sock_info socketInfo;
+        result = pj_stun_sock_get_info(mSocket, &socketInfo);
+        if (fail(result))
+        {
+            throw InternalInitializationException("get_info on STUN socket failed");
+        }
+        if (pj_sockaddr_has_addr(&socketInfo.mapped_addr))
+        {
+            updateBinding(socketInfo);
+        }
+    };
+
+    ~STUNTransport()
+    {
+        if (mSocket)
+        {
+            pj_stun_sock_destroy(mSocket);
+        }
+    };
+
+    AsteriskSCF::Helpers::AddressPtr getAddress() const
+    {
+        boost::shared_lock<boost::shared_mutex> lock(mLock);
+        return mMappedAddress;
+    }
+
+    string id()
+    {
+        AsteriskSCF::Helpers::AddressPtr localAddr = Transport::getAddress();
+        ostringstream os;
+        os << idPrefix() << ':' << localAddr->address() << ':' << localAddr->port();
+        return os.str();
+    }
+
+    bool initSelector(pjsip_tpselector& selector)
+    {
+        boost::shared_lock<boost::shared_mutex> lock(mLock);
+        selector.type = PJSIP_TPSELECTOR_TRANSPORT;
+        selector.u.transport = mTransport.get();
+        return true;
+    }
+
+private:
+    mutable boost::shared_mutex mLock;
+    bool mDestroyed;
... 2282 lines suppressed ...


-- 
asterisk-scf/integration/sip.git



More information about the asterisk-scf-commits mailing list