Commit e3a19304 authored by Tristan Matthews's avatar Tristan Matthews

Merge branch 'fix_account_crasher' into account_id_in_treestore

parents 37292c9b 3ee12fe3
......@@ -37,6 +37,7 @@
#include "config.h"
#include "../manager.h"
#include "sip/sipvoiplink.h"
#include "sip/siptransport.h"
#include "account.h"
#include "sip/sipaccount.h"
......@@ -411,17 +412,17 @@ std::vector<std::map<std::string, std::string> > ConfigurationManager::getHistor
std::string
ConfigurationManager::getAddrFromInterfaceName(const std::string& interface)
{
return SIPVoIPLink::getInterfaceAddrFromName(interface);
return SipTransport::getInterfaceAddrFromName(interface);
}
std::vector<std::string> ConfigurationManager::getAllIpInterface()
{
return SIPVoIPLink::getAllIpInterface();
return SipTransport::getAllIpInterface();
}
std::vector<std::string> ConfigurationManager::getAllIpInterfaceByName()
{
return SIPVoIPLink::getAllIpInterfaceByName();
return SipTransport::getAllIpInterfaceByName();
}
std::map<std::string, std::string> ConfigurationManager::getShortcuts()
......
......@@ -2563,7 +2563,7 @@ void ManagerImpl::loadDefaultAccountMap()
{
// build a default IP2IP account with default parameters
accountMap_[SIPAccount::IP2IP_PROFILE] = new SIPAccount(SIPAccount::IP2IP_PROFILE);
SIPVoIPLink::instance()->createDefaultSipUdpTransport();
SIPVoIPLink::instance()->sipTransport.createDefaultSipUdpTransport();
accountMap_[SIPAccount::IP2IP_PROFILE]->registerVoIPLink();
}
......@@ -2627,7 +2627,7 @@ void ManagerImpl::loadAccountMap(Conf::YamlParser &parser)
// Initialize default UDP transport according to
// IP to IP settings (most likely using port 5060)
SIPVoIPLink::instance()->createDefaultSipUdpTransport();
SIPVoIPLink::instance()->sipTransport.createDefaultSipUdpTransport();
// Force IP2IP settings to be loaded to be loaded
// No registration in the sense of the REGISTER method is performed.
......
......@@ -9,12 +9,14 @@ libsiplink_la_SOURCES = \
sipaccount.cpp \
sipcall.cpp \
sipvoiplink.cpp \
siptransport.cpp \
pattern.h \
sdes_negotiator.h \
sdp.h \
sipaccount.h \
sipcall.h \
sipvoiplink.h \
siptransport.h \
sip_utils.cpp \
sip_utils.h
......
......@@ -405,6 +405,11 @@ void SIPAccount::setAccountDetails(std::map<std::string, std::string> details)
publishedIpAddress_ = details[CONFIG_PUBLISHED_ADDRESS];
localPort_ = atoi(details[CONFIG_LOCAL_PORT].c_str());
publishedPort_ = atoi(details[CONFIG_PUBLISHED_PORT].c_str());
if(stunServer_ != details[CONFIG_STUN_SERVER]) {
DEBUG("Stun server changed!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
link_->sipTransport.destroyStunResolver(stunServer_);
// pj_stun_sock_destroy(pj_stun_sock *stun_sock);
}
stunServer_ = details[CONFIG_STUN_SERVER];
stunEnabled_ = details[CONFIG_STUN_ENABLE] == "true";
dtmfType_ = details[CONFIG_ACCOUNT_DTMF_TYPE];
......@@ -811,7 +816,7 @@ std::string SIPAccount::getContactHeader() const
// Else we determine this infor based on transport information
std::string address, port;
link_->findLocalAddressFromTransport(transport_, transportType, address, port);
link_->sipTransport.findLocalAddressFromTransport(transport_, transportType, address, port);
// UDP does not require the transport specification
std::string scheme;
......@@ -838,9 +843,9 @@ void SIPAccount::keepAliveRegistrationCb(UNUSED pj_timer_heap_t *th, pj_timer_en
// IP2IP default does not require keep-alive
if (sipAccount->isIP2IP())
return;
return;
// TLS is connection oriented and does not require keep-alive
// TLS is connection oriented and does not require keep-alive
if (sipAccount->isTlsEnabled())
return;
......
/*
* 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;
}