SIP: Register, STUN and authentication support
authorAndreas Eversberg <jolly@eversberg.eu>
Wed, 1 Nov 2017 19:43:13 +0000 (20:43 +0100)
committerAndreas Eversberg <jolly@eversberg.eu>
Sat, 4 Nov 2017 12:32:20 +0000 (13:32 +0100)
default/interface.conf
interface.c
interface.h
sip.cpp
sip.h
trace.c

index 1450ae3..8593c9f 100644 (file)
 #earlyb no
 #tones no
 
+# Use Sofia-SIP as SIP interface to register to a SIP gateway/proxy
+#[sip]
+## define source and destination IP to make a call
+#sip 192.168.0.55 sipgate.de
+## define <user> <host> to register to a SIP gateway
+#register <user> sipgate.de
+##define RTP port range or use default
+#rtp-ports 30000 39999
+## use authentication credentials
+#authenticate <user> <password>
+## define keepalive timer to keep INVITE/REGISTER alive
+#options-timer 15
+## define stun server and resolving interval
+#stun stun.sipgate.net 300
+## screen caller ID to SIP caller ID
+#screen-out % <callerID>
+#tones no
+#earlyb no
+
 
 # Hint: Enter "lcr interface" for quick help on interface options.
 
index 7619294..71e1d80 100644 (file)
@@ -994,6 +994,139 @@ static int inter_sip(struct interface *interface, char *filename, int line, char
        return(0);
 #endif
 }
