#if HAVE_CRT #define _CRTDBG_MAP_ALLOC #include <stdlib.h> #include <crtdbg.h> #endif //HAVE_CRT /* * Copyright (C) 2020, University of the Basque Country (UPV/EHU) * Contact for licensing options: <licensing-mcpttclient(at)mcopenplatform(dot)com> * * The original file was part of Open Source Doubango Framework * Copyright (C) 2010-2011 Mamadou Diop. * Copyright (C) 2012 Doubango Telecom <http://doubango.org> * * 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 <stdarg.h> #include <string.h> #include <tsip.h> 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); 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){ if(curr == tsip_pname_impu || curr == tsip_pname_preferred_id){ /* This change is for if the device receive two o more IMPU in P-Associated-URI the device will compare all IMPU receive with IMPU configurate. */ TSK_OBJECT_SAFE_FREE(self->identity.impu); self->identity.impu = uri; 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); 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) && i<len); i+=2, j++){ #if 0 /* Could cause SIGBUS error (if memory misaligned) */ sscanf(&hexstr[i], "%2x", &self->security.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; } /* === PTT MCPTT Server === */ case tsip_pname_mcptt_psi_private: { const char* PSI_PRIVATE = va_arg(*app, const char*); tsip_uri_t *uri; if(!tsk_strnullORempty(PSI_PRIVATE) && (uri = tsip_uri_parse(PSI_PRIVATE, tsk_strlen(PSI_PRIVATE)))){ if(uri->type == uri_unknown){ /* scheme is missing or unsupported? */ tsk_strupdate(&uri->scheme, "sip"); uri->type = uri_sip; } TSK_OBJECT_SAFE_FREE(self->pttMCPTT.psi_private); /* delete old */ self->pttMCPTT.psi_private = uri; } else TSK_DEBUG_ERROR("'%s' is an invalid PSI for CALL PRIVATE MCPTT", PSI_PRIVATE); break; } case tsip_pname_mcptt_psi_group: { const char* PSI_GROUP = va_arg(*app, const char*); tsip_uri_t *uri; if(!tsk_strnullORempty(PSI_GROUP) && (uri = tsip_uri_parse(PSI_GROUP, tsk_strlen(PSI_GROUP)))){ if(uri->type == uri_unknown){ /* scheme is missing or unsupported? */ tsk_strupdate(&uri->scheme, "sip"); uri->type = uri_sip; } TSK_OBJECT_SAFE_FREE(self->pttMCPTT.psi_group); /* delete old */ self->pttMCPTT.psi_group = uri; } else TSK_DEBUG_ERROR("'%s' is an invalid PSI for CALL GROUP MCPTT", PSI_GROUP); break; } case tsip_pname_mcptt_psi_preestablished: { const char* PSI_PREESTABLISHED = va_arg(*app, const char*); tsip_uri_t *uri; if(!tsk_strnullORempty(PSI_PREESTABLISHED) && (uri = tsip_uri_parse(PSI_PREESTABLISHED, tsk_strlen(PSI_PREESTABLISHED)))){ if(uri->type == uri_unknown){ /* scheme is missing or unsupported? */ tsk_strupdate(&uri->scheme, "sip"); uri->type = uri_sip; } TSK_OBJECT_SAFE_FREE(self->pttMCPTT.psi_preestablished); /* delete old */ self->pttMCPTT.psi_preestablished = uri; } else TSK_DEBUG_ERROR("'%s' is an invalid PSI for CALL PREESTABLISHED MCPTT", PSI_PREESTABLISHED); break; } case tsip_pname_mcptt_psi_cms: { const char* PSI_CMS = va_arg(*app, const char*); tsip_uri_t *uri; if(!tsk_strnullORempty(PSI_CMS) && (uri = tsip_uri_parse(PSI_CMS, tsk_strlen(PSI_CMS)))){ if(uri->type == uri_unknown){ /* scheme is missing or unsupported? */ tsk_strupdate(&uri->scheme, "sip"); uri->type = uri_sip; } TSK_OBJECT_SAFE_FREE(self->pttMCPTT.psi_cms); /* delete old */ self->pttMCPTT.psi_cms = uri; } else TSK_DEBUG_ERROR("'%s' is an invalid PSI for CMS MCPTT", PSI_CMS); break; } case tsip_pname_mcptt_psi_gms: { const char* PSI_GMS = va_arg(*app, const char*); tsip_uri_t *uri; if(!tsk_strnullORempty(PSI_GMS) && (uri = tsip_uri_parse(PSI_GMS, tsk_strlen(PSI_GMS)))){ if(uri->type == uri_unknown){ /* scheme is missing or unsupported? */ tsk_strupdate(&uri->scheme, "sip"); uri->type = uri_sip; } TSK_OBJECT_SAFE_FREE(self->pttMCPTT.psi_gms); /* delete old */ self->pttMCPTT.psi_gms = uri; } else TSK_DEBUG_ERROR("'%s' is an invalid PSI for CMS MCPTT", PSI_GMS); break; } case tsip_pname_mcptt_id: { const char* MCPTT_ID = va_arg(*app, const char*); tsip_uri_t *uri; if(!tsk_strnullORempty(MCPTT_ID) && (uri = tsip_uri_parse(MCPTT_ID, tsk_strlen(MCPTT_ID)))){ //MCPTT ID isn�t URI_SIP TSK_OBJECT_SAFE_FREE(self->pttMCPTT.mcptt_id); /* delete old */ self->pttMCPTT.mcptt_id = uri; } else TSK_DEBUG_ERROR("'%s' is an invalid MCPTT ID", MCPTT_ID); break; } /*case tsip_pname_mcptt_client_id: { const char* MCPTT_CLIENT_ID = va_arg(*app, const char*); tsk_buffer_t* mcptt_client_id = tsk_buffer_create_null(); if(!tsk_strnullORempty(MCPTT_CLIENT_ID) && (mcptt_client_id = tsk_buffer_create(MCPTT_CLIENT_ID,tsk_strlen(MCPTT_CLIENT_ID)))){ //MCPTT ID isn�t URI_SIP TSK_OBJECT_SAFE_FREE(self->pttMCPTT.mcptt_client_id); // delete old self->pttMCPTT.mcptt_client_id = mcptt_client_id; } else TSK_DEBUG_ERROR("it is an invalid MCPTT CLIENT ID"); break; }*/ case tsip_pname_mcptt_client_id: { const char* MCPTT_CLIENT_ID = va_arg(*app, const char*); tsip_uri_t *uri; if(!tsk_strnullORempty(MCPTT_CLIENT_ID) && (uri = tsip_uri_parse(MCPTT_CLIENT_ID, tsk_strlen(MCPTT_CLIENT_ID)))){ //MCPTT ID isn�t URI_SIP TSK_OBJECT_SAFE_FREE(self->pttMCPTT.client_id); /* delete old */ self->pttMCPTT.client_id = uri; } else TSK_DEBUG_ERROR("'%s' is an invalid MCPTT CLient ID", MCPTT_CLIENT_ID); break; } case tsip_pname_client_id: { const char* CLIENT_ID = va_arg(*app, const char*); tsip_uri_t *uri; if(!tsk_strnullORempty(CLIENT_ID) && (uri = tsip_uri_parse(CLIENT_ID, tsk_strlen(CLIENT_ID)))){ //MCPTT ID isn�t URI_SIP TSK_OBJECT_SAFE_FREE(self->pttMCPTT.client_id); /* delete old */ self->pttMCPTT.client_id = uri; } else TSK_DEBUG_ERROR("'%s' is an invalid CLIENT ID", CLIENT_ID); break; } case tsip_pname_mcptt_priority: { const int MCPTT_PRIORITY = va_arg(*app, const int); self->pttMCPTT.mcptt_priority = MCPTT_PRIORITY; break; } case tsip_pname_mcptt_implicit: { const tsk_bool_t MCPTT_IMPLICIT = va_arg(*app, const tsk_bool_t); self->pttMCPTT.mcptt_implicit = MCPTT_IMPLICIT; break; } case tsip_pname_mcptt_granted: { const tsk_bool_t MCPTT_GRANTED = va_arg(*app, const tsk_bool_t); self->pttMCPTT.mcptt_granted = MCPTT_GRANTED; break; } case tsip_pname_mcptt_answer_mode: { const tsk_bool_t MCPTT_ANSWER_MODE = va_arg(*app, const tsk_bool_t); self->pttMCPTT.mcptt_answer_mode = MCPTT_ANSWER_MODE; break; } case tsip_pname_mcptt_priv_answer_mode: { const tsk_bool_t MCPTT_PRIV_ANSWER_MODE = va_arg(*app, const tsk_bool_t); self->pttMCPTT.mcptt_priv_answer_mode = MCPTT_PRIV_ANSWER_MODE; break; } case tsip_pname_mcptt_namespace: { const tsk_bool_t MCPTT_NAMESPACE = va_arg(*app, const tsk_bool_t); self->pttMCPTT.mcptt_namespace = MCPTT_NAMESPACE; break; } case tsip_pname_mcptt_insert_x_Framed_IP: { const tsk_bool_t MCPTT_INSERT_X_FRAMED_IP= va_arg(*app, const tsk_bool_t); self->pttMCPTT.mcptt_insert_x_Framed_IP = MCPTT_INSERT_X_FRAMED_IP; break; } case tsip_pname_mcptt_timer_T100: { const int MCPTT_TIMER_T100 = va_arg(*app, const int); self->pttMCPTT.timer_s.timer_t100 = MCPTT_TIMER_T100; break; } case tsip_pname_mcptt_timer_T101: { const int MCPTT_TIMER_T101 = va_arg(*app, const int); self->pttMCPTT.timer_s.timer_t101 = MCPTT_TIMER_T101; break; } case tsip_pname_mcptt_timer_T103: { const int MCPTT_TIMER_T103 = va_arg(*app, const int); self->pttMCPTT.timer_s.timer_t103 = MCPTT_TIMER_T103; break; } case tsip_pname_mcptt_timer_T104: { const int MCPTT_TIMER_T104 = va_arg(*app, const int); self->pttMCPTT.timer_s.timer_t104 = MCPTT_TIMER_T104; break; } case tsip_pname_mcptt_timer_T132: { const int MCPTT_TIMER_T132 = va_arg(*app, const int); self->pttMCPTT.timer_s.timer_t100 = MCPTT_TIMER_T132; break; } //MCPTT LOCATION case tsip_location_p_asserted_identity: { const char* DATA_IDENTITY = va_arg(*app, const char*); self->sessionLocation.p_asserted_identity_location=tsip_uri_parse(DATA_IDENTITY, tsk_strlen(DATA_IDENTITY)); break; } //MCPTT MBMS case tsip_mbms_p_asserted_identity: { const char* DATA_IDENTITY = va_arg(*app, const char*); self->pttMCPTTMbms.p_asserted_identity_mbms=tsip_uri_parse(DATA_IDENTITY, tsk_strlen(DATA_IDENTITY)); break; } //MCPTT MBMS case tsip_mbms_port_manager: { self->pttMCPTTMbms.port_manager= va_arg(*app, unsigned); break; } //MCPTT MBMS case tsip_mbms_addr_manager: { const char* MCPTT_ID = va_arg(*app, const char*); self->pttMCPTTMbms.addr_multicast=tsk_strdup(va_arg(*app, const char*)); break; } //MCPTT MBMS case tsip_mbms_is_rtcp_mux: { const tsk_bool_t MCPTT_MBMS_RTCP_MUX = va_arg(*app, const tsk_bool_t); self->pttMCPTTMbms.is_rtcp_mux = MCPTT_MBMS_RTCP_MUX; break; } //MCPTT AFFILIATION case tsip_pname_mcptt_psi_affiliation: { const char* PSI_AFFILIATION = va_arg(*app, const char*); tsip_uri_t *uri; if(!tsk_strnullORempty(PSI_AFFILIATION) && (uri = tsip_uri_parse(PSI_AFFILIATION, tsk_strlen(PSI_AFFILIATION)))){ if(uri->type == uri_unknown){ /* scheme is missing or unsupported? */ tsk_strupdate(&uri->scheme, "sip"); uri->type = uri_sip; } TSK_OBJECT_SAFE_FREE(self->pttMCPTTAffiliation.psi_affiliation); /* delete old */ self->pttMCPTTAffiliation.psi_affiliation = uri; } else TSK_DEBUG_ERROR("'%s' is an invalid PSI for MCPTT AFFILIATION", PSI_AFFILIATION); break; } case tsip_pname_mcptt_affiliation_is_enable: { const tsk_bool_t MCPTT_AFFILATION_IS_ENABLE = va_arg(*app, const tsk_bool_t); self->pttMCPTTAffiliation.mcptt_affiliation_is_enable = MCPTT_AFFILATION_IS_ENABLE; break; } case tsip_pname_mcptt_affiliation_groups_default: { /* tsip_uri_t* uriTemp; tsip_uris_L_t* uris; int con=0; int numItems=0; const char** MCPTT_AFFILIATION_GROUPS_DEFAULT = va_arg(*app, const char**); uris=tsk_list_create(); numItems=sizeof(MCPTT_AFFILIATION_GROUPS_DEFAULT); for(con=0;con<numItems;con++){ if(!tsk_strnullORempty(MCPTT_AFFILIATION_GROUPS_DEFAULT[con]) && (uriTemp = tsip_uri_parse(MCPTT_AFFILIATION_GROUPS_DEFAULT[con], tsk_strlen(MCPTT_AFFILIATION_GROUPS_DEFAULT[con])))){ if(uriTemp->type == uri_unknown){ // scheme is missing or unsupported? tsk_strupdate(&uriTemp->scheme, "sip"); uriTemp->type = uri_sip; } } tsk_list_push_back_item(uris,uriTemp); TSK_OBJECT_SAFE_FREE(uriTemp); } self->pttMCPTTAffiliation.mcptt_affiliation_groups_default=uris; */ tsip_uris_L_t* MCPTT_AFFILIATION_GROUPS_DEFAULT = va_arg(*app, tsip_uris_L_t*); //tsip_uri_t* a=MCPTT_AFFILIATION_GROUPS_DEFAULT[1]; //tsk_list_push_back_data(self->pttMCPTTAffiliation.mcptt_affiliation_groups_default,&MCPTT_AFFILIATION_GROUPS_DEFAULT[1]); self->pttMCPTTAffiliation.mcptt_affiliation_groups_default=MCPTT_AFFILIATION_GROUPS_DEFAULT; break; } //MCPTT AUTHENTICATION case tsip_pname_mcptt_psi_authentcation: { const char* PSI_AUTHENTICATION = va_arg(*app, const char*); tsip_uri_t *uri; if(!tsk_strnullORempty(PSI_AUTHENTICATION) && (uri = tsip_uri_parse(PSI_AUTHENTICATION, tsk_strlen(PSI_AUTHENTICATION)))){ if(uri->type == uri_unknown){ /* scheme is missing or unsupported? */ tsk_strupdate(&uri->scheme, "sip"); uri->type = uri_sip; } TSK_OBJECT_SAFE_FREE(self->pttMCPTTAuthentication.psi_authentication); /* delete old */ self->pttMCPTTAuthentication.psi_authentication = uri; } else TSK_DEBUG_ERROR("'%s' is an invalid PSI for MCPTT AUTHENTICATION", PSI_AUTHENTICATION); 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 <a href="http://doubango.org/API/tinyNET/tnet_8c.html#affba6c2710347476f615b0135777c640"> tnet_startup()</a> before using any SIP function (tsip_*). * <a href="http://doubango.org/API/tinyNET/tnet_8c.html#ac42b22a7ac5831f04326aee9de033c84"> tnet_cleanup()</a> 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()<br>@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 HAVE_CRT //Debug memory TSK_DEBUG_INFO("Detected Memoty leak start"); //Stop in this point memory _CrtSetBreakAlloc(18078); _CrtSetBreakAlloc(18077); _CrtSetBreakAlloc(18076); _CrtSetBreakAlloc(18075); _CrtSetBreakAlloc(18074); _CrtSetBreakAlloc(18073); _CrtSetBreakAlloc(18072); #endif //HAVE_CRT 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", 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", 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 === */ if(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); } } } /* 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", "IMS-client-MCPTT/organization/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.<br> * 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()<br>@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); #if HAVE_CRT TSK_DEBUG_INFO("Detected Memoty leak"); _CrtDumpMemoryLeaks(); _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); #endif //HAVE_CRT 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); //MCPTT LOCATION stack->sessionLocation.p_asserted_identity_location=tsk_null; //MCPTT MBMS stack->pttMCPTTMbms.p_asserted_identity_mbms=tsk_null; stack->pttMCPTTMbms.port_manager=-1; stack->pttMCPTTMbms.addr_multicast=tsk_null; stack->pttMCPTTMbms.is_rtcp_mux=tsk_false; stack->pttMCPTTMbms.sdp_ro=tsk_null; //MCPTT AUTHENTICATION stack->pttMCPTTAuthentication.psi_authentication=tsk_null; //MCPTT AFFILIATION stack->pttMCPTTAffiliation.psi_affiliation=tsk_null; stack->pttMCPTTAffiliation.mcptt_affiliation_groups_default=tsk_null; //MCPTT TIMERS stack->pttMCPTT.timer_s.timer_t100=0; stack->pttMCPTT.timer_s.timer_t101=0; stack->pttMCPTT.timer_s.timer_t103=0; stack->pttMCPTT.timer_s.timer_t104=0; stack->pttMCPTT.timer_s.timer_t132=0; stack->pttMCPTT.client_id=tsk_null; } 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); /* PTT MCPTT*/ TSK_OBJECT_SAFE_FREE(stack->pttMCPTT.psi_private); TSK_OBJECT_SAFE_FREE(stack->pttMCPTT.psi_group); TSK_OBJECT_SAFE_FREE(stack->pttMCPTT.psi_preestablished); TSK_OBJECT_SAFE_FREE(stack->pttMCPTT.psi_cms); TSK_OBJECT_SAFE_FREE(stack->pttMCPTT.psi_gms); /*MBMS*/ TSK_OBJECT_SAFE_FREE(stack->pttMCPTTMbms.p_asserted_identity_mbms); stack->pttMCPTTMbms.port_manager=-1; TSK_OBJECT_SAFE_FREE(stack->pttMCPTTMbms.addr_multicast); stack->pttMCPTTMbms.is_rtcp_mux=tsk_false; TSK_OBJECT_SAFE_FREE(stack->pttMCPTTMbms.sdp_ro); //SessionLocation TSK_OBJECT_SAFE_FREE(stack->sessionLocation.p_asserted_identity_location); //MCPTT AFFILIATION TSK_OBJECT_SAFE_FREE(stack->pttMCPTTAffiliation.psi_affiliation); TSK_OBJECT_SAFE_FREE(stack->pttMCPTTAffiliation.mcptt_affiliation_groups_default); //MCPTT MBMS TSK_OBJECT_SAFE_FREE(stack->pttMCPTTMbms.p_asserted_identity_mbms); //MCPTT AUTHENTICATION TSK_OBJECT_SAFE_FREE(stack->pttMCPTTAuthentication.psi_authentication); //MCPTT TIMERS stack->pttMCPTT.timer_s.timer_t100=0; stack->pttMCPTT.timer_s.timer_t101=0; stack->pttMCPTT.timer_s.timer_t103=0; stack->pttMCPTT.timer_s.timer_t104=0; stack->pttMCPTT.timer_s.timer_t132=0; TSK_OBJECT_SAFE_FREE(stack->pttMCPTT.client_id); 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;