lcr/sip.cpp

3158 lines
92 KiB
C++

/*****************************************************************************\
** **
** Linux Call Router **
** **
**---------------------------------------------------------------------------**
** Copyright: Andreas Eversberg **
** **
** SIP port **
** **
\*****************************************************************************/
#include "main.h"
#include <sofia-sip/sip_status.h>
#include <sofia-sip/su_log.h>
#include <sofia-sip/sdp.h>
#include <sofia-sip/sip_header.h>
#include <sofia-sip/stun.h>
#include <sofia-sip/stun_tag.h>
#include <sofia-sip/su_md5.h>
#ifndef SOFIA_SIP_GCC_4_8_PATCH_APLLIED
#warning ********************************************************
#warning Please apply the sofia-sip-gcc-4.8.patch !
#warning If this issue is already fixed, just remove this check.
#warning ********************************************************
#error
#endif
#undef NUTAG_AUTO100
unsigned char flip[256];
int any_sip_interface = 0;
//pthread_mutex_t mutex_msg;
su_home_t sip_home[1];
#define REGISTER_STATE_UNREGISTERED 1
#define REGISTER_STATE_REGISTERING 2
#define REGISTER_STATE_REGISTERED 3
#define REGISTER_STATE_FAILED 4
#define STUN_RETRY_TIMER 10, 0
#define REGISTER_RETRY_TIMER 10, 0
#define STUN_STATE_UNRESOLVED 1
#define STUN_STATE_RESOLVING 2
#define STUN_STATE_RESOLVED 3
#define STUN_STATE_FAILED 4
#define RTP_PORT_BASE 30000
#define RTP_PORT_MAX 39998
struct sip_inst {
char interface_name[64];
char local_peer[128];
char remote_peer[128];
char asserted_id[128];
int allow_register;
int register_state;
char register_user[128];
char register_host[128];
nua_handle_t *register_handle;
struct lcr_timer register_retry_timer;
struct lcr_timer register_option_timer;
int register_interval;
int options_interval;
char auth_user[128];
char auth_password[128];
char auth_realm[128];
char auth_nonce[128];
su_root_t *root;
nua_t *nua;
char public_ip[128];
int stun_state;
char stun_server[128];
stun_handle_t *stun_handle;
su_socket_t stun_socket;
struct lcr_timer stun_retry_timer;
int stun_interval;
unsigned short rtp_port_from;
unsigned short rtp_port_to;
unsigned short next_rtp_port;
};
static int delete_event(struct lcr_work *work, void *instance, int index);
static int invite_option_timer(struct lcr_timer *timer, void *instance, int index);
static int load_timer(struct lcr_timer *timer, void *instance, int index);
/*
* initialize SIP port
*/
Psip::Psip(int type, char *portname, struct port_settings *settings, struct interface *interface) : Port(type, portname, settings, interface)
{
p_s_rtp_bridge = 0;
if (interface->rtp_bridge)
p_s_rtp_bridge = 1;
p_s_sip_inst = interface->sip_inst;
memset(&p_s_delete, 0, sizeof(p_s_delete));
add_work(&p_s_delete, delete_event, this, 0);
p_s_handle = 0;
p_s_magic = 0;
memset(&p_s_rtp_fd, 0, sizeof(p_s_rtp_fd));
memset(&p_s_rtcp_fd, 0, sizeof(p_s_rtcp_fd));
memset(&p_s_rtp_sin_local, 0, sizeof(p_s_rtp_sin_local));
memset(&p_s_rtcp_sin_local, 0, sizeof(p_s_rtcp_sin_local));
memset(&p_s_rtp_sin_remote, 0, sizeof(p_s_rtp_sin_remote));
memset(&p_s_rtcp_sin_remote, 0, sizeof(p_s_rtcp_sin_remote));
p_s_rtp_ip_local = 0;
p_s_rtp_ip_remote = 0;
p_s_rtp_port_local = 0;
p_s_rtp_port_remote = 0;
p_s_b_sock = -1;
p_s_b_index = -1;
p_s_b_active = 0;
p_s_rxpos = 0;
p_s_rtp_tx_action = 0;
p_s_rtp_is_connected = 0;
/* create option timer */
memset(&p_s_invite_option_timer, 0, sizeof(p_s_invite_option_timer));
add_timer(&p_s_invite_option_timer, invite_option_timer, this, 0);
p_s_invite_direction = 0;
/* audio */
memset(&p_s_load_timer, 0, sizeof(p_s_load_timer));
add_timer(&p_s_load_timer, load_timer, this, 0);
p_s_next_tv_sec = 0;
PDEBUG(DEBUG_SIP, "Created new Psip(%s).\n", portname);
if (!p_s_sip_inst)
FATAL("No SIP instance for interface\n");
}
/*
* destructor
*/
Psip::~Psip()
{
PDEBUG(DEBUG_SIP, "Destroyed SIP process(%s).\n", p_name);
del_timer(&p_s_invite_option_timer);
del_timer(&p_s_load_timer);
del_work(&p_s_delete);
rtp_close();
}
static const char *media_type2name(uint8_t media_type) {
switch (media_type) {
case MEDIA_TYPE_ULAW:
return "PCMU";
case MEDIA_TYPE_ALAW:
return "PCMA";
case MEDIA_TYPE_GSM:
return "GSM";
case MEDIA_TYPE_GSM_HR:
return "GSM-HR";
case MEDIA_TYPE_GSM_EFR:
return "GSM-EFR";
case MEDIA_TYPE_AMR:
return "AMR";
}
return "UKN";
}
static void sip_trace_header(class Psip *sip, const char *interface_name, const char *message, int direction)
{
struct interface *interface = NULL;
if (interface_name)
interface = getinterfacebyname(interface_name);
/* init trace with given values */
start_trace(-1,
interface,
sip?numberrize_callerinfo(sip->p_callerinfo.id, sip->p_callerinfo.ntype, options.national, options.international):NULL,
sip?sip->p_dialinginfo.id:NULL,
direction,
CATEGORY_CH,
sip?sip->p_serial:0,
message);
}
/*
* RTP
*/
/* according to RFC 3550 */
struct rtp_hdr {
#if __BYTE_ORDER == __LITTLE_ENDIAN
uint8_t csrc_count:4,
extension:1,
padding:1,
version:2;
uint8_t payload_type:7,
marker:1;
#elif __BYTE_ORDER == __BIG_ENDIAN
uint8_t version:2,
padding:1,
extension:1,
csrc_count:4;
uint8_t marker:1,
payload_type:7;
#endif
uint16_t sequence;
uint32_t timestamp;
uint32_t ssrc;
} __attribute__((packed));
struct rtp_x_hdr {
uint16_t by_profile;
uint16_t length;
} __attribute__((packed));
#define RTP_VERSION 2
#define PAYLOAD_TYPE_ULAW 0
#define PAYLOAD_TYPE_ALAW 8
#define PAYLOAD_TYPE_GSM 3
/* decode an rtp frame */
static int rtp_decode(class Psip *psip, unsigned char *data, int len)
{
struct rtp_hdr *rtph = (struct rtp_hdr *)data;
struct rtp_x_hdr *rtpxh;
uint8_t *payload;
int payload_len;
int x_len;
unsigned char *from, *to;
int n;
if (len < 12) {
PDEBUG(DEBUG_SIP, "received RTP frame too short (len = %d)\n", len);
return -EINVAL;
}
if (rtph->version != RTP_VERSION) {
PDEBUG(DEBUG_SIP, "received RTP version %d not supported.\n", rtph->version);
return -EINVAL;
}
payload = data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2);
payload_len = len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2);
if (payload_len < 0) {
PDEBUG(DEBUG_SIP, "received RTP frame too short (len = %d, "
"csrc count = %d)\n", len, rtph->csrc_count);
return -EINVAL;
}
if (rtph->extension) {
if (payload_len < (int)sizeof(struct rtp_x_hdr)) {
PDEBUG(DEBUG_SIP, "received RTP frame too short for "
"extension header\n");
return -EINVAL;
}
rtpxh = (struct rtp_x_hdr *)payload;
x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr);
payload += x_len;
payload_len -= x_len;
if (payload_len < 0) {
PDEBUG(DEBUG_SIP, "received RTP frame too short, "
"extension header exceeds frame length\n");
return -EINVAL;
}
}
if (rtph->padding) {
if (payload_len < 0) {
PDEBUG(DEBUG_SIP, "received RTP frame too short for "
"padding length\n");
return -EINVAL;
}
payload_len -= payload[payload_len - 1];
if (payload_len < 0) {
PDEBUG(DEBUG_SIP, "received RTP frame with padding "
"greater than payload\n");
return -EINVAL;
}
}
switch (rtph->payload_type) {
#if 0
we only support alaw and ulaw!
case RTP_PT_GSM_FULL:
if (payload_len != 33) {
PDEBUG(DEBUG_SIP, "received RTP full rate frame with "
"payload length != 33 (len = %d)\n",
payload_len);
return -EINVAL;
}
break;
case RTP_PT_GSM_EFR:
if (payload_len != 31) {
PDEBUG(DEBUG_SIP, "received RTP full rate frame with "
"payload length != 31 (len = %d)\n",
payload_len);
return -EINVAL;
}
break;
case RTP_PT_GSM_HALF:
if (payload_len != 14) {
PDEBUG(DEBUG_SIP, "received RTP half rate frame with "
"payload length != 14 (len = %d)\n",
payload_len);
return -EINVAL;
}
break;
#endif
case PAYLOAD_TYPE_ALAW:
if (options.law != 'a') {
PDEBUG(DEBUG_SIP, "received Alaw, but we don't do Alaw\n");
return -EINVAL;
}
break;
case PAYLOAD_TYPE_ULAW:
if (options.law == 'a') {
PDEBUG(DEBUG_SIP, "received Ulaw, but we don't do Ulaw\n");
return -EINVAL;
}
break;
default:
PDEBUG(DEBUG_SIP, "received RTP frame with unknown payload "
"type %d\n", rtph->payload_type);
return -EINVAL;
}
if (payload_len <= 0) {
PDEBUG(DEBUG_SIP, "received RTP payload is too small: %d\n", payload_len);
return 0;
}
/* record audio */
if (psip->p_record)
psip->record(payload, payload_len, 0); // from down
if (psip->p_tap)
psip->tap(payload, payload_len, 0); // from down
n = payload_len;
from = payload;
to = payload;
if (psip->p_echotest) {
/* echo rtp data we just received */
psip->rtp_send_frame(from, n, (options.law=='a')?PAYLOAD_TYPE_ALAW:PAYLOAD_TYPE_ULAW);
return 0;
}
while(n--)
*to++ = flip[*from++];
if (psip->p_dov_rx)
psip->dov_rx(payload, payload_len);
psip->bridge_tx(payload, payload_len);
return 0;
}
static int rtp_sock_callback(struct lcr_fd *fd, unsigned int what, void *instance, int index)
{
class Psip *psip = (class Psip *) instance;
int len;
unsigned char buffer[256];
int rc = 0;
if ((what & LCR_FD_READ)) {
len = read(fd->fd, &buffer, sizeof(buffer));
if (len <= 0) {
PDEBUG(DEBUG_SIP, "read result=%d\n", len);
// psip->rtp_close();
// psip->rtp_shutdown();
return len;
}
if (psip->p_s_rtp_is_connected)
rc = rtp_decode(psip, buffer, len);
}
return rc;
}
static int rtcp_sock_callback(struct lcr_fd *fd, unsigned int what, void *instance, int index)
{
// class Psip *psip = (class Psip *) instance;
int len;
unsigned char buffer[256];
if ((what & LCR_FD_READ)) {
len = read(fd->fd, &buffer, sizeof(buffer));
if (len <= 0) {
PDEBUG(DEBUG_SIP, "read result=%d\n", len);
// psip->rtp_close();
// psip->rtp_shutdown();
return len;
}
PDEBUG(DEBUG_SIP, "rtcp!\n");
}
return 0;
}
static int rtp_sub_socket_bind(int fd, struct sockaddr_in *sin_local, uint32_t ip, uint16_t port)
{
int rc;
socklen_t alen = sizeof(*sin_local);
sin_local->sin_family = AF_INET;
sin_local->sin_addr.s_addr = htonl(ip);
sin_local->sin_port = htons(port);
rc = bind(fd, (struct sockaddr *) sin_local, sizeof(*sin_local));
if (rc < 0)
return rc;
/* retrieve the address we actually bound to, in case we
* passed INADDR_ANY as IP address */
return getsockname(fd, (struct sockaddr *) sin_local, &alen);
}
static int rtp_sub_socket_connect(int fd, struct sockaddr_in *sin_local, struct sockaddr_in *sin_remote, uint32_t ip, uint16_t port)
{
int rc;
socklen_t alen = sizeof(*sin_local);
sin_remote->sin_family = AF_INET;
sin_remote->sin_addr.s_addr = htonl(ip);
sin_remote->sin_port = htons(port);
rc = connect(fd, (struct sockaddr *) sin_remote, sizeof(*sin_remote));
if (rc < 0) {
PERROR("failed to connect to ip %08x port %d rc=%d\n", ip, port, rc);
return rc;
}
return getsockname(fd, (struct sockaddr *) sin_local, &alen);
}
int Psip::rtp_open(void)
{
struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
int rc, rc2;
// struct in_addr ia;
unsigned int ip;
unsigned short start_port;
PDEBUG(DEBUG_SIP, "rtp_open\n");
/* create socket */
rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (rc < 0) {
rtp_close();
return -EIO;
}
p_s_rtp_fd.fd = rc;
register_fd(&p_s_rtp_fd, LCR_FD_READ, rtp_sock_callback, this, 0);
rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (rc < 0) {
rtp_close();
return -EIO;
}
p_s_rtcp_fd.fd = rc;
register_fd(&p_s_rtcp_fd, LCR_FD_READ, rtcp_sock_callback, this, 0);
/* bind socket */
ip = htonl(INADDR_ANY);
start_port = inst->next_rtp_port;
while (1) {
rc = rtp_sub_socket_bind(p_s_rtp_fd.fd, &p_s_rtp_sin_local, ip, inst->next_rtp_port);
if (rc != 0)
goto try_next_port;
rc = rtp_sub_socket_bind(p_s_rtcp_fd.fd, &p_s_rtcp_sin_local, ip, inst->next_rtp_port + 1);
if (rc == 0) {
p_s_rtp_port_local = inst->next_rtp_port;
inst->next_rtp_port = (inst->next_rtp_port + 2 > inst->rtp_port_to) ? inst->rtp_port_from : inst->next_rtp_port + 2;
break;
}
/* reopen rtp socket and try again with next udp port */
unregister_fd(&p_s_rtp_fd);
close(p_s_rtp_fd.fd);
p_s_rtp_fd.fd = 0;
rc2 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (rc2 < 0) {
rtp_close();
return -EIO;
}
p_s_rtp_fd.fd = rc2;
register_fd(&p_s_rtp_fd, LCR_FD_READ, rtp_sock_callback, this, 0);
try_next_port:
inst->next_rtp_port = (inst->next_rtp_port + 2 > inst->rtp_port_to) ? inst->rtp_port_from : inst->next_rtp_port + 2;
if (inst->next_rtp_port == start_port)
break;
/* we must use rc2, in order to preserve rc */
}
if (rc < 0) {
PDEBUG(DEBUG_SIP, "failed to find port\n");
rtp_close();
return rc;
}
p_s_rtp_ip_local = ntohl(p_s_rtp_sin_local.sin_addr.s_addr);
PDEBUG(DEBUG_SIP, "local ip %08x port %d\n", p_s_rtp_ip_local, p_s_rtp_port_local);
PDEBUG(DEBUG_SIP, "remote ip %08x port %d\n", p_s_rtp_ip_remote, p_s_rtp_port_remote);
return p_s_rtp_port_local;
}
int Psip::rtp_connect(void)
{
int rc;
struct in_addr ia;
ia.s_addr = htonl(p_s_rtp_ip_remote);
if (p_s_rtp_is_connected)
PDEBUG(DEBUG_SIP, "reconnecting existing RTP connection to new/same destination\n");
PDEBUG(DEBUG_SIP, "rtp_connect(ip=%s, port=%u)\n", inet_ntoa(ia), p_s_rtp_port_remote);
rc = rtp_sub_socket_connect(p_s_rtp_fd.fd, &p_s_rtp_sin_local, &p_s_rtp_sin_remote, p_s_rtp_ip_remote, p_s_rtp_port_remote);
if (rc < 0)
return rc;
rc = rtp_sub_socket_connect(p_s_rtcp_fd.fd, &p_s_rtcp_sin_local, &p_s_rtcp_sin_remote, p_s_rtp_ip_remote, p_s_rtp_port_remote + 1);
if (rc < 0)
return rc;
p_s_rtp_ip_local = ntohl(p_s_rtp_sin_local.sin_addr.s_addr);
PDEBUG(DEBUG_SIP, "local ip %08x port %d\n", p_s_rtp_ip_local, p_s_rtp_port_local);
PDEBUG(DEBUG_SIP, "remote ip %08x port %d\n", p_s_rtp_ip_remote, p_s_rtp_port_remote);
p_s_rtp_is_connected = 1;
return 0;
}
void Psip::rtp_close(void)
{
if (p_s_rtp_fd.fd > 0) {
unregister_fd(&p_s_rtp_fd);
close(p_s_rtp_fd.fd);
p_s_rtp_fd.fd = 0;
}
if (p_s_rtcp_fd.fd > 0) {
unregister_fd(&p_s_rtcp_fd);
close(p_s_rtcp_fd.fd);
p_s_rtcp_fd.fd = 0;
}
if (p_s_rtp_is_connected) {
PDEBUG(DEBUG_SIP, "rtp closed\n");
p_s_rtp_is_connected = 0;
}
}
/* "to - from" */
void tv_difference(struct timeval *diff, const struct timeval *from,
const struct timeval *__to)
{
struct timeval _to = *__to, *to = &_to;
if (to->tv_usec < from->tv_usec) {
to->tv_sec -= 1;
to->tv_usec += 1000000;
}
diff->tv_usec = to->tv_usec - from->tv_usec;
diff->tv_sec = to->tv_sec - from->tv_sec;
}
/* encode and send a rtp frame */
int Psip::rtp_send_frame(unsigned char *data, unsigned int len, uint8_t payload_type)
{
struct rtp_hdr *rtph;
int payload_len;
int duration; /* in samples */
unsigned char buffer[256];
/* record audio */
if (p_record)
record(data, len, 1); // from up
if (p_tap)
tap(data, len, 1); // from up
if (!p_s_rtp_is_connected) {
/* drop silently */
return 0;
}
if (!p_s_rtp_tx_action) {
/* initialize sequences */
p_s_rtp_tx_action = 1;
p_s_rtp_tx_ssrc = rand();
p_s_rtp_tx_sequence = random();
p_s_rtp_tx_timestamp = random();
memset(&p_s_rtp_tx_last_tv, 0, sizeof(p_s_rtp_tx_last_tv));
}
switch (payload_type) {
#if 0
we only support alaw and ulaw!
case RTP_PT_GSM_FULL:
payload_len = 33;
duration = 160;
break;
case RTP_PT_GSM_EFR:
payload_len = 31;
duration = 160;
break;
case RTP_PT_GSM_HALF:
payload_len = 14;
duration = 160;
break;
#endif
case PAYLOAD_TYPE_ALAW:
case PAYLOAD_TYPE_ULAW:
payload_len = len;
duration = len;
break;
default:
PERROR("unsupported message type %d\n", payload_type);
return -EINVAL;
}
#if 0
{
struct timeval tv, tv_diff;
long int usec_diff, frame_diff;
gettimeofday(&tv, NULL);
tv_difference(&tv_diff, &p_s_rtp_tx_last_tv, &tv);
p_s_rtp_tx_last_tv = tv;
usec_diff = tv_diff.tv_sec * 1000000 + tv_diff.tv_usec;
frame_diff = (usec_diff / 20000);
if (abs(frame_diff) > 1) {
long int frame_diff_excess = frame_diff - 1;
PDEBUG(DEBUG_SIP, "Correcting frame difference of %ld frames\n", frame_diff_excess);
p_s_rtp_tx_sequence += frame_diff_excess;
p_s_rtp_tx_timestamp += frame_diff_excess * duration;
}
}
#endif
rtph = (struct rtp_hdr *) buffer;
rtph->version = RTP_VERSION;
rtph->padding = 0;
rtph->extension = 0;
rtph->csrc_count = 0;
rtph->marker = 0;
rtph->payload_type = payload_type;
rtph->sequence = htons(p_s_rtp_tx_sequence++);
rtph->timestamp = htonl(p_s_rtp_tx_timestamp);
p_s_rtp_tx_timestamp += duration;
rtph->ssrc = htonl(p_s_rtp_tx_ssrc);
memcpy(buffer + sizeof(struct rtp_hdr), data, payload_len);
if (p_s_rtp_fd.fd > 0) {
len = write(p_s_rtp_fd.fd, &buffer, sizeof(struct rtp_hdr) + payload_len);
if (len != sizeof(struct rtp_hdr) + payload_len) {
PDEBUG(DEBUG_SIP, "write result=%d\n", len);
// rtp_close();
// rtp_shutdown();
return -EIO;
}
}
return 0;
}
/* receive from remote */
int Psip::bridge_rx(unsigned char *data, int len)
{
int ret;
/* don't bridge, if tones are provided */
if (p_tone_name[0] || p_dov_tx)
return -EBUSY;
if (p_dov_tx)
return -EBUSY;
if ((ret = Port::bridge_rx(data, len)))
return ret;
/* write to rx buffer */
while(len--) {
p_s_rxdata[p_s_rxpos++] = flip[*data++];
if (p_s_rxpos == 160) {
p_s_rxpos = 0;
/* transmit data via rtp */
rtp_send_frame(p_s_rxdata, 160, (options.law=='a')?PAYLOAD_TYPE_ALAW:PAYLOAD_TYPE_ULAW);
}
}
return 0;
}
/* taken from freeswitch */
/* map sip responses to QSIG cause codes ala RFC4497 section 8.4.4 */
static int status2cause(int status)
{
switch (status) {
case 200:
return 16; //SWITCH_CAUSE_NORMAL_CLEARING;
case 401:
case 402:
case 403:
case 407:
case 603:
return 21; //SWITCH_CAUSE_CALL_REJECTED;
case 404:
return 1; //SWITCH_CAUSE_UNALLOCATED_NUMBER;
case 485:
case 604:
return 3; //SWITCH_CAUSE_NO_ROUTE_DESTINATION;
case 408:
case 504:
return 102; //SWITCH_CAUSE_RECOVERY_ON_TIMER_EXPIRE;
case 410:
return 22; //SWITCH_CAUSE_NUMBER_CHANGED;
case 413:
case 414:
case 416:
case 420:
case 421:
case 423:
case 505:
case 513:
return 127; //SWITCH_CAUSE_INTERWORKING;
case 480:
return 180; //SWITCH_CAUSE_NO_USER_RESPONSE;
case 400:
case 481:
case 500:
case 503:
return 41; //SWITCH_CAUSE_NORMAL_TEMPORARY_FAILURE;
case 486:
case 600:
return 17; //SWITCH_CAUSE_USER_BUSY;
case 484:
return 28; //SWITCH_CAUSE_INVALID_NUMBER_FORMAT;
case 488:
case 606:
return 88; //SWITCH_CAUSE_INCOMPATIBLE_DESTINATION;
case 502:
return 38; //SWITCH_CAUSE_NETWORK_OUT_OF_ORDER;
case 405:
return 63; //SWITCH_CAUSE_SERVICE_UNAVAILABLE;
case 406:
case 415:
case 501:
return 79; //SWITCH_CAUSE_SERVICE_NOT_IMPLEMENTED;
case 482:
case 483:
return 25; //SWITCH_CAUSE_EXCHANGE_ROUTING_ERROR;
case 487:
return 31; //??? SWITCH_CAUSE_ORIGINATOR_CANCEL;
default:
return 31; //SWITCH_CAUSE_NORMAL_UNSPECIFIED;
}
}
static int cause2status(int cause, int location, const char **st)
{
int s;
switch (cause) {
case 1:
s = 404; *st = sip_404_Not_found;
break;
case 2:
s = 404; *st = sip_404_Not_found;
break;
case 3:
s = 404; *st = sip_404_Not_found;
break;
case 17:
s = 486; *st = sip_486_Busy_here;
break;
case 18:
s = 408; *st = sip_408_Request_timeout;
break;
case 19:
s = 480; *st = sip_480_Temporarily_unavailable;
break;
case 20:
s = 480; *st = sip_480_Temporarily_unavailable;
break;
case 21:
if (location == LOCATION_USER) {
s = 603; *st = sip_603_Decline;
} else {
s = 403; *st = sip_403_Forbidden;
}
break;
case 22:
//s = 301; *st = sip_301_Moved_permanently;
s = 410; *st = sip_410_Gone;
break;
case 23:
s = 410; *st = sip_410_Gone;
break;
case 26:
s = 404; *st = sip_404_Not_found;
break;
case 27:
s = 502; *st = sip_502_Bad_gateway;
break;
case 28:
s = 484; *st = sip_484_Address_incomplete;
break;
case 29:
s = 501; *st = sip_501_Not_implemented;
break;
case 31:
s = 480; *st = sip_480_Temporarily_unavailable;
break;
case 34:
s = 503; *st = sip_503_Service_unavailable;
break;
case 38:
s = 503; *st = sip_503_Service_unavailable;
break;
case 41:
s = 503; *st = sip_503_Service_unavailable;
break;
case 42:
s = 503; *st = sip_503_Service_unavailable;
break;
case 47:
s = 503; *st = sip_503_Service_unavailable;
break;
case 55:
s = 403; *st = sip_403_Forbidden;
break;
case 57:
s = 403; *st = sip_403_Forbidden;
break;
case 58:
s = 503; *st = sip_503_Service_unavailable;
break;
case 65:
s = 488; *st = sip_488_Not_acceptable;
break;
case 69:
s = 501; *st = sip_501_Not_implemented;
break;
case 70:
s = 488; *st = sip_488_Not_acceptable;
break;
case 79:
s = 501; *st = sip_501_Not_implemented;
break;
case 87:
s = 403; *st = sip_403_Forbidden;
break;
case 88:
s = 503; *st = sip_503_Service_unavailable;
break;
case 102:
s = 504; *st = sip_504_Gateway_time_out;
break;
case 111:
s = 500; *st = sip_500_Internal_server_error;
break;
case 127:
s = 500; *st = sip_500_Internal_server_error;
break;
default:
s = 468; *st = sip_486_Busy_here;
}
return s;
}
/* use STUN ip, or return the ip without change */
unsigned int Psip::get_local_ip(unsigned int ip)
{
struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
if (inst->public_ip[0]) {
PDEBUG(DEBUG_SIP, "RTP local IP is replaced by STUN ip %s\n", inst->public_ip);
inet_pton(AF_INET, inst->public_ip, &ip);
return htonl(ip);
}
return ip;
}
/* some simple nonce generator */
static void generate_nonce(char *result)
{
UPRINT(result, "%08x", (unsigned int)random());
result += 8;
UPRINT(result, "%08x", (unsigned int)random());
result += 8;
UPRINT(result, "%08x", (unsigned int)random());
result += 8;
UPRINT(result, "%08x", (unsigned int)random());
}
/* check authorization */
static int check_authorization(sip_authorization_t const *authorization, const char *regstr, const char *check_user, const char *check_pass, const char *check_realm, const char *check_nonce, const char **auth_text)
{
int ret = 500;
*auth_text = "Internal Server Error";
char *username = NULL;
char *realm = NULL;
char *nonce = NULL;
char *uri = NULL;
char *qop = NULL;
char *cnonce = NULL;
char *nc = NULL;
char *response = NULL;
int indexnum;
const char *cur;
char temp[256], first_digest[2 * SU_MD5_DIGEST_SIZE + 1], second_digest[2 * SU_MD5_DIGEST_SIZE + 1], third_digest[2 * SU_MD5_DIGEST_SIZE + 1];
su_md5_t md5_ctx;
if (!check_nonce || !check_nonce[0] || !authorization || !authorization->au_params) {
if (!strcmp(regstr, "REGISTER")) {
*auth_text = "Unauthorized";
ret = 401;
} else {
*auth_text = "Proxy Authentication Required";
ret = 407;
}
goto end;
}
/* parse header (stolen from freeswitch) */
for (indexnum = 0; (cur = authorization->au_params[indexnum]); indexnum++) {
char *var, *val, *p, *work;
var = val = work = NULL;
if ((work = strdup(cur))) {
var = work;
if ((val = strchr(var, '='))) {
*val++ = '\0';
while (*val == '"') {
*val++ = '\0';
}
if ((p = strchr(val, '"'))) {
*p = '\0';
}
PDEBUG(DEBUG_SIP, "Found in Auth header: %s = %s\n", var, val);
if (!strcasecmp(var, "username")) {
username = strdup(val);
} else if (!strcasecmp(var, "realm")) {
realm = strdup(val);
} else if (!strcasecmp(var, "nonce")) {
nonce = strdup(val);
} else if (!strcasecmp(var, "uri")) {
uri = strdup(val);
} else if (!strcasecmp(var, "qop")) {
qop = strdup(val);
} else if (!strcasecmp(var, "cnonce")) {
cnonce = strdup(val);
} else if (!strcasecmp(var, "response")) {
response = strdup(val);
} else if (!strcasecmp(var, "nc")) {
nc = strdup(val);
}
}
free(work);
}
}
if (!username || !realm || !nonce || ! uri || !response) {
*auth_text = "Authorization header incomplete";
ret = 400;
goto end;
}
if (!!strcmp(username, check_user)) {
*auth_text = "Authorization Username Missmatch";
ret = 403;
goto end;
}
if (!!strcmp(realm, check_realm)) {
*auth_text = "Authorization Realm Missmatch";
ret = 403;
goto end;
}
if (!!strcmp(nonce, check_nonce)) {
*auth_text = "Authorization Nonce Missmatch";
ret = 403;
goto end;
}
/* perform hash */
SPRINT(temp, "%s:%s:%s", check_user, realm, check_pass);
PDEBUG(DEBUG_SIP, "First hash: %s\n", temp);
su_md5_init(&md5_ctx);
su_md5_strupdate(&md5_ctx, temp);
su_md5_hexdigest(&md5_ctx, first_digest);
su_md5_deinit(&md5_ctx);
SPRINT(temp, "%s:%s", regstr, uri);
PDEBUG(DEBUG_SIP, "Second hash: %s\n", temp);
su_md5_init(&md5_ctx);
su_md5_strupdate(&md5_ctx, temp);
su_md5_hexdigest(&md5_ctx, second_digest);
su_md5_deinit(&md5_ctx);
if (nc && cnonce && qop)
SPRINT(temp, "%s:%s:%s:%s:%s:%s", first_digest, nonce, nc, cnonce, qop, second_digest);
else
SPRINT(temp, "%s:%s:%s", first_digest, nonce, second_digest);
PDEBUG(DEBUG_SIP, "Third hash: %s\n", temp);
su_md5_init(&md5_ctx);
su_md5_strupdate(&md5_ctx, temp);
su_md5_hexdigest(&md5_ctx, third_digest);
su_md5_deinit(&md5_ctx);
if (!!strcmp(response, third_digest)) {
*auth_text = "Authorization Failed";
ret = 403;
goto end;
}
*auth_text = "Authorization Success";
ret = 200;
end:
free(username);
free(realm);
free(nonce);
free(uri);
free(qop);
free(cnonce);
free(nc);
free(response);
return ret;
}
/*
* endpoint sends messages to the SIP port
*/
int Psip::message_connect(unsigned int epoint_id, int message_id, union parameter *param)
{
struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
const char *sdp_str = NULL;
struct lcr_msg *message;
struct interface *interface;
int media_type;
unsigned char payload_type;
interface = getinterfacebyname(inst->interface_name);
if (!interface) {
PERROR("Cannot find interface %s.\n", inst->interface_name);
return 0;
}
if (param->connectinfo.rtpinfo.port) {
PDEBUG(DEBUG_SIP, "RTP info given by remote, forward that\n");
p_s_rtp_bridge = 1;
media_type = param->connectinfo.rtpinfo.media_types[0];
payload_type = param->connectinfo.rtpinfo.payload_types[0];
p_s_rtp_ip_local = param->connectinfo.rtpinfo.ip;
p_s_rtp_port_local = param->connectinfo.rtpinfo.port;
PDEBUG(DEBUG_SIP, "payload type %d\n", payload_type);
PDEBUG(DEBUG_SIP, "local ip %08x port %d\n", p_s_rtp_ip_local, p_s_rtp_port_local);
PDEBUG(DEBUG_SIP, "remote ip %08x port %d\n", p_s_rtp_ip_remote, p_s_rtp_port_remote);
} else {
PDEBUG(DEBUG_SIP, "RTP info not given by remote, so we do our own RTP\n");
media_type = (options.law=='a') ? MEDIA_TYPE_ALAW : MEDIA_TYPE_ULAW;
payload_type = (options.law=='a') ? PAYLOAD_TYPE_ALAW : PAYLOAD_TYPE_ULAW;
/* open local RTP peer (if not bridging) */
if (rtp_connect() < 0) {
nua_cancel(p_s_handle, TAG_END());
PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", p_s_handle);
nua_handle_destroy(p_s_handle);
p_s_handle = NULL;
sip_trace_header(this, inst->interface_name, "CANCEL", DIRECTION_OUT);
add_trace("reason", NULL, "failed to connect RTP/RTCP sockts");
end_trace();
message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
message->param.disconnectinfo.cause = 41;
message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
message_put(message);
new_state(PORT_STATE_RELEASE);
trigger_work(&p_s_delete);
return 0;
}
}
sdp_str = generate_sdp(p_s_rtp_ip_local, p_s_rtp_port_local, 1, &payload_type, &media_type);
PDEBUG(DEBUG_SIP, "Using SDP response: %s\n", sdp_str);
/* NOTE:
* If this response causes corrupt messages, like SDP body inside or
* before header, check if the sofia-sip-gcc-4.8.patch was applied.
* If it is still corrupted, try to disable optimization when compiling
* sofia-sip.
*/
nua_respond(p_s_handle, SIP_200_OK,
NUTAG_MEDIA_ENABLE(0),
SIPTAG_CONTENT_TYPE_STR("application/sdp"),
SIPTAG_PAYLOAD_STR(sdp_str), TAG_END());
new_state(PORT_STATE_CONNECT);
sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
add_trace("respond", "value", "200 OK");
add_trace("reason", NULL, "call connected");
struct in_addr ia;
memset(&ia, 0, sizeof(ia));
ia.s_addr = htonl(get_local_ip(p_s_rtp_ip_local));
add_trace("rtp", "ip", "%s", inet_ntoa(ia));
add_trace("rtp", "port", "%d,%d", p_s_rtp_port_local, p_s_rtp_port_local + 1);
add_trace("rtp", "payload", "%s:%d", media_type2name(media_type), payload_type);
end_trace();
return 0;
}
int Psip::message_release(unsigned int epoint_id, int message_id, union parameter *param)
{
struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
struct lcr_msg *message;
char cause_str[128] = "";
int cause = param->disconnectinfo.cause;
int location = param->disconnectinfo.cause;
int status;
const char *status_text;
if (cause > 0 && cause <= 127) {
SPRINT(cause_str, "Q.850;cause=%d;text=\"%s\"", cause, isdn_cause[cause].english);
}
switch (p_state) {
case PORT_STATE_OUT_SETUP:
case PORT_STATE_OUT_PROCEEDING:
case PORT_STATE_OUT_ALERTING:
PDEBUG(DEBUG_SIP, "RELEASE/DISCONNECT will cancel\n");
sip_trace_header(this, inst->interface_name, "CANCEL", DIRECTION_OUT);
if (cause_str[0])
add_trace("cause", "value", "%d", cause);
end_trace();
nua_cancel(p_s_handle, TAG_IF(cause_str[0], SIPTAG_REASON_STR(cause_str)), TAG_END());
break;
case PORT_STATE_IN_SETUP:
case PORT_STATE_IN_PROCEEDING:
case PORT_STATE_IN_ALERTING:
PDEBUG(DEBUG_SIP, "RELEASE/DISCONNECT will respond\n");
status = cause2status(cause, location, &status_text);
sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
if (cause_str[0])
add_trace("cause", "value", "%d", cause);
add_trace("respond", "value", "%d %s", status, status_text);
end_trace();
nua_respond(p_s_handle, status, status_text, TAG_IF(cause_str[0], SIPTAG_REASON_STR(cause_str)), TAG_END());
PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", p_s_handle);
nua_handle_destroy(p_s_handle);
p_s_handle = NULL;
trigger_work(&p_s_delete);
break;
default:
PDEBUG(DEBUG_SIP, "RELEASE/DISCONNECT will perform nua_bye\n");
sip_trace_header(this, inst->interface_name, "BYE", DIRECTION_OUT);
if (cause_str[0])
add_trace("cause", "value", "%d", cause);
end_trace();
nua_bye(p_s_handle, TAG_IF(cause_str[0], SIPTAG_REASON_STR(cause_str)), TAG_END());
}
if (message_id == MESSAGE_DISCONNECT) {
while(p_epointlist) {
message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
message->param.disconnectinfo.cause = CAUSE_NORMAL;
message->param.disconnectinfo.location = LOCATION_BEYOND;
message_put(message);
/* remove epoint */
free_epointlist(p_epointlist);
}
}
new_state(PORT_STATE_RELEASE);
return(0);
}
int Psip::message_setup(unsigned int epoint_id, int message_id, union parameter *param)
{
struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
char from[128] = "";
char asserted_id[128] = "", asserted_msg[256] = "";
char to[128] = "";
char contact[128] = "";
const char *local = inst->local_peer;
char local_ip[16];
const char *remote = inst->remote_peer;
const char *sdp_str = NULL;
struct epoint_list *epointlist;
sip_cseq_t *cseq = NULL;
struct lcr_msg *message;
int lcr_media = { (options.law=='a') ? MEDIA_TYPE_ALAW : MEDIA_TYPE_ULAW };
unsigned char lcr_payload = { (options.law=='a') ? (unsigned char )PAYLOAD_TYPE_ALAW : (unsigned char )PAYLOAD_TYPE_ULAW };
int *media_types;
unsigned char *payload_types;
int payloads = 0;
int i;
if (!remote[0]) {
sip_trace_header(this, inst->interface_name, "INVITE", DIRECTION_OUT);
add_trace("failed", "reason", "No remote peer set or no peer has registered to us.");
end_trace();
message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
message->param.disconnectinfo.cause = 27;
message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
message_put(message);
new_state(PORT_STATE_RELEASE);
trigger_work(&p_s_delete);
return 0;
}
PDEBUG(DEBUG_SIP, "Doing Setup (inst %p)\n", inst);
memcpy(&p_dialinginfo, &param->setup.dialinginfo, sizeof(p_dialinginfo));
memcpy(&p_callerinfo, &param->setup.callerinfo, sizeof(p_callerinfo));
// memcpy(&p_redirinfo, &param->setup.redirinfo, sizeof(p_redirinfo));
do_screen(1, p_callerinfo.id, sizeof(p_callerinfo.id), &p_callerinfo.ntype, &p_callerinfo.present, inst->interface_name);
// do_screen(1, p_redirinfo.id, sizeof(p_redirinfo.id), &p_redirinfo.ntype, &p_redirinfo.present, inst->interface_name);
if (param->setup.rtpinfo.port) {
PDEBUG(DEBUG_SIP, "RTP info given by remote, forward that\n");
p_s_rtp_bridge = 1;
media_types = param->setup.rtpinfo.media_types;
payload_types = param->setup.rtpinfo.payload_types;
payloads = param->setup.rtpinfo.payloads;
p_s_rtp_ip_local = param->setup.rtpinfo.ip;
p_s_rtp_port_local = param->setup.rtpinfo.port;
PDEBUG(DEBUG_SIP, "local ip %08x port %d\n", p_s_rtp_ip_local, p_s_rtp_port_local);
PDEBUG(DEBUG_SIP, "remote ip %08x port %d\n", p_s_rtp_ip_remote, p_s_rtp_port_remote);
} else {
PDEBUG(DEBUG_SIP, "RTP info not given by remote, so we do our own RTP\n");
p_s_rtp_bridge = 0;
media_types = &lcr_media;
payload_types = &lcr_payload;
payloads = 1;
/* open local RTP peer (if not bridging) */
if (rtp_open() < 0) {
PERROR("Failed to open RTP sockets\n");
/* send release message to endpoit */
message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
message->param.disconnectinfo.cause = 41;
message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
message_put(message);
new_state(PORT_STATE_RELEASE);
trigger_work(&p_s_delete);
return 0;
}
if (!p_s_rtp_ip_local) {
char *p;
/* extract IP from local peer */
SCPY(local_ip, local);
p = strchr(local_ip, ':');
if (p)
*p = '\0';
PDEBUG(DEBUG_SIP, "RTP local IP not known, so we use our local SIP ip %s\n", local_ip);
inet_pton(AF_INET, local_ip, &p_s_rtp_ip_local);
p_s_rtp_ip_local = ntohl(p_s_rtp_ip_local);
}
}
p_s_handle = nua_handle(inst->nua, NULL, TAG_END());
if (!p_s_handle) {
PERROR("Failed to create handle\n");
/* send release message to endpoit */
message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
message->param.disconnectinfo.cause = 41;
message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
message_put(message);
new_state(PORT_STATE_RELEASE);
trigger_work(&p_s_delete);
return 0;
}
/* apply handle to trace */
// sip_trace_header(this, inst->interface_name, "NEW handle", DIRECTION_IN);
// add_trace("handle", "new", "0x%x", p_s_handle);
// end_trace();
sdp_str = generate_sdp(p_s_rtp_ip_local, p_s_rtp_port_local, payloads, payload_types, media_types);
PDEBUG(DEBUG_SIP, "Using SDP for invite: %s\n", sdp_str);
SPRINT(from, "sip:%s@%s", p_callerinfo.id, remote);
// SPRINT(from, "\"%s\" <sip:%s@%s>", /*p_callerinfo.id*/ "4946448519988", p_callerinfo.id, remote);
SPRINT(to, "sip:%s@%s", p_dialinginfo.id, remote);
if (inst->asserted_id[0]) {
SPRINT(asserted_id, "sip:%s@%s", inst->asserted_id, remote);
SPRINT(asserted_msg, "P-Asserted-Identity: <%s>", asserted_id);
}
if (inst->public_ip[0]) {
char *p;
SPRINT(contact, "sip:%s@%s", p_callerinfo.id, inst->public_ip);
p = strchr(inst->local_peer, ':');
if (p)
SCAT(contact, p);
}
sip_trace_header(this, inst->interface_name, "INVITE", DIRECTION_OUT);
add_trace("from", "uri", "%s", from);
add_trace("to", "uri", "%s", to);
if (asserted_id[0])
add_trace("assert-id", "uri", "%s", asserted_id);
struct in_addr ia;
memset(&ia, 0, sizeof(ia));
ia.s_addr = htonl(get_local_ip(p_s_rtp_ip_local));
add_trace("rtp", "ip", "%s", inet_ntoa(ia));
add_trace("rtp", "port", "%d,%d", p_s_rtp_port_local, p_s_rtp_port_local + 1);
for (i = 0; i < payloads; i++)
add_trace("rtp", "payload", "%s:%d", media_type2name(media_types[i]), payload_types[i]);
end_trace();
// cseq = sip_cseq_create(sip_home, 123, SIP_METHOD_INVITE);
nua_invite(p_s_handle,
TAG_IF(from[0], SIPTAG_FROM_STR(from)),
TAG_IF(to[0], SIPTAG_TO_STR(to)),
TAG_IF(asserted_msg[0], SIPTAG_HEADER_STR(asserted_msg)),
TAG_IF(contact[0], SIPTAG_CONTACT_STR(contact)),
TAG_IF(cseq, SIPTAG_CSEQ(cseq)),
NUTAG_MEDIA_ENABLE(0),
SIPTAG_CONTENT_TYPE_STR("application/sdp"),
SIPTAG_PAYLOAD_STR(sdp_str), TAG_END());
new_state(PORT_STATE_OUT_SETUP);
p_s_invite_direction = DIRECTION_OUT;
#if 0
PDEBUG(DEBUG_SIP, "do overlap\n");
new_state(PORT_STATE_OUT_OVERLAP);
message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_OVERLAP);
message_put(message);
#else
PDEBUG(DEBUG_SIP, "do proceeding\n");
new_state(PORT_STATE_OUT_PROCEEDING);
message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_PROCEEDING);
message_put(message);
#endif
/* attach only if not already */
epointlist = p_epointlist;
while(epointlist) {
if (epointlist->epoint_id == epoint_id)
break;
epointlist = epointlist->next;
}
if (!epointlist)
epointlist_new(epoint_id);
return 0;
}
int Psip::message_notify(unsigned int epoint_id, int message_id, union parameter *param)
{
// char
// struct in_addr ia;
switch (param->notifyinfo.notify) {
case INFO_NOTIFY_REMOTE_HOLD:
#if 0
sdp_str = generate_sdp(0, 0, 0, NULL, NULL);
SPRINT(sdp_str,
"v=0\r\n"
"o=LCR-Sofia-SIP 0 0 IN IP4 0.0.0.0\r\n"
"s=SIP Call\r\n"
"c=IN IP4 0.0.0.0\r\n"
"t=0 0\r\n"
);
PDEBUG(DEBUG_SIP, "Using SDP for hold: %s\n", sdp_str);
nua_info(p_s_handle,
// TAG_IF(from[0], SIPTAG_FROM_STR(from)),
// TAG_IF(to[0], SIPTAG_TO_STR(to)),
// TAG_IF(cseq, SIPTAG_CSEQ(cseq)),
NUTAG_MEDIA_ENABLE(0),
SIPTAG_CONTENT_TYPE_STR("application/sdp"),
SIPTAG_PAYLOAD_STR(sdp_str), TAG_END());
#endif
break;
case INFO_NOTIFY_REMOTE_RETRIEVAL:
#if 0
sdp_str = generate_sdp(p_s_rtp_ip_local, p_s_rtp_port_local, 1, &payload_type, &media_type);
PDEBUG(DEBUG_SIP, "Using SDP for rertieve: %s\n", sdp_str);
nua_info(p_s_handle,
// TAG_IF(from[0], SIPTAG_FROM_STR(from)),
// TAG_IF(to[0], SIPTAG_TO_STR(to)),
// TAG_IF(cseq, SIPTAG_CSEQ(cseq)),
NUTAG_MEDIA_ENABLE(0),
SIPTAG_CONTENT_TYPE_STR("application/sdp"),
SIPTAG_PAYLOAD_STR(sdp_str), TAG_END());
#endif
break;
}
return 0;
}
int Psip::message_dtmf(unsigned int epoint_id, int message_id, union parameter *param)
{
char dtmf_str[64];
/* prepare DTMF info payload */
SPRINT(dtmf_str,
"Signal=%c\n"
"Duration=160\n"
, param->dtmf);
/* start invite to handle DTMF */
nua_info(p_s_handle,
NUTAG_MEDIA_ENABLE(0),
SIPTAG_CONTENT_TYPE_STR("application/dtmf-relay"),
SIPTAG_PAYLOAD_STR(dtmf_str), TAG_END());
return 0;
}
/* NOTE: incomplete and not working */
int Psip::message_information(unsigned int epoint_id, int message_id, union parameter *param)
{
char dtmf_str[64];
/* prepare DTMF info payload */
SPRINT(dtmf_str,
"Signal=%s\n"
"Duration=160\n"
, param->information.id);
/* start invite to handle DTMF */
nua_info(p_s_handle,
NUTAG_MEDIA_ENABLE(0),
SIPTAG_CONTENT_TYPE_STR("application/dtmf-relay"),
SIPTAG_PAYLOAD_STR(dtmf_str), TAG_END());
return 0;
}
int Psip::message_epoint(unsigned int epoint_id, int message_id, union parameter *param)
{
struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
if (Port::message_epoint(epoint_id, message_id, param))
return 1;
switch(message_id) {
case MESSAGE_ALERTING: /* call is ringing on LCR side */
if (p_state != PORT_STATE_IN_SETUP
&& p_state != PORT_STATE_IN_PROCEEDING)
return 0;
nua_respond(p_s_handle, SIP_180_RINGING, TAG_END());
sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
add_trace("respond", "value", "180 Ringing");
end_trace();
new_state(PORT_STATE_IN_ALERTING);
return 1;
case MESSAGE_CONNECT: /* call is connected on LCR side */
if (p_state != PORT_STATE_IN_SETUP
&& p_state != PORT_STATE_IN_PROCEEDING
&& p_state != PORT_STATE_IN_ALERTING)
return 0;
message_connect(epoint_id, message_id, param);
return 1;
case MESSAGE_DISCONNECT: /* call has been disconnected */
case MESSAGE_RELEASE: /* call has been released */
message_release(epoint_id, message_id, param);
return 1;
case MESSAGE_SETUP: /* dial-out command received from epoint */
message_setup(epoint_id, message_id, param);
return 1;
case MESSAGE_INFORMATION: /* overlap dialing */
if (p_state != PORT_STATE_OUT_OVERLAP)
return 0;
message_information(epoint_id, message_id, param);
return 1;
case MESSAGE_DTMF: /* DTMF info to be transmitted via INFO transaction */
if (p_state == PORT_STATE_CONNECT)
message_dtmf(epoint_id, message_id, param);
case MESSAGE_NOTIFY: /* notification about remote hold/retrieve */
if (p_state == PORT_STATE_CONNECT)
message_notify(epoint_id, message_id, param);
return(1);
default:
PDEBUG(DEBUG_SIP, "PORT(%s) SIP port with (caller id %s) received an unsupported message: %d\n", p_name, p_callerinfo.id, message_id);
}
return 0;
}
int Psip::parse_sdp(sip_t const *sip, unsigned int *ip, unsigned short *port, uint8_t *payload_types, int *media_types, int *payloads, int max_payloads)
{
*payloads = 0;
if (!sip->sip_payload) {
PDEBUG(DEBUG_SIP, "no payload given\n");
return 0;
}
sdp_parser_t *parser;
sdp_session_t *sdp;
sdp_media_t *m;
sdp_attribute_t *attr;
sdp_rtpmap_t *map;
sdp_connection_t *conn;
PDEBUG(DEBUG_SIP, "payload given: %s\n", sip->sip_payload->pl_data);
parser = sdp_parse(NULL, sip->sip_payload->pl_data, (int) strlen(sip->sip_payload->pl_data), 0);
if (!parser) {
return 400;
}
if (!(sdp = sdp_session(parser))) {
sdp_parser_free(parser);
return 400;
}
for (m = sdp->sdp_media; m; m = m->m_next) {
if (m->m_proto != sdp_proto_rtp)
continue;
if (m->m_type != sdp_media_audio)
continue;
PDEBUG(DEBUG_SIP, "RTP port:'%u'\n", m->m_port);
*port = m->m_port;
for (attr = m->m_attributes; attr; attr = attr->a_next) {
PDEBUG(DEBUG_SIP, "ATTR: name:'%s' value='%s'\n", attr->a_name, attr->a_value);
}
if (m->m_connections) {
conn = m->m_connections;
PDEBUG(DEBUG_SIP, "CONN: address:'%s'\n", conn->c_address);
inet_pton(AF_INET, conn->c_address, ip);
*ip = ntohl(p_s_rtp_ip_remote);
} else {
char *p = sip->sip_payload->pl_data;
char addr[16];
PDEBUG(DEBUG_SIP, "sofia cannot find connection tag, so we try ourself\n");
p = strstr(p, "c=IN IP4 ");
if (!p) {
PDEBUG(DEBUG_SIP, "missing c-tag with internet address\n");
sdp_parser_free(parser);
return 400;
}
SCPY(addr, p + 9);
if ((p = strchr(addr, '\n'))) *p = '\0';
if ((p = strchr(addr, '\r'))) *p = '\0';
PDEBUG(DEBUG_SIP, "CONN: address:'%s'\n", addr);
inet_pton(AF_INET, addr, ip);
*ip = ntohl(p_s_rtp_ip_remote);
}
for (map = m->m_rtpmaps; map; map = map->rm_next) {
int media_type = 0;
PDEBUG(DEBUG_SIP, "RTPMAP: coding:'%s' rate='%d' pt='%d'\n", map->rm_encoding, map->rm_rate, map->rm_pt);
/* append to payload list, if there is space */
add_trace("rtp", "payload", "%s:%d", map->rm_encoding, map->rm_pt);
if (map->rm_pt == PAYLOAD_TYPE_ALAW)
media_type = MEDIA_TYPE_ALAW;
else if (map->rm_pt == PAYLOAD_TYPE_ULAW)
media_type = MEDIA_TYPE_ULAW;
else if (map->rm_pt == PAYLOAD_TYPE_GSM)
media_type = MEDIA_TYPE_GSM;
else if (!strcmp(map->rm_encoding, "GSM-EFR"))
media_type = MEDIA_TYPE_GSM_EFR;
else if (!strcmp(map->rm_encoding, "AMR"))
media_type = MEDIA_TYPE_AMR;
else if (!strcmp(map->rm_encoding, "GSM-HR"))
media_type = MEDIA_TYPE_GSM_HR;
if (media_type && *payloads <= max_payloads) {
*payload_types++ = map->rm_pt;
*media_types++ = media_type;
(*payloads)++;
}
}
}
sdp_parser_free(parser);
return 0;
}
const char *Psip::generate_sdp(unsigned int rtp_ip_local, unsigned short rtp_port_local, int payloads, unsigned char *payload_types, int *media_types)
{
struct in_addr ia;
static char sdp_str[256], sub_str[128];
int i;
memset(&ia, 0, sizeof(ia));
ia.s_addr = htonl(get_local_ip(p_s_rtp_ip_local));
SPRINT(sdp_str,
"v=0\r\n"
"o=LCR-Sofia-SIP 0 0 IN IP4 %s\r\n"
"s=SIP Call\r\n"
"c=IN IP4 %s\r\n"
"t=0 0\r\n", inet_ntoa(ia), inet_ntoa(ia));
if (payloads) {
SPRINT(sub_str, "m=audio %d RTP/AVP", p_s_rtp_port_local);
SCAT(sdp_str, sub_str);
for (i = 0; i < payloads; i++) {
SPRINT(sub_str, " %d", payload_types[i]);
SCAT(sdp_str, sub_str);
}
SCAT(sdp_str, "\r\n");
for (i = 0; i < payloads; i++) {
SPRINT(sub_str, "a=rtpmap:%d %s/8000\r\n", payload_types[i], media_type2name(media_types[i]));
SCAT(sdp_str, sub_str);
}
}
return sdp_str;
}
static int challenge(struct sip_inst *inst, class Psip *psip, int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
{
sip_www_authenticate_t const *authenticate = NULL;
char const *realm = NULL;
char const *scheme = NULL;
int i;
char *cur;
char authentication[256] = "";
PDEBUG(DEBUG_SIP, "challenge order received\n");
if (!inst->auth_user[0]) {
PDEBUG(DEBUG_SIP, "No credentials available\n");
sip_trace_header(psip, inst->interface_name, "AUTHENTICATE", DIRECTION_OUT);
add_trace("error", NULL, "There are no credentials given for interface");
end_trace();
return -1;
}
if (sip->sip_www_authenticate) {
authenticate = sip->sip_www_authenticate;
} else if (sip->sip_proxy_authenticate) {
authenticate = sip->sip_proxy_authenticate;
} else {
PDEBUG(DEBUG_SIP, "No authentication header found\n");
sip_trace_header(psip, inst->interface_name, "AUTHENTICATE", DIRECTION_OUT);
add_trace("error", NULL, "Authentication method unknwon");
end_trace();
return -1;
}
scheme = (char const *) authenticate->au_scheme;
if (authenticate->au_params) {
for (i = 0; (cur = (char *) authenticate->au_params[i]); i++) {
if ((realm = strstr(cur, "realm="))) {
realm += 6;
break;
}
}
}
if (!scheme || !realm) {
PDEBUG(DEBUG_SIP, "No scheme or no realm in authentication header found\n");
sip_trace_header(psip, inst->interface_name, "AUTHENTICATE", DIRECTION_OUT);
add_trace("error", NULL, "Authentication header has no realm or scheme");
end_trace();
return -1;
}
SPRINT(authentication, "%s:%s:%s:%s", scheme, realm, inst->auth_user, inst->auth_password);
PDEBUG(DEBUG_SIP, "auth: '%s'\n", authentication);
sip_trace_header(psip, inst->interface_name, "AUTHENTICATE", DIRECTION_OUT);
add_trace("scheme", NULL, "%s", scheme);
add_trace("realm", NULL, "%s", realm);
add_trace("user", NULL, "%s", inst->auth_user);
add_trace("pass", NULL, "%s", inst->auth_password);
end_trace();
nua_authenticate(nh, /*SIPTAG_EXPIRES_STR("3600"),*/ NUTAG_AUTH(authentication), TAG_END());
return 0;
}
static void i_options(struct sip_inst *inst, int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
{
#define NUTAG_WITH_THIS_MSG(msg) nutag_with, tag_ptr_v(msg)
nua_saved_event_t saved[1];
nua_save_event(nua, saved);
nua_event_data_t const *data = nua_event_data(saved);
sip_trace_header(NULL, inst->interface_name, "OPTIONS", DIRECTION_IN);
end_trace();
sip_trace_header(NULL, inst->interface_name, "RESPOND", DIRECTION_OUT);
add_trace("respond", "value", "200 OK");
end_trace();
nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(data->e_msg), TAG_END());
}
static void i_register(struct sip_inst *inst, int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
{
#define NUTAG_WITH_THIS_MSG(msg) nutag_with, tag_ptr_v(msg)
nua_saved_event_t saved[1];
sip_contact_t const *contact = NULL;
contact = sip->sip_contact;
nua_save_event(nua, saved);
nua_event_data_t const *data = nua_event_data(saved);
sip_authorization_t const *authorization;
char uri[256] = "";
const char *auth_text = NULL;
char auth_str[256] = "";
if (contact->m_url->url_host)
SCPY(uri, contact->m_url->url_host);
if (contact->m_url->url_port && contact->m_url->url_port[0]) {
SCAT(uri, ":");
SCAT(uri, contact->m_url->url_port);
}
if (!inst->allow_register) {
sip_trace_header(NULL, inst->interface_name, "REGISTER", DIRECTION_IN);
add_trace("error", NULL, "forbidden, because we don't accept registration");
end_trace();
nua_respond(nh, SIP_403_FORBIDDEN, NUTAG_WITH_THIS_MSG(data->e_msg), TAG_END());
PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
nua_handle_destroy(nh);
inst->register_handle = NULL;
return;
}
sip_trace_header(NULL, inst->interface_name, "REGISTER", DIRECTION_IN);
add_trace("contact", "uri", "%s", uri);
end_trace();
sip_trace_header(NULL, inst->interface_name, "Authorization", DIRECTION_IN);
if (inst->auth_realm[0]) {
authorization = sip->sip_authorization;
status = check_authorization(authorization, "REGISTER", inst->auth_user, inst->auth_password, inst->auth_realm, inst->auth_nonce, &auth_text);
if (status == 401) {
if (!inst->auth_nonce[0])
generate_nonce(inst->auth_nonce);
SPRINT(auth_str, "Digest realm=\"%s\", nonce=\"%s\", algorithm=MD5, qop=\"auth\"", inst->auth_realm, inst->auth_nonce);
}
} else {
status = 200;
auth_text = "Authentication not required";
}
add_trace("result", NULL, "%s", auth_text);
end_trace();
if (status == 200) {
SCPY(inst->remote_peer, uri);
}
sip_trace_header(NULL, inst->interface_name, "RESPOND", DIRECTION_OUT);
add_trace("respond", "value", "%d", status);
add_trace("reason", NULL, "peer registers");
end_trace();
nua_respond(nh, status, auth_text, SIPTAG_CONTACT(sip->sip_contact), NUTAG_WITH_THIS_MSG(data->e_msg), TAG_IF(auth_str[0], SIPTAG_WWW_AUTHENTICATE_STR(auth_str)), TAG_END());
PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
nua_handle_destroy(nh);
inst->register_handle = NULL;
}
static void r_register(struct sip_inst *inst, int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
{
int rc;
sip_trace_header(NULL, inst->interface_name, "STATUS", DIRECTION_IN);
add_trace("value", NULL, "%d", status);
add_trace("phrase", NULL, "%s", phrase);
end_trace();
switch (status) {
case 200:
status_200:
/* if not registered, become registered and start register interval timer */
if (inst->register_state != REGISTER_STATE_REGISTERED) {
if (inst->register_interval)
schedule_timer(&inst->register_retry_timer, inst->register_interval, 0);
inst->register_state = REGISTER_STATE_REGISTERED;
}
/* start option timer */
if (inst->options_interval) {
PDEBUG(DEBUG_SIP, "register ok, scheduling option timer with %d seconds\n", inst->options_interval);
schedule_timer(&inst->register_option_timer, inst->options_interval, 0);
}
break;
case 401:
case 407:
PDEBUG(DEBUG_SIP, "Register challenge received\n");
rc = challenge(inst, NULL, status, phrase, nua, magic, nh, hmagic, sip, tags);
if (rc < 0)
goto status_400;
break;
default:
if (status >= 200 && status <= 299)
goto status_200;
if (status < 400)
break;
status_400:
PDEBUG(DEBUG_SIP, "Register failed, starting register timer\n");
inst->register_state = REGISTER_STATE_FAILED;
PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
nua_handle_destroy(nh);
inst->register_handle = NULL;
/* stop option timer */
unsched_timer(&inst->register_option_timer);
/* if failed, start register interval timer with REGISTER_RETRY_TIMER */
schedule_timer(&inst->register_retry_timer, REGISTER_RETRY_TIMER);
}
}
static void r_options(struct sip_inst *inst, int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
{
PDEBUG(DEBUG_SIP, "options result %d received\n", status);
// if (status >= 200 && status <= 299) {
PDEBUG(DEBUG_SIP, "options ok, scheduling option timer with %d seconds\n", inst->options_interval);
/* restart option timer */
schedule_timer(&inst->register_option_timer, inst->options_interval, 0);
return;
/// }
PDEBUG(DEBUG_SIP, "Register options failed, starting register timer\n");
inst->register_state = REGISTER_STATE_FAILED;
PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
nua_handle_destroy(nh);
inst->register_handle = NULL;
/* if failed, start register interval timer with REGISTER_RETRY_TIMER */
schedule_timer(&inst->register_retry_timer, REGISTER_RETRY_TIMER);
}
void Psip::i_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
{
struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
const char *from = "", *to = "", *name = "";
char imsi[16] = "";
int ret;
class Endpoint *epoint;
struct lcr_msg *message;
struct interface *interface;
const char *sdp_str = NULL;
int media_types[32];
uint8_t payload_types[32];
int payloads = 0;
unsigned char payload_type;
int media_type;
sip_authorization_t const *authorization;
const char *auth_text = NULL;
char auth_str[256] = "";
interface = getinterfacebyname(inst->interface_name);
if (!interface) {
PERROR("Cannot find interface %s.\n", inst->interface_name);
return;
}
if (sip->sip_from) {
if (sip->sip_from->a_url)
from = sip->sip_from->a_url->url_user;
if (sip->sip_from->a_display) {
name = sip->sip_from->a_display;
if (!strncmp(name, "\"IMSI", 5)) {
strncpy(imsi, name + 5, 15);
imsi[15] = '\0';
name = "";
}
}
}
if (sip->sip_to) {
if (sip->sip_to->a_url)
to = sip->sip_to->a_url->url_user;
}
PDEBUG(DEBUG_SIP, "invite received (%s->%s)\n", from, to);
sip_trace_header(this, inst->interface_name, "Authorization", DIRECTION_IN);
if (inst->auth_realm[0] && p_state == PORT_STATE_IDLE) {
/* only authenticate remote, if we have a realm set and we don't have re-invite */
authorization = sip->sip_proxy_authorization;
status = check_authorization(authorization, "INVITE", inst->auth_user, inst->auth_password, inst->auth_realm, inst->auth_nonce, &auth_text);
if (status == 407) {
if (!inst->auth_nonce[0])
generate_nonce(inst->auth_nonce);
SPRINT(auth_str, "Digest realm=\"%s\", nonce=\"%s\", algorithm=MD5, qop=\"auth\"", inst->auth_realm, inst->auth_nonce);
}
} else {
status = 200;
auth_text = "Authentication not required";
}
add_trace("result", NULL, "%s", auth_text);
end_trace();
if (status == 200) {
} else {
sip_trace_header(this, inst->interface_name, "INVITE", DIRECTION_IN);
end_trace();
sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
add_trace("respond", "value", "%d", status);
add_trace("reason", NULL, "peer invited");
end_trace();
nua_respond(nh, status, auth_text, SIPTAG_CONTACT(sip->sip_contact), TAG_IF(auth_str[0], SIPTAG_PROXY_AUTHENTICATE_STR(auth_str)), TAG_END());
new_state(PORT_STATE_RELEASE);
trigger_work(&p_s_delete);
return;
}
sip_trace_header(this, inst->interface_name, "Payload received", DIRECTION_NONE);
ret = parse_sdp(sip, &p_s_rtp_ip_remote, &p_s_rtp_port_remote, payload_types, media_types, &payloads, sizeof(payload_types));
if (!ret) {
/* if no RTP bridge, we must support LAW codec, otherwise we forward what we have */
if (!p_s_rtp_bridge) {
int i;
/* check if supported payload type exists */
for (i = 0; i < payloads; i++) {
if (media_types[i] == ((options.law=='a') ? MEDIA_TYPE_ALAW : MEDIA_TYPE_ULAW))
break;
}
if (i == payloads) {
add_trace("error", NULL, "Expected LAW payload type (not bridged)");
ret = 415;
}
}
}
end_trace();
if (ret) {
if (ret == 400)
nua_respond(nh, SIP_400_BAD_REQUEST, TAG_END());
else
nua_respond(nh, SIP_415_UNSUPPORTED_MEDIA, TAG_END());
PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
nua_handle_destroy(nh);
p_s_handle = NULL;
sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
if (ret == 400)
add_trace("respond", "value", "415 Unsupported Media");
else
add_trace("respond", "value", "400 Bad Request");
add_trace("reason", NULL, "offered codec does not match");
end_trace();
if (p_state != PORT_STATE_IDLE) {
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE);
message->param.disconnectinfo.cause = 41;
message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
message_put(message);
}
new_state(PORT_STATE_RELEASE);
trigger_work(&p_s_delete);
return;
}
/* handle re-invite */
if (p_state != PORT_STATE_IDLE) {
sip_trace_header(this, inst->interface_name, "RE-INVITE", DIRECTION_IN);
end_trace();
if (p_s_rtp_bridge) {
PDEBUG(DEBUG_SIP, "RE-INVITE not implemented for RTP forwarding\n");
nua_respond(nh, SIP_501_NOT_IMPLEMENTED, TAG_END());
sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
add_trace("respond", "value", "501 NOT IMPLEMENTED");
add_trace("reason", NULL, "RE-INVITE not implemented for RTP forwarding");
end_trace();
} else {
PDEBUG(DEBUG_SIP, "RTP info given by remote, forward that\n");
media_type = (options.law=='a') ? MEDIA_TYPE_ALAW : MEDIA_TYPE_ULAW;
payload_type = (options.law=='a') ? PAYLOAD_TYPE_ALAW : PAYLOAD_TYPE_ULAW;
if (rtp_connect() < 0) {
goto rtp_failed;
}
sdp_str = generate_sdp(p_s_rtp_ip_local, p_s_rtp_port_local, 1, &payload_type, &media_type);
PDEBUG(DEBUG_SIP, "Using SDP response: %s\n", sdp_str);
nua_respond(p_s_handle, SIP_200_OK,
NUTAG_MEDIA_ENABLE(0),
SIPTAG_CONTENT_TYPE_STR("application/sdp"),
SIPTAG_PAYLOAD_STR(sdp_str), TAG_END());
}
return;
}
/* open local RTP peer (if not bridging) */
if (!p_s_rtp_bridge && rtp_open() < 0) {
nua_respond(nh, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
nua_handle_destroy(nh);
p_s_handle = NULL;
sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
add_trace("respond", "value", "500 Internal Server Error");
add_trace("reason", NULL, "failed to open RTP/RTCP sockts");
end_trace();
new_state(PORT_STATE_RELEASE);
trigger_work(&p_s_delete);
return;
}
/* apply handle */
// sip_trace_header(this, inst->interface_name, "NEW handle", DIRECTION_IN);
// add_trace("handle", "new", "0x%x", nh);
// end_trace();
//
p_s_handle = nh;
sip_trace_header(this, inst->interface_name, "INVITE", DIRECTION_IN);
add_trace("rtp", "port", "%d", p_s_rtp_port_remote);
/* caller information */
if (!from[0]) {
p_callerinfo.present = INFO_PRESENT_NOTAVAIL;
p_callerinfo.ntype = INFO_NTYPE_NOTPRESENT;
add_trace("calling", "present", "unavailable");
} else {
p_callerinfo.present = INFO_PRESENT_ALLOWED;
add_trace("calling", "present", "allowed");
p_callerinfo.screen = INFO_SCREEN_NETWORK;
p_callerinfo.ntype = INFO_NTYPE_UNKNOWN;
SCPY(p_callerinfo.id, from);
add_trace("calling", "number", "%s", from);
SCPY(p_callerinfo.name, name);
if (name[0])
add_trace("calling", "name", "%s", name);
SCPY(p_callerinfo.imsi, imsi);
if (imsi[0])
add_trace("calling", "imsi", "%s", imsi);
}
SCPY(p_callerinfo.interface, inst->interface_name);
/* dialing information */
if (to[0]) {
p_dialinginfo.ntype = INFO_NTYPE_UNKNOWN;
SCAT(p_dialinginfo.id, to);
add_trace("dialing", "number", "%s", to);
}
/* redir info */
/* bearer capability */
p_capainfo.bearer_capa = INFO_BC_SPEECH;
p_capainfo.bearer_info1 = (options.law=='a')?3:2;
p_capainfo.bearer_mode = INFO_BMODE_CIRCUIT;
add_trace("bearer", "capa", "speech");
add_trace("bearer", "mode", "circuit");
/* if packet mode works some day, see dss1.cpp for conditions */
p_capainfo.source_mode = B_MODE_TRANSPARENT;
end_trace();
/* create endpoint */
if (p_epointlist)
FATAL("Incoming call but already got an endpoint.\n");
if (!(epoint = new Endpoint(p_serial, 0)))
FATAL("No memory for Endpoint instance\n");
epoint->ep_app = new_endpointapp(epoint, 0, interface->app); //incoming
epointlist_new(epoint->ep_serial);
#ifdef NUTAG_AUTO100
/* send trying (proceeding) */
nua_respond(nh, SIP_100_TRYING, TAG_END());
sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
add_trace("respond", "value", "100 Trying");
end_trace();
#endif
new_state(PORT_STATE_IN_PROCEEDING);
/* send setup message to endpoit */
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_SETUP);
message->param.setup.port_type = p_type;
// message->param.setup.dtmf = 0;
memcpy(&message->param.setup.dialinginfo, &p_dialinginfo, sizeof(struct dialing_info));
memcpy(&message->param.setup.callerinfo, &p_callerinfo, sizeof(struct caller_info));
memcpy(&message->param.setup.capainfo, &p_capainfo, sizeof(struct capa_info));
// SCPY((char *)message->param.setup.useruser.data, useruser.info);
// message->param.setup.useruser.len = strlen(mncc->useruser.info);
// message->param.setup.useruser.protocol = mncc->useruser.proto;
if (p_s_rtp_bridge) {
int i;
PDEBUG(DEBUG_SIP, "sending setup with RTP info\n");
message->param.setup.rtpinfo.ip = p_s_rtp_ip_remote;
message->param.setup.rtpinfo.port = p_s_rtp_port_remote;
/* add codecs to setup message */
for (i = 0; i < payloads; i++) {
message->param.setup.rtpinfo.media_types[i] = media_types[i];
message->param.setup.rtpinfo.payload_types[i] = payload_types[i];
if (i == sizeof(message->param.setup.rtpinfo.payload_types))
break;
}
message->param.setup.rtpinfo.payloads = i;
}
message_put(message);
/* start option timer */
if (inst->options_interval) {
PDEBUG(DEBUG_SIP, "Invite received, scheduling option timer with %d seconds\n", inst->options_interval);
schedule_timer(&p_s_invite_option_timer, inst->options_interval, 0);
}
p_s_invite_direction = DIRECTION_IN;
/* send progress, if tones are available and if we don't bridge */
if (!p_s_rtp_bridge && interface->is_tones == IS_YES) {
PDEBUG(DEBUG_SIP, "Connecting audio, since we have tones available\n");
media_type = (options.law=='a') ? MEDIA_TYPE_ALAW : MEDIA_TYPE_ULAW;
payload_type = (options.law=='a') ? PAYLOAD_TYPE_ALAW : PAYLOAD_TYPE_ULAW;
/* open local RTP peer (if not bridging) */
if (rtp_connect() < 0) {
rtp_failed:
nua_respond(nh, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
nua_handle_destroy(nh);
p_s_handle = NULL;
sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
add_trace("respond", "value", "500 Internal Server Error");
add_trace("reason", NULL, "failed to connect RTP/RTCP sockts");
end_trace();
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE);
message->param.disconnectinfo.cause = 41;
message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
message_put(message);
new_state(PORT_STATE_RELEASE);
trigger_work(&p_s_delete);
return;
}
sdp_str = generate_sdp(p_s_rtp_ip_local, p_s_rtp_port_local, 1, &payload_type, &media_type);
PDEBUG(DEBUG_SIP, "Using SDP response: %s\n", sdp_str);
nua_respond(p_s_handle, SIP_183_SESSION_PROGRESS,
NUTAG_MEDIA_ENABLE(0),
SIPTAG_CONTENT_TYPE_STR("application/sdp"),
SIPTAG_PAYLOAD_STR(sdp_str), TAG_END());
sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
add_trace("respond", "value", "183 SESSION PROGRESS");
add_trace("reason", NULL, "audio available");
struct in_addr ia;
memset(&ia, 0, sizeof(ia));
ia.s_addr = htonl(get_local_ip(p_s_rtp_ip_local));
add_trace("rtp", "ip", "%s", inet_ntoa(ia));
add_trace("rtp", "port", "%d,%d", p_s_rtp_port_local, p_s_rtp_port_local + 1);
add_trace("rtp", "payload", "%s:%d", media_type2name(media_type), payload_type);
end_trace();
}
}
void Psip::i_options(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
{
struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
PDEBUG(DEBUG_SIP, "options received\n");
sip_trace_header(this, inst->interface_name, "OPTIONS", DIRECTION_IN);
end_trace();
nua_respond(nh, SIP_200_OK, TAG_END());
}
// code stolen from freeswitch....
static char RFC2833_CHARS[] = "0123456789*#ABCDF";
static char switch_rfc2833_to_char(int event)
{
if (event > -1 && event < (int32_t) sizeof(RFC2833_CHARS)) {
return RFC2833_CHARS[event];
}
return '\0';
}
void Psip::i_info(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
{
struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
char digit = '\0';
PDEBUG(DEBUG_SIP, "options received\n");
// code stolen from freeswitch....
if (sip && sip->sip_content_type && sip->sip_content_type->c_type && sip->sip_content_type->c_subtype && sip->sip_payload && sip->sip_payload->pl_data) {
if (!strncasecmp(sip->sip_content_type->c_type, "application", 11) && !strcasecmp(sip->sip_content_type->c_subtype, "dtmf-relay")) {
const char *signal_ptr;
if ((signal_ptr = strstr(sip->sip_payload->pl_data, "Signal="))) {
int tmp;
/* move signal_ptr where we need it (right past Signal=) */
signal_ptr = signal_ptr + 7;
/* handle broken devices with spaces after the = (cough) VegaStream (cough) */
while (*signal_ptr && *signal_ptr == ' ')
signal_ptr++;
if (*signal_ptr
&& (*signal_ptr == '*' || *signal_ptr == '#' || *signal_ptr == 'A' || *signal_ptr == 'B' || *signal_ptr == 'C'
|| *signal_ptr == 'D')) {
digit = *signal_ptr;
} else {
tmp = atoi(signal_ptr);
digit = switch_rfc2833_to_char(tmp);
}
}
} else if (!strncasecmp(sip->sip_content_type->c_type, "application", 11) && !strcasecmp(sip->sip_content_type->c_subtype, "dtmf")) {
int tmp = atoi(sip->sip_payload->pl_data);
digit = switch_rfc2833_to_char(tmp);
}
}
sip_trace_header(this, inst->interface_name, "INFO", DIRECTION_IN);
if (digit) {
char digitstr[2] = { digit, '\0' };
struct lcr_msg *message;
add_trace("dtmf", "digit", digitstr);
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DTMF);
message->param.dtmf = digit;
PDEBUG(DEBUG_SIP, "Psip(%s) INFO WITH DTMF digit '%c'\n", p_name, message->param.dtmf);
message_put(message);
}
end_trace();
nua_respond(nh, SIP_200_OK, TAG_END());
}
void Psip::i_bye(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
{
struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
struct lcr_msg *message;
int cause = 0;
PDEBUG(DEBUG_SIP, "bye received\n");
sip_trace_header(this, inst->interface_name, "BYE", DIRECTION_IN);
if (sip->sip_reason && sip->sip_reason->re_protocol && !strcasecmp(sip->sip_reason->re_protocol, "Q.850") && sip->sip_reason->re_cause) {
cause = atoi(sip->sip_reason->re_cause);
add_trace("cause", "value", "%d", cause);
}
end_trace();
// let stack do bye automaticall, since it will not accept our response for some reason
// nua_respond(nh, SIP_200_OK, TAG_END());
sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
add_trace("respond", "value", "200 OK");
end_trace();
PDEBUG(DEBUG_SIP, "just remove nua_handle without destruction %x\n", nh);
// nua_handle_destroy(nh);
p_s_handle = NULL;
rtp_close();
while(p_epointlist) {
/* send setup message to endpoit */
message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
message->param.disconnectinfo.cause = cause ? : 16;
message->param.disconnectinfo.location = LOCATION_BEYOND;
message_put(message);
/* remove epoint */
free_epointlist(p_epointlist);
}
new_state(PORT_STATE_RELEASE);
trigger_work(&p_s_delete);
}
void Psip::i_cancel(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
{
struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
struct lcr_msg *message;
PDEBUG(DEBUG_SIP, "cancel received\n");
sip_trace_header(this, inst->interface_name, "CANCEL", DIRECTION_IN);
end_trace();
PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
nua_handle_destroy(nh);
p_s_handle = NULL;
rtp_close();
while(p_epointlist) {
/* send setup message to endpoit */
message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
message->param.disconnectinfo.cause = 16;
message->param.disconnectinfo.location = LOCATION_BEYOND;
message_put(message);
/* remove epoint */
free_epointlist(p_epointlist);
}
new_state(PORT_STATE_RELEASE);
trigger_work(&p_s_delete);
}
void Psip::r_bye(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
{
PDEBUG(DEBUG_SIP, "bye response received\n");
PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
nua_handle_destroy(nh);
p_s_handle = NULL;
rtp_close();
trigger_work(&p_s_delete);
}
void Psip::r_cancel(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
{
PDEBUG(DEBUG_SIP, "cancel response received\n");
PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
nua_handle_destroy(nh);
p_s_handle = NULL;
rtp_close();
trigger_work(&p_s_delete);
}
void Psip::r_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
{
struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
struct lcr_msg *message;
int cause = 0, location = 0;
int media_types[32];
uint8_t payload_types[32];
int payloads = 0;
PDEBUG(DEBUG_SIP, "response to invite received (status = %d)\n", status);
sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
add_trace("respond", "value", "%d", status);
end_trace();
if (status == 401 || status == 407) {
PDEBUG(DEBUG_SIP, "Invite challenge received\n");
challenge(inst, this, status, phrase, nua, magic, nh, hmagic, sip, tags);
return;
}
/* connect audio */
if (status == 183 || (status >= 200 && status <= 299)) {
int ret;
sip_trace_header(this, inst->interface_name, "Payload received", DIRECTION_NONE);
ret = parse_sdp(sip, &p_s_rtp_ip_remote, &p_s_rtp_port_remote, payload_types, media_types, &payloads, sizeof(payload_types));
if (!ret) {
if (payloads != 1)
ret = 415;
else if (!p_s_rtp_bridge) {
if (media_types[0] != ((options.law=='a') ? MEDIA_TYPE_ALAW : MEDIA_TYPE_ULAW)) {
add_trace("error", NULL, "Expected LAW payload type (not bridged)");
ret = 415;
}
}
}
end_trace();
if (ret) {
nua_cancel(nh, TAG_END());
sip_trace_header(this, inst->interface_name, "CANCEL", DIRECTION_OUT);
add_trace("reason", NULL, "accepted codec does not match");
end_trace();
cause = 88;
location = LOCATION_PRIVATE_LOCAL;
goto release_with_cause;
}
/* connect to remote RTP (if not bridging) */
if (!p_s_rtp_bridge && rtp_connect() < 0) {
nua_cancel(nh, TAG_END());
sip_trace_header(this, inst->interface_name, "CANCEL", DIRECTION_OUT);
add_trace("reason", NULL, "failed to open RTP/RTCP sockts");
end_trace();
cause = 31;
location = LOCATION_PRIVATE_LOCAL;
goto release_with_cause;
}
}
/* start option timer */
if (inst->options_interval) {
PDEBUG(DEBUG_SIP, "Invite response, scheduling option timer with %d seconds\n", inst->options_interval);
schedule_timer(&p_s_invite_option_timer, inst->options_interval, 0);
}
switch (status) {
case 100:
#if 0
PDEBUG(DEBUG_SIP, "do proceeding\n");
new_state(PORT_STATE_OUT_PROCEEDING);
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_PROCEEDING);
message_put(message);
#endif
return;
case 180:
PDEBUG(DEBUG_SIP, "do alerting\n");
new_state(PORT_STATE_OUT_ALERTING);
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_ALERTING);
message_put(message);
return;
case 183:
PDEBUG(DEBUG_SIP, "do progress\n");
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_PROGRESS);
message->param.progressinfo.progress = 8;
message->param.progressinfo.location = 10;
if (p_s_rtp_bridge) {
message->param.progressinfo.rtpinfo.ip = p_s_rtp_ip_remote;
message->param.progressinfo.rtpinfo.port = p_s_rtp_port_remote;
message->param.progressinfo.rtpinfo.media_types[0] = media_types[0];
message->param.progressinfo.rtpinfo.payload_types[0] = payload_types[0];
message->param.progressinfo.rtpinfo.payloads = 1;
}
message_put(message);
return;
case 200:
status_200:
PDEBUG(DEBUG_SIP, "do connect\n");
nua_ack(nh, TAG_END());
new_state(PORT_STATE_CONNECT);
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CONNECT);
if (p_s_rtp_bridge) {
message->param.connectinfo.rtpinfo.ip = p_s_rtp_ip_remote;
message->param.connectinfo.rtpinfo.port = p_s_rtp_port_remote;
message->param.connectinfo.rtpinfo.media_types[0] = media_types[0];
message->param.connectinfo.rtpinfo.payload_types[0] = payload_types[0];
message->param.connectinfo.rtpinfo.payloads = 1;
}
message_put(message);
return;
default:
if (status >= 200 && status <= 299)
goto status_200;
if (status < 100 || status > 199)
break;
PDEBUG(DEBUG_SIP, "skipping 1xx message\n");
return;
}
cause = status2cause(status);
location = LOCATION_BEYOND;
release_with_cause:
PDEBUG(DEBUG_SIP, "do release (cause %d)\n", cause);
while(p_epointlist) {
/* send setup message to endpoit */
message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
message->param.disconnectinfo.cause = cause;
message->param.disconnectinfo.location = location;
message_put(message);
/* remove epoint */
free_epointlist(p_epointlist);
}
new_state(PORT_STATE_RELEASE);
rtp_close();
trigger_work(&p_s_delete);
}
void Psip::r_options(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
{
struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
int cause = 0, location = 0;
struct lcr_msg *message;
PDEBUG(DEBUG_SIP, "options result %d received\n", status);
if (status >= 200 && status <= 299) {
PDEBUG(DEBUG_SIP, "options ok, scheduling option timer with %d seconds\n", inst->options_interval);
/* restart option timer */
schedule_timer(&p_s_invite_option_timer, inst->options_interval, 0);
return;
}
PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
nua_handle_destroy(nh);
p_s_handle = NULL;
rtp_close();
cause = status2cause(status);
location = LOCATION_BEYOND;
while(p_epointlist) {
/* send setup message to endpoit */
message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
message->param.disconnectinfo.cause = cause;
message->param.disconnectinfo.location = location;
message_put(message);
/* remove epoint */
free_epointlist(p_epointlist);
}
new_state(PORT_STATE_RELEASE);
trigger_work(&p_s_delete);
}
void Psip::i_state(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
{
struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
PDEBUG(DEBUG_SIP, "state change received\n");
sip_trace_header(this, inst->interface_name, "STATUS", DIRECTION_OUT);
add_trace("value", NULL, "%d", status);
add_trace("phrase", NULL, "%s", phrase);
end_trace();
}
static void sip_callback(nua_event_t event, int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
{
struct sip_inst *inst = (struct sip_inst *) magic;
class Port *port;
class Psip *psip = NULL;
PDEBUG(DEBUG_SIP, "Event %d from SIP stack received (handle=%p)\n", event, nh);
if (!nh)
return;
/* hunt for existing handles */
port = port_first;
while(port) {
if ((port->p_type & PORT_CLASS_mISDN_MASK) == PORT_CLASS_SIP) {
psip = (class Psip *)port;
if (psip->p_s_handle == nh) {
PDEBUG(DEBUG_SIP, "Event found for port %s\n", psip->p_name);
break;
}
}
port = port->next;
}
if (!port)
psip = NULL;
/* new handle */
switch (event) {
case nua_i_options:
if (!psip) {
i_options(inst, status, phrase, nua, magic, nh, hmagic, sip, tags);
if (inst->register_handle != nh) {
PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
nua_handle_destroy(nh);
}
return;
}
break;
case nua_r_options:
if (!psip) {
r_options(inst, status, phrase, nua, magic, nh, hmagic, sip, tags);
return;
}
break;
case nua_i_register:
if (!psip && !inst->register_handle) {
PDEBUG(DEBUG_SIP, "New register instance\n");
inst->register_handle = nh;
}
if (!psip) {
i_register(inst, status, phrase, nua, magic, nh, hmagic, sip, tags);
return;
}
break;
case nua_r_register:
if (!psip) {
r_register(inst, status, phrase, nua, magic, nh, hmagic, sip, tags);
return;
}
break;
case nua_i_invite:
if (!psip) {
char name[64];
struct interface *interface = interface_first;
PDEBUG(DEBUG_SIP, "New psip instance\n");
/* create call instance */
SPRINT(name, "%s-%d-in", inst->interface_name, 0);
while (interface) {
if (!strcmp(interface->name, inst->interface_name))
break;
interface = interface->next;
}
if (!interface)
FATAL("Cannot find interface %s.\n", inst->interface_name);
if (!(psip = new Psip(PORT_TYPE_SIP_IN, name, NULL, interface)))
FATAL("Cannot create Port instance.\n");
}
break;
case nua_i_outbound:
PDEBUG(DEBUG_SIP, "Outbound status\n");
break;
default:
;
}
/* handle port process */
if (!psip) {
if (nh != inst->register_handle) {
PERROR("no SIP Port found for handel %p\n", nh);
nua_respond(nh, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
nua_handle_destroy(nh);
}
return;
}
switch (event) {
case nua_r_set_params:
PDEBUG(DEBUG_SIP, "setparam response\n");
break;
case nua_r_options:
psip->r_options(status, phrase, nua, magic, nh, hmagic, sip, tags);
break;
case nua_i_error:
PDEBUG(DEBUG_SIP, "error received\n");
break;
case nua_i_state:
psip->i_state(status, phrase, nua, magic, nh, hmagic, sip, tags);
break;
case nua_i_invite:
psip->i_invite(status, phrase, nua, magic, nh, hmagic, sip, tags);
break;
case nua_i_ack:
PDEBUG(DEBUG_SIP, "ack received\n");
break;
case nua_i_active:
PDEBUG(DEBUG_SIP, "active received\n");
break;
case nua_i_options:
psip->i_options(status, phrase, nua, magic, nh, hmagic, sip, tags);
break;
case nua_i_info:
psip->i_info(status, phrase, nua, magic, nh, hmagic, sip, tags);
break;
case nua_i_bye:
psip->i_bye(status, phrase, nua, magic, nh, hmagic, sip, tags);
break;
case nua_i_cancel:
psip->i_cancel(status, phrase, nua, magic, nh, hmagic, sip, tags);
break;
case nua_r_bye:
psip->r_bye(status, phrase, nua, magic, nh, hmagic, sip, tags);
break;
case nua_r_cancel:
psip->r_cancel(status, phrase, nua, magic, nh, hmagic, sip, tags);
break;
case nua_r_invite:
psip->r_invite(status, phrase, nua, magic, nh, hmagic, sip, tags);
break;
case nua_i_terminated:
PDEBUG(DEBUG_SIP, "terminated received\n");
break;
default:
PDEBUG(DEBUG_SIP, "Event %d not handled\n", event);
}
}
static void stun_bind_cb(stun_discovery_magic_t *magic, stun_handle_t *sh, stun_discovery_t *sd, stun_action_t action, stun_state_t event)
{
struct sip_inst *inst = (struct sip_inst *) magic;
su_sockaddr_t sa;
socklen_t addrlen;
PDEBUG(DEBUG_SIP, "Event %d from STUN stack received\n", event);
switch (event) {
case stun_discovery_done:
addrlen = sizeof(sa);
memset(&sa, 0, addrlen);
if (stun_discovery_get_address(sd, &sa, &addrlen) < 0) {
PDEBUG(DEBUG_SIP, "stun_discovery_get_address failed\n");
goto failed;
}
su_inet_ntop(sa.su_family, SU_ADDR(&sa), inst->public_ip, sizeof(inst->public_ip));
inst->stun_state = STUN_STATE_RESOLVED;
/* start timer for next stun request with inst->stun_interval */
schedule_timer(&inst->stun_retry_timer, inst->stun_interval, 0);
sip_trace_header(NULL, inst->interface_name, "STUN resolved", DIRECTION_OUT);
add_trace("ip", "addr", "%s", inst->public_ip);
end_trace();
break;
default:
failed:
PDEBUG(DEBUG_SIP, "STUN failed, starting timer\n");
inst->stun_state = STUN_STATE_FAILED;
/* start timer for next stun request (after failing) with STUN_RETRY_TIMER */
schedule_timer(&inst->stun_retry_timer, STUN_RETRY_TIMER);
sip_trace_header(NULL, inst->interface_name, "STUN failed", DIRECTION_OUT);
end_trace();
}
}
/* received shutdown due to termination of RTP */
void Psip::rtp_shutdown(void)
{
struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
struct lcr_msg *message;
PDEBUG(DEBUG_SIP, "RTP stream terminated\n");
sip_trace_header(this, inst->interface_name, "RTP terminated", DIRECTION_IN);
end_trace();
PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", p_s_handle);
nua_handle_destroy(p_s_handle);
p_s_handle = NULL;
while(p_epointlist) {
/* send setup message to endpoit */
message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
message->param.disconnectinfo.cause = 16;
message->param.disconnectinfo.location = LOCATION_BEYOND;
message_put(message);
/* remove epoint */
free_epointlist(p_epointlist);
}
new_state(PORT_STATE_RELEASE);
trigger_work(&p_s_delete);
}
static int invite_option_timer(struct lcr_timer *timer, void *instance, int index)
{
class Psip *psip = (class Psip *)instance;
struct sip_inst *inst = (struct sip_inst *) psip->p_s_sip_inst;
PDEBUG(DEBUG_SIP, "invite options timer fired\n");
sip_trace_header(psip, inst->interface_name, "OPTIONS", psip->p_s_invite_direction);
end_trace();
nua_options(psip->p_s_handle,
TAG_END());
return 0;
}
static int stun_retry_timer(struct lcr_timer *timer, void *instance, int index)
{
struct sip_inst *inst = (struct sip_inst *)instance;
PDEBUG(DEBUG_SIP, "timeout, restart stun lookup\n");
inst->stun_state = STUN_STATE_UNRESOLVED;
return 0;
}
static int register_retry_timer(struct lcr_timer *timer, void *instance, int index)
{
struct sip_inst *inst = (struct sip_inst *)instance;
PDEBUG(DEBUG_SIP, "timeout, restart register\n");
/* if we have a handle, destroy it and becom unregistered, so registration is
* triggered next */
if (inst->register_handle) {
/* stop option timer */
unsched_timer(&inst->register_option_timer);
PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", inst->register_handle);
nua_handle_destroy(inst->register_handle);
inst->register_handle = NULL;
}
inst->register_state = REGISTER_STATE_UNREGISTERED;
return 0;
}
static int register_option_timer(struct lcr_timer *timer, void *instance, int index)
{
struct sip_inst *inst = (struct sip_inst *)instance;
PDEBUG(DEBUG_SIP, "register options timer fired\n");
sip_trace_header(NULL, inst->interface_name, "OPTIONS", DIRECTION_OUT);
end_trace();
nua_options(inst->register_handle,
TAG_END());
return 0;
}
int sip_init_inst(struct interface *interface)
{
struct sip_inst *inst = (struct sip_inst *) MALLOC(sizeof(*inst));
char local[256];
interface->sip_inst = inst;
SCPY(inst->interface_name, interface->name);
SCPY(inst->local_peer, interface->sip_local_peer);
SCPY(inst->remote_peer, interface->sip_remote_peer);
if (!inst->remote_peer[0])
inst->allow_register = 1;
SCPY(inst->asserted_id, interface->sip_asserted_id);
if (interface->sip_register) {
inst->register_state = REGISTER_STATE_UNREGISTERED;
SCPY(inst->register_user, interface->sip_register_user);
SCPY(inst->register_host, interface->sip_register_host);
}
SCPY(inst->auth_user, interface->sip_auth_user);
SCPY(inst->auth_password, interface->sip_auth_password);
SCPY(inst->auth_realm, interface->sip_auth_realm);
inst->register_interval = interface->sip_register_interval;
inst->options_interval = interface->sip_options_interval;
inst->rtp_port_from = interface->rtp_port_from;
inst->rtp_port_to = interface->rtp_port_to;
if (!inst->rtp_port_from || !inst->rtp_port_to) {
inst->rtp_port_from = RTP_PORT_BASE;
inst->rtp_port_to = RTP_PORT_MAX;
}
inst->next_rtp_port = inst->rtp_port_from;
/* create timers */
memset(&inst->stun_retry_timer, 0, sizeof(inst->stun_retry_timer));
add_timer(&inst->stun_retry_timer, stun_retry_timer, inst, 0);
memset(&inst->register_retry_timer, 0, sizeof(inst->register_retry_timer));
add_timer(&inst->register_retry_timer, register_retry_timer, inst, 0);
memset(&inst->register_option_timer, 0, sizeof(inst->register_option_timer));
add_timer(&inst->register_option_timer, register_option_timer, inst, 0);
/* init root object */
inst->root = su_root_create(inst);
if (!inst->root) {
PERROR("Failed to create SIP root\n");
sip_exit_inst(interface);
return -EINVAL;
}
SPRINT(local, "sip:%s",inst->local_peer);
if (!strchr(inst->local_peer, ':'))
SCAT(local, ":5060");
inst->nua = nua_create(inst->root, sip_callback, inst, NUTAG_URL(local), TAG_END());
if (!inst->nua) {
PERROR("Failed to create SIP stack object\n");
sip_exit_inst(interface);
return -EINVAL;
}
nua_set_params(inst->nua,
SIPTAG_ALLOW_STR("REGISTER,INVITE,ACK,BYE,CANCEL,OPTIONS,NOTIFY,INFO"),
NUTAG_APPL_METHOD("REGISTER"),
NUTAG_APPL_METHOD("INVITE"),
NUTAG_APPL_METHOD("ACK"),
// NUTAG_APPL_METHOD("BYE"), /* we must reply to BYE */
NUTAG_APPL_METHOD("CANCEL"),
NUTAG_APPL_METHOD("OPTIONS"),
NUTAG_APPL_METHOD("NOTIFY"),
NUTAG_APPL_METHOD("INFO"),
NUTAG_AUTOACK(0),
#ifdef NUTAG_AUTO100
NUTAG_AUTO100(0),
#endif
NUTAG_AUTOALERT(0),
NUTAG_AUTOANSWER(0),
TAG_NULL());
SCPY(inst->public_ip, interface->sip_public_ip);
if (interface->sip_stun_server[0]) {
SCPY(inst->stun_server, interface->sip_stun_server);
inst->stun_interval = interface->sip_stun_interval;
inst->stun_handle = stun_handle_init(inst->root,
STUNTAG_SERVER(inst->stun_server),
TAG_NULL());
if (!inst->stun_handle) {
PERROR("Failed to create STUN handle\n");
sip_exit_inst(interface);
return -EINVAL;
}
inst->stun_socket = su_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (inst->stun_socket < 0) {
PERROR("Failed to create STUN socket\n");
sip_exit_inst(interface);
return -EINVAL;
}
inst->stun_state = STUN_STATE_UNRESOLVED;
}
PDEBUG(DEBUG_SIP, "SIP interface created (inst=%p)\n", inst);
any_sip_interface = 1;
return 0;
}
void sip_exit_inst(struct interface *interface)
{
struct sip_inst *inst = (struct sip_inst *) interface->sip_inst;
if (!inst)
return;
del_timer(&inst->stun_retry_timer);
del_timer(&inst->register_retry_timer);
del_timer(&inst->register_option_timer);
if (inst->stun_socket)
su_close(inst->stun_socket);
if (inst->stun_handle)
stun_handle_destroy(inst->stun_handle);
if (inst->register_handle)
nua_handle_destroy(inst->register_handle);
if (inst->root)
su_root_destroy(inst->root);
if (inst->nua)
nua_destroy(inst->nua);
FREE(inst, sizeof(*inst));
interface->sip_inst = NULL;
PDEBUG(DEBUG_SIP, "SIP interface removed\n");
/* check if there is any other SIP interface left */
interface = interface_first;
while (interface) {
if (interface->sip_inst)
break;
interface = interface->next;
}
if (!interface)
any_sip_interface = 0;
}
extern su_log_t su_log_default[];
extern su_log_t nua_log[];
int sip_init(void)
{
int i;
/* init SOFIA lib */
su_init();
su_home_init(sip_home);
if (options.deb & DEBUG_SIP) {
su_log_set_level(su_log_default, 9);
su_log_set_level(nua_log, 9);
//su_log_set_level(soa_log, 9);
}
for (i = 0; i < 256; i++)
flip[i] = ((i & 1) << 7) + ((i & 2) << 5) + ((i & 4) << 3) + ((i & 8) << 1) + ((i & 16) >> 1) + ((i & 32) >> 3) + ((i & 64) >> 5) + ((i & 128) >> 7);
PDEBUG(DEBUG_SIP, "SIP globals initialized\n");
return 0;
}
void sip_exit(void)
{
su_home_deinit(sip_home);
su_deinit();
PDEBUG(DEBUG_SIP, "SIP globals de-initialized\n");
}
static void sip_handle_stun(struct sip_inst *inst)
{
int rc;
switch (inst->stun_state) {
case STUN_STATE_UNRESOLVED:
PDEBUG(DEBUG_SIP, "Trying to to get local IP from stun server\n");
rc = stun_bind(inst->stun_handle, stun_bind_cb, (stun_discovery_magic_t *)inst,
STUNTAG_SOCKET(inst->stun_socket),
STUNTAG_REGISTER_EVENTS(1),
TAG_NULL());
if (rc < 0) {
PERROR("Failed to call stun_bind()\n");
inst->stun_state = STUN_STATE_FAILED;
break;
}
inst->stun_state = STUN_STATE_RESOLVING;
sip_trace_header(NULL, inst->interface_name, "STUN resolving", DIRECTION_OUT);
add_trace("server", "addr", "%s", inst->stun_server);
end_trace();
break;
}
}
static void sip_handle_register(struct sip_inst *inst)
{
char from[128] = "";
char to[128] = "";
char contact[128] = "";
char expires[128] = "";
switch (inst->register_state) {
case REGISTER_STATE_UNREGISTERED:
/* wait for resoved stun */
if (inst->stun_handle && inst->stun_state != STUN_STATE_RESOLVED)
return;
PDEBUG(DEBUG_SIP, "Registering to peer\n");
inst->register_handle = nua_handle(inst->nua, NULL, TAG_END());
if (!inst->register_handle) {
PERROR("Failed to create handle\n");
inst->register_state = REGISTER_STATE_FAILED;
break;
}
/* apply handle to trace */
// sip_trace_header(NULL, inst->interface_name, "NEW handle", DIRECTION_NONE);
// add_trace("handle", "new", "0x%x", inst->register_handle);
// end_trace();
SPRINT(from, "sip:%s@%s", inst->register_user, inst->register_host);
SPRINT(to, "sip:%s@%s", inst->register_user, inst->register_host);
if (inst->public_ip[0]) {
char *p;
SPRINT(contact, "sip:%s@%s", inst->register_user, inst->public_ip);
p = strchr(inst->local_peer, ':');
if (p)
SCAT(contact, p);
}
if (inst->register_interval) {
SPRINT(expires, "%d", inst->register_interval + 60);
}
sip_trace_header(NULL, inst->interface_name, "REGISTER", DIRECTION_OUT);
add_trace("from", "uri", "%s", from);
add_trace("to", "uri", "%s", to);
if (expires[0])
add_trace("expires", NULL, "%s", expires);
end_trace();
nua_register(inst->register_handle,
TAG_IF(from[0], SIPTAG_FROM_STR(from)),
TAG_IF(to[0], SIPTAG_TO_STR(to)),
TAG_IF(contact[0], SIPTAG_CONTACT_STR(contact)),
TAG_IF(expires[0], SIPTAG_EXPIRES_STR(expires)),
TAG_END());
inst->register_state = REGISTER_STATE_REGISTERING;
break;
}
}
void sip_handle(void)
{
struct interface *interface = interface_first;
struct sip_inst *inst;
while (interface) {
if (interface->sip_inst) {
inst = (struct sip_inst *) interface->sip_inst;
su_root_step(inst->root, 0);
sip_handle_stun(inst);
sip_handle_register(inst);
}
interface = interface->next;
}
}
/* deletes when back in event loop */
static int delete_event(struct lcr_work *work, void *instance, int index)
{
class Psip *psip = (class Psip *)instance;
delete psip;
return 0;
}
/*
* generate audio, if no data is received from bridge
*/
void Psip::set_tone(const char *dir, const char *tone)
{
Port::set_tone(dir, tone);
update_load();
}
void Psip::update_load(void)
{
/* don't trigger load event if event already active */
if (p_s_load_timer.active)
return;
/* don't start timer if ... */
if (!p_tone_name[0] && !p_dov_tx)
return;
p_s_next_tv_sec = 0;
schedule_timer(&p_s_load_timer, 0, 0); /* no delay the first time */
}
static int load_timer(struct lcr_timer *timer, void *instance, int index)
{
class Psip *psip = (class Psip *)instance;
/* stop timer if ... */
if (!psip->p_tone_name[0] && !psip->p_dov_tx)
return 0;
psip->load_tx();
return 0;
}
#define SEND_SIP_LEN 160
void Psip::load_tx(void)
{
int diff;
struct timeval current_time;
int tosend = SEND_SIP_LEN, i;
unsigned char buf[SEND_SIP_LEN], *p = buf;
/* get elapsed */
gettimeofday(&current_time, NULL);
if (!p_s_next_tv_sec) {
/* if timer expired the first time, set next expected timeout 160 samples in advance */
p_s_next_tv_sec = current_time.tv_sec;
p_s_next_tv_usec = current_time.tv_usec + SEND_SIP_LEN * 125;
if (p_s_next_tv_usec >= 1000000) {
p_s_next_tv_usec -= 1000000;
p_s_next_tv_sec++;
}
schedule_timer(&p_s_load_timer, 0, SEND_SIP_LEN * 125);
} else {
diff = 1000000 * (current_time.tv_sec - p_s_next_tv_sec)
+ (current_time.tv_usec - p_s_next_tv_usec);
if (diff < -SEND_SIP_LEN * 125 || diff > SEND_SIP_LEN * 125) {
/* if clock drifts too much, set next timeout event to current timer + 160 */
diff = 0;
p_s_next_tv_sec = current_time.tv_sec;
p_s_next_tv_usec = current_time.tv_usec + SEND_SIP_LEN * 125;
if (p_s_next_tv_usec >= 1000000) {
p_s_next_tv_usec -= 1000000;
p_s_next_tv_sec++;
}
} else {
/* if diff is positive, it took too long, so next timeout will be earlier */
p_s_next_tv_usec += SEND_SIP_LEN * 125;
if (p_s_next_tv_usec >= 1000000) {
p_s_next_tv_usec -= 1000000;
p_s_next_tv_sec++;
}
}
schedule_timer(&p_s_load_timer, 0, SEND_SIP_LEN * 125 - diff);
}
/* copy tones */
if (p_tone_name[0]) {
tosend -= read_audio(p, tosend);
} else
if (p_dov_tx) {
tosend -= dov_tx(p, tosend);
}
if (tosend) {
PERROR("buffer is not completely filled\n");
return;
}
p = buf;
for (i = 0; i < SEND_SIP_LEN; i++) {
*p = flip[*p];
p++;
}
/* transmit data via rtp */
rtp_send_frame(buf, SEND_SIP_LEN, (options.law=='a')?PAYLOAD_TYPE_ALAW:PAYLOAD_TYPE_ULAW);
}