Skip to content
Snippets Groups Projects
Commit b937b3db authored by Alexandre Savard's avatar Alexandre Savard
Browse files

#9547: Add SipTransport class

parent 6f3bffff
No related branches found
No related tags found
No related merge requests found
/*
* Copyright (C) [2004, 2012] Savoir-Faire Linux Inc.
*
* Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Additional permission under GNU GPL version 3 section 7:
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#include <map>
#include <pjsip.h>
#include <pjlib.h>
#include <pjsip_ua.h>
#include <pjlib-util.h>
#include <pjnath.h>
#include <pjnath/stun_config.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include "siptransport.h"
#include "manager.h"
#include "sip/sdp.h"
#include "sipcall.h"
#include "sipaccount.h"
#include "eventthread.h"
#include "sdes_negotiator.h"
#include "dbus/dbusmanager.h"
#include "dbus/callmanager.h"
#include "dbus/configurationmanager.h"
static pjsip_transport *localUDPTransport_ = NULL; /** The default transport (5060) */
std::string SipTransport::getSIPLocalIP()
{
pj_sockaddr ip_addr;
if (pj_gethostip(pj_AF_INET(), &ip_addr) == PJ_SUCCESS)
return pj_inet_ntoa(ip_addr.ipv4.sin_addr);
else {
ERROR("SipTransport: Could not get local IP");
return "";
}
}
std::vector<std::string> SipTransport::getAllIpInterfaceByName()
{
static ifreq ifreqs[20];
ifconf ifconf;
std::vector<std::string> ifaceList;
ifaceList.push_back("default");
ifconf.ifc_buf = (char*) (ifreqs);
ifconf.ifc_len = sizeof(ifreqs);
int sock = socket(AF_INET,SOCK_STREAM,0);
if (sock >= 0) {
if (ioctl(sock, SIOCGIFCONF, &ifconf) >= 0)
for (unsigned i = 0; i < ifconf.ifc_len / sizeof(ifreq); ++i)
ifaceList.push_back(std::string(ifreqs[i].ifr_name));
close(sock);
}
return ifaceList;
}
std::string SipTransport::getInterfaceAddrFromName(const std::string &ifaceName)
{
int fd = socket(AF_INET, SOCK_DGRAM,0);
if (fd < 0) {
ERROR("SipTransport: Error: could not open socket: %m");
return "";
}
ifreq ifr;
strcpy(ifr.ifr_name, ifaceName.c_str());
memset(&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr));
ifr.ifr_addr.sa_family = AF_INET;
ioctl(fd, SIOCGIFADDR, &ifr);
close(fd);
sockaddr_in *saddr_in = (sockaddr_in *) &ifr.ifr_addr;
return inet_ntoa(saddr_in->sin_addr);
}
std::vector<std::string> SipTransport::getAllIpInterface()
{
pj_sockaddr addrList[16];
unsigned addrCnt = PJ_ARRAY_SIZE(addrList);
std::vector<std::string> ifaceList;
if (pj_enum_ip_interface(pj_AF_INET(), &addrCnt, addrList) == PJ_SUCCESS) {
for (unsigned i = 0; i < addrCnt; i++) {
char addr[PJ_INET_ADDRSTRLEN];
pj_sockaddr_print(&addrList[i], addr, sizeof(addr), 0);
ifaceList.push_back(std::string(addr));
}
}
return ifaceList;
}
SipTransport::SipTransport(pjsip_endpoint *endpt, pj_caching_pool *cp, pj_pool_t *pool) : transportMap_(), stunSocketMap_(), cp_(cp), pool_(pool), endpt_(endpt)
{
}
SipTransport::~SipTransport()
{
}
pj_bool_t
stun_sock_on_status_cb(pj_stun_sock * /*stun_sock*/, pj_stun_sock_op op,
pj_status_t status)
{
switch (op) {
case PJ_STUN_SOCK_DNS_OP:
DEBUG("SipTransport: Stun operation dns resolution");
break;
case PJ_STUN_SOCK_BINDING_OP:
DEBUG("SipTransport: Stun operation binding");
break;
case PJ_STUN_SOCK_KEEP_ALIVE_OP:
DEBUG("SipTransport: Stun operation keep alive");
break;
case PJ_STUN_SOCK_MAPPED_ADDR_CHANGE:
DEBUG("SipTransport: Stun operation address mapping change");
break;
default:
DEBUG("SipTransport: Stun unknown operation");
break;
}
if (status == PJ_SUCCESS) {
DEBUG("SipTransport: Stun operation success");
} else {
ERROR("SipTransport: Stun operation failure");
}
// Always return true so the stun transport registration retry even on failure
return true;
}
static pj_bool_t
stun_sock_on_rx_data_cb(pj_stun_sock * /*stun_sock*/, void * /*pkt*/,
unsigned /*pkt_len*/,
const pj_sockaddr_t * /*src_addr*/,
unsigned /*addr_len*/)
{
return PJ_TRUE;
}
pj_status_t SipTransport::createStunResolver(pj_str_t serverName, pj_uint16_t port)
{
pj_stun_config stunCfg;
pj_stun_config_init(&stunCfg, &cp_->factory, 0, pjsip_endpt_get_ioqueue(endpt_), pjsip_endpt_get_timer_heap(endpt_));
DEBUG("***************** Create Stun Resolver *********************");
static const pj_stun_sock_cb stun_sock_cb = {
stun_sock_on_rx_data_cb,
NULL,
stun_sock_on_status_cb
};
pj_stun_sock *stun_sock;
std::string stunResolverName(serverName.ptr, serverName.slen);
pj_status_t status = pj_stun_sock_create(&stunCfg, stunResolverName.c_str(), pj_AF_INET(), &stun_sock_cb, NULL, NULL, &stun_sock);
// store socket inside list
DEBUG(" insert %s resolver in map", stunResolverName.c_str());
stunSocketMap_.insert(std::pair<std::string, pj_stun_sock *>(stunResolverName, stun_sock));
if (status != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
ERROR("SipTransport: Error creating STUN socket for %.*s: %s", (int) serverName.slen, serverName.ptr, errmsg);
return status;
}
status = pj_stun_sock_start(stun_sock, &serverName, port, NULL);
if (status != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
DEBUG("SipTransport: Error starting STUN socket for %.*s: %s", (int) serverName.slen, serverName.ptr, errmsg);
pj_stun_sock_destroy(stun_sock);
}
return status;
}
pj_status_t SipTransport::destroyStunResolver(const std::string serverName)
{
std::map<std::string, pj_stun_sock *>::iterator it;
it = stunSocketMap_.find(serverName);
DEBUG("***************** Destroy Stun Resolver *********************");
if (it != stunSocketMap_.end()) {
DEBUG("SipTransport: Deleting stun resolver %s", it->first.c_str());
pj_stun_sock_destroy(it->second);
stunSocketMap_.erase(it);
}
return PJ_SUCCESS;
}
void SipTransport::createTlsListener(pj_uint16_t tlsListenerPort, pjsip_tls_setting *tlsSetting, pjsip_tpfactory **listener)
{
pj_sockaddr_in local_addr;
pj_sockaddr_in_init(&local_addr, 0, 0);
local_addr.sin_port = pj_htons(tlsListenerPort);
if (tlsSetting == NULL) {
ERROR("SipTransport: Error TLS settings not specified");
return;
}
if (listener == NULL) {
ERROR("SipTransport: Error no pointer to store new TLS listener");
return;
}
pj_str_t pjAddress;
pj_cstr(&pjAddress, PJ_INADDR_ANY);
pj_sockaddr_in_set_str_addr(&local_addr, &pjAddress);
std::string localIP(getSIPLocalIP());
pjsip_host_port a_name = {
pj_str((char*) localIP.c_str()),
local_addr.sin_port
};
pjsip_tls_transport_start(endpt_, tlsSetting, &local_addr, &a_name, 1, listener);
}
pjsip_transport *
SipTransport::createTlsTransport(const std::string &remoteAddr,
pj_uint16_t tlsListenerPort,
pjsip_tls_setting *tlsSettings)
{
pjsip_transport *transport = NULL;
pj_str_t remote;
pj_cstr(&remote, remoteAddr.c_str());
pj_sockaddr_in rem_addr;
pj_sockaddr_in_init(&rem_addr, &remote, (pj_uint16_t) DEFAULT_SIP_TLS_PORT);
// The local tls listener
static pjsip_tpfactory *localTlsListener = NULL;
if (localTlsListener == NULL)
createTlsListener(tlsListenerPort, tlsSettings, &localTlsListener);
pjsip_endpt_acquire_transport(endpt_, PJSIP_TRANSPORT_TLS, &rem_addr,
sizeof rem_addr, NULL, &transport);
if (transport == NULL)
ERROR("SipTransport: Could not create new TLS transport\n");
return transport;
}
void SipTransport::createSipTransport(SIPAccount *account)
{
if (account == NULL) {
ERROR("SipTransport: Account is NULL while creating sip transport");
return;
}
shutdownSipTransport(account);
if (account->isTlsEnabled()) {
std::string remoteSipUri(account->getServerUri());
static const char SIPS_PREFIX[] = "<sips:";
size_t sips = remoteSipUri.find(SIPS_PREFIX) + (sizeof SIPS_PREFIX) - 1;
size_t trns = remoteSipUri.find(";transport");
std::string remoteAddr(remoteSipUri.substr(sips, trns-sips));
pjsip_transport *transport = createTlsTransport(remoteAddr, account->getTlsListenerPort(), account->getTlsSetting());
account->transport_ = transport;
} else if (account->isStunEnabled()) {
pjsip_transport *transport = createStunTransport(account->getStunServerName(), account->getStunPort());
account->transport_ = transport;
}
else {
pjsip_transport *transport = createUdpTransport(account->getLocalInterface(), account->getLocalPort());
account->transport_ = transport;
}
if (!account->transport_) {
// Could not create new transport, this transport may already exists
account->transport_ = transportMap_[account->getLocalPort()];
if (account->transport_)
pjsip_transport_add_ref(account->transport_);
else {
account->transport_ = localUDPTransport_;
account->setLocalPort(localUDPTransport_->local_name.port);
}
}
}
void SipTransport::createDefaultSipUdpTransport()
{
pj_uint16_t port = 0;
int counter = 0;
DEBUG("SipTransport: Create default sip udp transport");
SIPAccount *account = Manager::instance().getIP2IPAccount();
pjsip_transport *transport = NULL;
static const int DEFAULT_TRANSPORT_ATTEMPTS = 5;
for (; transport == NULL and counter < DEFAULT_TRANSPORT_ATTEMPTS; ++counter) {
// if default udp transport fails to init on 5060, try other ports
// with 2 step size increment (i.e. 5062, 5064, ...)
port = account->getLocalPort() + (counter * 2);
transport = createUdpTransport(account->getLocalInterface(), port);
}
if (transport == NULL) {
ERROR("SipTransport: Create UDP transport");
return;
}
DEBUG("SipTransport: Created default sip transport on %d", port);
// set transport for this account
account->transport_ = transport;
// set local udp transport
localUDPTransport_ = account->transport_;
}
pjsip_transport *SipTransport::createUdpTransport(std::string interface, unsigned int port)
{
// init socket to bind this transport to
pj_sockaddr_in bound_addr;
pj_bzero(&bound_addr, sizeof(bound_addr));
pj_uint16_t listeningPort = (pj_uint16_t) port;
bound_addr.sin_port = pj_htons(listeningPort);
bound_addr.sin_family = PJ_AF_INET;
DEBUG("SipTransport: Create UDP transport on %s:%d", interface.c_str(), port);
// determine the ip address for this transport
static const char * const DEFAULT_INTERFACE = "default";
std::string listeningAddress;
if (interface == DEFAULT_INTERFACE) {
listeningAddress = getSIPLocalIP();
bound_addr.sin_addr.s_addr = pj_htonl(PJ_INADDR_ANY);
} else {
listeningAddress = getInterfaceAddrFromName(interface);
bound_addr.sin_addr = pj_inet_addr2(listeningAddress.c_str());
}
if (listeningAddress.empty()) {
ERROR("SipTransport: Could not determine ip address for this transport");
return NULL;
}
if (listeningPort == 0) {
ERROR("SipTransport: Could not determine port for this transport");
return NULL;
}
DEBUG("SipTransport: Listening address %s, listening port %d", listeningAddress.c_str(), listeningPort);
// The published address for this transport
const pjsip_host_port a_name = {
pj_str((char*) listeningAddress.c_str()),
listeningPort
};
pjsip_transport *transport = NULL;
pj_status_t status = pjsip_udp_transport_start(endpt_, &bound_addr, &a_name, 1, &transport);
if (status != PJ_SUCCESS) {
ERROR("SipTransport: Could not create UDP transport for port %u", port);
return NULL;
}
// dump debug information to stdout
pjsip_tpmgr_dump_transports(pjsip_endpt_get_tpmgr(endpt_));
transportMap_[listeningPort] = transport;
return transport;
}
pjsip_tpselector *SipTransport::initTransportSelector(pjsip_transport *transport, pj_pool_t *tp_pool) const
{
assert(transport);
pjsip_tpselector *tp = (pjsip_tpselector *) pj_pool_zalloc(tp_pool, sizeof(pjsip_tpselector));
tp->type = PJSIP_TPSELECTOR_TRANSPORT;
tp->u.transport = transport;
return tp;
}
pjsip_transport *SipTransport::createStunTransport(pj_str_t serverName, pj_uint16_t port)
{
pjsip_transport *transport;
DEBUG("SipTransport: Create stun transport server name: %s, port: %d", serverName, port);// account->getStunPort());
if (createStunResolver(serverName, port) != PJ_SUCCESS) {
ERROR("SipTransport: Can't resolve STUN server");
Manager::instance().getDbusManager()->getConfigurationManager()->stunStatusFailure("");
return NULL;
}
pj_sock_t sock = PJ_INVALID_SOCKET;
pj_sockaddr_in boundAddr;
if (pj_sockaddr_in_init(&boundAddr, &serverName, 0) != PJ_SUCCESS) {
ERROR("SipTransport: Can't initialize IPv4 socket on %*s:%i", serverName.slen, serverName.ptr, port);
Manager::instance().getDbusManager()->getConfigurationManager()->stunStatusFailure("");
return NULL;
}
if (pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock) != PJ_SUCCESS) {
ERROR("SipTransport: Can't create or bind socket");
Manager::instance().getDbusManager()->getConfigurationManager()->stunStatusFailure("");
return NULL;
}
// Query the mapped IP address and port on the 'outside' of the NAT
pj_sockaddr_in pub_addr;
if (pjstun_get_mapped_addr(&cp_->factory, 1, &sock, &serverName, port, &serverName, port, &pub_addr) != PJ_SUCCESS) {
ERROR("SipTransport: Can't contact STUN server");
pj_sock_close(sock);
Manager::instance().getDbusManager()->getConfigurationManager()->stunStatusFailure("");
return NULL;
}
pjsip_host_port a_name = {
pj_str(pj_inet_ntoa(pub_addr.sin_addr)),
pj_ntohs(pub_addr.sin_port)
};
pjsip_udp_transport_attach2(endpt_, PJSIP_TRANSPORT_UDP, sock, &a_name, 1,
&transport);
pjsip_tpmgr_dump_transports(pjsip_endpt_get_tpmgr(endpt_));
return transport;
}
void SipTransport::shutdownSipTransport(SIPAccount *account)
{
if (account->isStunEnabled()) {
pj_str_t stunServerName = account->getStunServerName();
std::string server(stunServerName.ptr, stunServerName.slen);
destroyStunResolver(server);
}
if (account->transport_) {
pjsip_transport_dec_ref(account->transport_);
account->transport_ = NULL;
}
}
void SipTransport::findLocalAddressFromTransport(pjsip_transport *transport, pjsip_transport_type_e transportType, std::string &addr, std::string &port) const
{
// Initialize the sip port with the default SIP port
std::stringstream ss;
ss << DEFAULT_SIP_PORT;
port = ss.str();
// Initialize the sip address with the hostname
const pj_str_t *pjMachineName = pj_gethostname();
addr = std::string(pjMachineName->ptr, pjMachineName->slen);
// Update address and port with active transport
if (!transport) {
ERROR("SipTransport: Transport is NULL in findLocalAddress, using local address %s:%s", addr.c_str(), port.c_str());
return;
}
// get the transport manager associated with the SIP enpoint
pjsip_tpmgr *tpmgr = pjsip_endpt_get_tpmgr(endpt_);
if (!tpmgr) {
ERROR("SipTransport: Transport manager is NULL in findLocalAddress, using local address %s:%s", addr.c_str(), port.c_str());
return;
}
// initialize a transport selector
// TODO Need to determine why we exclude TLS here...
// if (transportType == PJSIP_TRANSPORT_UDP and transport_)
pjsip_tpselector *tp_sel = initTransportSelector(transport, pool_);
if (!tp_sel) {
ERROR("SipTransport: Could not initialize transport selector, using local address %s:%s", addr.c_str(), port.c_str());
return;
}
pj_str_t localAddress = {0,0};
int i_port = 0;
// Find the local address and port for this transport
if (pjsip_tpmgr_find_local_addr(tpmgr, pool_, transportType, tp_sel, &localAddress, &i_port) != PJ_SUCCESS) {
WARN("SipTransport: Could not retrieve local address and port from transport, using %s:%s", addr.c_str(), port.c_str());
return;
}
// Update local address based on the transport type
addr = std::string(localAddress.ptr, localAddress.slen);
// Fallback on local ip provided by pj_gethostip()
if (addr == "0.0.0.0")
addr = getSIPLocalIP();
// Determine the local port based on transport information
ss.str("");
ss << i_port;
port = ss.str();
}
/*
* Copyright (C) [2004, 2012] Savoir-Faire Linux Inc.
*
* Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Additional permission under GNU GPL version 3 section 7:
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#ifndef SIPTRANSPORT_H_
#define SIPTRANSPORT_H_
#include <string>
#include <pjsip.h>
#include <pjlib.h>
#include <pjsip_ua.h>
#include <pjlib-util.h>
#include <pjnath.h>
#include <pjnath/stun_config.h>
#include "sipaccount.h"
class SipTransport {
public:
SipTransport(pjsip_endpoint *endpt, pj_caching_pool *cp, pj_pool_t *pool);
~SipTransport();
static std::string getSIPLocalIP(void);
/**
* List all the interfaces on the system and return
* a vector list containing their name (eth0, eth0:1 ...).
* @param void
* @return std::vector<std::string> A std::string vector
* of interface name available on all of the interfaces on
* the system.
*/
static std::vector<std::string> getAllIpInterfaceByName(void);
/**
* List all the interfaces on the system and return
* a vector list containing their name (eth0, eth0:1 ...).
* @param void
* @return std::vector<std::string> A std::string vector
* of interface name available on all of the interfaces on
* the system.
*/
static std::string getInterfaceAddrFromName(const std::string &ifaceName);
/**
* List all the interfaces on the system and return
* a vector list containing their IPV4 address.
* @param void
* @return std::vector<std::string> A std::string vector
* of IPV4 address available on all of the interfaces on
* the system.
*/
static std::vector<std::string> getAllIpInterface();
void setEndpoint(pjsip_endpoint *endpt) {
endpt_ = endpt;
}
void setCachingPool(pj_caching_pool *cp) {
cp_ = cp;
}
void setPool(pj_pool_t *pool) {
pool_ = pool;
}
/**
* Create a new stun resolver. Store it inside the array. Resolve public address for this
* server name.
* @param serverName The name of the stun server
* @param port number
*/
pj_status_t createStunResolver(pj_str_t serverName, pj_uint16_t port);
pj_status_t destroyStunResolver(const std::string serverName);
/**
* General Sip transport creation method according to the
* transport type specified in account settings
* @param account The account for which a transport must be created.
*/
void createSipTransport(SIPAccount *account);
void createDefaultSipUdpTransport(void);
/**
* Create SIP UDP transport from account's setting
* @param account The account for which a transport must be created.
*/
pjsip_transport *createUdpTransport(std::string interface, unsigned int port);
/**
* Initialize the transport selector
* @param transport A transport associated with an account
*
* @return A pointer to the transport selector structure
*/
pjsip_tpselector *initTransportSelector(pjsip_transport *transport, pj_pool_t *tp_pool) const;
/**
* Create The default TLS listener which is global to the application. This means that
* only one TLS connection can be established for the momment.
* @param the port number to create the TCP socket
* @param pjsip's tls settings for the transport to be created which contains:
* - path to ca certificate list file
* - path to certertificate file
* - path to private key file
* - the password for the file
* - the TLS method
* @param a pointer to store the listener created, in our case this is a static pointer
*/
void createTlsListener(pj_uint16_t, pjsip_tls_setting *, pjsip_tpfactory **);
/**
* Create a connection oriented TLS transport and register to the specified remote address.
* First, initialize the TLS listener sole instance. This means that, for the momment, only one TLS transport
* is allowed to be created in the application. Any subsequent account attempting to
* register a new using this transport even if new settings are specified.
* @param the remote address for this transport to be connected
* @param the local port to initialize the TCP socket
* @param pjsip's tls transport parameters
*/
pjsip_transport *
createTlsTransport(const std::string &remoteAddr,
pj_uint16_t tlsListenerPort,
pjsip_tls_setting *tlsSetting);
/**
* Create a UDP transport using stun server to resove public address
* @param account The account for which a transport must be created.
*/
pjsip_transport *createStunTransport(pj_str_t serverName, pj_uint16_t port);
/**
* This function unset the transport for a given account.
*/
void shutdownSipTransport(SIPAccount *account);
/**
* Get the correct address to use (ie advertised) from
* a uri. The corresponding transport that should be used
* with that uri will be discovered.
*
* @param uri The uri from which we want to discover the address to use
* @param transport The transport to use to discover the address
*/
void findLocalAddressFromTransport(pjsip_transport *transport, pjsip_transport_type_e transportType, std::string &address, std::string &port) const;
private:
/**
* UDP Transports are stored in this map in order to retreive them in case
* several accounts would share the same port number.
*/
std::map<pj_uint16_t, pjsip_transport*> transportMap_;
/**
* Stun resolver array
*/
std::map<std::string, pj_stun_sock *> stunSocketMap_;
pj_caching_pool *cp_;
pj_pool_t *pool_;
pjsip_endpoint *endpt_;
};
#endif // SIPTRANSPORT_H_
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment