/* * Copyright (C) 2010-2011 Mamadou Diop. * Copyright (C) 2012 Doubango Telecom * * This file is part of Open Source Doubango Framework. * * DOUBANGO is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * DOUBANGO is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with DOUBANGO. * */ /**@file tsip.c * @brief SIP (RFC 3261) and 3GPP IMS/LTE (TS 24.229) implementation. */ #include "tsip.h" #include "tinysip/tsip_event.h" #include "tinysip/parsers/tsip_parser_uri.h" #include "tinysip/transactions/tsip_transac_layer.h" #include "tinysip/dialogs/tsip_dialog_layer.h" #include "tinysip/transports/tsip_transport_layer.h" #include "tinysip/api/tsip_api_register.h" #include "tinysip/api/tsip_api_subscribe.h" #include "tinysip/api/tsip_api_message.h" #include "tinymedia/tmedia_defaults.h" #include "tnet.h" #include "tsk_memory.h" #include "tsk_debug.h" #include "tsk_time.h" #include #include static void* TSK_STDCALL run(void* self); /* For tests: * http://www.voip-info.org/wiki/view/PROTOS+Test-Suite * http://tools.ietf.org/html/rfc4475 * http://portal.etsi.org/docbox/EC_Files/EC_Files/ts_10202702v030101p.pdf */ /**@defgroup tsip_stack_group 3GPP IMS/LTE Stack */ extern tsip_event_t* tsip_event_create(tsip_ssession_t* ss, short code, const char* phrase, const tsip_message_t* sipmessage, tsip_event_type_t type); #define TSIP_STACK_SIGNAL(self, code, phrase) \ { \ tsip_event_t* e; \ if((e = tsip_event_create(tsk_null, code, phrase, tsk_null, tsip_event_stack))){ \ TSK_RUNNABLE_ENQUEUE_OBJECT(TSK_RUNNABLE(self), e); \ } \ } static int __tsip_stack_get_transport_idx_by_name(tsip_stack_t *self, const char* name) { if(tsk_strnullORempty(name) && TSIP_STACK_MODE_IS_CLIENT(self)) { return self->network.transport_idx_default; // for backward compatibility } return tsip_transport_get_idx_by_name(name); } /* Internal function used to set all user's parameters */ static int __tsip_stack_set(tsip_stack_t *self, va_list* app) { tsip_stack_param_type_t curr; while((curr = va_arg(*app, tsip_stack_param_type_t)) != tsip_pname_null) { switch(curr) { /* === Identity === */ case tsip_pname_display_name: { /* (const char*)NAME_STR */ const char* NAME_STR = va_arg(*app, const char*); tsk_strupdate(&self->identity.display_name, NAME_STR); if (self->identity.impu) { tsk_strupdate(&self->identity.impu->display_name, NAME_STR); } break; } case tsip_pname_impu: case tsip_pname_preferred_id: { /* (const char*)URI_STR */ const char* URI_STR = va_arg(*app, const char*); if(!tsk_strnullORempty(URI_STR)) { tsip_uri_t *uri = tsip_uri_parse(URI_STR, tsk_strlen(URI_STR)); if(uri) { tsk_strupdate(&uri->display_name, self->identity.display_name); if(curr == tsip_pname_impu) { TSK_OBJECT_SAFE_FREE(self->identity.impu); self->identity.impu = uri; } else { TSK_OBJECT_SAFE_FREE(self->identity.preferred); self->identity.preferred = uri; } } else { TSK_DEBUG_ERROR("'%s' is an invalid SIP/TEL URI", URI_STR); if(curr == tsip_pname_impu) { return -1; /* IMPU is mandatory but P-Preferred-Identity isn't. */ } } } else if(curr == tsip_pname_impu) { TSK_DEBUG_ERROR("IMPU (IMS Public Identity) is mandatory."); return -1; } break; } case tsip_pname_impi: { /* (const char*)IMPI_STR */ const char* IMPI_STR = va_arg(*app, const char*); if(tsk_strnullORempty(IMPI_STR)) { TSK_DEBUG_ERROR("IMPI (IMS Private Identity) is mandatory."); return -1; /* mandatory */ } tsk_strupdate(&self->identity.impi, IMPI_STR); break; } case tsip_pname_password: { /* (const char*)PASSORD_STR */ const char* PASSORD_STR = va_arg(*app, const char*); tsk_strupdate(&self->identity.password, PASSORD_STR); break; } /* === SigComp === */ case tsip_pname_sigcomp: { /* (unsigned)DMS_UINT, (unsigned)SMS_UINT, (unsigned)CPB_UINT, (tsk_bool_t)PRES_DICT_BOOL */ self->sigcomp.dms = va_arg(*app, unsigned); self->sigcomp.sms = va_arg(*app, unsigned); self->sigcomp.cpb = va_arg(*app, unsigned); self->sigcomp.pres_dict = va_arg(*app, tsk_bool_t); break; } case tsip_pname_sigcomp_add_compartment: { /* (const char*)COMPARTMENT_ID_STR */ if(!self->sigcomp.handle) { self->sigcomp.handle = tsip_sigcomp_handler_create(self->sigcomp.cpb, self->sigcomp.dms, self->sigcomp.sms); tsip_sigcomp_handler_set_dicts(self->sigcomp.handle, self->sigcomp.sip_dict, self->sigcomp.pres_dict); } tsip_sigcomp_handler_add_compartment(self->sigcomp.handle, va_arg(*app, const char*)); break; } case tsip_pname_sigcomp_remove_compartment: { /* (const char*)COMPARTMENT_ID_STR */ if(self->sigcomp.handle) { tsip_sigcomp_handler_remove_compartment(self->sigcomp.handle, va_arg(*app, const char*)); } break; } /* === Network === */ case tsip_pname_realm: { /* (const char*)URI_STR */ const char* URI_STR = va_arg(*app, const char*); tsip_uri_t *uri; if(!tsk_strnullORempty(URI_STR) && (uri = tsip_uri_parse(URI_STR, tsk_strlen(URI_STR)))) { if(uri->type == uri_unknown) { /* scheme is missing or unsupported? */ tsk_strupdate(&uri->scheme, "sip"); uri->type = uri_sip; } TSK_OBJECT_SAFE_FREE(self->network.realm); /* delete old */ self->network.realm = uri; } else { TSK_DEBUG_ERROR("'%s' is an invalid SIP/TEL URI", URI_STR); return -1; /* mandatory */ } break; } case tsip_pname_local_ip: { /* (const char*)TRANSPORT_STR, (const char*)IP_STR */ const char* TRANSPORT_STR = va_arg(*app, const char*); const char* IP_STR = va_arg(*app, const char*); int t_idx = __tsip_stack_get_transport_idx_by_name(self, TRANSPORT_STR); if(t_idx < 0) { TSK_DEBUG_ERROR("%s not valid as transport or you're probably using deprecated function", TRANSPORT_STR); return -1; } tsk_strupdate(&self->network.local_ip[t_idx], IP_STR); self->network.local_ip_is_set_by_user[t_idx] = tsk_true; // do not query best source when stack is re-started if(TSIP_STACK_MODE_IS_SERVER(self) && !tsk_strnullORempty(TRANSPORT_STR)) { self->network.transport_types[t_idx] = tsip_transport_get_type_by_name(TRANSPORT_STR); } break; } case tsip_pname_local_port: { /* (const char*)TRANSPORT_STR, (unsigned)PORT_UINT */ const char* TRANSPORT_STR = va_arg(*app, const char*); unsigned PORT_UINT = va_arg(*app, unsigned); int t_idx = __tsip_stack_get_transport_idx_by_name(self, TRANSPORT_STR); if(t_idx < 0) { TSK_DEBUG_ERROR("%s not valid as transport or you're probably using deprecated function", TRANSPORT_STR); return -1; } self->network.local_port[t_idx] = PORT_UINT; if(TSIP_STACK_MODE_IS_SERVER(self) && !tsk_strnullORempty(TRANSPORT_STR)) { self->network.transport_types[t_idx] = tsip_transport_get_type_by_name(TRANSPORT_STR); } break; } case tsip_pname_aor: { /* (const char*)TRANSPORT_STR, (const char*)IP_STR, (unsigned)PORT_UINT */ const char* TRANSPORT_STR = va_arg(*app, const char*); const char* IP_STR = va_arg(*app, const char*); tnet_port_t PORT_UINT = (tnet_port_t)va_arg(*app, unsigned); int t_idx = __tsip_stack_get_transport_idx_by_name(self, TRANSPORT_STR); if(t_idx < 0) { TSK_DEBUG_ERROR("%s not valid as transport or you're probably using deprecated function", TRANSPORT_STR); return -1; } if(!tsk_strnullORempty(IP_STR)) { tsk_strupdate(&self->network.aor.ip[t_idx], IP_STR); } if(PORT_UINT) { self->network.aor.port[t_idx] = PORT_UINT; } break; } case tsip_pname_discovery_naptr: { /* (tsk_bool_t)ENABLED_BOOL */ self->network.discovery_naptr = va_arg(*app, tsk_bool_t); break; } case tsip_pname_discovery_dhcp: { /* (tsk_bool_t)ENABLED_BOOL */ self->network.discovery_dhcp = va_arg(*app, tsk_bool_t); break; } case tsip_pname_proxy_cscf: { /* (const char*)FQDN_STR, (unsigned)PORT_UINT, (const char*)TRANSPORT_STR, (const char*)IP_VERSION_STR */ const char* FQDN_STR = va_arg(*app, const char*); tnet_port_t PORT_UINT = va_arg(*app, unsigned); const char* TRANSPORT_STR = va_arg(*app, const char*); const char* IP_VERSION_STR = va_arg(*app, const char*); int t_idx = __tsip_stack_get_transport_idx_by_name(self, TRANSPORT_STR); if(t_idx < 0) { TSK_DEBUG_ERROR("%s not valid as transport or you're probably using deprecated function", TRANSPORT_STR); return -1; } if(TSIP_STACK_MODE_IS_CLIENT(self)) { // "client" mode support a unique proxy_cscf -> reset previous transports int k; for(k = 0; k < sizeof(self->network.proxy_cscf_type)/sizeof(self->network.proxy_cscf_type[0]); ++k) { self->network.proxy_cscf_type[k] = tnet_socket_type_invalid; } } /* IP Address */ tsk_strupdate(&self->network.proxy_cscf[t_idx], FQDN_STR); /* Port */ if(PORT_UINT) { self->network.proxy_cscf_port[t_idx] = PORT_UINT; } /* Transport */ if(tsk_strnullORempty(TRANSPORT_STR) || tsk_striequals(TRANSPORT_STR, "UDP")) { TNET_SOCKET_TYPE_SET_UDP(self->network.proxy_cscf_type[t_idx]); } else if(tsk_striequals(TRANSPORT_STR, "DTLS")) { TNET_SOCKET_TYPE_SET_DTLS(self->network.proxy_cscf_type[t_idx]); } else if(tsk_striequals(TRANSPORT_STR, "TCP")) { TNET_SOCKET_TYPE_SET_TCP(self->network.proxy_cscf_type[t_idx]); } else if(tsk_striequals(TRANSPORT_STR, "TLS")) { TNET_SOCKET_TYPE_SET_TLS(self->network.proxy_cscf_type[t_idx]); } else if(tsk_striequals(TRANSPORT_STR, "SCTP")) { TNET_SOCKET_TYPE_SET_SCTP(self->network.proxy_cscf_type[t_idx]); } else if(tsk_striequals(TRANSPORT_STR, "WS")) { TNET_SOCKET_TYPE_SET_WS(self->network.proxy_cscf_type[t_idx]); } else if(tsk_striequals(TRANSPORT_STR, "WSS")) { TNET_SOCKET_TYPE_SET_WSS(self->network.proxy_cscf_type[t_idx]); } TNET_SOCKET_TYPE_SET_IPV4(self->network.proxy_cscf_type[t_idx]); // IPv4 is the default version /* whether to use ipv6 or not */ if(!tsk_strnullORempty(IP_VERSION_STR)) { if(tsk_strcontains(IP_VERSION_STR, tsk_strlen(IP_VERSION_STR), "6")) { TNET_SOCKET_TYPE_SET_IPV6Only(self->network.proxy_cscf_type[t_idx]); // "only" to clear IPv4 flag } if(tsk_strcontains(IP_VERSION_STR, tsk_strlen(IP_VERSION_STR), "4")) { TNET_SOCKET_TYPE_SET_IPV4(self->network.proxy_cscf_type[t_idx]); /* Not IPV4only ==> '46'/'64' */ } } /* use same transport type as the proxy-cscf */ self->network.transport_types[t_idx] = self->network.proxy_cscf_type[t_idx]; /* set default transport */ self->network.transport_idx_default = t_idx; break; } case tsip_pname_dnsserver: { /* (const char*)IP_STR */ const char* IP_STR = va_arg(*app, const char*); if(tnet_dns_add_server(self->dns_ctx, IP_STR)) { TSK_DEBUG_ERROR("Failed to add [%s] as DNS server", IP_STR); } break; } case tsip_pname_max_fds: { /* (unsigned)MAX_FDS_UINT */ self->network.max_fds = va_arg(*app, unsigned); break; } case tsip_pname_mode: { /* (tsip_stack_mode_t)MODE_ENUM */ self->network.mode = va_arg(*app, tsip_stack_mode_t); break; } /* === Security === */ case tsip_pname_early_ims: { /* (tsk_bool_t)ENABLED_BOOL */ self->security.earlyIMS = va_arg(*app, tsk_bool_t); break; } case tsip_pname_secagree_ipsec: { /* (const char*)TRANSPORT_STR, (tsk_bool_t)ENABLED_BOOL */ const char* TRANSPORT_STR = va_arg(*app, const char*); tsk_bool_t ENABLED_BOOL = va_arg(*app, tsk_bool_t); int t_idx = __tsip_stack_get_transport_idx_by_name(self, TRANSPORT_STR); if(t_idx < 0) { TSK_DEBUG_ERROR("%s not valid as transport or you're probably using deprecated function", TRANSPORT_STR); return -1; } if(ENABLED_BOOL) { tsk_strupdate(&self->security.secagree_mech, "ipsec-3gpp"); TNET_SOCKET_TYPE_SET_IPSEC(self->network.proxy_cscf_type[t_idx]); } else { TNET_SOCKET_TYPE_UNSET(self->network.proxy_cscf_type[t_idx], IPSEC); } break; } case tsip_pname_secagree_tls: { /* (tsk_bool_t)ENABLED_BOOL */ if((self->security.enable_secagree_tls = va_arg(*app, tsk_bool_t))) { tsk_strupdate(&self->security.secagree_mech, "tls"); } break; } case tsip_pname_amf: { /* (uint16_t)AMF_UINT16 */ unsigned amf = va_arg(*app, unsigned); self->security.amf[0] = (amf >> 8); self->security.amf[1] = (amf & 0xFF); break; } case tsip_pname_operator_id: { /* (const char*)OPID_HEX_STR */ const char* hexstr = va_arg(*app, const char*); tsk_size_t len = tsk_strlen(hexstr); if(len && !(len & 0x01)) { tsk_size_t i, j; if(tsk_strindexOf(hexstr, tsk_strlen(hexstr), "0x") == 0) { hexstr += 2; len -= 2; } /* reset old value */ memset(self->security.operator_id, 0, sizeof(self->security.operator_id)); /* set new value */ if(len) { /* perhaps there were only 2 chars*/ for(i = 0, j = 0; (i<(sizeof(operator_id_t) * 2) && isecurity.operator_id[j]); #else static unsigned _1bytes; /* do not use neither int8_t nor uint8_t */ sscanf(&hexstr[i], "%2x", &_1bytes); self->security.operator_id[j] = (_1bytes & 0xFF); #endif } } } else { TSK_DEBUG_ERROR("%s is invalid for an Operator Id value.", hexstr); } break; } case tsip_pname_ipsec_params: { /* (const char*)ALG_STR, (const char*)EALG_STR, (const char*)MODE_STR, (const char*)PROTOCOL_STR*/ tsk_strupdate(&self->security.ipsec.alg, va_arg(*app, const char*)); tsk_strupdate(&self->security.ipsec.ealg, va_arg(*app, const char*)); tsk_strupdate(&self->security.ipsec.mode, va_arg(*app, const char*)); tsk_strupdate(&self->security.ipsec.protocol, va_arg(*app, const char*)); break; } case tsip_pname_tls_certs: { /* (const char*)CA_FILE_STR, (const char*)PUB_FILE_STR, (const char*)PRIV_FILE_STR, (tsk_bool_t)VERIF_BOOL */ tsk_strupdate(&self->security.tls.ca, va_arg(*app, const char*)); tsk_strupdate(&self->security.tls.pbk, va_arg(*app, const char*)); tsk_strupdate(&self->security.tls.pvk, va_arg(*app, const char*)); self->security.tls.verify = va_arg(*app, tsk_bool_t); break; } /* === Dummy Headers === */ case tsip_pname_header: { /* (const char*)NAME_STR, (const char*)VALUE_STR */ const char* NAME_STR = va_arg(*app, const char*); const char* VALUE_STR = va_arg(*app, const char*); if(VALUE_STR == ((const char*)-1)) { /* UNSET */ tsk_params_remove_param(self->headers, NAME_STR); } else { /* SET */ tsk_params_add_param(&self->headers, NAME_STR, VALUE_STR); } break; } /* === Nat Traversal === */ case tsip_pname_stun_server: { /* (const char*)IP_STR, (unsigned)PORT_UINT */ const char* IP_STR = va_arg(*app, const char*); unsigned PORT_UINT = va_arg(*app, unsigned); /* do not check, Null==> disable STUN */ tsk_strupdate(&self->natt.stun.ip, IP_STR); self->natt.stun.port = PORT_UINT; break; } case tsip_pname_stun_cred: { /* (const char*)USR_STR, (const char*)PASSORD_STR */ const char* USR_STR = va_arg(*app, const char*); const char* PASSORD_STR = va_arg(*app, const char*); tsk_strupdate(&self->natt.stun.login, USR_STR); tsk_strupdate(&self->natt.stun.pwd, PASSORD_STR); break; } case tsip_pname_stun_enabled: { /* (tsk_bool_t)ENABLED_BOOL */ self->natt.stun.enabled = va_arg(*app, tsk_bool_t); break; } /* === User Data === */ case tsip_pname_userdata: { /* (const void*)DATA_PTR */ self->userdata = va_arg(*app, const void*); break; } default: { /* va_list will be unsafe ==> must exit */ TSK_DEBUG_WARN("Found unknown pname."); goto bail; } }/* switch */ }/* while */ bail: return 0; } /**@ingroup tsip_stack_group * Creates new 3GPP IMS/LTE stack handle. * As the 3GPP IMS/LTE stack depends on the network library (tinyNET), you MUST call tnet_startup() before using any SIP function (tsip_*). * tnet_cleanup() is used to terminate use of network functions. * @param callback Callback function to call to alert the application for new SIP or media events. * @param realm_uri The realm is the name of the domain name to authenticate to. It should be a valid SIP URI (e.g. sip:open-ims.test). * @param impi_uri The IMPI is a unique identifier assigned to a user (or UE) by the home network. * It could be either a SIP URI (e.g. sip:bob@open-ims.test), a tel URI (e.g. tel:+33100000) or any alphanumeric string (e.g. bob@open-ims.test or bob). * It is used to authenticate the UE (username field in SIP Authorization/Proxy-Authorization header). * @param impu_uri As its name says, it’s you public visible identifier where you are willing to receive calls or any demands. * An IMPU could be either a SIP or tel URI (e.g. tel:+33100000 or sip:bob@open-ims.test). In IMS world, a user can have multiple IMPUs associated to its unique IMPI. * @param ... Any TSIP_STACK_SET_*() macros. * @retval A valid handle if succeed and Null-handle otherwise. As a stack is a well-defined object, you should use @a TSK_OBJECT_SAFE_FREE() to safely destroy the handle. * * @code int app_callback(const tsip_event_t *sipevent); const char* realm_uri = "sip:open-ims.test"; const char* impi_uri = "bob@open-ims.test"; const char* impu_uri = "sip:bob@open-ims.test"; tsip_stack_handle_t* stack = tsip_stack_create(app_callback, realm_uri, impi_uri, impu_uri, TSIP_STACK_SET_PASSWORD("mysecret"), // ...other macros... TSIP_STACK_SET_NULL()); // ...whatever TSK_OBJECT_SAFE_FREE(stack); * @endcode * @sa @ref tsip_stack_set()
@ref tsip_stack_start() */ tsip_stack_handle_t* tsip_stack_create(tsip_stack_callback_f callback, const char* realm_uri, const char* impi_uri, const char* impu_uri, ...) { tsip_stack_t* stack = tsk_null; va_list ap; int i; /* === check values === */ if(!realm_uri || !impi_uri || !impu_uri) { TSK_DEBUG_ERROR("Invalid parameter."); goto bail; } /* === create the stack === */ if(!(stack = tsk_object_new(tsip_stack_def_t))) { /* should never happen */ TSK_DEBUG_ERROR("Failed to create the stack."); goto bail; } /* === Set mandatory values (realm, IMPI and IMPU) === */ if(tsip_stack_set(stack, TSIP_STACK_SET_REALM(realm_uri), TSIP_STACK_SET_IMPI(impi_uri), TSIP_STACK_SET_IMPU(impu_uri), TSIP_STACK_SET_NULL())) { TSK_DEBUG_ERROR("Invalid parameter."); TSK_OBJECT_SAFE_FREE(stack); goto bail; } /* === Default values (Network) === */ stack->network.mode = tsip_stack_mode_ua; for(i = 0; i < sizeof(stack->network.local_port)/sizeof(stack->network.local_port[0]); ++i) { stack->network.local_port[i] = TNET_SOCKET_PORT_ANY; } for(i = 0; i < sizeof(stack->network.proxy_cscf_port)/sizeof(stack->network.proxy_cscf_port[0]); ++i) { stack->network.proxy_cscf_port[i] = 5060; } for(i = 0; i < sizeof(stack->network.proxy_cscf_type)/sizeof(stack->network.proxy_cscf_type[0]); ++i) { stack->network.proxy_cscf_type[i] = tnet_socket_type_invalid; } stack->network.max_fds = tmedia_defaults_get_max_fds(); // all events should be delivered to the user before the stack stop tsk_runnable_set_important(TSK_RUNNABLE(stack), tsk_true); /* === SigComp === */ // only create the handler on-demand: when compartment is added stack->sigcomp.dms = TSIP_SIGCOMP_DMS; stack->sigcomp.sms = TSIP_SIGCOMP_SMS; stack->sigcomp.cpb = TSIP_SIGCOMP_CPB; stack->sigcomp.sip_dict = TSIP_SIGCOMP_SIP_DICT; stack->sigcomp.pres_dict = TSIP_SIGCOMP_PRES_DICT; /* === DNS context === * Because of TSIP_STACK_SET_DNS_SERVER(), ctx should be created before calling __tsip_stack_set() */ stack->dns_ctx = tnet_dns_ctx_create(); /* === DHCP context === */ /* === NAT Traversal === */ { const char *server_ip, *usr_name, *usr_pwd; uint16_t server_port; stack->natt.stun.enabled = tmedia_defaults_get_stun_enabled(); if(tmedia_defaults_get_stun_server(&server_ip, &server_port) == 0) { tsk_strupdate(&stack->natt.stun.ip, server_ip); stack->natt.stun.port = server_port; } if(tmedia_defaults_get_stun_cred(&usr_name, &usr_pwd) == 0) { tsk_strupdate(&stack->natt.stun.login, usr_name); tsk_strupdate(&stack->natt.stun.pwd, usr_pwd); } } /* === Set user supplied parameters === */ va_start(ap, impu_uri); if(__tsip_stack_set(stack, &ap)) { TSK_DEBUG_ERROR("Invalid parameter."); TSK_OBJECT_SAFE_FREE(stack); va_end(ap); goto bail; } va_end(ap); /* === Internals === */ stack->callback = callback; if(!stack->ssessions) { stack->ssessions = tsk_list_create(); } if(!stack->headers) { /* could be created by tsk_params_add_param() */ stack->headers = tsk_list_create(); } /* === Layers === */ if(!(stack->layer_dialog = tsip_dialog_layer_create(stack))) { TSK_DEBUG_ERROR("Failed to create Dialog layer"); TSK_OBJECT_SAFE_FREE(stack); goto bail; } if(!(stack->layer_transac = tsip_transac_layer_create(stack))) { TSK_DEBUG_ERROR("Failed to create Transac layer"); TSK_OBJECT_SAFE_FREE(stack); goto bail; } if(!(stack->layer_transport = tsip_transport_layer_create(stack))) { TSK_DEBUG_ERROR("Failed to create Transport layer"); TSK_OBJECT_SAFE_FREE(stack); goto bail; } bail: return stack; } /**@ingroup tsip_stack_group * Starts a 3GPP IMS/LTE stack. This function MUST be called before you start calling any SIP function (@a tsip_*). * @param self The 3GPP IMS/LTE stack to start. This handle should be created using @ref tsip_stack_create(). * @retval Zero if succeed and non-zero error code otherwise. */ int tsip_stack_start(tsip_stack_handle_t *self) { int ret = -1, t_idx, tx_count; tsip_stack_t *stack = self; tnet_socket_type_t* tx_values; const char* stack_error_desc = "Failed to start the stack"; if(!stack) { TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if(stack->started) { TSK_DEBUG_INFO("Stack Already started"); return 0; } tsk_safeobj_lock(stack); // transports if(TSIP_STACK_MODE_IS_SERVER(stack)) { TSK_DEBUG_INFO("Stack running in SERVER mode"); tx_values = &stack->network.transport_types[0]; tx_count = sizeof(stack->network.transport_types) / sizeof(stack->network.transport_types[0]); } else { TSK_DEBUG_INFO("Stack running in CLIENT mode"); tx_values = &stack->network.proxy_cscf_type[0]; tx_count = sizeof(stack->network.proxy_cscf_type) / sizeof(stack->network.proxy_cscf_type[0]); } /* === Timer manager === */ if(!stack->timer_mgr_global) { stack->timer_mgr_global = tsk_timer_mgr_global_ref(); } if((ret = tsk_timer_manager_start(stack->timer_mgr_global))) { goto bail; } /* === Set P-Preferred-Identity === */ if(!stack->identity.preferred && stack->identity.impu) { stack->identity.preferred = tsk_object_ref((void*)stack->identity.impu); } /* === Set Max FDs === */ if (stack->network.max_fds > 0 && stack->network.max_fds < 0xFFFF) { TSK_DEBUG_INFO("Setting max FDs to %u", (unsigned)stack->network.max_fds); ret = tnet_set_fd_max_allowed(stack->network.max_fds); if (ret) { TSK_DEBUG_ERROR("Failed to set max FDs to %u", (unsigned)stack->network.max_fds); /* goto bail; */ // Not fatal error } } /* === Transport type === */ if(!tsk_strnullORempty(stack->security.secagree_mech)) { if(tsk_striequals(stack->security.secagree_mech, "ipsec-3gpp") && stack->security.enable_secagree_ipsec) { #if 0 TNET_SOCKET_TYPE_SET_IPSEC(stack->network.proxy_cscf_type); #endif TSK_DEBUG_ERROR("Not implemented"); } //else if if(tsk_striquals(stack->security.secagree_mech, "ipsec-ike")) } for(t_idx = 0; t_idx < tx_count; ++t_idx) { if(!TNET_SOCKET_TYPE_IS_VALID(tx_values[t_idx])) { continue; } /* === Use DNS NAPTR+SRV for the P-CSCF discovery if we are in client mode === */ if(TSIP_STACK_MODE_IS_CLIENT(stack)) { if(tsk_strnullORempty(stack->network.proxy_cscf[t_idx]) || (stack->network.discovery_dhcp || stack->network.discovery_naptr)) { if(stack->network.discovery_dhcp) { /* DHCP v4/v6 */ /* FIXME: */ TSK_DEBUG_ERROR("Unexpected code called"); ret = -2; } /* DHCP */ else { /* DNS NAPTR + SRV*/ char* hostname = tsk_null; tnet_port_t port = 0; const char* service = TNET_SOCKET_TYPE_IS_DGRAM(tx_values[t_idx]) ? "SIP+D2U" : (TNET_SOCKET_TYPE_IS_TLS(tx_values[t_idx]) ? "SIPS+D2T" : "SIP+D2T"); if((ret = tnet_dns_query_naptr_srv(stack->dns_ctx, stack->network.realm->host, service, &hostname, &port)) == 0) { TSK_DEBUG_INFO("DNS SRV(NAPTR(%s, %s) = [%s / %d]", stack->network.realm->host, service, hostname, port); tsk_strupdate(&stack->network.proxy_cscf[t_idx], hostname); if(!stack->network.proxy_cscf_port[t_idx] || stack->network.proxy_cscf_port[t_idx]==5060) { /* Only if the Proxy-CSCF port is missing or default */ stack->network.proxy_cscf_port[t_idx] = port; } } else { TSK_DEBUG_ERROR("P-CSCF discovery using DNS NAPTR failed. The stack will use the user supplied address and port."); } TSK_FREE(hostname); } /* NAPTR */ } /* Check Proxy-CSCF IP address */ if(!tsk_strnullORempty(stack->network.proxy_cscf[t_idx])) { TSK_DEBUG_INFO("Proxy-CSCF=[%s]:%d", stack->network.proxy_cscf[t_idx], stack->network.proxy_cscf_port[t_idx]); } else { TSK_DEBUG_ERROR("Proxy-CSCF IP address is Null."); ret = -1983; goto bail; } }// !Server mode /* === Get Best source address === */ // Best local address must be updated if not defined or none is set by the user. // The local address could be no-null if the stack is re-starting: https://code.google.com/p/doubango/issues/detail?id=454 and https://code.google.com/p/idoubs/issues/detail?id=195 if (!stack->network.local_ip_is_set_by_user[t_idx] || tsk_strnullORempty(stack->network.local_ip[t_idx]) || tsk_striequals(stack->network.local_ip[t_idx], "127.0.0.1")) { /* loacal-ip is missing? */ tnet_ip_t bestsource; if ((ret = tnet_getbestsource(stack->network.proxy_cscf[t_idx] ? stack->network.proxy_cscf[t_idx] : "google.com", stack->network.proxy_cscf_port[t_idx] ? stack->network.proxy_cscf_port[t_idx] : 5060, tx_values[t_idx], &bestsource))) { TSK_DEBUG_ERROR("Failed to get best source [%d]", ret); /* do not exit ==> will use default IP address */ } else { tsk_strupdate(&stack->network.local_ip[t_idx], bestsource); TSK_DEBUG_INFO("Best source at %d: %s", t_idx, bestsource); } } else { TSK_DEBUG_INFO("Do not query for best source address at %d, local_ip_is_set_by_user=%d, local_ip=%s", t_idx, stack->network.local_ip_is_set_by_user[t_idx], stack->network.local_ip[t_idx]); } } /* for (t_idx...) */ /* === Runnable === */ TSK_RUNNABLE(stack)->run = run; if((ret = tsk_runnable_start(TSK_RUNNABLE(stack), tsip_event_def_t))) { stack_error_desc = "Failed to start runnable"; TSK_DEBUG_ERROR("%s", stack_error_desc); goto bail; } // must be here because the runnable object is only valid after start() TSIP_STACK_SIGNAL(self, tsip_event_code_stack_starting, "Stack starting"); /* === Nat Traversal === */ // delete previous context TSK_OBJECT_SAFE_FREE(stack->natt.ctx); if(stack->natt.stun.enabled && !tsk_strnullORempty(stack->natt.stun.ip)) { if(stack->natt.stun.port == 0) { /* FIXME: for now only UDP(IPv4/IPv6) is supported */ stack->natt.stun.port = kStunPortDefaultTcpUdp; } TSK_DEBUG_INFO("STUN server = %s:%u", stack->natt.stun.ip, stack->natt.stun.port); stack->natt.ctx = tnet_nat_context_create(TNET_SOCKET_TYPE_IS_IPV6(tx_values[stack->network.transport_idx_default])? tnet_socket_type_udp_ipv6: tnet_socket_type_udp_ipv4, stack->natt.stun.login, stack->natt.stun.pwd); ret = tnet_nat_set_server(stack->natt.ctx, stack->natt.stun.ip, stack->natt.stun.port); } /* === Transport Layer === */ for(t_idx = 0; t_idx < tx_count; ++t_idx) { if(!TNET_SOCKET_TYPE_IS_VALID(tx_values[t_idx])) { continue; } if((ret = tsip_transport_layer_add(stack->layer_transport, stack->network.local_ip[t_idx], stack->network.local_port[t_idx], tx_values[t_idx], "SIP transport"))) { stack_error_desc = "Failed to add new transport"; TSK_DEBUG_ERROR("%s", stack_error_desc); goto bail; } } /* Starts the transport Layer */ if((ret = tsip_transport_layer_start(stack->layer_transport))) { stack_error_desc = "Failed to start sip transport"; TSK_DEBUG_ERROR("%s", stack_error_desc); goto bail; } /* Update the local_ip */ for(t_idx = 0; t_idx < tx_count; ++t_idx) { if(!TNET_SOCKET_TYPE_IS_VALID(tx_values[t_idx])) { continue; } if(tsk_strnullORempty(stack->network.local_ip[t_idx])) { const tsip_transport_t* transport = tsip_transport_layer_find_by_type(stack->layer_transport, tx_values[t_idx]); if(transport) { tnet_ip_t ip; if(!tnet_transport_get_ip_n_port_2(transport->net_transport, &ip, tsk_null)) { tsk_strupdate(&stack->network.local_ip[t_idx], ip); } else { TSK_DEBUG_WARN("Failed to get local_ip for transport type = %d", tx_values[t_idx]); /* Do not exit */ } } } } /* === ALL IS OK === */ stack->started = tsk_true; /* Signal to the end-user that the stack has been started */ TSIP_STACK_SIGNAL(self, tsip_event_code_stack_started, "Stack started"); TSK_DEBUG_INFO("SIP STACK -- START"); tsk_safeobj_unlock(stack); return 0; bail: TSIP_STACK_SIGNAL(self, tsip_event_code_stack_failed_to_start, stack_error_desc); /* stop all running instances */ if(stack->layer_transport) { tsip_transport_layer_shutdown(stack->layer_transport); } if(TSK_RUNNABLE(stack)->running) { tsk_runnable_stop(TSK_RUNNABLE(stack)); } tsk_safeobj_unlock(stack); return ret; } /**@ingroup tsip_stack_group * Configures the stack. * @param self The 3GPP IMS/LTE stack to configure. This handle should be created using @ref tsip_stack_create(). * @param ... Any TSIP_STACK_SET_*() or TSIP_STACK_UNSET_*() macros. * @retval Zero if succeed and non-zero error code otherwise. * * @code int ret = tsip_stack_set(stack, TSIP_STACK_SET_HEADER("User-Agent", "IM-client/OMA1.0 doubango/v1.0.0"), TSIP_STACK_SET_NULL()); * @endcode * * @sa @ref tsip_stack_create() */ int tsip_stack_set(tsip_stack_handle_t *self, ...) { if(self) { int ret; tsip_stack_t *stack = self; va_list ap; va_start(ap, self); ret = __tsip_stack_set(stack, &ap); va_end(ap); return ret; } else { TSK_DEBUG_ERROR("Invalid parameter"); return -1; } } /**@ingroup tsip_stack_group * Gets user's data, previously set using @ref TSIP_STACK_SET_USERDATA() macro. * @param self Stack from which to get the user's data. */ const void* tsip_stack_get_userdata(const tsip_stack_handle_t *self) { if(self) { return ((const tsip_stack_t *)self)->userdata; } else { TSK_DEBUG_ERROR("Invalid parameter"); return tsk_null; } } /**@ingroup tsip_stack_group */ tnet_dns_ctx_t* tsip_stack_get_dnsctx(tsip_stack_handle_t *self) { if(self) { return (tnet_dns_ctx_t*)tsk_object_ref(((tsip_stack_t *)self)->dns_ctx); } else { TSK_DEBUG_ERROR("Invalid parameter"); return tsk_null; } } /**@ingroup tsip_stack_group */ tsip_uri_t* tsip_stack_get_preferred_id(tsip_stack_handle_t *self) { if(self) { return (tsip_uri_t*)tsk_object_ref(((tsip_stack_t *)self)->identity.preferred); } else { TSK_DEBUG_ERROR("Invalid parameter"); return tsk_null; } } /**@ingroup tsip_stack_group */ int tsip_stack_get_local_ip_n_port(const tsip_stack_handle_t *self, const char* protocol, tnet_port_t *port, tnet_ip_t *ip) { const tsip_stack_t *stack = self; if(!stack || !port || !ip) { TSK_DEBUG_ERROR("Invalid parameter"); return -1; } if(stack) { tsk_list_item_t *item; tsk_list_foreach(item, stack->layer_transport->transports) { tsip_transport_t *transport = item->data; if(transport) { if(tsk_striequals(transport->protocol, protocol)) { return tnet_transport_get_public_ip_n_port(transport->net_transport,transport->connectedFD, ip, port); } } } } TSK_DEBUG_ERROR("No transport with such protocol (%s) could be found", protocol); return -2; } /**@ingroup tsip_stack_group * Stops the stack. * @param self The 3GPP IMS/LTE stack to stop. This handle should be created using @ref tsip_stack_create() and started using tsip_stack_start(). * This function is also called by the garbage collector when the stack is destroyed but you should call it yourself before destroying the stack.
* Before stopping, the stack will hangup all SIP dialogs (starting with non-register dialogs) and destroy all sessions. This is called shutdown phase. * At the end of this phase, all the SIP sessions will be destroyed. * @sa @ref tsip_stack_create()
@ref tsip_stack_start() */ int tsip_stack_stop(tsip_stack_handle_t *self) { tsip_stack_t *stack = self; if(stack) { tsk_bool_t one_failed = tsk_false; int ret = 0; tsk_safeobj_lock(stack); if(!stack->started) { TSK_DEBUG_INFO("Stack already stopped"); goto bail; } TSIP_STACK_SIGNAL(self, tsip_event_code_stack_stopping, "Stack stopping"); /* Hangup all dialogs starting by REGISTER */ if((ret = tsip_dialog_layer_shutdownAll(stack->layer_dialog))) { TSK_DEBUG_WARN("Failed to hang-up all dialogs"); one_failed = tsk_true; } /* do not try to clean up transactions ==> each dialog will cancel its transactions. * see tsip_dialog_deinit() which call tsip_transac_layer_cancel_by_dialog() */ /* Stop the timer manager */ // not done as it's global (shared). Will be done when all instance are destoyed /* Stop the transport layer */ if((ret = tsip_transport_layer_shutdown(stack->layer_transport))) { TSK_DEBUG_WARN("Failed to stop the transport layer"); one_failed = tsk_true; } /* Signal to the end-user that the stack has been stopped * should be done before tsk_runnable_stop() which will stop the thread * responsible for the callbacks. The enqueued data have been marked as "important". * As both the timer manager and the transport layer have been stoped there is no * chance to got additional events */ if(one_failed) { TSIP_STACK_SIGNAL(self, tsip_event_code_stack_failed_to_stop, "Stack failed to stop"); } else { TSIP_STACK_SIGNAL(self, tsip_event_code_stack_stopped, "Stack stopped"); } /* Stop runnable (run() thread) */ if((ret = tsk_runnable_stop(TSK_RUNNABLE(stack)))) { TSK_DEBUG_WARN("Failed to stop the stack"); one_failed = tsk_true; } /* Close all SigComp Compartments (do not remove them) */ if(stack->sigcomp.handle) { tsip_sigcomp_close_all(stack->sigcomp.handle); } /* reset AoR */ TSK_FREE_TABLE(stack->network.aor.ip); memset(stack->network.aor.port, 0, sizeof(stack->network.aor.port)); /* stops timer manager */ if(stack->timer_mgr_global) { tsk_timer_mgr_global_unref(&stack->timer_mgr_global); } if(!one_failed) { stack->started = tsk_false; } TSK_DEBUG_INFO("SIP STACK -- STOP"); bail: tsk_safeobj_unlock(stack); return ret; } return -1; } /* internal function used to construct a valid contact URI */ tsip_uri_t* tsip_stack_get_contacturi(const tsip_stack_t *stack, const char* protocol) { if(stack) { tsk_list_item_t *item; tsk_list_foreach(item, stack->layer_transport->transports) { tsip_transport_t *transport = item->data; if(transport) { if(tsk_striequals(transport->protocol, protocol)) { tsip_uri_t* uri = tsk_null; if((uri = tsip_transport_get_uri(transport, tsk_false))) { tsk_strupdate(&uri->user_name, stack->identity.impu->user_name); return uri; } } } } } return tsk_null; } /* internal function used to construct a valid Proxy-CSCF URI used as the default first route */ tsip_uri_t* tsip_stack_get_pcscf_uri(const tsip_stack_t *stack, tnet_socket_type_t type, tsk_bool_t lr) { if(stack) { const tsip_transport_t *transport = tsk_null; if(!TNET_SOCKET_TYPE_IS_VALID(type) && !TSK_LIST_IS_EMPTY(stack->layer_transport->transports)) { transport = stack->layer_transport->transports->head->data; } else { transport = tsip_transport_layer_find_by_type(stack->layer_transport, type); } if(transport) { tsip_uri_t* uri = tsk_null; tsk_bool_t ipv6 = TNET_SOCKET_TYPE_IS_IPV6(transport->type); tsk_bool_t quote_ip = (ipv6 && tsk_strcontains(stack->network.proxy_cscf[transport->idx], tsk_strlen(stack->network.proxy_cscf[transport->idx]), ":")) /* IPv6 IP string?*/; char* uristring = tsk_null; tsk_sprintf(&uristring, "%s:%s%s%s:%d;%s;transport=%s", transport->scheme, quote_ip ? "[" : "", stack->network.proxy_cscf[transport->idx], quote_ip ? "]" : "", stack->network.proxy_cscf_port[transport->idx], lr ? "lr" : "", transport->protocol); if(uristring) { if((uri = tsip_uri_parse(uristring, tsk_strlen(uristring)))) { //uri->host_type = ipv6 ? thttp_host_ipv6 : thttp_host_ipv4; } TSK_FREE(uristring); } return uri; } } return tsk_null; } static void* TSK_STDCALL run(void* self) { tsk_list_item_t *curr; tsip_stack_t *stack = self; TSK_DEBUG_INFO("SIP STACK::run -- START"); TSK_RUNNABLE_RUN_BEGIN(stack); if((curr = TSK_RUNNABLE_POP_FIRST(stack))) { tsip_event_t *sipevent = (tsip_event_t*)curr->data; if(stack->callback) { sipevent->userdata = stack->userdata; // needed by sessionless events stack->callback(sipevent); } tsk_object_unref(curr); } TSK_RUNNABLE_RUN_END(self); TSK_DEBUG_INFO("SIP STACK::run -- STOP"); return 0; } //======================================================== // SIP stack object definition // static tsk_object_t* tsip_stack_ctor(tsk_object_t * self, va_list * app) { tsip_stack_t *stack = self; if(stack) { tsk_safeobj_init(stack); } return self; } static tsk_object_t* tsip_stack_dtor(tsk_object_t * self) { tsip_stack_t *stack = self; if(stack) { /* /!\ Order in which objects are destroyed is very important */ /* Stop * Will try to hangup all dialogs */ if(stack->started) { tsip_stack_stop(stack); } /* Layers(1/1): Transacs and dialogs use timer_mgr when destroyed * Dialogs =>(use)=> transacs =>(use)=> transport. */ TSK_OBJECT_SAFE_FREE(stack->layer_dialog); TSK_OBJECT_SAFE_FREE(stack->layer_transac); TSK_OBJECT_SAFE_FREE(stack->layer_transport); /* Internals(1/2) */ if(stack->timer_mgr_global) { tsk_timer_mgr_global_unref(&stack->timer_mgr_global); } /* Identity */ TSK_FREE(stack->identity.display_name); TSK_OBJECT_SAFE_FREE(stack->identity.impu); TSK_OBJECT_SAFE_FREE(stack->identity.preferred); //TSK_OBJECT_SAFE_FREE(stack->associated_identity); TSK_FREE(stack->identity.impi); TSK_FREE(stack->identity.password); /* Network(1/1) */ TSK_FREE_TABLE(stack->network.local_ip); TSK_OBJECT_SAFE_FREE(stack->network.realm); TSK_FREE_TABLE(stack->network.proxy_cscf); TSK_OBJECT_SAFE_FREE(stack->paths); TSK_FREE_TABLE(stack->network.aor.ip); TSK_OBJECT_SAFE_FREE(stack->service_routes); TSK_OBJECT_SAFE_FREE(stack->associated_uris); /* SigComp (MUST be done after transports) */ TSK_OBJECT_SAFE_FREE(stack->sigcomp.handle); /* Security(1/1) */ TSK_FREE(stack->security.secagree_mech); TSK_FREE(stack->security.ipsec.alg); TSK_FREE(stack->security.ipsec.ealg); TSK_FREE(stack->security.ipsec.mode); TSK_FREE(stack->security.ipsec.protocol); TSK_FREE(stack->security.tls.ca); TSK_FREE(stack->security.tls.pbk); TSK_FREE(stack->security.tls.pvk); /* DNS */ TSK_OBJECT_SAFE_FREE(stack->dns_ctx); /* NAT Traversal context */ TSK_FREE(stack->natt.stun.ip); TSK_FREE(stack->natt.stun.login); TSK_FREE(stack->natt.stun.pwd); TSK_OBJECT_SAFE_FREE(stack->natt.ctx); /* DHCP */ /* features */ /* QoS */ /* Internals (2/2) */ TSK_OBJECT_SAFE_FREE(stack->ssessions); TSK_OBJECT_SAFE_FREE(stack->headers); tsk_safeobj_deinit(stack); TSK_DEBUG_INFO("*** SIP Stack destroyed ***"); } return self; } static const tsk_object_def_t tsip_stack_def_s = { sizeof(tsip_stack_t), tsip_stack_ctor, tsip_stack_dtor, tsk_null, }; const tsk_object_def_t *tsip_stack_def_t = &tsip_stack_def_s;