From a3a443a6d073ea0015dcc4379d9d1fb712b7828b Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Wed, 1 Nov 2017 20:43:13 +0100 Subject: [PATCH] SIP: Register, STUN and authentication support --- default/interface.conf | 19 ++ interface.c | 149 ++++++++++- interface.h | 14 +- sip.cpp | 677 +++++++++++++++++++++++++++++++++++++++++-------- sip.h | 6 +- trace.c | 4 + 6 files changed, 752 insertions(+), 117 deletions(-) diff --git a/default/interface.conf b/default/interface.conf index 1450ae3..8593c9f 100644 --- a/default/interface.conf +++ b/default/interface.conf @@ -208,6 +208,25 @@ #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 to register to a SIP gateway +#register sipgate.de +##define RTP port range or use default +#rtp-ports 30000 39999 +## use authentication credentials +#authenticate +## 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 % +#tones no +#earlyb no + # Hint: Enter "lcr interface" for quick help on interface options. diff --git a/interface.c b/interface.c index 7619294..71e1d80 100644 --- a/interface.c +++ b/interface.c @@ -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, "", "Sets up GSM mobile station interface for using Osmocom-BB.\n" "The socket will be /tmp/ms_mncc_."}, - {"sip", &inter_sip, " ", - "Sets up SIP interface that represents one SIP endpoint.\n" - "Give SIP configuration file."}, + {"sip", &inter_sip, " ", + "Sets up SIP interface that represents one SIP endpoint."}, + {"register", &inter_register, " ", + "Registers to given SIP registrar."}, + {"authenticate", &inter_authenticate, " ", + "Defines SIP user and password for authentication."}, + {"options-timer", &options_timer, " | 0", + "Defines SIP timer to send OPTIONS messages to keepalive SIP sessions."}, + {"stun", &options_stun, " ", + "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, " ", + "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" diff --git a/interface.h b/interface.h index 4b624f3..58773bb 100644 --- a/interface.h +++ b/interface.h @@ -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 --- a/sip.cpp +++ b/sip.cpp @@ -14,6 +14,8 @@ #include #include #include +#include +#include #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, ¶m->setup.dialinginfo, sizeof(p_dialinginfo)); memcpy(&p_callerinfo, ¶m->setup.callerinfo, sizeof(p_callerinfo)); - memcpy(&p_redirinfo, ¶m->setup.redirinfo, sizeof(p_redirinfo)); +// memcpy(&p_redirinfo, ¶m->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 --- 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 --- 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); -- 2.13.6