mirror of https://github.com/sipwise/sems.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
252 lines
6.4 KiB
252 lines
6.4 KiB
/*
|
|
* Copyright (C) 2002-2003 Fhg Fokus
|
|
*
|
|
* This file is part of SEMS, a free SIP media server.
|
|
*
|
|
* SEMS 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 2 of the License, or
|
|
* (at your option) any later version. This program is released under
|
|
* the GPL with the additional exemption that compiling, linking,
|
|
* and/or using OpenSSL is allowed.
|
|
*
|
|
* For a license to use the SEMS software under conditions
|
|
* other than those described here, or to purchase support for this
|
|
* software, please contact iptel.org by e-mail at the following addresses:
|
|
* info@iptel.org
|
|
*
|
|
* SEMS 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "AmRtpSocket.h"
|
|
#include "AmRtpReceiver.h"
|
|
#include "AmConfig.h"
|
|
#include "sip/resolver.h"
|
|
#include "sip/ip_util.h"
|
|
#include "sip/msg_logger.h"
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <stdexcept>
|
|
|
|
using std::runtime_error;
|
|
|
|
class AmRtpSocketPair;
|
|
|
|
/*
|
|
* AmRtpSocket
|
|
* Manages Network socket
|
|
* Receives the traffic from network
|
|
* Sends traffic to network
|
|
*/
|
|
|
|
//TODO: Add a Type (SOCK_DGRAM, SOCK_STREAM) parameter for choosing UDP, TCP
|
|
AmRtpSocket::AmRtpSocket(AmRtpSocketPair* listener, int interface,
|
|
const string& ip, unsigned int port=0)
|
|
: logger(NULL),
|
|
listener(listener),
|
|
l_if(interface),
|
|
sys_if(AmConfig::RTP_Ifs[l_if].NetIfIdx),
|
|
l_sd(0),
|
|
ev_base(NULL),ev_read(NULL),
|
|
is_udp(false)
|
|
{
|
|
|
|
/* Set local and remote sockets to Zeroes */
|
|
memset(&r_saddr,0,sizeof(struct sockaddr_storage));
|
|
memset(&l_saddr,0,sizeof(struct sockaddr_storage));
|
|
|
|
/* Initialise the local socket */
|
|
|
|
// Set IP address
|
|
if (!am_inet_pton(ip.c_str(), &l_saddr)) {
|
|
throw runtime_error("Invalid IP address: " + ip);
|
|
}
|
|
DBG("set local ip = %s\n",ip.c_str());
|
|
|
|
// Bind port
|
|
int retry = 10;
|
|
for(;retry; --retry) {
|
|
|
|
if (port) {
|
|
|
|
// Set Local Socket
|
|
if((l_sd = socket(l_saddr.ss_family,SOCK_DGRAM,0)) == -1) {
|
|
ERROR("%s\n",strerror(errno));
|
|
close(l_sd);
|
|
throw runtime_error("while creating new socket.");
|
|
}
|
|
|
|
int true_opt = 1;
|
|
if(ioctl(l_sd, FIONBIO , &true_opt) == -1){
|
|
ERROR("%s\n",strerror(errno));
|
|
close(l_sd);
|
|
throw runtime_error("while setting socket non blocking.");
|
|
}
|
|
|
|
am_set_port(&l_saddr,port);
|
|
if (bind(l_sd,(const struct sockaddr*)&l_saddr,SA_len(&l_saddr))) {
|
|
DBG("bind: %s\n",strerror(errno));
|
|
port = 0;
|
|
close(l_sd);
|
|
l_sd = 0;
|
|
if (retry == 10)
|
|
throw runtime_error("unable to bind on the specified port");
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
else {
|
|
port = AmConfig::RTP_Ifs[l_if].getNextRtpPort();
|
|
if(!port) throw runtime_error("disabled RTP interface");
|
|
}
|
|
}
|
|
|
|
if (!retry){
|
|
ERROR("could not find a free RTP port\n");
|
|
throw runtime_error("could not find a free RTP port");
|
|
}
|
|
|
|
// rco: does that make sense after bind() ????
|
|
int true_opt = 1;
|
|
if(setsockopt(l_sd, SOL_SOCKET, SO_REUSEADDR,
|
|
(void*)&true_opt, sizeof (true_opt)) == -1) {
|
|
|
|
ERROR("%s\n",strerror(errno));
|
|
close(l_sd);
|
|
l_sd = 0;
|
|
throw runtime_error("while setting local address reusable.");
|
|
}
|
|
|
|
ev_base = AmRtpReceiver::instance()->getBase(l_sd);
|
|
|
|
addToReceiver();
|
|
}
|
|
|
|
AmRtpSocket::~AmRtpSocket()
|
|
{
|
|
if(l_sd) {
|
|
close(l_sd);
|
|
}
|
|
|
|
// event_free does remove the Socket
|
|
// from the event loop as well.
|
|
if(ev_read)
|
|
event_free(ev_read);
|
|
}
|
|
|
|
void AmRtpSocket::read_cb(evutil_socket_t sd, short what, void* arg)
|
|
{
|
|
AmRtpSocket* socket = static_cast<AmRtpSocket*>(arg);
|
|
|
|
socket->recv();
|
|
}
|
|
|
|
|
|
bool AmRtpSocket::isAttachedToReceiver()
|
|
{
|
|
return (ev_read && event_pending(ev_read,EV_READ,NULL));
|
|
}
|
|
|
|
void AmRtpSocket::addToReceiver()
|
|
{
|
|
assert(ev_base && l_sd);
|
|
|
|
if(!ev_read)
|
|
ev_read = event_new(ev_base,l_sd,EV_READ|EV_PERSIST, read_cb, this);
|
|
|
|
event_add(ev_read,NULL);
|
|
|
|
DBG("added RTP Socket [%p] to RTP receiver (%s:%i)\n", this,
|
|
getLocalAddress().c_str(), getLocalPort());
|
|
}
|
|
|
|
void AmRtpSocket::removeFromReceiver()
|
|
{
|
|
if(ev_read) {
|
|
event_del(ev_read);
|
|
|
|
DBG("removed RTP Socket [%p] from RTP receiver (%s:%i)\n", this,
|
|
getLocalAddress().c_str(), getLocalPort());
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Receive data from network
|
|
*/
|
|
void AmRtpSocket::recv()
|
|
{
|
|
sockaddr_storage addr;
|
|
socklen_t addr_len = sizeof(addr);
|
|
unsigned char buffer[4096];
|
|
ssize_t len = recvfrom(l_sd,buffer,sizeof(buffer),0,(struct sockaddr*)&addr,
|
|
&addr_len);
|
|
|
|
if (!len) {
|
|
ERROR("Received empty packet");
|
|
return;
|
|
} else if (len < 0) {
|
|
if((errno != EINTR) && (errno != EAGAIN)) {
|
|
ERROR("recv(%d): %s",l_sd,strerror(errno));
|
|
}
|
|
return;
|
|
} else if ((size_t)len > sizeof(buffer)) {
|
|
ERROR("Received huge RTCP packet (%zd)",len);
|
|
return;
|
|
}
|
|
|
|
recvData(buffer, len, addr);
|
|
}
|
|
|
|
void AmRtpSocket::setRemoteAddress(const string& addr, unsigned short port)
|
|
{
|
|
|
|
DBG("Setting remote address to: %s:%u [%p]", addr.c_str(), port, this);
|
|
|
|
struct sockaddr_storage ss;
|
|
memset (&ss, 0, sizeof (ss));
|
|
|
|
/* inet_aton only supports dot-notation IP address strings... but an RFC
|
|
* 4566 unicast-address, as found in c=, can be an FQDN (or other!).
|
|
*/
|
|
dns_handle dh;
|
|
if (resolver::instance()->resolve_name(addr.c_str(),&dh,&ss,IPv4) < 0) {
|
|
WARN("Address not valid (host: %s).\n", addr.c_str());
|
|
throw runtime_error("invalid address" + addr);
|
|
}
|
|
|
|
memcpy(&r_saddr,&ss,sizeof(struct sockaddr_storage));
|
|
|
|
if (port)
|
|
am_set_port(&r_saddr, port);
|
|
}
|
|
|
|
void AmRtpSocket::setLogger(const shared_ptr<msg_logger>& _logger)
|
|
{
|
|
// Set logger
|
|
logger = _logger;
|
|
}
|
|
|
|
|
|
void AmRtpSocket::logReceived(const unsigned char* buffer, size_t size, sockaddr_storage *remote)
|
|
{
|
|
static const cstring empty;
|
|
logger->log((const char *)buffer, size, remote, &l_saddr, empty);
|
|
}
|
|
|
|
|
|
void AmRtpSocket::logSent(const unsigned char* buffer, size_t size, sockaddr_storage *remote)
|
|
{
|
|
static const cstring empty;
|
|
logger->log((const char *)buffer, size, &l_saddr, remote, empty);
|
|
}
|
|
|
|
|