From 863bc6421940efe897dfd6d610e1f86ed9992cf6 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 13 Jan 2012 06:24:21 +0100 Subject: [PATCH] Adding basic SIP support, using Sofia-SIP stack This support is just a simple peer-to-peer support for basic calls. Currently it requires mISDN_l1loop interface, as every non-ISDN interface does. Later it will be possible to use it without. --- Makefile.am | 24 +- apppbx.cpp | 10 + configure.ac | 15 + interface.c | 43 ++ interface.h | 6 + mISDN.cpp | 10 +- mISDN.h | 2 +- main.c | 15 + main.h | 7 +- port.h | 4 + sip.cpp | 1768 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ sip.h | 73 +++ 12 files changed, 1965 insertions(+), 12 deletions(-) create mode 100644 sip.cpp create mode 100644 sip.h diff --git a/Makefile.am b/Makefile.am index 61ddb8c..a97a7ec 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,7 +3,7 @@ ## This file is part of linux-call-router ## Copyright (C) 2007 Joerg Habenicht (j.habenicht@gmx.de) ## Copyright (C) 2008 Peter Schlaile (peter -at- schlaile.de) -## Copyright (C) 2008 Andreas Eversberg (andreas@eversberg.eu) +## Copyright (C) 2008-2012 Andreas Eversberg (andreas@eversberg.eu) ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License as @@ -84,6 +84,22 @@ SS5_SOURCE = ss5.cpp ss5_encode.c ss5_decode.c endif +SIP_LIB = + +if ENABLE_SIP + +# FIXME: remove that +#pkgconfigdir = $(libdir)/pkgconfig +#pkgconfig_DATA = sofia-sip-ua.pc + +SIP_INCLUDE = -DWITH_SIP $(SOFIA_CFLAGS) + +SIP_SOURCE = sip.cpp + +SIP_LIB += $(SOFIA_LIBS) + +endif + bin_PROGRAMS = lcradmin gentones genwave sbin_PROGRAMS = lcr genrc genextension @@ -115,9 +131,9 @@ install-exec-hook: $(INSTALL) chan_lcr.so $(astmoddir) endif -INCLUDES = $(all_includes) $(GSM_INCLUDE) $(SS5_INCLUDE) -Wall $(INSTALLATION_DEFINES) +INCLUDES = $(all_includes) $(GSM_INCLUDE) $(SS5_INCLUDE) $(SIP_INCLUDE) -Wall $(INSTALLATION_DEFINES) -lcr_SOURCES = $(GSM_SOURCE) $(SS5_SOURCE) select.c action.cpp mISDN.cpp \ +lcr_SOURCES = $(GSM_SOURCE) $(SS5_SOURCE) $(SIP_SOURCE) select.c action.cpp mISDN.cpp \ tones.c loop.c remote.cpp action_efi.cpp crypt.cpp mail.c trace.c \ action_vbox.cpp dss1.cpp main.c \ vbox.cpp alawulaw.c endpoint.cpp interface.c message.c \ @@ -126,7 +142,7 @@ lcr_SOURCES = $(GSM_SOURCE) $(SS5_SOURCE) select.c action.cpp mISDN.cpp \ callerid.c joinremote.cpp route.c \ cause.c socket_server.c -lcr_LDADD = $(LIBCRYPTO) -lmisdn -lpthread $(GSM_LIB) +lcr_LDADD = $(LIBCRYPTO) -lmisdn -lpthread $(GSM_LIB) $(SIP_LIB) lcradmin_SOURCES = lcradmin.c cause.c options.c diff --git a/apppbx.cpp b/apppbx.cpp index fff80ce..d3937a7 100644 --- a/apppbx.cpp +++ b/apppbx.cpp @@ -990,6 +990,11 @@ void EndpointAppPBX::out_setup(int cfnr) port = new Pgsm_ms(PORT_TYPE_GSM_MS_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode); else #endif +#ifdef WITH_SIP + if (mISDNport->ifport->interface->sip) + port = new Psip(PORT_TYPE_SIP_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode, mISDNport->ifport->interface); + else +#endif if (mISDNport->ifport->remote) { admin = admin_first; while(admin) { @@ -1224,6 +1229,11 @@ void EndpointAppPBX::out_setup(int cfnr) port = new Pgsm_ms(PORT_TYPE_GSM_MS_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode); else #endif +#ifdef WITH_SIP + if (mISDNport->ifport->interface->sip) + port = new Psip(PORT_TYPE_SIP_OUT, mISDNport, portname, &port_settings, channel, mISDNport->ifport->channel_force, mode, mISDNport->ifport->interface); + else +#endif if (mISDNport->ifport->remote) { admin = admin_first; while(admin) { diff --git a/configure.ac b/configure.ac index 0c7e67d..343409a 100644 --- a/configure.ac +++ b/configure.ac @@ -196,6 +196,20 @@ AC_ARG_WITH([ss5], AM_CONDITIONAL(ENABLE_SS5, test "x$with_ss5" == "xyes" ) +# check for SIP +AC_ARG_WITH([sip], + [AS_HELP_STRING([--with-sip], + [compile with SIP support (sofia-sip is required) @<:@default=no@:>@]) + ], + [], + [with_sip="check"]) + +AM_CONDITIONAL(ENABLE_SIP, test "x$with_sip" == "xyes" ) + +AS_IF([test "x$with_sip" == xyes -o "x$with_sip" == xyes], [ + PKG_CHECK_MODULES(SOFIA, sofia-sip-ua >= 1.12) + ]) + # Checks for libraries. AC_CHECK_LIB([m], [main]) AC_CHECK_LIB([ncurses], [main]) @@ -237,4 +251,5 @@ AS_IF([test "x$with_gsm_bs" == xyes],[AC_MSG_NOTICE( Compiled with GSM network s AS_IF([test "x$with_gsm_ms" == xyes],[AC_MSG_NOTICE( Compiled with GSM mobile side support )],[AC_MSG_NOTICE( Not compiled with GSM mobile side support)]) AS_IF([test "x$with_asterisk" == xyes],[AC_MSG_NOTICE( Compiled with Asterisk channel driver support )],[AC_MSG_NOTICE( Not compiled with Asterisk channel driver support)]) AS_IF([test "x$with_ss5" == xyes],[AC_MSG_NOTICE( Compiled with CCITT No.5 support )],[AC_MSG_NOTICE( Not compiled with CCITT No.5 support)]) +AS_IF([test "x$with_sip" == xyes],[AC_MSG_NOTICE( Compiled with SIP support )],[AC_MSG_NOTICE( Not compiled with SIP support)]) diff --git a/interface.c b/interface.c index 3c1a792..264f4f2 100644 --- a/interface.c +++ b/interface.c @@ -967,6 +967,36 @@ static int inter_gsm_ms(struct interface *interface, char *filename, int line, c return(0); #endif } +static int inter_sip(struct interface *interface, char *filename, int line, char *parameter, char *value) +{ +#ifndef WITH_SIP + SPRINT(interface_error, "Error in %s (line %d): SIP not compiled in.\n", filename, line); + return(-1); +#else + char *p; + + /* set portname */ + if (inter_portname(interface, filename, line, (char *)"portname", options.loopback_lcr)) + return(-1); + + interface->sip = 1; + + /* copy values */ + if (!value || !value[0]) { + SPRINT(interface_error, "Error in %s (line %d): Missing SIP local IP.\n", filename, line); + return(-1); + } + p = get_seperated(value); + if (!p[0]) { + SPRINT(interface_error, "Error in %s (line %d): Missing SIP remote IP.\n", filename, line); + return(-1); + } + SCPY(interface->sip_local_ip, value); + SCPY(interface->sip_remote_ip, p); + + return(0); +#endif +} static int inter_nonotify(struct interface *interface, char *filename, int line, char *parameter, char *value) { struct interface_port *ifport; @@ -1214,6 +1244,9 @@ struct interface_param interface_param[] = { "The name of the MS folows the interface name.\n" "The socket is /tmp/osmocom_l2 by default and need to be changed when multiple\n" "MS interfaces are used."}, + {"sip", &inter_sip, " ", + "Sets up SIP interface that represents one SIP endpoint.\n" + "Give SIP configuration file."}, {"nonotify", &inter_nonotify, "", "Prevents sending notify messages to this interface. A call placed on hold will\n" "Not affect the remote end (phone or telcom switch).\n" @@ -1507,6 +1540,11 @@ void relink_interfaces(void) if (ifport->gsm_bs) gsm_bs_exit(0); #endif +#ifdef WITH_SIP +#warning FIXME: get out of mISDNport + if (ifport->interface->sip) + sip_exit_inst(mISDNport->ifport->interface); +#endif } mISDNport->ifport = NULL; mISDNport = mISDNport->next; @@ -1595,6 +1633,11 @@ void load_port(struct interface_port *ifport) if (ifport->gsm_bs) gsm_bs_init(); #endif +#ifdef WITH_SIP + if (ifport->interface->sip) + if (sip_init_inst(ifport->interface)) + ifport->block = 2; /* not available */ +#endif } else { ifport->block = 2; /* not available */ } diff --git a/interface.h b/interface.h index 2dd9889..d9462ed 100644 --- a/interface.h +++ b/interface.h @@ -111,6 +111,12 @@ struct interface { char pipeline[256]; /* filter pipeline */ unsigned char bf_key[56]; /* filter blowfish */ int bf_len; /* filter length of blowfish */ +#ifdef WITH_SIP + int sip; /* interface is a SIP interface */ + char sip_local_ip[16]; + char sip_remote_ip[16]; + void *sip_inst; /* sip instance */ +#endif }; struct interface_param { diff --git a/mISDN.cpp b/mISDN.cpp index 75a1b62..d683bcd 100644 --- a/mISDN.cpp +++ b/mISDN.cpp @@ -2130,15 +2130,15 @@ struct mISDNport *mISDNport_open(struct interface_port *ifport) struct mISDN_devinfo devinfo; unsigned int protocol, prop; -#if defined WITH_GSM_BS && defined WITH_GSM_MS - loop = ifport->gsm_ms | ifport->gsm_bs; -#else + loop = 0; #ifdef WITH_GSM_BS - loop = ifport->gsm_bs; + loop |= ifport->gsm_bs; #endif #ifdef WITH_GSM_MS - loop = ifport->gsm_ms; + loop |= ifport->gsm_ms; #endif +#ifdef WITH_SIP + loop |= ifport->interface->sip; #endif //printf("%s == %s\n", ifport->portname, options.loopback_int); if (!strcmp(ifport->portname, options.loopback_lcr)) diff --git a/mISDN.h b/mISDN.h index de223c8..859b073 100644 --- a/mISDN.h +++ b/mISDN.h @@ -69,7 +69,7 @@ struct mISDNport { int gsm_bs; /* this is the (only) GSM BS interface */ #endif #ifdef WITH_GSM_MS - int gsm_ms; /* this is the an GSM MS interface */ + int gsm_ms; /* this is an GSM MS interface */ #endif int lcr_sock; /* socket of loopback on LCR side */ int isloopback; /* will be set on open, in case it is a loopback if */ diff --git a/main.c b/main.c index 84f357f..07cfb8f 100644 --- a/main.c +++ b/main.c @@ -362,6 +362,12 @@ int main(int argc, char *argv[]) /* generate alaw / ulaw tables */ generate_tables(options.law); +#ifdef WITH_SIP + /* init SIP globals */ + sip_init(); + polling = 1; /* must poll, because of SIP events */ +#endif + #ifdef WITH_SS5 /* init ss5 sine tables */ ss5_sine_generate(); @@ -480,6 +486,10 @@ init is done when interface is up #else if (options.polling) { if (!select_main(1, NULL, NULL, NULL)) { +#ifdef WITH_SIP + /* FIXME: check if work was done */ + sip_handle(); +#endif usleep(10000); } } else @@ -600,6 +610,11 @@ exit is done when interface is down gsm_exit(0); #endif +#ifdef WITH_SIP + /* cleanup SIP globals */ + sip_exit(); +#endif + /* close loopback, if used by GSM or remote */ if (mISDNloop.sock > -1) mISDNloop_close(); diff --git a/main.h b/main.h index 7aad3c2..a0295b5 100644 --- a/main.h +++ b/main.h @@ -69,13 +69,13 @@ void debug(const char *function, int line, const char *prefix, char *buffer); #define DEBUG_GSM 0x0120 #define DEBUG_SS5 0x0140 #define DEBUG_VBOX 0x0180 +#define DEBUG_SIP 0x10100 #define DEBUG_EPOINT 0x0200 #define DEBUG_JOIN 0x0400 #define DEBUG_VERSATEL 0x0800 #define DEBUG_CRYPT 0x1000 #define DEBUG_ROUTE 0x2000 #define DEBUG_IDLETIME 0x4000 -#define DEBUG_LOG 0x7fff // check any faulty malloc #define MALLOC_CHECK_ 1 @@ -83,7 +83,7 @@ void debug(const char *function, int line, const char *prefix, char *buffer); /* * one of the bits must be enabled in order to write log files */ -#define DEBUG_LOG 0x7fff +#define DEBUG_LOG 0xfffff /* * load transmit buffer to avoid gaps at the beginning due to jitter @@ -164,6 +164,9 @@ extern "C" { #include "ss5_decode.h" #include "ss5.h" #endif +#ifdef WITH_SIP +#include "sip.h" +#endif #include "vbox.h" #include "join.h" #include "joinpbx.h" diff --git a/port.h b/port.h index a4da7de..0d6a5f9 100644 --- a/port.h +++ b/port.h @@ -23,6 +23,7 @@ #define PORT_CLASS_GSM_MS 0x1220 #define PORT_CLASS_SS5 0x1300 #define PORT_CLASS_REMOTE 0x1400 +#define PORT_CLASS_SIP 0x1500 #define PORT_CLASS_MASK 0xf000 #define PORT_CLASS_mISDN_MASK 0xff00 #define PORT_CLASS_DSS1_MASK 0xfff0 @@ -48,6 +49,9 @@ /* remote */ #define PORT_TYPE_REMOTE_IN 0x1411 #define PORT_TYPE_REMOTE_OUT 0x1412 + /* SIP */ +#define PORT_TYPE_SIP_IN 0x1511 +#define PORT_TYPE_SIP_OUT 0x1512 /* answering machine */ #define PORT_TYPE_VBOX_OUT 0x3111 diff --git a/sip.cpp b/sip.cpp new file mode 100644 index 0000000..adcc057 --- /dev/null +++ b/sip.cpp @@ -0,0 +1,1768 @@ +/*****************************************************************************\ +** ** +** Linux Call Router ** +** ** +**---------------------------------------------------------------------------** +** Copyright: Andreas Eversberg ** +** ** +** SIP port ** +** ** +\*****************************************************************************/ + +#include "main.h" +#include +#include +#include +#include + +unsigned char flip[256]; + +//pthread_mutex_t mutex_msg; +su_home_t sip_home[1]; + +struct sip_inst { + struct interface *interface; + su_root_t *root; + nua_t *nua; +}; + +static int delete_event(struct lcr_work *work, void *instance, int index); + +/* + * initialize SIP port + */ +Psip::Psip(int type, struct mISDNport *mISDNport, char *portname, struct port_settings *settings, int channel, int exclusive, int mode, struct interface *interface) : PmISDN(type, mISDNport, portname, settings, channel, exclusive, mode) +{ + p_m_s_sip_inst = interface->sip_inst; + memset(&p_m_s_delete, 0, sizeof(p_m_s_delete)); + add_work(&p_m_s_delete, delete_event, this, 0); + p_m_s_handle = 0; + p_m_s_magic = 0; + memset(&p_m_s_rtp_fd, 0, sizeof(p_m_s_rtp_fd)); + memset(&p_m_s_rtcp_fd, 0, sizeof(p_m_s_rtcp_fd)); + memset(&p_m_s_rtp_sin_local, 0, sizeof(p_m_s_rtp_sin_local)); + memset(&p_m_s_rtcp_sin_local, 0, sizeof(p_m_s_rtcp_sin_local)); + memset(&p_m_s_rtp_sin_remote, 0, sizeof(p_m_s_rtp_sin_remote)); + memset(&p_m_s_rtcp_sin_remote, 0, sizeof(p_m_s_rtcp_sin_remote)); + p_m_s_rtp_ip_local = 0; + p_m_s_rtp_ip_remote = 0; + p_m_s_rtp_port_local = 0; + p_m_s_rtp_port_remote = 0; + p_m_s_b_sock = -1; + p_m_s_b_index = -1; + p_m_s_b_active = 0; + p_m_s_rxpos = 0; + p_m_s_rtp_tx_action = 0; + + PDEBUG(DEBUG_SIP, "Created new Psip(%s).\n", portname); +} + + +/* + * destructor + */ +Psip::~Psip() +{ + PDEBUG(DEBUG_SIP, "Destroyed SIP process(%s).\n", p_name); + + del_work(&p_m_s_delete); + + /* close audio transfer socket */ + if (p_m_s_b_sock > -1) + bchannel_close(); + + rtp_close(); +} + + +static void sip_trace_header(class Psip *sip, const char *message, int direction) +{ + /* init trace with given values */ + start_trace(-1, + NULL, + 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 RTP_PT_ULAW 0 +#define RTP_PT_ALAW 8 +#define RTP_PT_GSM_FULL 3 +#define RTP_PT_GSM_HALF 96 +#define RTP_PT_GSM_EFR 97 +#define RTP_PT_AMR 98 + +/* 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; + + 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) { + 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_ALAW: + if (options.law != 'a') { + PDEBUG(DEBUG_SIP, "received Alaw, but we don't do Alaw\n"); + return -EINVAL; + } + break; + case RTP_PT_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; + } + + psip->bchannel_send(PH_DATA_REQ, 0, 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; + } + 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!"); + } + + return 0; +} + +#define RTP_PORT_BASE 30000 +static unsigned int next_udp_port = RTP_PORT_BASE; + +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) +{ + int rc; + struct in_addr ia; + unsigned int ip; + + /* create socket */ + rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (!rc) { + rtp_close(); + return -EIO; + } + p_m_s_rtp_fd.fd = rc; + register_fd(&p_m_s_rtp_fd, LCR_FD_READ, rtp_sock_callback, this, 0); + + rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (!rc) { + rtp_close(); + return -EIO; + } + p_m_s_rtcp_fd.fd = rc; + register_fd(&p_m_s_rtcp_fd, LCR_FD_READ, rtcp_sock_callback, this, 0); + + /* bind socket */ + ip = htonl(INADDR_ANY); + ia.s_addr = ip; + for (next_udp_port = next_udp_port % 0xffff; + next_udp_port < 0xffff; next_udp_port += 2) { + rc = rtp_sub_socket_bind(p_m_s_rtp_fd.fd, &p_m_s_rtp_sin_local, ip, next_udp_port); + if (rc != 0) + continue; + + rc = rtp_sub_socket_bind(p_m_s_rtcp_fd.fd, &p_m_s_rtcp_sin_local, ip, next_udp_port+1); + if (rc == 0) + break; + } + if (rc < 0) { + PDEBUG(DEBUG_SIP, "failed to find port\n"); + rtp_close(); + return rc; + } + p_m_s_rtp_port_local = next_udp_port; + p_m_s_rtp_ip_local = ntohl(p_m_s_rtp_sin_local.sin_addr.s_addr); + PDEBUG(DEBUG_SIP, "local ip %08x port %d\n", p_m_s_rtp_ip_local, p_m_s_rtp_port_local); + PDEBUG(DEBUG_SIP, "remote ip %08x port %d\n", p_m_s_rtp_ip_remote, p_m_s_rtp_port_remote); + + return p_m_s_rtp_port_local; +} + +int Psip::rtp_connect(void) +{ + int rc; + struct in_addr ia; + + ia.s_addr = htonl(p_m_s_rtp_ip_remote); + PDEBUG(DEBUG_SIP, "rtp_connect(ip=%s, port=%u)\n", inet_ntoa(ia), p_m_s_rtp_port_remote); + + rc = rtp_sub_socket_connect(p_m_s_rtp_fd.fd, &p_m_s_rtp_sin_local, &p_m_s_rtp_sin_remote, p_m_s_rtp_ip_remote, p_m_s_rtp_port_remote); + if (rc < 0) + return rc; + + rc = rtp_sub_socket_connect(p_m_s_rtcp_fd.fd, &p_m_s_rtcp_sin_local, &p_m_s_rtcp_sin_remote, p_m_s_rtp_ip_remote, p_m_s_rtp_port_remote + 1); + if (rc < 0) + return rc; + + p_m_s_rtp_ip_local = ntohl(p_m_s_rtp_sin_local.sin_addr.s_addr); + PDEBUG(DEBUG_SIP, "local ip %08x port %d\n", p_m_s_rtp_ip_local, p_m_s_rtp_port_local); + PDEBUG(DEBUG_SIP, "remote ip %08x port %d\n", p_m_s_rtp_ip_remote, p_m_s_rtp_port_remote); + p_m_s_rtp_is_connected = 1; + + return 0; +} +void Psip::rtp_close(void) +{ + if (p_m_s_rtp_fd.fd > 0) { + unregister_fd(&p_m_s_rtp_fd); + close(p_m_s_rtp_fd.fd); + p_m_s_rtp_fd.fd = 0; + } + if (p_m_s_rtcp_fd.fd > 0) { + unregister_fd(&p_m_s_rtcp_fd); + close(p_m_s_rtcp_fd.fd); + p_m_s_rtcp_fd.fd = 0; + } + if (p_m_s_rtp_is_connected) { + PDEBUG(DEBUG_SIP, "rtp closed\n"); + p_m_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, int payload_type) +{ + struct rtp_hdr *rtph; + int payload_len; + int duration; /* in samples */ + unsigned char buffer[256]; + + if (!p_m_s_rtp_is_connected) { + /* drop silently */ + return 0; + } + + if (!p_m_s_rtp_tx_action) { + /* initialize sequences */ + p_m_s_rtp_tx_action = 1; + p_m_s_rtp_tx_ssrc = rand(); + p_m_s_rtp_tx_sequence = random(); + p_m_s_rtp_tx_timestamp = random(); + memset(&p_m_s_rtp_tx_last_tv, 0, sizeof(p_m_s_rtp_tx_last_tv)); + } + + switch (payload_type) { + 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_ALAW: + case RTP_PT_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_m_s_rtp_tx_last_tv, &tv); + p_m_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_m_s_rtp_tx_sequence += frame_diff_excess; + p_m_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_m_s_rtp_tx_sequence++); + rtph->timestamp = htonl(p_m_s_rtp_tx_timestamp); + p_m_s_rtp_tx_timestamp += duration; + rtph->ssrc = htonl(p_m_s_rtp_tx_ssrc); + memcpy(buffer + sizeof(struct rtp_hdr), data, payload_len); + + if (p_m_s_rtp_fd.fd > 0) { + len = write(p_m_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; +} + +/* + * bchannel handling + */ + +/* select free bchannel from loopback interface */ +int Psip::hunt_bchannel(void) +{ + return loop_hunt_bchannel(this, p_m_mISDNport); +} + +/* close SIP side bchannel */ +void Psip::bchannel_close(void) +{ + if (p_m_s_b_sock > -1) { + unregister_fd(&p_m_s_b_fd); + close(p_m_s_b_sock); + } + p_m_s_b_sock = -1; + p_m_s_b_index = -1; + p_m_s_b_active = 0; +} + +static int b_handler(struct lcr_fd *fd, unsigned int what, void *instance, int index); + +/* open external side bchannel */ +int Psip::bchannel_open(int index) +{ + int ret; + struct sockaddr_mISDN addr; + struct mISDNhead act; + + if (p_m_s_b_sock > -1) { + PERROR("Socket already created for index %d\n", index); + return(-EIO); + } + + /* open socket */ + ret = p_m_s_b_sock = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_B_RAW); + if (ret < 0) { + PERROR("Failed to open bchannel-socket for index %d\n", index); + bchannel_close(); + return(ret); + } + memset(&p_m_s_b_fd, 0, sizeof(p_m_s_b_fd)); + p_m_s_b_fd.fd = p_m_s_b_sock; + register_fd(&p_m_s_b_fd, LCR_FD_READ, b_handler, this, 0); + + + /* bind socket to bchannel */ + addr.family = AF_ISDN; + addr.dev = mISDNloop.port; + addr.channel = index+1+(index>15); + ret = bind(p_m_s_b_sock, (struct sockaddr *)&addr, sizeof(addr)); + if (ret < 0) { + PERROR("Failed to bind bchannel-socket for index %d\n", index); + bchannel_close(); + return(ret); + } + /* activate bchannel */ + PDEBUG(DEBUG_SIP, "Activating SIP side channel index %i.\n", index); + act.prim = PH_ACTIVATE_REQ; + act.id = 0; + ret = sendto(p_m_s_b_sock, &act, MISDN_HEADER_LEN, 0, NULL, 0); + if (ret < 0) { + PERROR("Failed to activate index %d\n", index); + bchannel_close(); + return(ret); + } + + p_m_s_b_index = index; + + return(0); +} + +/* receive from bchannel */ +void Psip::bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len) +{ + /* write to rx buffer */ + while(len--) { + p_m_s_rxdata[p_m_s_rxpos++] = flip[*data++]; + if (p_m_s_rxpos == 160) { + p_m_s_rxpos = 0; + + /* transmit data via rtp */ + rtp_send_frame(p_m_s_rxdata, 160, (options.law=='a')?RTP_PT_ALAW:RTP_PT_ULAW); + } + } +} + +/* transmit to bchannel */ +void Psip::bchannel_send(unsigned int prim, unsigned int id, unsigned char *data, int len) +{ + unsigned char buf[MISDN_HEADER_LEN+len]; + struct mISDNhead *hh = (struct mISDNhead *)buf; + unsigned char *to = buf + MISDN_HEADER_LEN; + int n = len; + int ret; + + if (!p_m_s_b_active) + return; + + /* make and send frame */ + hh->prim = prim; + hh->id = 0; + while(n--) + *to++ = flip[*data++]; + ret = sendto(p_m_s_b_sock, buf, MISDN_HEADER_LEN+len, 0, NULL, 0); + if (ret <= 0) + PERROR("Failed to send to socket index %d\n", p_m_s_b_index); +} + +/* handle socket input */ +static int b_handler(struct lcr_fd *fd, unsigned int what, void *instance, int index) +{ + class Psip *psip = (class Psip *)instance; + int ret; + unsigned char buffer[2048+MISDN_HEADER_LEN]; + struct mISDNhead *hh = (struct mISDNhead *)buffer; + + /* handle message from bchannel */ + if (psip->p_m_s_b_sock > -1) { + ret = recv(psip->p_m_s_b_sock, buffer, sizeof(buffer), 0); + if (ret >= (int)MISDN_HEADER_LEN) { + switch(hh->prim) { + /* we don't care about confirms, we use rx data to sync tx */ + case PH_DATA_CNF: + break; + /* we receive audio data, we respond to it AND we send tones */ + case PH_DATA_IND: + psip->bchannel_receive(hh, buffer+MISDN_HEADER_LEN, ret-MISDN_HEADER_LEN); + break; + case PH_ACTIVATE_IND: + psip->p_m_s_b_active = 1; + break; + case PH_DEACTIVATE_IND: + psip->p_m_s_b_active = 0; + break; + } + } else { + if (ret < 0 && errno != EWOULDBLOCK) + PERROR("Read from GSM port, index %d failed with return code %d\n", ret); + } + } + + 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 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; + default: + s = 468; *st = sip_486_Busy_here; + } + + return s; +} + +/* + * endpoint sends messages to the SIP port + */ + +int Psip::message_connect(unsigned int epoint_id, int message_id, union parameter *param) +{ + char sdp_str[256]; + struct in_addr ia; + struct lcr_msg *message; + + if (rtp_connect() < 0) { + nua_cancel(p_m_s_handle, TAG_END()); + nua_handle_destroy(p_m_s_handle); + p_m_s_handle = NULL; + sip_trace_header(this, "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_m_s_delete); + return 0; + } + ia.s_addr = htonl(p_m_s_rtp_ip_local); + + SPRINT(sdp_str, + "v=0\n" + "o=LCR-Sofia-SIP 0 0 IN IP4 %s\n" + "s=SIP Call\n" + "c=IN IP4 %s\n" + "t=0 0\n" + "m=audio %d RTP/AVP %d\n" + "a=rtpmap:%d %s/8000\n" + , inet_ntoa(ia), inet_ntoa(ia), p_m_s_rtp_port_local, (options.law=='a')?RTP_PT_ALAW:RTP_PT_ULAW, (options.law=='a')?RTP_PT_ALAW:RTP_PT_ULAW, (options.law=='a')?"PCMA":"PCMU"); + PDEBUG(DEBUG_SIP, "Using SDP response: %s\n", sdp_str); + + nua_respond(p_m_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, "RESPOND", DIRECTION_OUT); + add_trace("respond", "value", "200 OK"); + add_trace("reason", NULL, "call connected"); + end_trace(); + + return 0; +} + +int Psip::message_release(unsigned int epoint_id, int message_id, union parameter *param) +{ + 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, "CANCEL", DIRECTION_OUT); + if (cause_str[0]) + add_trace("cause", "value", "%d", cause); + end_trace(); + nua_cancel(p_m_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, "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_m_s_handle, status, status_text, TAG_IF(cause_str[0], SIPTAG_REASON_STR(cause_str)), TAG_END()); + nua_handle_destroy(p_m_s_handle); + p_m_s_handle = NULL; + trigger_work(&p_m_s_delete); + break; + default: + PDEBUG(DEBUG_SIP, "RELEASE/DISCONNECT will perform nua_bye\n"); + sip_trace_header(this, "BYE", DIRECTION_OUT); + if (cause_str[0]) + add_trace("cause", "value", "%d", cause); + end_trace(); + nua_bye(p_m_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_m_s_sip_inst; + char from[128]; + char to[128]; + const char *local = inst->interface->sip_local_ip; + const char *remote = inst->interface->sip_remote_ip; + char sdp_str[256]; + struct in_addr ia; + struct epoint_list *epointlist; + sip_cseq_t *cseq = NULL; + int ret; + int channel; + + PDEBUG(DEBUG_SIP, "Doing Setup (inst %p)\n", inst); + + /* release if port is blocked */ + if (p_m_mISDNport->ifport->block) { + struct lcr_msg *message; + + sip_trace_header(this, "INVITE", DIRECTION_OUT); + add_trace("failure", NULL, "Port blocked."); + end_trace(); + message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE); + message->param.disconnectinfo.cause = 27; // temp. unavail. + message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; + message_put(message); + new_state(PORT_STATE_RELEASE); + trigger_work(&p_m_s_delete); + return 0; + } + + /* hunt channel */ + ret = channel = hunt_bchannel(); + if (ret < 0) + goto no_channel; + /* open channel */ + ret = seize_bchannel(channel, 1); + if (ret < 0) { + struct lcr_msg *message; + + no_channel: + sip_trace_header(this, "INVITE", DIRECTION_OUT); + add_trace("failure", NULL, "No internal audio channel available."); + end_trace(); + message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE); + message->param.disconnectinfo.cause = 34; + message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL; + message_put(message); + new_state(PORT_STATE_RELEASE); + trigger_work(&p_m_s_delete); + return 0; + } + bchannel_event(p_m_mISDNport, p_m_b_index, B_EVENT_USE); + if (bchannel_open(p_m_b_index)) + goto no_channel; + + memcpy(&p_dialinginfo, ¶m->setup.dialinginfo, sizeof(p_dialinginfo)); + memcpy(&p_callerinfo, ¶m->setup.callerinfo, sizeof(p_callerinfo)); + memcpy(&p_redirinfo, ¶m->setup.redirinfo, sizeof(p_redirinfo)); + + /* connect to remote RTP */ + if (rtp_open() < 0) { + struct lcr_msg *message; + + 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_m_s_delete); + return 0; + } + SPRINT(from, "sip:%s@%s", param->setup.callerinfo.id, local); + SPRINT(to, "sip:%s@%s", param->setup.dialinginfo.id, remote); + + sip_trace_header(this, "INVITE", DIRECTION_OUT); + add_trace("from", "uri", "%s", from); + add_trace("to", "uri", "%s", to); + add_trace("rtp", "port", "%d,%d", p_m_s_rtp_port_local, p_m_s_rtp_port_local + 1); + end_trace(); + + p_m_s_handle = nua_handle(inst->nua, NULL, TAG_END()); + if (!p_m_s_handle) { + struct lcr_msg *message; + + 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_m_s_delete); + return 0; + } + /* apply handle */ + sip_trace_header(this, "NEW handle", DIRECTION_IN); + add_trace("handle", "new", "0x%x", p_m_s_handle); + end_trace(); + + inet_pton(AF_INET, local, &p_m_s_rtp_ip_local); + p_m_s_rtp_ip_local = ntohl(p_m_s_rtp_ip_local); + ia.s_addr = htonl(p_m_s_rtp_ip_local); + SPRINT(sdp_str, + "v=0\n" + "o=LCR-Sofia-SIP 0 0 IN IP4 %s\n" + "s=SIP Call\n" + "c=IN IP4 %s\n" + "t=0 0\n" + "m=audio %d RTP/AVP %d\n" + "a=rtpmap:%d %s/8000\n" + , inet_ntoa(ia), inet_ntoa(ia), p_m_s_rtp_port_local, (options.law=='a')?RTP_PT_ALAW:RTP_PT_ULAW, (options.law=='a')?RTP_PT_ALAW:RTP_PT_ULAW, (options.law=='a')?"PCMA":"PCMU"); + PDEBUG(DEBUG_SIP, "Using SDP for invite: %s\n", sdp_str); + +// cseq = sip_cseq_create(sip_home, 123, SIP_METHOD_INVITE); + + nua_invite(p_m_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()); + new_state(PORT_STATE_OUT_SETUP); + + /* 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_epoint(unsigned int epoint_id, int message_id, union parameter *param) +{ + class Endpoint *epoint; + + if (PmISDN::message_epoint(epoint_id, message_id, param)) + return(1); + + epoint = find_epoint_id(epoint_id); + if (!epoint) { + PDEBUG(DEBUG_SIP, "PORT(%s) no endpoint object found where the message is from.\n", p_name); + return(0); + } + + switch(message_id) { + case MESSAGE_DATA: + return(1); + + 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_m_s_handle, SIP_180_RINGING, TAG_END()); + sip_trace_header(this, "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); + + default: + PDEBUG(DEBUG_SIP, "PORT(%s) SP 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) +{ + int codec_supported = 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_m_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_m_s_rtp_ip_remote); + } + for (map = m->m_rtpmaps; map; map = map->rm_next) { + PDEBUG(DEBUG_SIP, "RTPMAP: coding:'%s' rate='%d'\n", map->rm_encoding, map->rm_rate); + if (!strcmp(map->rm_encoding, (options.law=='a')?"PCMA":"PCMU") && map->rm_rate == 8000) { + PDEBUG(DEBUG_SIP, "supported codec found\n"); + codec_supported = 1; + goto done_codec; + } + } + } + done_codec: + + sdp_parser_free(parser); + + if (!codec_supported) + return 415; + + return 0; +} + +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 tagss[]) +{ + const char *from = "", *to = ""; + int ret; + class Endpoint *epoint; + struct lcr_msg *message; + int channel; + + if (sip->sip_from && sip->sip_from->a_url) + from = sip->sip_from->a_url->url_user; + if (sip->sip_to && sip->sip_to->a_url) + to = sip->sip_to->a_url->url_user; + PDEBUG(DEBUG_SIP, "invite received (%s->%s)\n", from, to); + + ret = parse_sdp(sip, &p_m_s_rtp_ip_remote, &p_m_s_rtp_port_remote); + if (ret) { + if (ret == 400) + nua_respond(nh, SIP_400_BAD_REQUEST, TAG_END()); + else + nua_respond(nh, SIP_415_UNSUPPORTED_MEDIA, TAG_END()); + nua_handle_destroy(nh); + p_m_s_handle = NULL; + sip_trace_header(this, "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(); + new_state(PORT_STATE_RELEASE); + trigger_work(&p_m_s_delete); + return; + } + + /* connect to remote RTP */ + if (rtp_open() < 0) { + nua_respond(nh, SIP_500_INTERNAL_SERVER_ERROR, TAG_END()); + nua_handle_destroy(nh); + p_m_s_handle = NULL; + sip_trace_header(this, "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_m_s_delete); + return; + } + + /* apply handle */ + sip_trace_header(this, "NEW handle", DIRECTION_IN); + add_trace("handle", "new", "0x%x", nh); + p_m_s_handle = nh; + end_trace(); + + /* if blocked, release call */ + if (p_m_mISDNport->ifport->block) { + nua_respond(nh, SIP_503_SERVICE_UNAVAILABLE, TAG_END()); + nua_handle_destroy(nh); + p_m_s_handle = NULL; + sip_trace_header(this, "RESPOND", DIRECTION_OUT); + add_trace("respond", "value", "503 Service Unavailable"); + add_trace("reason", NULL, "port is blocked"); + end_trace(); + new_state(PORT_STATE_RELEASE); + trigger_work(&p_m_s_delete); + return; + } + sip_trace_header(this, "INVITE", DIRECTION_IN); + add_trace("RTP", "port", "%d", p_m_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); + } + p_callerinfo.isdn_port = p_m_portnum; + SCPY(p_callerinfo.interface, p_m_mISDNport->ifport->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(); + + /* hunt channel */ + ret = channel = hunt_bchannel(); + if (ret < 0) + goto no_channel; + + /* open channel */ + ret = seize_bchannel(channel, 1); + if (ret < 0) { + no_channel: + nua_respond(nh, SIP_480_TEMPORARILY_UNAVAILABLE, TAG_END()); + nua_handle_destroy(nh); + p_m_s_handle = NULL; + sip_trace_header(this, "RESPOND", DIRECTION_OUT); + add_trace("respond", "value", "480 Temporarily Unavailable"); + add_trace("reason", NULL, "no channel"); + end_trace(); + new_state(PORT_STATE_RELEASE); + trigger_work(&p_m_s_delete); + return; + } + bchannel_event(p_m_mISDNport, p_m_b_index, B_EVENT_USE); + if (bchannel_open(p_m_b_index)) + goto no_channel; + + /* 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"); + if (!(epoint->ep_app = new DEFAULT_ENDPOINT_APP(epoint, 0))) //incoming + FATAL("No memory for Endpoint Application instance\n"); + epointlist_new(epoint->ep_serial); + + /* send trying (proceeding) */ + nua_respond(nh, SIP_100_TRYING, TAG_END()); + sip_trace_header(this, "RESPOND", DIRECTION_OUT); + add_trace("respond", "value", "100 Trying"); + end_trace(); + + 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.isdn_port = p_m_portnum; + 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; + message_put(message); +} + +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 tagss[]) +{ + struct lcr_msg *message; + int cause = 0; + + PDEBUG(DEBUG_SIP, "bye received\n"); + + sip_trace_header(this, "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, "RESPOND", DIRECTION_OUT); + add_trace("respond", "value", "200 OK"); + end_trace(); +// nua_handle_destroy(nh); + p_m_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_m_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 tagss[]) +{ + struct lcr_msg *message; + + PDEBUG(DEBUG_SIP, "cancel received\n"); + + sip_trace_header(this, "CANCEL", DIRECTION_IN); + end_trace(); + + nua_handle_destroy(nh); + p_m_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_m_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 tagss[]) +{ + PDEBUG(DEBUG_SIP, "bye response received\n"); + + nua_handle_destroy(nh); + p_m_s_handle = NULL; + + rtp_close(); + + trigger_work(&p_m_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 tagss[]) +{ + PDEBUG(DEBUG_SIP, "cancel response received\n"); + + nua_handle_destroy(nh); + p_m_s_handle = NULL; + + rtp_close(); + + trigger_work(&p_m_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 tagss[]) +{ + struct lcr_msg *message; + int cause = 0, location = 0; + + PDEBUG(DEBUG_SIP, "response to invite received (status = %d)\n", status); + + sip_trace_header(this, "RESPOND", DIRECTION_OUT); + add_trace("respond", "value", "%d", status); + end_trace(); + + /* process 1xx */ + switch (status) { + case 100: + 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); + 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; + default: + if (status < 100 || status > 199) + break; + PDEBUG(DEBUG_SIP, "skipping 1xx message\n"); + + return; + } + + /* process 2xx */ + if (status >= 200 && status <= 299) { + int ret; + + ret = parse_sdp(sip, &p_m_s_rtp_ip_remote, &p_m_s_rtp_port_remote); + if (ret) { + if (ret == 400) + nua_cancel(nh, TAG_END()); + else + nua_cancel(nh, TAG_END()); + sip_trace_header(this, "CANCEL", DIRECTION_OUT); + add_trace("reason", NULL, "offered codec does not match"); + end_trace(); + cause = 88; + location = LOCATION_PRIVATE_LOCAL; + goto release_with_cause; + } + + /* connect to remote RTP */ + if (rtp_connect() < 0) { + nua_cancel(nh, TAG_END()); + sip_trace_header(this, "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; + } + + 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); + message_put(message); + + 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_m_s_delete); +} + +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 stack received (handle=%p)\n", event, nh); + if (!nh) + return; + + /* create or find port instance */ + if (event == nua_i_invite) + { + char name[64]; + /* create call instance */ + SPRINT(name, "%s-%d-in", inst->interface->name, 0); + if (!(psip = new Psip(PORT_TYPE_SIP_IN, (inst->interface->ifport) ? inst->interface->ifport->mISDNport : NULL, name, NULL, 0, 0, B_MODE_TRANSPARENT, inst->interface))) + FATAL("Cannot create Port instance.\n"); + } else { + port = port_first; + while(port) { + if ((port->p_type & PORT_CLASS_mISDN_MASK) == PORT_CLASS_SIP) { + psip = (class Psip *)port; + if (psip->p_m_s_handle == nh) { + break; + } + } + port = port->next; + } + } + if (!psip) { + PERROR("no SIP Port found for handel %p\n", nh); + nua_respond(nh, SIP_500_INTERNAL_SERVER_ERROR, TAG_END()); + nua_handle_destroy(nh); + return; + } + + switch (event) { + case nua_r_set_params: + PDEBUG(DEBUG_SIP, "setparam response\n"); + break; + case nua_i_error: + PDEBUG(DEBUG_SIP, "error received\n"); + break; + case nua_i_state: + PDEBUG(DEBUG_SIP, "state change received\n"); + break; + case nua_i_register: + PDEBUG(DEBUG_SIP, "register received\n"); + 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_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); + } +} + +/* received shutdown due to termination of RTP */ +void Psip::rtp_shutdown(void) +{ + struct lcr_msg *message; + + PDEBUG(DEBUG_SIP, "RTP stream terminated\n"); + + sip_trace_header(this, "RTP terminated", DIRECTION_IN); + end_trace(); + + nua_handle_destroy(p_m_s_handle); + p_m_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_m_s_delete); +} + +int sip_init_inst(struct interface *interface) +{ + struct sip_inst *inst = (struct sip_inst *) MALLOC(sizeof(*inst)); + + interface->sip_inst = inst; + inst->interface = interface; + + /* 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; + } + + inst->nua = nua_create(inst->root, sip_callback, inst, TAG_NULL()); + 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("INVITE,ACK,BYE,CANCEL,OPTIONS,NOTIFY,INFO"), + 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), + NUTAG_AUTO100(0), + NUTAG_AUTOALERT(0), + NUTAG_AUTOANSWER(0), + TAG_NULL()); + + PDEBUG(DEBUG_SIP, "SIP interface created (inst=%p)\n", inst); + + return 0; +} + +void sip_exit_inst(struct interface *interface) +{ + struct sip_inst *inst = (struct sip_inst *) interface->sip_inst; + + if (!inst) + return; + 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"); +} + +extern su_log_t su_log_default[]; +extern su_log_t nua_log[]; +//extern su_log_t soa_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"); +} + +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); + } + 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; +} + diff --git a/sip.h b/sip.h new file mode 100644 index 0000000..1bc06f7 --- /dev/null +++ b/sip.h @@ -0,0 +1,73 @@ +/*****************************************************************************\ +** ** +** Linux Call Router ** +** ** +**---------------------------------------------------------------------------** +** Copyright: Andreas Eversberg ** +** ** +** SIP-port header file ** +** ** +\*****************************************************************************/ + +#include + +/* SIP port class */ +class Psip : public PmISDN +{ + public: + Psip(int type, struct mISDNport *mISDNport, char *portname, struct port_settings *settings, int channel, int exclusive, int mode, struct interface *interface); + ~Psip(); + int message_epoint(unsigned int epoint_id, int message, union parameter *param); + int message_connect(unsigned int epoint_id, int message, union parameter *param); + int message_release(unsigned int epoint_id, int message, union parameter *param); + int message_setup(unsigned int epoint_id, int message, union parameter *param); + void 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[]); + void 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[]); + void 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[]); + void 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[]); + void 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[]); + void 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[]); + void *p_m_s_sip_inst; + struct lcr_work p_m_s_delete; + nua_handle_t *p_m_s_handle; + nua_magic_t *p_m_s_magic; + unsigned short p_m_s_rtp_port_local; + unsigned short p_m_s_rtp_port_remote; + unsigned int p_m_s_rtp_ip_local; + unsigned int p_m_s_rtp_ip_remote; + struct sockaddr_in p_m_s_rtp_sin_local; + struct sockaddr_in p_m_s_rtp_sin_remote; + struct sockaddr_in p_m_s_rtcp_sin_local; + struct sockaddr_in p_m_s_rtcp_sin_remote; + struct lcr_fd p_m_s_rtp_fd; + struct lcr_fd p_m_s_rtcp_fd; + int p_m_s_rtp_is_connected; /* if RTP session is connected, so we may send frames */ + int p_m_s_rtp_tx_action; + uint16_t p_m_s_rtp_tx_sequence; + uint32_t p_m_s_rtp_tx_timestamp; + uint32_t p_m_s_rtp_tx_ssrc; + struct timeval p_m_s_rtp_tx_last_tv; + int rtp_open(void); + int rtp_connect(void); + void rtp_close(void); + int rtp_send_frame(unsigned char *data, unsigned int len, int payload_type); + int p_m_s_b_sock; /* SIP bchannel socket */ + struct lcr_fd p_m_s_b_fd; /* event node */ + int p_m_s_b_index; /* SIP bchannel socket index to use */ + int p_m_s_b_active; /* SIP bchannel socket is activated */ + unsigned char p_m_s_rxdata[160]; /* receive audio buffer */ + int p_m_s_rxpos; /* position in audio buffer 0..159 */ + int hunt_bchannel(void); + void bchannel_close(void); + int bchannel_open(int); + void bchannel_receive(struct mISDNhead *hh, unsigned char *data, int len); + void bchannel_send(unsigned int prim, unsigned int id, unsigned char *data, int len); + int parse_sdp(sip_t const *sip, unsigned int *ip, unsigned short *port); + void rtp_shutdown(void); +}; + +int sip_init_inst(struct interface *interface); +void sip_exit_inst(struct interface *interface); +int sip_init(void); +void sip_exit(void); +void sip_handle(void); -- 2.13.6