Adding basic SIP support, using Sofia-SIP stack
authorAndreas Eversberg <jolly@eversberg.eu>
Fri, 13 Jan 2012 05:24:21 +0000 (06:24 +0100)
committerAndreas Eversberg <jolly@eversberg.eu>
Fri, 13 Jan 2012 05:24:21 +0000 (06:24 +0100)
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.

12 files changed:
Makefile.am
apppbx.cpp
configure.ac
interface.c
interface.h
mISDN.cpp
mISDN.h
main.c
main.h
port.h
sip.cpp [new file with mode: 0644]
sip.h [new file with mode: 0644]

index 61ddb8c..a97a7ec 100644 (file)
@@ -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
index fff80ce..d3937a7 100644 (file)
@@ -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) {
index 0c7e67d..343409a 100644 (file)
@@ -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)])
 
index 3c1a792..264f4f2 100644 (file)
@@ -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, "<local IP> <remote IP>",
+       "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 */
        }
index 2dd9889..d9462ed 100644 (file)
@@ -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 {
index 75a1b62..d683bcd 100644 (file)
--- 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 (file)
--- 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 (file)
--- 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 (file)
--- 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 (file)
--- 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 (file)
index 0000000..adcc057
--- /dev/null
+++ b/sip.cpp
@@ -0,0 +1,1768 @@
+/*****************************************************************************\
+**                                                                           **
+** 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>
+
+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, &param->setup.dialinginfo, sizeof(p_dialinginfo));
+       memcpy(&p_callerinfo, &param->setup.callerinfo, sizeof(p_callerinfo));
+       memcpy(&p_redirinfo, &param->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 (file)
index 0000000..1bc06f7
--- /dev/null
+++ b/sip.h
@@ -0,0 +1,73 @@
+/*****************************************************************************\
+**                                                                           **
+** Linux Call Router                                                         **
+**                                                                           **
+**---------------------------------------------------------------------------**
+** Copyright: Andreas Eversberg                                              **
+**                                                                           **
+** SIP-port header file                                                      **
+**                                                                           **
+\*****************************************************************************/ 
+
+#include <sofia-sip/nua.h>
+
+/* 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);