+static int inter_authenticate(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;
+
+       if (!interface->sip) {
+               SPRINT(interface_error, "Error in %s (line %d): This is not a SIP interface.\n", filename, line);
+               return(-1);
+       }
+
+       /* copy values */
+       if (!value || !value[0]) {
+               SPRINT(interface_error, "Error in %s (line %d): Missing SIP user.\n", filename, line);
+               return(-1);
+       }
+       p = get_seperated(value);
+       if (!p[0]) {
+               SPRINT(interface_error, "Error in %s (line %d): Missing SIP password.\n", filename, line);
+               return(-1);
+       }
+       SCPY(interface->sip_auth_user, value);
+       SCPY(interface->sip_auth_password, p);
+
+       return(0);
+#endif
+}
+static int options_timer(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
+       if (!interface->sip) {
+               SPRINT(interface_error, "Error in %s (line %d): This is not a SIP interface.\n", filename, line);
+               return(-1);
+       }
+
+       if (value)
+               interface->sip_options_timer = atoi(value);
+
+       return(0);
+#endif
+}
+static int options_stun(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;
+
+       if (!interface->sip) {
+               SPRINT(interface_error, "Error in %s (line %d): This is not a SIP interface.\n", filename, line);
+               return(-1);
+       }
+
+       if (!value || !value[0]) {
+               SPRINT(interface_error, "Error in %s (line %d): Missing STUN server.\n", filename, line);
+               return(-1);
+       }
+       p = get_seperated(value);
+       if (!p[0]) {
+               SPRINT(interface_error, "Error in %s (line %d): Missing STUN timer.\n", filename, line);
+               return(-1);
+       }
+       SCPY(interface->sip_stun_server, value);
+       interface->sip_stun_interval = atoi(p);
+
+       return(0);
+#endif
+}
+static int inter_rtp_ports(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;
+
+       if (!interface->sip) {
+               SPRINT(interface_error, "Error in %s (line %d): This is not a SIP interface.\n", filename, line);
+               return(-1);
+       }
+
+       /* copy values */
+       if (!value || !value[0]) {
+               SPRINT(interface_error, "Error in %s (line %d): Missing 'from' port.\n", filename, line);
+               return(-1);
+       }
+       p = get_seperated(value);
+       if (!p[0]) {
+               SPRINT(interface_error, "Error in %s (line %d): Missing 'to' port.\n", filename, line);
+               return(-1);
+       }
+       interface->rtp_port_from = atoi(value);
+       interface->rtp_port_to = atoi(p);
+
+       return(0);
+#endif
+}
+static int inter_register(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;
+
+       if (!interface->sip) {
+               SPRINT(interface_error, "Error in %s (line %d): This is not a SIP interface.\n", filename, line);
+               return(-1);
+       }
+
+       /* copy values */
+       if (!value || !value[0]) {
+               SPRINT(interface_error, "Error in %s (line %d): Missing SIP user.\n", filename, line);
+               return(-1);
+       }
+       p = get_seperated(value);
+       if (!p[0]) {
+               SPRINT(interface_error, "Error in %s (line %d): Missing SIP host\n", filename, line);
+               return(-1);
+       }
+       interface->sip_register = 1;
+       SCPY(interface->sip_register_user, value);
+       SCPY(interface->sip_register_host, p);
+
+       return(0);
+#endif
+}
 static int inter_rtp_bridge(struct interface *interface, char *filename, int line, char *parameter, char *value)
 {
        int supported = 0;
@@ -1366,9 +1499,19 @@ struct interface_param interface_param[] = {
        {"gsm-ms", &inter_gsm_ms, "<socket name>",
        "Sets up GSM mobile station interface for using Osmocom-BB.\n"
        "The socket will be /tmp/ms_mncc_<socket name>."},
-       {"sip", &inter_sip, "<local IP> <remote IP>",
-       "Sets up SIP interface that represents one SIP endpoint.\n"
-       "Give SIP configuration file."},
+       {"sip", &inter_sip, "<local IP/host> <remote IP/host>",
+       "Sets up SIP interface that represents one SIP endpoint."},
+       {"register", &inter_register, "<user> <host>",
+       "Registers to given SIP registrar."},
+       {"authenticate", &inter_authenticate, "<user> <password>",
+       "Defines SIP user and password for authentication."},
+       {"options-timer", &options_timer, "<timer> | 0",
+       "Defines SIP timer to send OPTIONS messages to keepalive SIP sessions."},
+       {"stun", &options_stun, "<server> <interval>",
+       "Defines STUN server to resolv real local IP.\n"
+       "The interval is used to check if IP has changed. (use 300)"},
+       {"rtp-ports", &inter_rtp_ports, "<port from> <port to>",
+       "Defines the range of ports to be used for RTP. This overrides the default."},
        {"rtp-bridge", &inter_rtp_bridge, "",
        "Enables RTP bridging directly from this interface.\n"
        "This only works if both bridged interfaces use RTP, e.g. between gsm-bs and sip.\n"
index 4b624f3..58773bb 100644 (file)
@@ -126,9 +126,19 @@ struct interface {
 #endif
 #ifdef WITH_SIP
        int                     sip; /* interface is a SIP interface */
-       char                    sip_local_peer[32];
-       char                    sip_remote_peer[32];
+       char                    sip_local_peer[128];
+       char                    sip_remote_peer[128];
+       char                    sip_auth_user[128];
+       char                    sip_auth_password[128];
+       int                     sip_register;
+       char                    sip_register_user[128];
+       char                    sip_register_host[128];
+       int                     sip_options_timer; /* timer to keepalive invite/register transactions */
+       char                    sip_stun_server[128];
+       int                     sip_stun_interval; /* timer to check own IP address */
        void                    *sip_inst; /* sip instance */
+       unsigned short          rtp_port_from;
+       unsigned short          rtp_port_to;
 #endif
        int                     rtp_bridge; /* bridge RTP directly (for calls comming from interface) */
 };
diff --git a/sip.cpp b/sip.cpp
index 075447c..21309d1 100644 (file)
--- a/sip.cpp
+++ b/sip.cpp
@@ -14,6 +14,8 @@
 #include <sofia-sip/su_log.h>
 #include <sofia-sip/sdp.h>
 #include <sofia-sip/sip_header.h>
+#include <sofia-sip/stun.h>
+#include <sofia-sip/stun_tag.h>
 
 #ifndef SOFIA_SIP_GCC_4_8_PATCH_APLLIED
 #warning ********************************************************
@@ -32,15 +34,54 @@ int any_sip_interface = 0;
 //pthread_mutex_t mutex_msg;
 su_home_t      sip_home[1];
 
+#define REGISTER_STATE_UNREGISTERED    1
+#define REGISTER_STATE_REGISTERING     2
+#define REGISTER_STATE_REGISTERED      3
+#define REGISTER_STATE_FAILED          4
+
+#define STUN_RETRY_TIMER               10, 0
+#define REGISTER_RETRY_TIMER           10, 0
+
+#define STUN_STATE_UNRESOLVED          1
+#define STUN_STATE_RESOLVING           2
+#define STUN_STATE_RESOLVED            3
+#define STUN_STATE_FAILED              4
+
+#define RTP_PORT_BASE  30000
+#define RTP_PORT_MAX   39998
+
 struct sip_inst {
        char                    interface_name[64];
-       char                    local_peer[32];
-       char                    remote_peer[32];
+       char                    local_peer[128];
+       char                    remote_peer[128];
+       int                     register_state;
+       char                    register_user[128];
+       char                    register_host[128];
+       nua_handle_t            *register_handle;
+       struct lcr_timer        register_retry_timer;
+       struct lcr_timer        register_option_timer;
+       int                     options_timeout;
+       char                    auth_user[128];
+       char                    auth_password[128];
        su_root_t               *root;
        nua_t                   *nua;
+
+       int                     stun_state;
+       char                    stun_server[128];
+       stun_handle_t           *stun_handle;
+       su_socket_t             stun_socket;
+       char                    stun_ipaddr[128];
+       struct lcr_timer        stun_retry_timer;
+       int                     stun_interval;
+
+       unsigned short          rtp_port_from;
+       unsigned short          rtp_port_to;
+       unsigned short          next_rtp_port;
+
 };
 
 static int delete_event(struct lcr_work *work, void *instance, int index);
+static int invite_option_timer(struct lcr_timer *timer, void *instance, int index);
 static int load_timer(struct lcr_timer *timer, void *instance, int index);
 
 /*
@@ -71,10 +112,16 @@ Psip::Psip(int type, char *portname, struct port_settings *settings, struct inte
        p_s_b_active = 0;
        p_s_rxpos = 0;
        p_s_rtp_tx_action = 0;
+       p_s_rtp_is_connected = 0;
+
+       /* create option timer */
+       memset(&p_s_invite_option_timer, 0, sizeof(p_s_invite_option_timer));
+        add_timer(&p_s_invite_option_timer, invite_option_timer, this, 0);
+       p_s_invite_direction = 0;
 
        /* audio */
-       memset(&p_s_loadtimer, 0, sizeof(p_s_loadtimer));
-       add_timer(&p_s_loadtimer, load_timer, this, 0);
+       memset(&p_s_load_timer, 0, sizeof(p_s_load_timer));
+       add_timer(&p_s_load_timer, load_timer, this, 0);
        p_s_next_tv_sec = 0;
 
        PDEBUG(DEBUG_SIP, "Created new Psip(%s).\n", portname);
@@ -90,7 +137,8 @@ Psip::~Psip()
 {
        PDEBUG(DEBUG_SIP, "Destroyed SIP process(%s).\n", p_name);
 
-       del_timer(&p_s_loadtimer);
+       del_timer(&p_s_invite_option_timer);
+       del_timer(&p_s_load_timer);
        del_work(&p_s_delete);
 
        rtp_close();
@@ -115,11 +163,16 @@ static const char *media_type2name(uint8_t media_type) {
        return "UKN";
 }
 
-static void sip_trace_header(class Psip *sip, const char *message, int direction)
+static void sip_trace_header(class Psip *sip, const char *interface_name, const char *message, int direction)
 {
+       struct interface *interface = NULL;
+
+       if (interface_name)
+               interface = getinterfacebyname(interface_name);
+
        /* init trace with given values */
        start_trace(-1,
-                   NULL,
+                   interface,
                    sip?numberrize_callerinfo(sip->p_callerinfo.id, sip->p_callerinfo.ntype, options.national, options.international):NULL,
                    sip?sip->p_dialinginfo.id:NULL,
                    direction,
@@ -337,10 +390,6 @@ static int rtcp_sock_callback(struct lcr_fd *fd, unsigned int what, void *instan
        return 0;
 }
 
-#define RTP_PORT_BASE  30000
-#define RTP_PORT_MAX   39998
-static unsigned short 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;
@@ -379,8 +428,9 @@ static int rtp_sub_socket_connect(int fd, struct sockaddr_in *sin_local, struct
 
 int Psip::rtp_open(void)
 {
+       struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
        int rc, rc2;
-       struct in_addr ia;
+//     struct in_addr ia;
        unsigned int ip;
        unsigned short start_port;
 
@@ -403,17 +453,17 @@ int Psip::rtp_open(void)
 
        /* bind socket */
        ip = htonl(INADDR_ANY);
-       ia.s_addr = ip;
-       start_port = next_udp_port;
+//     ia.s_addr = ip;
+       start_port = inst->next_rtp_port;
        while (1) {
-               rc = rtp_sub_socket_bind(p_s_rtp_fd.fd, &p_s_rtp_sin_local, ip, next_udp_port);
+               rc = rtp_sub_socket_bind(p_s_rtp_fd.fd, &p_s_rtp_sin_local, ip, inst->next_rtp_port);
                if (rc != 0)
                        goto try_next_port;
 
-               rc = rtp_sub_socket_bind(p_s_rtcp_fd.fd, &p_s_rtcp_sin_local, ip, next_udp_port + 1);
+               rc = rtp_sub_socket_bind(p_s_rtcp_fd.fd, &p_s_rtcp_sin_local, ip, inst->next_rtp_port + 1);
                if (rc == 0) {
-                       p_s_rtp_port_local = next_udp_port;
-                       next_udp_port = (next_udp_port + 2 > RTP_PORT_MAX) ? RTP_PORT_BASE : next_udp_port + 2;
+                       p_s_rtp_port_local = inst->next_rtp_port;
+                       inst->next_rtp_port = (inst->next_rtp_port + 2 > inst->rtp_port_to) ? inst->rtp_port_from : inst->next_rtp_port + 2;
                        break;
                }
                /* reopen rtp socket and try again with next udp port */
@@ -429,8 +479,8 @@ int Psip::rtp_open(void)
                register_fd(&p_s_rtp_fd, LCR_FD_READ, rtp_sock_callback, this, 0);
 
 try_next_port:
-               next_udp_port = (next_udp_port + 2 > RTP_PORT_MAX) ? RTP_PORT_BASE : next_udp_port + 2;
-               if (next_udp_port == start_port)
+               inst->next_rtp_port = (inst->next_rtp_port + 2 > inst->rtp_port_to) ? inst->rtp_port_from : inst->next_rtp_port + 2;
+               if (inst->next_rtp_port == start_port)
                        break;
                /* we must use rc2, in order to preserve rc */
        }
@@ -738,6 +788,9 @@ static int cause2status(int cause, int location, const char **st)
        case 23:
                s = 410; *st = sip_410_Gone;
                break;
+       case 26:
+               s = 404; *st = sip_404_Not_found;
+               break;
        case 27:
                s = 502; *st = sip_502_Bad_gateway;
                break;
@@ -795,6 +848,12 @@ static int cause2status(int cause, int location, const char **st)
        case 102:
                s = 504; *st = sip_504_Gateway_time_out;
                break;
+       case 111:
+               s = 500; *st = sip_500_Internal_server_error;
+               break;
+       case 127:
+               s = 500; *st = sip_500_Internal_server_error;
+               break;
        default:
                s = 468; *st = sip_486_Busy_here;
        }
@@ -802,12 +861,26 @@ static int cause2status(int cause, int location, const char **st)
        return s;
 }
 
+/* use STUN ip, or return the ip without change */
+unsigned int Psip::get_local_ip(unsigned int ip)
+{
+       struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
+
+       if (inst->stun_handle && inst->stun_ipaddr[0]) {
+               PDEBUG(DEBUG_SIP, "RTP local IP is replaced by STUN ip %s\n", inst->stun_ipaddr);
+               inet_pton(AF_INET, inst->stun_ipaddr, &ip);
+               return htonl(ip);
+       }
+       return ip;
+}
+
 /*
  * endpoint sends messages to the SIP port
  */
 
 int Psip::message_connect(unsigned int epoint_id, int message_id, union parameter *param)
 {
+       struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
        char sdp_str[256];
        struct in_addr ia;
        struct lcr_msg *message;
@@ -833,7 +906,7 @@ int Psip::message_connect(unsigned int epoint_id, int message_id, union paramete
                        nua_cancel(p_s_handle, TAG_END());
                        nua_handle_destroy(p_s_handle);
                        p_s_handle = NULL;
-                       sip_trace_header(this, "CANCEL", DIRECTION_OUT);
+                       sip_trace_header(this, inst->interface_name, "CANCEL", DIRECTION_OUT);
                        add_trace("reason", NULL, "failed to connect RTP/RTCP sockts");
                        end_trace();
                        message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
@@ -846,16 +919,16 @@ int Psip::message_connect(unsigned int epoint_id, int message_id, union paramete
                }
        }
 
-       ia.s_addr = htonl(p_s_rtp_ip_local);
-
+       memset(&ia, 0, sizeof(ia));
+       ia.s_addr = htonl(get_local_ip(p_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"
+               "v=0\r\n"
+               "o=LCR-Sofia-SIP 0 0 IN IP4 %s\r\n"
+               "s=SIP Call\r\n"
+               "c=IN IP4 %s\r\n"
+               "t=0 0\r\n"
+               "m=audio %d RTP/AVP %d\r\n"
+               "a=rtpmap:%d %s/8000\r\n"
                , inet_ntoa(ia), inet_ntoa(ia), p_s_rtp_port_local, payload_type, payload_type, media_type2name(media_type));
        PDEBUG(DEBUG_SIP, "Using SDP response: %s\n", sdp_str);
 
@@ -869,8 +942,9 @@ int Psip::message_connect(unsigned int epoint_id, int message_id, union paramete
                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);
+       sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
        add_trace("respond", "value", "200 OK");
        add_trace("reason", NULL, "call connected");
        add_trace("rtp", "ip", "%s", inet_ntoa(ia));
@@ -883,6 +957,7 @@ int Psip::message_connect(unsigned int epoint_id, int message_id, union paramete
 
 int Psip::message_release(unsigned int epoint_id, int message_id, union parameter *param)
 {
+       struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
        struct lcr_msg *message;
        char cause_str[128] = "";
        int cause = param->disconnectinfo.cause;
@@ -899,7 +974,7 @@ int Psip::message_release(unsigned int epoint_id, int message_id, union paramete
        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);
+               sip_trace_header(this, inst->interface_name, "CANCEL", DIRECTION_OUT);
                if (cause_str[0])
                        add_trace("cause", "value", "%d", cause);
                end_trace();
@@ -910,7 +985,7 @@ int Psip::message_release(unsigned int epoint_id, int message_id, union paramete
        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);
+               sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
                if (cause_str[0])
                        add_trace("cause", "value", "%d", cause);
                add_trace("respond", "value", "%d %s", status, status_text);
@@ -922,7 +997,7 @@ int Psip::message_release(unsigned int epoint_id, int message_id, union paramete
                break;
        default:
                PDEBUG(DEBUG_SIP, "RELEASE/DISCONNECT will perform nua_bye\n");
-               sip_trace_header(this, "BYE", DIRECTION_OUT);
+               sip_trace_header(this, inst->interface_name, "BYE", DIRECTION_OUT);
                if (cause_str[0])
                        add_trace("cause", "value", "%d", cause);
                end_trace();
@@ -948,8 +1023,9 @@ int Psip::message_release(unsigned int epoint_id, int message_id, union paramete
 int Psip::message_setup(unsigned int epoint_id, int message_id, union parameter *param)
 {
        struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
-       char from[128];
-       char to[128];
+       char from[128] = "";
+       char to[128] = "";
+       char contact[128] = "";
        const char *local = inst->local_peer;
        char local_ip[16];
        const char *remote = inst->remote_peer;
@@ -959,7 +1035,7 @@ int Psip::message_setup(unsigned int epoint_id, int message_id, union parameter
        sip_cseq_t *cseq = NULL;
        struct lcr_msg *message;
        int lcr_media = { (options.law=='a') ? MEDIA_TYPE_ALAW : MEDIA_TYPE_ULAW };
-       unsigned char lcr_payload = { (options.law=='a') ? PAYLOAD_TYPE_ALAW : PAYLOAD_TYPE_ULAW };
+       unsigned char lcr_payload = { (options.law=='a') ? (unsigned char )PAYLOAD_TYPE_ALAW : (unsigned char )PAYLOAD_TYPE_ULAW };
        int *media_types;
        unsigned char *payload_types;
        int payloads = 0;
@@ -969,7 +1045,9 @@ int Psip::message_setup(unsigned int epoint_id, int message_id, union parameter
 
        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));
+//     memcpy(&p_redirinfo, &param->setup.redirinfo, sizeof(p_redirinfo));
+       do_screen(1, p_callerinfo.id, sizeof(p_callerinfo.id), &p_callerinfo.ntype, &p_callerinfo.present, inst->interface_name);
+//     do_screen(1, p_redirinfo.id, sizeof(p_redirinfo.id), &p_redirinfo.ntype, &p_redirinfo.present, inst->interface_name);
 
        if (param->setup.rtpinfo.port) {
                PDEBUG(DEBUG_SIP, "RTP info given by remote, forward that\n");
@@ -1000,8 +1078,22 @@ int Psip::message_setup(unsigned int epoint_id, int message_id, union parameter
                        trigger_work(&p_s_delete);
                        return 0;
                }
+               if (!p_s_rtp_ip_local) {
+                       char *p;
+
+                       /* extract IP from local peer */
+                       SCPY(local_ip, local);
+                       p = strchr(local_ip, ':');
+                       if (p)
+                               *p = '\0';
+                       PDEBUG(DEBUG_SIP, "RTP local IP not known, so we use our local SIP ip %s\n", local_ip);
+                       inet_pton(AF_INET, local_ip, &p_s_rtp_ip_local);
+                       p_s_rtp_ip_local = ntohl(p_s_rtp_ip_local);
+               }
        }
 
+       memset(&ia, 0, sizeof(ia));
+       ia.s_addr = htonl(get_local_ip(p_s_rtp_ip_local));
        p_s_handle = nua_handle(inst->nua, NULL, TAG_END());
        if (!p_s_handle) {
                PERROR("Failed to create handle\n");
@@ -1014,47 +1106,36 @@ int Psip::message_setup(unsigned int epoint_id, int message_id, union parameter
                trigger_work(&p_s_delete);
                return 0;
        }
-       /* apply handle */
-       sip_trace_header(this, "NEW handle", DIRECTION_IN);
+       /* apply handle to trace */
+       sip_trace_header(this, inst->interface_name, "NEW handle", DIRECTION_IN);
        add_trace("handle", "new", "0x%x", p_s_handle);
        end_trace();
 
-       if (!p_s_rtp_ip_local) {
-               char *p;
-
-               /* extract IP from local peer */
-               SCPY(local_ip, local);
-               p = strchr(local_ip, ':');
-               if (p)
-                       *p = '\0';
-               PDEBUG(DEBUG_SIP, "RTP local IP not known, so we use our local SIP ip %s\n", local_ip);
-               inet_pton(AF_INET, local_ip, &p_s_rtp_ip_local);
-               p_s_rtp_ip_local = ntohl(p_s_rtp_ip_local);
-       }
-       ia.s_addr = htonl(p_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"
+               "v=0\r\n"
+               "o=LCR-Sofia-SIP 0 0 IN IP4 %s\r\n"
+               "s=SIP Call\r\n"
+               "c=IN IP4 %s\r\n"
+               "t=0 0\r\n"
                "m=audio %d RTP/AVP"
                , inet_ntoa(ia), inet_ntoa(ia), p_s_rtp_port_local);
        for (i = 0; i < payloads; i++) {
                SPRINT(pt_str, " %d", payload_types[i]);
                SCAT(sdp_str, pt_str);
        }
-       SCAT(sdp_str, "\n");
+       SCAT(sdp_str, "\r\n");
        for (i = 0; i < payloads; i++) {
-               SPRINT(pt_str, "a=rtpmap:%d %s/8000\n", payload_types[i], media_type2name(media_types[i]));
+               SPRINT(pt_str, "a=rtpmap:%d %s/8000\r\n", payload_types[i], media_type2name(media_types[i]));
                SCAT(sdp_str, pt_str);
        }
        PDEBUG(DEBUG_SIP, "Using SDP for invite: %s\n", sdp_str);
 
-       SPRINT(from, "sip:%s@%s", param->setup.callerinfo.id, local);
-       SPRINT(to, "sip:%s@%s", param->setup.dialinginfo.id, remote);
+       SPRINT(from, "sip:%s@%s", p_callerinfo.id, remote);
+       SPRINT(to, "sip:%s@%s", p_dialinginfo.id, remote);
+       if (inst->stun_handle && inst->stun_ipaddr[0])
+               SPRINT(contact, "sip:%s@%s", p_callerinfo.id, inst->stun_ipaddr);
 
-       sip_trace_header(this, "INVITE", DIRECTION_OUT);
+       sip_trace_header(this, inst->interface_name, "INVITE", DIRECTION_OUT);
        add_trace("from", "uri", "%s", from);
        add_trace("to", "uri", "%s", to);
        add_trace("rtp", "ip", "%s", inet_ntoa(ia));
@@ -1068,12 +1149,15 @@ int Psip::message_setup(unsigned int epoint_id, int message_id, union parameter
        nua_invite(p_s_handle,
                TAG_IF(from[0], SIPTAG_FROM_STR(from)),
                TAG_IF(to[0], SIPTAG_TO_STR(to)),
+               TAG_IF(contact[0], SIPTAG_CONTACT_STR(contact)),
                TAG_IF(cseq, SIPTAG_CSEQ(cseq)),
                NUTAG_MEDIA_ENABLE(0),
                SIPTAG_CONTENT_TYPE_STR("application/sdp"),
                SIPTAG_PAYLOAD_STR(sdp_str), TAG_END());
        new_state(PORT_STATE_OUT_SETUP);
 
+       p_s_invite_direction = DIRECTION_OUT;
+
 #if 0
        PDEBUG(DEBUG_SIP, "do overlap\n");
        new_state(PORT_STATE_OUT_OVERLAP);
@@ -1101,18 +1185,18 @@ int Psip::message_setup(unsigned int epoint_id, int message_id, union parameter
        
 int Psip::message_notify(unsigned int epoint_id, int message_id, union parameter *param)
 {
-//     char sdp_str[256];
+//     char 
 //     struct in_addr ia;
 
        switch (param->notifyinfo.notify) {
        case INFO_NOTIFY_REMOTE_HOLD:
 #if 0
                SPRINT(sdp_str,
-                       "v=0\n"
-                       "o=LCR-Sofia-SIP 0 0 IN IP4 0.0.0.0\n"
-                       "s=SIP Call\n"
-                       "c=IN IP4 0.0.0.0\n"
-                       "t=0 0\n"
+                       "v=0\r\n"
+                       "o=LCR-Sofia-SIP 0 0 IN IP4 0.0.0.0\r\n"
+                       "s=SIP Call\r\n"
+                       "c=IN IP4 0.0.0.0\r\n"
+                       "t=0 0\r\n"
                        );
                PDEBUG(DEBUG_SIP, "Using SDP for hold: %s\n", sdp_str);
                nua_info(p_s_handle,
@@ -1126,15 +1210,16 @@ int Psip::message_notify(unsigned int epoint_id, int message_id, union parameter
                break;
        case INFO_NOTIFY_REMOTE_RETRIEVAL:
 #if 0
-               ia.s_addr = htonl(p_s_rtp_ip_local);
+               memset(&ia, 0, sizeof(ia));
+               ia.s_addr = htonl(get_local_ip(p_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"
+                       "v=0\r\n"
+                       "o=LCR-Sofia-SIP 0 0 IN IP4 %s\r\n"
+                       "s=SIP Call\r\n"
+                       "c=IN IP4 %s\r\n"
+                       "t=0 0\r\n"
+                       "m=audio %d RTP/AVP %d\r\n"
+                       "a=rtpmap:%d %s/8000\r\n"
                        , inet_ntoa(ia), inet_ntoa(ia), p_s_rtp_port_local, p_s_rtp_payload_type, p_s_rtp_payload_type, media_type2name(p_s_rtp_media_type));
                PDEBUG(DEBUG_SIP, "Using SDP for rertieve: %s\n", sdp_str);
                nua_info(p_s_handle,
@@ -1192,6 +1277,8 @@ int Psip::message_information(unsigned int epoint_id, int message_id, union para
 
 int Psip::message_epoint(unsigned int epoint_id, int message_id, union parameter *param)
 {
+       struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
+
        if (Port::message_epoint(epoint_id, message_id, param))
                return 1;
 
@@ -1201,7 +1288,7 @@ int Psip::message_epoint(unsigned int epoint_id, int message_id, union parameter
                 && p_state != PORT_STATE_IN_PROCEEDING)
                        return 0;
                nua_respond(p_s_handle, SIP_180_RINGING, TAG_END());
-               sip_trace_header(this, "RESPOND", DIRECTION_OUT);
+               sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
                add_trace("respond", "value", "180 Ringing");
                end_trace();
                new_state(PORT_STATE_IN_ALERTING);
@@ -1373,7 +1460,7 @@ void Psip::i_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *mag
        }
        PDEBUG(DEBUG_SIP, "invite received (%s->%s)\n", from, to);
 
-       sip_trace_header(this, "Payload received", DIRECTION_NONE);
+       sip_trace_header(this, inst->interface_name, "Payload received", DIRECTION_NONE);
        ret = parse_sdp(sip, &p_s_rtp_ip_remote, &p_s_rtp_port_remote, payload_types, media_types, &payloads, sizeof(payload_types));
        if (!ret) {
                /* if no RTP bridge, we must support LAW codec, otherwise we forward what we have */
@@ -1399,7 +1486,7 @@ void Psip::i_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *mag
                        nua_respond(nh, SIP_415_UNSUPPORTED_MEDIA, TAG_END());
                nua_handle_destroy(nh);
                p_s_handle = NULL;
-               sip_trace_header(this, "RESPOND", DIRECTION_OUT);
+               sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
                if (ret == 400)
                        add_trace("respond", "value", "415 Unsupported Media");
                else
@@ -1416,7 +1503,7 @@ void Psip::i_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *mag
                nua_respond(nh, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
                nua_handle_destroy(nh);
                p_s_handle = NULL;
-               sip_trace_header(this, "RESPOND", DIRECTION_OUT);
+               sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
                add_trace("respond", "value", "500 Internal Server Error");
                add_trace("reason", NULL, "failed to open RTP/RTCP sockts");
                end_trace();
@@ -1426,12 +1513,12 @@ void Psip::i_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *mag
        }
 
        /* apply handle */
-       sip_trace_header(this, "NEW handle", DIRECTION_IN);
+       sip_trace_header(this, inst->interface_name, "NEW handle", DIRECTION_IN);
        add_trace("handle", "new", "0x%x", nh);
        p_s_handle = nh;
        end_trace();
 
-       sip_trace_header(this, "INVITE", DIRECTION_IN);
+       sip_trace_header(this, inst->interface_name, "INVITE", DIRECTION_IN);
        add_trace("rtp", "port", "%d", p_s_rtp_port_remote);
        /* caller information */
        if (!from[0]) {
@@ -1482,7 +1569,7 @@ void Psip::i_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *mag
 #ifdef NUTAG_AUTO100
        /* send trying (proceeding) */
        nua_respond(nh, SIP_100_TRYING, TAG_END());
-       sip_trace_header(this, "RESPOND", DIRECTION_OUT);
+       sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
        add_trace("respond", "value", "100 Trying");
        end_trace();
 #endif
@@ -1516,6 +1603,11 @@ void Psip::i_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *mag
        }
        message_put(message);
 
+       PDEBUG(DEBUG_SIP, "Invite received, scheduling option timer\n");
+       if (inst->options_timeout)
+               schedule_timer(&p_s_invite_option_timer, inst->options_timeout, 0);
+       p_s_invite_direction = DIRECTION_IN;
+
        /* send progress, if tones are available and if we don't bridge */
        if (!p_s_rtp_bridge && interface->is_tones == IS_YES) {
                char sdp_str[256];
@@ -1530,7 +1622,7 @@ void Psip::i_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *mag
                        nua_respond(nh, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
                        nua_handle_destroy(nh);
                        p_s_handle = NULL;
-                       sip_trace_header(this, "RESPOND", DIRECTION_OUT);
+                       sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
                        add_trace("respond", "value", "500 Internal Server Error");
                        add_trace("reason", NULL, "failed to connect RTP/RTCP sockts");
                        end_trace();
@@ -1545,16 +1637,16 @@ void Psip::i_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *mag
                        return;
                }
 
-               ia.s_addr = htonl(p_s_rtp_ip_local);
-
+               memset(&ia, 0, sizeof(ia));
+               ia.s_addr = htonl(get_local_ip(p_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"
+                       "v=0\r\n"
+                       "o=LCR-Sofia-SIP 0 0 IN IP4 %s\r\n"
+                       "s=SIP Call\r\n"
+                       "c=IN IP4 %s\r\n"
+                       "t=0 0\r\n"
+                       "m=audio %d RTP/AVP %d\r\n"
+                       "a=rtpmap:%d %s/8000\r\n"
                        , inet_ntoa(ia), inet_ntoa(ia), p_s_rtp_port_local, payload_type, payload_type, media_type2name(media_type));
                PDEBUG(DEBUG_SIP, "Using SDP response: %s\n", sdp_str);
 
@@ -1562,7 +1654,7 @@ void Psip::i_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *mag
                        NUTAG_MEDIA_ENABLE(0),
                        SIPTAG_CONTENT_TYPE_STR("application/sdp"),
                        SIPTAG_PAYLOAD_STR(sdp_str), TAG_END());
-               sip_trace_header(this, "RESPOND", DIRECTION_OUT);
+               sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
                add_trace("respond", "value", "183 SESSION PROGRESS");
                add_trace("reason", NULL, "audio available");
                add_trace("rtp", "ip", "%s", inet_ntoa(ia));
@@ -1574,12 +1666,13 @@ void Psip::i_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *mag
 
 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 sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
        struct lcr_msg *message;
        int cause = 0;
 
        PDEBUG(DEBUG_SIP, "bye received\n");
 
-       sip_trace_header(this, "BYE", DIRECTION_IN);
+       sip_trace_header(this, inst->interface_name, "BYE", DIRECTION_IN);
        if (sip->sip_reason && sip->sip_reason->re_protocol && !strcasecmp(sip->sip_reason->re_protocol, "Q.850") && sip->sip_reason->re_cause) {
                cause = atoi(sip->sip_reason->re_cause);
                add_trace("cause", "value", "%d", cause);
@@ -1588,7 +1681,7 @@ void Psip::i_bye(int status, char const *phrase, nua_t *nua, nua_magic_t *magic,
 
 // 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);
+       sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
        add_trace("respond", "value", "200 OK");
        end_trace();
 //     nua_handle_destroy(nh);
@@ -1611,11 +1704,12 @@ void Psip::i_bye(int status, char const *phrase, nua_t *nua, nua_magic_t *magic,
 
 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 sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
        struct lcr_msg *message;
 
        PDEBUG(DEBUG_SIP, "cancel received\n");
 
-       sip_trace_header(this, "CANCEL", DIRECTION_IN);
+       sip_trace_header(this, inst->interface_name, "CANCEL", DIRECTION_IN);
        end_trace();
 
        nua_handle_destroy(nh);
@@ -1660,8 +1754,11 @@ void Psip::r_cancel(int status, char const *phrase, nua_t *nua, nua_magic_t *mag
        trigger_work(&p_s_delete);
 }
 
+#warning i_invite
+#warning r_invite
 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 sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
        struct lcr_msg *message;
        int cause = 0, location = 0;
        int media_types[32];
@@ -1670,7 +1767,7 @@ void Psip::r_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *mag
 
        PDEBUG(DEBUG_SIP, "response to invite received (status = %d)\n", status);
 
-       sip_trace_header(this, "RESPOND", DIRECTION_OUT);
+       sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
        add_trace("respond", "value", "%d", status);
        end_trace();
 
@@ -1678,7 +1775,7 @@ void Psip::r_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *mag
        if (status == 183 || (status >= 200 && status <= 299)) {
                int ret;
 
-               sip_trace_header(this, "Payload received", DIRECTION_NONE);
+               sip_trace_header(this, inst->interface_name, "Payload received", DIRECTION_NONE);
                ret = parse_sdp(sip, &p_s_rtp_ip_remote, &p_s_rtp_port_remote, payload_types, media_types, &payloads, sizeof(payload_types));
                if (!ret) {
                        if (payloads != 1)
@@ -1693,7 +1790,7 @@ void Psip::r_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *mag
                end_trace();
                if (ret) {
                        nua_cancel(nh, TAG_END());
-                       sip_trace_header(this, "CANCEL", DIRECTION_OUT);
+                       sip_trace_header(this, inst->interface_name, "CANCEL", DIRECTION_OUT);
                        add_trace("reason", NULL, "accepted codec does not match");
                        end_trace();
                        cause = 88;
@@ -1704,7 +1801,7 @@ void Psip::r_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *mag
                /* connect to remote RTP (if not bridging) */
                if (!p_s_rtp_bridge && rtp_connect() < 0) {
                        nua_cancel(nh, TAG_END());
-                       sip_trace_header(this, "CANCEL", DIRECTION_OUT);
+                       sip_trace_header(this, inst->interface_name, "CANCEL", DIRECTION_OUT);
                        add_trace("reason", NULL, "failed to open RTP/RTCP sockts");
                        end_trace();
                        cause = 31;
@@ -1713,6 +1810,10 @@ void Psip::r_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *mag
                }
        }
 
+       PDEBUG(DEBUG_SIP, "Invite response, scheduling option timer\n");
+       if (inst->options_timeout)
+               schedule_timer(&p_s_invite_option_timer, inst->options_timeout, 0);
+
        /* process 1xx */
        switch (status) {
        case 100:
@@ -1790,16 +1891,137 @@ release_with_cause:
        trigger_work(&p_s_delete);
 }
 
+void Psip::r_options(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tagss[])
+{
+       struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
+       int cause = 0, location = 0;
+       struct lcr_msg *message;
+
+       PDEBUG(DEBUG_SIP, "options result %d received\n", status);
+
+       if (status == 200) {
+               PDEBUG(DEBUG_SIP, "options ok, scheduling timer\n");
+               schedule_timer(&p_s_invite_option_timer, inst->options_timeout, 0);
+               return;
+       }
+
+       nua_handle_destroy(nh);
+       p_s_handle = NULL;
+
+       rtp_close();
+
+       cause = status2cause(status);
+       location = LOCATION_BEYOND;
+
+       while(p_epointlist) {
+               /* send setup message to endpoit */
+               message = message_create(p_serial, p_epointlist->epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
+               message->param.disconnectinfo.cause = cause;
+               message->param.disconnectinfo.location = location;
+               message_put(message);
+               /* remove epoint */
+               free_epointlist(p_epointlist);
+       }
+       new_state(PORT_STATE_RELEASE);
+       trigger_work(&p_s_delete);
+}
+
+static void challenge(struct sip_inst *inst, class Psip *psip, int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tagss[])
+{
+       sip_www_authenticate_t const *authenticate = NULL;
+       char const *realm = NULL;
+       char const *scheme = NULL;
+       int i;
+       char *cur;
+       char authentication[256] = "";
+
+       PDEBUG(DEBUG_SIP, "challenge order received\n");
+
+       if (sip->sip_www_authenticate) {
+               authenticate = sip->sip_www_authenticate;
+       } else if (sip->sip_proxy_authenticate) {
+               authenticate = sip->sip_proxy_authenticate;
+       } else {
+               PDEBUG(DEBUG_SIP, "No authentication header found\n");
+               sip_trace_header(psip, inst->interface_name, "AUTHENTICATE", DIRECTION_OUT);
+               add_trace("error", NULL, "Authentication method unknwon");
+               end_trace();
+               return;
+       }
+
+       scheme = (char const *) authenticate->au_scheme;
+       if (authenticate->au_params) {
+               for (i = 0; (cur = (char *) authenticate->au_params[i]); i++) {
+                       if ((realm = strstr(cur, "realm="))) {
+                               realm += 6;
+                               break;
+                       }
+               }
+       }
+
+       if (!scheme || !realm) {
+               PDEBUG(DEBUG_SIP, "No scheme or no realm in authentication header found\n");
+               sip_trace_header(psip, inst->interface_name, "AUTHENTICATE", DIRECTION_OUT);
+               add_trace("error", NULL, "Authentication header has no realm or scheme");
+               end_trace();
+               return;
+       }
+
+       SPRINT(authentication, "%s:%s:%s:%s", scheme, realm, inst->auth_user, inst->auth_password);
+       PDEBUG(DEBUG_SIP, "auth: '%s'\n", authentication);
+
+       sip_trace_header(psip, inst->interface_name, "AUTHENTICATE", DIRECTION_OUT);
+       add_trace("scheme", NULL, "%s", scheme);
+       add_trace("realm", NULL, "%s", realm);
+       add_trace("user", NULL, "%s", inst->auth_user);
+       add_trace("pass", NULL, "%s", inst->auth_password);
+       end_trace();
+
+#warning hier als option
+       nua_authenticate(nh, /*SIPTAG_EXPIRES_STR("3600"),*/ NUTAG_AUTH(authentication), TAG_END());
+}
+
 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);
+       PDEBUG(DEBUG_SIP, "Event %d from SIP stack received (handle=%p)\n", event, nh);
        if (!nh)
                return;
 
+       if (inst->register_handle == nh) {
+               sip_trace_header(NULL, inst->interface_name, "STATUS", DIRECTION_OUT);
+               add_trace("value", NULL, "%d", status);
+               add_trace("phrase", NULL, "%s", phrase);
+               end_trace();
+
+               switch (status) {
+               case 200:
+                       inst->register_state = REGISTER_STATE_REGISTERED;
+                       if (inst->options_timeout)
+                               schedule_timer(&inst->register_option_timer, inst->options_timeout, 0);
+                       break;
+               case 401:
+               case 407:
+                       PDEBUG(DEBUG_SIP, "Register challenge received\n");
+                       challenge(inst, NULL, status, phrase, nua, magic, nh, hmagic, sip, tags);
+                       break;
+               default:
+                       if (status < 400)
+                               break;
+                       PDEBUG(DEBUG_SIP, "Register failed, starting register timer\n");
+                       inst->register_state = REGISTER_STATE_FAILED;
+                       nua_handle_destroy(inst->register_handle);
+                       unsched_timer(&inst->register_option_timer);
+                       schedule_timer(&inst->register_retry_timer, REGISTER_RETRY_TIMER);
+                       inst->register_handle = NULL;
+                       break;
+               }
+               return;
+       }
+
        /* create or find port instance */
        if (event == nua_i_invite)
        {
@@ -1838,10 +2060,25 @@ static void sip_callback(nua_event_t event, int status, char const *phrase, nua_
                return;
        }
 
+       sip_trace_header(psip, inst->interface_name, "STATUS", DIRECTION_OUT);
+       add_trace("value", NULL, "%d", status);
+       add_trace("phrase", NULL, "%s", phrase);
+       end_trace();
+
+       switch (status) {
+       case 401:
+       case 407:
+               challenge(inst, psip, status, phrase, nua, magic, nh, hmagic, sip, tags);
+               return;
+       }
+
        switch (event) {
        case nua_r_set_params:
                PDEBUG(DEBUG_SIP, "setparam response\n");
                break;
+       case nua_r_options:
+               psip->r_options(status, phrase, nua, magic, nh, hmagic, sip, tags);
+               break;
        case nua_i_error:
                PDEBUG(DEBUG_SIP, "error received\n");
                break;
@@ -1883,14 +2120,48 @@ static void sip_callback(nua_event_t event, int status, char const *phrase, nua_
        }
 }
 
+static void stun_bind_cb(stun_discovery_magic_t *magic, stun_handle_t *sh, stun_discovery_t *sd, stun_action_t action, stun_state_t event)
+{
+       struct sip_inst *inst = (struct sip_inst *) magic;
+       su_sockaddr_t sa;
+       socklen_t addrlen;
+
+       PDEBUG(DEBUG_SIP, "Event %d from STUN stack received\n", event);
+
+       switch (event) {
+       case stun_discovery_done:
+               addrlen = sizeof(sa);
+               memset(&sa, 0, addrlen);
+               if (stun_discovery_get_address(sd, &sa, &addrlen) < 0) {
+                       PDEBUG(DEBUG_SIP, "stun_discovery_get_address failed\n");
+                       goto failed;
+               }
+               su_inet_ntop(sa.su_family, SU_ADDR(&sa), inst->stun_ipaddr, sizeof(inst->stun_ipaddr));
+               inst->stun_state = STUN_STATE_RESOLVED;
+               schedule_timer(&inst->stun_retry_timer, inst->stun_interval, 0);
+               sip_trace_header(NULL, inst->interface_name, "STUN resolved", DIRECTION_OUT);
+               add_trace("ip", "addr", "%s", inst->stun_ipaddr);
+               end_trace();
+               break;
+       default:
+failed:
+               PDEBUG(DEBUG_SIP, "STUN failed, starting timer\n");
+               inst->stun_state = STUN_STATE_FAILED;
+               schedule_timer(&inst->stun_retry_timer, STUN_RETRY_TIMER);
+               sip_trace_header(NULL, inst->interface_name, "STUN failed", DIRECTION_OUT);
+               end_trace();
+       }
+}
+
 /* received shutdown due to termination of RTP */
 void Psip::rtp_shutdown(void)
 {
+       struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
        struct lcr_msg *message;
 
        PDEBUG(DEBUG_SIP, "RTP stream terminated\n");
 
-       sip_trace_header(this, "RTP terminated", DIRECTION_IN);
+       sip_trace_header(this, inst->interface_name, "RTP terminated", DIRECTION_IN);
        end_trace();
 
        nua_handle_destroy(p_s_handle);
@@ -1909,15 +2180,89 @@ void Psip::rtp_shutdown(void)
        trigger_work(&p_s_delete);
 }
 
+static int invite_option_timer(struct lcr_timer *timer, void *instance, int index)
+{
+       class Psip *psip = (class Psip *)instance;
+       struct sip_inst *inst = (struct sip_inst *) psip->p_s_sip_inst;
+
+       sip_trace_header(psip, inst->interface_name, "OPTIONS", psip->p_s_invite_direction);
+       end_trace();
+
+       nua_options(psip->p_s_handle,
+               TAG_END());
+
+//     schedule_timer(&psip->p_s_invite_option_timer, inst->options_timeout, 0);
+
+       return 0;
+}
+
+static int stun_retry_timer(struct lcr_timer *timer, void *instance, int index)
+{
+       struct sip_inst *inst = (struct sip_inst *)instance;
+
+       PDEBUG(DEBUG_SIP, "timeout, restart stun lookup\n");
+       inst->stun_state = STUN_STATE_UNRESOLVED;
+
+       return 0;
+}
+
+static int register_retry_timer(struct lcr_timer *timer, void *instance, int index)
+{
+       struct sip_inst *inst = (struct sip_inst *)instance;
+
+       PDEBUG(DEBUG_SIP, "timeout, restart register\n");
+       inst->register_state = REGISTER_STATE_UNREGISTERED;
+
+       return 0;
+}
+
+static int register_option_timer(struct lcr_timer *timer, void *instance, int index)
+{
+       struct sip_inst *inst = (struct sip_inst *)instance;
+       sip_trace_header(NULL, inst->interface_name, "OPTIONS", DIRECTION_OUT);
+       end_trace();
+
+       nua_options(inst->register_handle,
+               TAG_END());
+
+//     schedule_timer(&inst->register_option_timer, inst->options_timeout, 0);
+
+       return 0;
+}
+
 int sip_init_inst(struct interface *interface)
 {
        struct sip_inst *inst = (struct sip_inst *) MALLOC(sizeof(*inst));
-       char local[64];
+       char local[256];
 
        interface->sip_inst = inst;
        SCPY(inst->interface_name, interface->name);
        SCPY(inst->local_peer, interface->sip_local_peer);
        SCPY(inst->remote_peer, interface->sip_remote_peer);
+       if (interface->sip_register) {
+               inst->register_state = REGISTER_STATE_UNREGISTERED;
+               SCPY(inst->register_user, interface->sip_register_user);
+               SCPY(inst->register_host, interface->sip_register_host);
+       }
+       SCPY(inst->auth_user, interface->sip_auth_user);
+       SCPY(inst->auth_password, interface->sip_auth_password);
+       inst->options_timeout = interface->sip_options_timer;
+
+       inst->rtp_port_from = interface->rtp_port_from;
+       inst->rtp_port_to = interface->rtp_port_to;
+       if (!inst->rtp_port_from || !inst->rtp_port_to) {
+               inst->rtp_port_from = RTP_PORT_BASE;
+               inst->rtp_port_to = RTP_PORT_MAX;
+       }
+       inst->next_rtp_port = inst->rtp_port_from;
+
+       /* create timers */
+       memset(&inst->stun_retry_timer, 0, sizeof(inst->stun_retry_timer));
+        add_timer(&inst->stun_retry_timer, stun_retry_timer, inst, 0);
+       memset(&inst->register_retry_timer, 0, sizeof(inst->register_retry_timer));
+        add_timer(&inst->register_retry_timer, register_retry_timer, inst, 0);
+       memset(&inst->register_option_timer, 0, sizeof(inst->register_option_timer));
+        add_timer(&inst->register_option_timer, register_option_timer, inst, 0);
 
        /* init root object */
        inst->root = su_root_create(inst);
@@ -1953,6 +2298,35 @@ int sip_init_inst(struct interface *interface)
                NUTAG_AUTOANSWER(0),
                TAG_NULL());
 
+       if (interface->sip_stun_server[0]) {
+#if 0
+               inst->stun_root = su_root_create(inst);
+               if (!inst->stun_root) {
+                       PERROR("Failed to create STUN root\n");
+                       sip_exit_inst(interface);
+                       return -EINVAL;
+               }
+               auch im exit
+#endif
+               SCPY(inst->stun_server, interface->sip_stun_server);
+               inst->stun_interval = interface->sip_stun_interval;
+               inst->stun_handle = stun_handle_init(inst->root,
+                       STUNTAG_SERVER(inst->stun_server),
+                       TAG_NULL());
+               if (!inst->stun_handle) {
+                       PERROR("Failed to create STUN handle\n");
+                       sip_exit_inst(interface);
+                       return -EINVAL;
+               }
+               inst->stun_socket = su_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+               if (inst->stun_socket < 0) {
+                       PERROR("Failed to create STUN socket\n");
+                       sip_exit_inst(interface);
+                       return -EINVAL;
+               }
+               inst->stun_state = STUN_STATE_UNRESOLVED;
+       }
+
        PDEBUG(DEBUG_SIP, "SIP interface created (inst=%p)\n", inst);
 
        any_sip_interface = 1;
@@ -1966,11 +2340,19 @@ void sip_exit_inst(struct interface *interface)
 
        if (!inst)
                return;
+       del_timer(&inst->stun_retry_timer);
+       del_timer(&inst->register_retry_timer);
+       del_timer(&inst->register_option_timer);
+       if (inst->stun_socket)
+               su_close(inst->stun_socket);
+       if (inst->stun_handle)
+               stun_handle_destroy(inst->stun_handle);
+       if (inst->register_handle)
+               nua_handle_destroy(inst->register_handle);
        if (inst->root)
                su_root_destroy(inst->root);
-       if (inst->nua) {
+       if (inst->nua)
                nua_destroy(inst->nua);
-       }
        FREE(inst, sizeof(*inst));
        interface->sip_inst = NULL;
 
@@ -2021,6 +2403,77 @@ void sip_exit(void)
        PDEBUG(DEBUG_SIP, "SIP globals de-initialized\n");
 }
 
+static void sip_handle_stun(struct sip_inst *inst)
+{
+       int rc;
+
+       switch (inst->stun_state) {
+       case STUN_STATE_UNRESOLVED:
+               PDEBUG(DEBUG_SIP, "Trying to to get local IP from stun server\n");
+               rc = stun_bind(inst->stun_handle, stun_bind_cb, (stun_discovery_magic_t *)inst,
+                       STUNTAG_SOCKET(inst->stun_socket),
+                       STUNTAG_REGISTER_EVENTS(1),
+                       TAG_NULL());
+               if (rc < 0) {
+                       PERROR("Failed to call stun_bind()\n");
+                       inst->stun_state = STUN_STATE_FAILED;
+                       break;
+               }
+               inst->stun_state = STUN_STATE_RESOLVING;
+               sip_trace_header(NULL, inst->interface_name, "STUN resolving", DIRECTION_OUT);
+               add_trace("server", "addr", "%s", inst->stun_server);
+               end_trace();
+               break;
+       }
+}
+
+static void sip_handle_register(struct sip_inst *inst)
+{
+       char from[128] = "";
+       char to[128] = "";
+       char contact[128] = "";
+
+       switch (inst->register_state) {
+       case REGISTER_STATE_UNREGISTERED:
+               /* wait for resoved stun */
+               if (inst->stun_handle && inst->stun_state != STUN_STATE_RESOLVED)
+                       return;
+
+               PDEBUG(DEBUG_SIP, "Registering to peer\n");
+               inst->register_handle = nua_handle(inst->nua, NULL, TAG_END());
+               if (!inst->register_handle) {
+                       PERROR("Failed to create handle\n");
+                       inst->register_state = REGISTER_STATE_FAILED;
+                       break;
+               }
+               /* apply handle to trace */
+               sip_trace_header(NULL, inst->interface_name, "NEW handle", DIRECTION_NONE);
+               add_trace("handle", "new", "0x%x", inst->register_handle);
+               end_trace();
+
+               SPRINT(from, "sip:%s@%s", inst->register_user, inst->register_host);
+               SPRINT(to, "sip:%s@%s", inst->register_user, inst->register_host);
+               if (inst->stun_handle && inst->stun_ipaddr[0])
+                       SPRINT(contact, "sip:%s@%s", inst->register_user, inst->stun_ipaddr);
+
+               sip_trace_header(NULL, inst->interface_name, "REGISTER", DIRECTION_OUT);
+               add_trace("from", "uri", "%s", from);
+               add_trace("to", "uri", "%s", to);
+               end_trace();
+
+               nua_register(inst->register_handle,
+                       TAG_IF(from[0], SIPTAG_FROM_STR(from)),
+                       TAG_IF(to[0], SIPTAG_TO_STR(to)),
+                       TAG_IF(contact[0], SIPTAG_CONTACT_STR(contact)),
+                       TAG_END());
+
+               inst->register_state = REGISTER_STATE_REGISTERING;
+
+               break;
+       }
+       
+}
+
 void sip_handle(void)
 {
        struct interface *interface = interface_first;
@@ -2030,6 +2483,8 @@ void sip_handle(void)
                if (interface->sip_inst) {
                        inst = (struct sip_inst *) interface->sip_inst;
                        su_root_step(inst->root, 0);
+                       sip_handle_stun(inst);
+                       sip_handle_register(inst);
                }
                interface = interface->next;
        }
@@ -2060,7 +2515,7 @@ void Psip::set_tone(const char *dir, const char *tone)
 void Psip::update_load(void)
 {
        /* don't trigger load event if event already active */
-       if (p_s_loadtimer.active)
+       if (p_s_load_timer.active)
                return;
 
        /* don't start timer if ... */
@@ -2068,7 +2523,7 @@ void Psip::update_load(void)
                return;
 
        p_s_next_tv_sec = 0;
-       schedule_timer(&p_s_loadtimer, 0, 0); /* no delay the first time */
+       schedule_timer(&p_s_load_timer, 0, 0); /* no delay the first time */
 }
 
 static int load_timer(struct lcr_timer *timer, void *instance, int index)
@@ -2103,7 +2558,7 @@ void Psip::load_tx(void)
                        p_s_next_tv_usec -= 1000000;
                        p_s_next_tv_sec++;
                }
-               schedule_timer(&p_s_loadtimer, 0, SEND_SIP_LEN * 125);
+               schedule_timer(&p_s_load_timer, 0, SEND_SIP_LEN * 125);
        } else {
                diff = 1000000 * (current_time.tv_sec - p_s_next_tv_sec)
                        + (current_time.tv_usec - p_s_next_tv_usec);
@@ -2124,7 +2579,7 @@ void Psip::load_tx(void)
                                p_s_next_tv_sec++;
                        }
                }
-               schedule_timer(&p_s_loadtimer, 0, SEND_SIP_LEN * 125 - diff);
+               schedule_timer(&p_s_load_timer, 0, SEND_SIP_LEN * 125 - diff);
        }
 
        /* copy tones */
diff --git a/sip.h b/sip.h
index 2c5d5ef..8357217 100644 (file)
--- a/sip.h
+++ b/sip.h
@@ -19,6 +19,7 @@ class Psip : public Port
        public:
        Psip(int type, char *portname, struct port_settings *settings, struct interface *interface);
        ~Psip();
+       unsigned int get_local_ip(unsigned int ip);
        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);
@@ -32,10 +33,13 @@ class Psip : public Port
        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 r_options(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[]);
        void *p_s_sip_inst;
        struct lcr_work p_s_delete;
        nua_handle_t *p_s_handle;
        nua_magic_t *p_s_magic;
+       struct lcr_timer p_s_invite_option_timer; /* time to send OPTION to invite transaction */
+       int p_s_invite_direction; /* DIRECTION_* of invite */
        int p_s_rtp_bridge; /* bridge RTP instead of having a local RTP peer */
        unsigned short p_s_rtp_port_local;
        unsigned short p_s_rtp_port_remote;
@@ -68,7 +72,7 @@ class Psip : public Port
        void rtp_shutdown(void);
 
        /* audio */
-       struct lcr_timer p_s_loadtimer;         /* timer for audio transmission */
+       struct lcr_timer p_s_load_timer;        /* timer for audio transmission */
        virtual void update_load(void);
        void load_tx(void);
        unsigned int p_s_next_tv_sec;           /* time stamp of next expected tx_load call, (to sync audio data) */
diff --git a/trace.c b/trace.c
index caa1e40..0d6c114 100644 (file)
--- a/trace.c
+++ b/trace.c
@@ -202,6 +202,10 @@ static char *print_trace(int detail, int port, char *interface, char *caller, ch
        /* elements */
        switch(detail) {
                case 1: /* brief */
+               if (trace.interface[0]) {
+                       SPRINT(buffer, "  iface %s", trace.interface);
+                       SCAT(trace_string, buffer);
+               }
                if (trace.port >= 0) {
                        SPRINT(buffer, "  port %d", trace.port);
                        SCAT(trace_string, buffer);