734 lines
16 KiB
C
734 lines
16 KiB
C
/*
|
|
* Copyright (C) 2009 Mamadou Diop.
|
|
*
|
|
* Contact: Mamadou Diop <diopmamadou@yahoo.fr>
|
|
*
|
|
* 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 (TS 24.229) implementation.
|
|
*
|
|
* @author Mamadou Diop <diopmamadou(at)yahoo.fr>
|
|
*
|
|
* @date Created: Sat Nov 8 16:54:58 2009 mdiop
|
|
*/
|
|
#include "tsip.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 "tnet.h"
|
|
|
|
#include "tsk_memory.h"
|
|
#include "tsk_debug.h"
|
|
#include "tsk_time.h"
|
|
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
|
|
void *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
|
|
*/
|
|
|
|
int __tsip_stack_set(tsip_stack_t *self, va_list values)
|
|
{
|
|
tsip_stack_param_type_t curr;
|
|
|
|
while((curr=va_arg(values, tsip_stack_param_type_t)) != pname_null)
|
|
{
|
|
switch(curr)
|
|
{
|
|
/*
|
|
* Identity
|
|
*/
|
|
case pname_display_name:
|
|
{
|
|
tsk_strupdate(&self->display_name, va_arg(values, const char*));
|
|
break;
|
|
}
|
|
case pname_public_identity:
|
|
case pname_preferred_identity:
|
|
{
|
|
const char *uristring = va_arg(values, const char*);
|
|
if(uristring)
|
|
{
|
|
tsip_uri_t *uri = tsip_uri_parse(uristring, strlen(uristring));
|
|
if(uri)
|
|
{
|
|
if(curr == pname_public_identity)
|
|
{
|
|
TSK_OBJECT_SAFE_FREE(self->public_identity);
|
|
self->public_identity = uri;
|
|
}
|
|
else
|
|
{
|
|
TSK_OBJECT_SAFE_FREE(self->preferred_identity);
|
|
self->preferred_identity = uri;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case pname_private_identity:
|
|
{
|
|
tsk_strupdate(&self->private_identity, va_arg(values, const char*));
|
|
break;
|
|
}
|
|
case pname_password:
|
|
{
|
|
tsk_strupdate(&self->password, va_arg(values, const char*));
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Network
|
|
*/
|
|
case pname_local_ip:
|
|
{
|
|
tsk_strupdate(&self->local_ip, va_arg(values, const char*));
|
|
break;
|
|
}
|
|
case pname_local_port:
|
|
{
|
|
#if defined (__GNUC__)
|
|
self->local_port = (uint16_t)va_arg(values, unsigned);
|
|
#else
|
|
self->local_port = va_arg(values, uint16_t);
|
|
#endif
|
|
break;
|
|
}
|
|
case pname_privacy:
|
|
{
|
|
tsk_strupdate(&self->privacy, va_arg(values, const char*));
|
|
break;
|
|
}
|
|
case pname_operator_id:
|
|
{
|
|
break;
|
|
}
|
|
case pname_amf:
|
|
{
|
|
break;
|
|
}
|
|
case pname_netinfo:
|
|
{
|
|
tsk_strupdate(&self->netinfo, va_arg(values, const char*));
|
|
break;
|
|
}
|
|
case pname_realm:
|
|
{
|
|
const char *uristring = va_arg(values, const char*);
|
|
if(uristring)
|
|
{
|
|
tsip_uri_t *uri = tsip_uri_parse(uristring, strlen(uristring));
|
|
if(uri)
|
|
{
|
|
TSK_OBJECT_SAFE_FREE(self->realm);
|
|
self->realm = uri;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case pname_discovery_naptr:
|
|
{
|
|
self->use_dns_naptr = va_arg(values, unsigned);
|
|
break;
|
|
}
|
|
case pname_discovery_dhcp:
|
|
{
|
|
self->use_dhcp = va_arg(values, unsigned);
|
|
break;
|
|
}
|
|
case pname_proxy_cscf:
|
|
{
|
|
const char* pcscf = va_arg(values, const char*);
|
|
const char* transport = va_arg(values, const char*);
|
|
int use_ipv6 = va_arg(values, int);
|
|
|
|
tsk_strupdate(&self->proxy_cscf, pcscf);
|
|
|
|
TSK_DEBUG_INFO("P-CSCF ==>%s", self->proxy_cscf);
|
|
|
|
if(tsk_striequals(transport, "UDP")) TNET_SOCKET_TYPE_SET_UDP(self->proxy_cscf_type);
|
|
else if(tsk_striequals(transport, "TCP")) TNET_SOCKET_TYPE_SET_TCP(self->proxy_cscf_type);
|
|
else if(tsk_striequals(transport, "TLS")) TNET_SOCKET_TYPE_SET_TLS(self->proxy_cscf_type);
|
|
else if(tsk_striequals(transport, "SCTP")) TNET_SOCKET_TYPE_SET_SCTP(self->proxy_cscf_type);
|
|
|
|
if(use_ipv6) TNET_SOCKET_TYPE_SET_IPV6(self->proxy_cscf_type);
|
|
else TNET_SOCKET_TYPE_SET_IPV4(self->proxy_cscf_type);
|
|
break;
|
|
}
|
|
|
|
case pname_proxy_cscf_port:
|
|
{
|
|
self->proxy_cscf_port = va_arg(values, int);
|
|
break;
|
|
}
|
|
|
|
case pname_device_id:
|
|
{
|
|
if(self->device_id)
|
|
TSK_FREE(self->device_id);
|
|
tsk_sprintf(&self->device_id, "\"%s\"", va_arg(values, const char*));
|
|
break;
|
|
}
|
|
|
|
case pname_mobility:
|
|
{
|
|
tsk_strupdate(&self->mobility, va_arg(values, const char*));
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Security
|
|
*/
|
|
case pname_secagree_ipsec:
|
|
{ /* ALG_STR, EALG_STR, MODE_STR, PROTOCOL_STR */
|
|
tsk_strupdate(&self->secagree_mech, "ipsec-3gpp");
|
|
tsk_strupdate(&self->secagree_ipsec.alg, va_arg(values, const char*));
|
|
tsk_strupdate(&self->secagree_ipsec.ealg, va_arg(values, const char*));
|
|
tsk_strupdate(&self->secagree_ipsec.mode, va_arg(values, const char*));
|
|
tsk_strupdate(&self->secagree_ipsec.protocol, va_arg(values, const char*));
|
|
self->enable_secagree_ipsec = 1;
|
|
break;
|
|
}
|
|
case pname_secagree_tls:
|
|
{ /* USE_TLS_SECAGREE_INT */
|
|
self->enable_secagree_tls = va_arg(values, int) ? 1 : 0;
|
|
break;
|
|
}
|
|
case pname_tls_certs:
|
|
{ /*CA_FILE_STR, PUB_FILE_STR, PRIV_FILE_STR*/
|
|
tsk_strupdate(&self->tls.ca, va_arg(values, const char*));
|
|
tsk_strupdate(&self->tls.pbk, va_arg(values, const char*));
|
|
tsk_strupdate(&self->tls.pvk, va_arg(values, const char*));
|
|
break;
|
|
}
|
|
|
|
|
|
/*
|
|
* Features
|
|
*/
|
|
case pname_enable_100rel:
|
|
{
|
|
self->enable_100rel = va_arg(values, unsigned);
|
|
break;
|
|
}
|
|
case pname_enable_gsmais:
|
|
{
|
|
self->enable_gsmais = va_arg(values, unsigned);
|
|
break;
|
|
}
|
|
case pname_enable_precond:
|
|
{
|
|
self->enable_precond = va_arg(values, unsigned);
|
|
break;
|
|
}
|
|
case pname_enable_3gppsms:
|
|
{
|
|
self->enable_3gppsms = va_arg(values, unsigned);
|
|
break;
|
|
}
|
|
case pname_enable_gsmarcs:
|
|
{
|
|
self->enable_gsmarcs = va_arg(values, unsigned);
|
|
break;
|
|
}
|
|
case pname_enable_earlyIMS:
|
|
{
|
|
self->enable_earlyIMS = va_arg(values, unsigned);
|
|
break;
|
|
}
|
|
case pname_enable_ofdr:
|
|
{
|
|
self->enable_ofdr = va_arg(values, unsigned);
|
|
break;
|
|
}
|
|
case pname_enable_aa:
|
|
{
|
|
self->enable_aa = va_arg(values, unsigned);
|
|
break;
|
|
}
|
|
case pname_enable_dnd:
|
|
{
|
|
self->enable_dnd = va_arg(values, unsigned);
|
|
break;
|
|
}
|
|
case pname_enable_option:
|
|
{
|
|
self->enable_option = va_arg(values, unsigned);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* QoS
|
|
*/
|
|
|
|
default:
|
|
{
|
|
TSK_DEBUG_WARN("Found unknown pname.");
|
|
goto bail;
|
|
}
|
|
}/* switch */
|
|
}/* while */
|
|
|
|
bail:
|
|
return 0;
|
|
}
|
|
|
|
static unsigned __tsip_initialized = 0;
|
|
|
|
int tsip_global_init()
|
|
{
|
|
if(!__tsip_initialized)
|
|
{
|
|
srand((unsigned int) tsk_time_epoch());
|
|
if(!tnet_startup())
|
|
{
|
|
__tsip_initialized = 1;
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int tsip_global_deinit()
|
|
{
|
|
if(__tsip_initialized)
|
|
{
|
|
if(!tnet_cleanup())
|
|
{
|
|
__tsip_initialized = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
tsip_stack_handle_t* tsip_stack_create(tsip_stack_callback callback, ...)
|
|
{
|
|
tsip_stack_t* stack = tsk_object_new(tsip_stack_def_t);
|
|
va_list params;
|
|
|
|
if(!stack){
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Default values
|
|
*/
|
|
stack->local_ip = TNET_SOCKET_HOST_ANY;
|
|
stack->local_port = TNET_SOCKET_PORT_ANY;
|
|
|
|
stack->proxy_cscf_port = 5060;
|
|
stack->proxy_cscf_type = tnet_socket_type_udp_ipv4;
|
|
|
|
va_start(params, callback);
|
|
if(__tsip_stack_set(stack, params)){
|
|
// Delete the stack?
|
|
}
|
|
va_end(params);
|
|
|
|
/*
|
|
* Internals
|
|
*/
|
|
stack->callback = callback;
|
|
stack->timer_mgr = TSK_TIMER_MANAGER_CREATE();
|
|
stack->operations = TSK_LIST_CREATE();
|
|
|
|
/*
|
|
* Layers
|
|
*/
|
|
stack->layer_dialog = TSIP_DIALOG_LAYER_CREATE(stack);
|
|
stack->layer_transac = TSIP_TRANSAC_LAYER_CREATE(stack);
|
|
stack->layer_transport = TSIP_TRANSPORT_LAYER_CREATE(stack);
|
|
|
|
/*
|
|
* DNS context
|
|
*/
|
|
stack->dns_ctx = TNET_DNS_CTX_CREATE();
|
|
|
|
/*
|
|
* DHCP context
|
|
*/
|
|
|
|
return stack;
|
|
}
|
|
|
|
int tsip_stack_start(tsip_stack_handle_t *self)
|
|
{
|
|
if(self)
|
|
{
|
|
int ret;
|
|
tsip_stack_t *stack = self;
|
|
|
|
TSK_RUNNABLE(stack)->run = run;
|
|
if(ret = tsk_runnable_start(TSK_RUNNABLE(stack), tsip_event_def_t)){
|
|
return ret;
|
|
}
|
|
|
|
/* Timer manager */
|
|
if(ret = tsk_timer_manager_start(stack->timer_mgr)){
|
|
// What to do ?
|
|
}
|
|
|
|
/* Set P-Preferred-Identity */
|
|
if(!stack->preferred_identity && stack->public_identity){
|
|
stack->preferred_identity = tsk_object_ref((void*)stack->public_identity);
|
|
}
|
|
/* Transport type */
|
|
if(stack->secagree_mech){
|
|
if(tsk_striequals(stack->secagree_mech, "ipsec-3gpp")){
|
|
TNET_SOCKET_TYPE_SET_IPSEC(stack->proxy_cscf_type);
|
|
}
|
|
//else if if(tsk_striquals(stack->secagree_mech, "ipsec-ike"))
|
|
}
|
|
|
|
/* Use DNS NAPTR for the P-CSCF discovery? */
|
|
if(stack->use_dns_naptr)
|
|
{
|
|
char* hostname = 0;
|
|
tnet_port_t port = 0;
|
|
|
|
if(!tnet_dns_query_naptr_srv(stack->dns_ctx, stack->realm->host, "SIP+D2U"/*FIXME*/, &hostname, &port)){
|
|
tsk_strupdate(&stack->proxy_cscf, hostname);
|
|
stack->proxy_cscf_port = 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);
|
|
}
|
|
|
|
/* Get Best source address */
|
|
if(stack->local_ip == TNET_SOCKET_HOST_ANY){
|
|
tnet_ip_t bestsource;
|
|
if((ret = tnet_getbestsource(stack->proxy_cscf, stack->proxy_cscf_port, stack->proxy_cscf_type, &bestsource))){
|
|
TSK_DEBUG_ERROR("Failed to get best source [%d].", ret);
|
|
}
|
|
else{
|
|
tsk_strupdate(&stack->local_ip, bestsource);
|
|
}
|
|
}
|
|
|
|
/* Transport Layer */
|
|
if((ret = tsip_transport_layer_add(stack->layer_transport, stack->local_ip, stack->local_port, stack->proxy_cscf_type, "SIP transport"))){
|
|
return ret;
|
|
}
|
|
if((ret = tsip_transport_layer_start(stack->layer_transport))){
|
|
return ret;
|
|
}
|
|
|
|
/* ALL IS OK
|
|
*
|
|
*/
|
|
if(stack->layer_transac){
|
|
stack->layer_transac->reliable = TNET_SOCKET_TYPE_IS_STREAM(stack->proxy_cscf_type);
|
|
}
|
|
|
|
TSK_DEBUG_INFO("SIP STACK -- START");
|
|
|
|
return ret;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int tsip_stack_set(tsip_stack_handle_t *self, ...)
|
|
{
|
|
if(self)
|
|
{
|
|
int ret;
|
|
tsip_stack_t *stack = self;
|
|
|
|
va_list params;
|
|
va_start(params, self);
|
|
ret = __tsip_stack_set(stack, params);
|
|
va_end(params);
|
|
return ret;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int tsip_stack_stop(tsip_stack_handle_t *self)
|
|
{
|
|
if(self)
|
|
{
|
|
int ret;
|
|
tsip_stack_t *stack = self;
|
|
|
|
|
|
/* Hangup all dialogs */
|
|
tsip_dialog_layer_hangupAll(stack->layer_dialog);
|
|
//tsk_thread_sleep(50000000);
|
|
|
|
/* Do not Stop timer manager ==> Dialogs have ref to the stack and rely on the timer manager(to gracefully shutdown).*/
|
|
//ret = tsk_timer_manager_stop(stack->timer_mgr);
|
|
|
|
if(ret = tsk_runnable_stop(TSK_RUNNABLE(stack))){
|
|
//return ret;
|
|
}
|
|
|
|
TSK_DEBUG_INFO("SIP STACK -- STOP");
|
|
|
|
return ret;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
tsip_uri_t* tsip_stack_get_contacturi(const tsip_stack_handle_t *self, const char* protocol)
|
|
{
|
|
if(self)
|
|
{
|
|
const tsip_stack_t *stack = self;
|
|
tsk_list_item_t *item;
|
|
tsk_list_foreach(item, stack->layer_transport->transports)
|
|
{
|
|
tsip_transport_t *transport = item->data;
|
|
|
|
if(transport)
|
|
{
|
|
if(tsk_strequals(transport->protocol, protocol))
|
|
{
|
|
tsip_uri_t* uri = 0;
|
|
if((uri = tsip_transport_get_uri(transport, 0)))
|
|
{
|
|
tsk_strupdate(&uri->user_name, stack->public_identity->user_name);
|
|
return uri;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
tsip_uri_t* tsip_stack_get_pcscf_uri(const tsip_stack_handle_t *self, int lr)
|
|
{
|
|
if(self)
|
|
{
|
|
const tsip_stack_t *stack = self;
|
|
if(stack->layer_transport->transports && !TSK_LIST_IS_EMPTY(stack->layer_transport->transports))
|
|
{
|
|
tsip_transport_t *transport = stack->layer_transport->transports->head->data;
|
|
if(transport)
|
|
{
|
|
tsip_uri_t* uri = 0;
|
|
int ipv6 = TNET_SOCKET_TYPE_IS_IPV6(transport->type);
|
|
int quote_ip = (ipv6 && tsk_strcontains(stack->proxy_cscf, strlen(stack->proxy_cscf), ":")) /* IPv6 IP string?*/;
|
|
|
|
char* uristring = 0;
|
|
tsk_sprintf(&uristring, "%s:%s%s%s:%d;%s;transport=%s",
|
|
transport->scheme,
|
|
quote_ip ? "[" : "",
|
|
stack->proxy_cscf,
|
|
quote_ip ? "]" : "",
|
|
stack->proxy_cscf_port,
|
|
lr ? "lr" : "",
|
|
transport->protocol);
|
|
if(uristring){
|
|
if((uri = tsip_uri_parse(uristring, strlen(uristring)))){
|
|
//uri->host_type = ipv6 ? host_ipv6 : host_ipv4;
|
|
}
|
|
TSK_FREE(uristring);
|
|
}
|
|
|
|
return uri;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const tsk_timer_manager_handle_t* tsip_stack_get_timer_mgr(const tsip_stack_handle_t *self)
|
|
{
|
|
if(self)
|
|
{
|
|
const tsip_stack_t *stack = self;
|
|
return stack->timer_mgr;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const struct tsip_dialog_layer_s * tsip_stack_get_dialog_layer(const tsip_stack_handle_t *self)
|
|
{
|
|
if(self)
|
|
{
|
|
const tsip_stack_t *stack = self;
|
|
return stack->layer_dialog;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const struct tsip_transac_layer_s* tsip_stack_get_transac_layer(const tsip_stack_handle_t *self)
|
|
{
|
|
if(self)
|
|
{
|
|
const tsip_stack_t *stack = self;
|
|
return stack->layer_transac;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const struct tsip_transport_layer_s* tsip_stack_get_transport_layer(const tsip_stack_handle_t *self)
|
|
{
|
|
if(self)
|
|
{
|
|
const tsip_stack_t *stack = self;
|
|
return stack->layer_transport;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void *run(void* self)
|
|
{
|
|
tsk_list_item_t *curr;
|
|
tsip_stack_t *stack = self;
|
|
|
|
TSK_RUNNABLE_RUN_BEGIN(stack);
|
|
|
|
TSK_DEBUG_INFO("SIP STACK::run -- START");
|
|
|
|
if((curr = TSK_RUNNABLE_POP_FIRST(stack)))
|
|
{
|
|
tsip_event_t *sipevent = (tsip_event_t*)curr->data;
|
|
if(stack->callback){
|
|
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 void* _tsip_stack_create(void * self, va_list * app)
|
|
{
|
|
tsip_stack_t *stack = self;
|
|
if(stack){
|
|
}
|
|
return self;
|
|
}
|
|
|
|
static void* tsip_stack_destroy(void * self)
|
|
{
|
|
tsip_stack_t *stack = self;
|
|
if(stack){
|
|
/* Stop */
|
|
tsip_stack_stop(stack);
|
|
|
|
/* Identity */
|
|
TSK_FREE(stack->display_name);
|
|
TSK_OBJECT_SAFE_FREE(stack->public_identity);
|
|
TSK_OBJECT_SAFE_FREE(stack->preferred_identity);
|
|
//TSK_OBJECT_SAFE_FREE(stack->associated_identity);
|
|
TSK_FREE(stack->private_identity);
|
|
TSK_FREE(stack->password);
|
|
|
|
/* Network */
|
|
TSK_FREE(stack->local_ip);
|
|
TSK_FREE(stack->privacy);
|
|
TSK_FREE(stack->netinfo);
|
|
TSK_OBJECT_SAFE_FREE(stack->realm);
|
|
TSK_FREE(stack->proxy_cscf);
|
|
TSK_FREE(stack->device_id);
|
|
TSK_FREE(stack->mobility);
|
|
TSK_OBJECT_SAFE_FREE(stack->paths);
|
|
TSK_OBJECT_SAFE_FREE(stack->service_routes);
|
|
TSK_OBJECT_SAFE_FREE(stack->associated_uris);
|
|
|
|
/* Security */
|
|
TSK_FREE(stack->secagree_mech);
|
|
TSK_FREE(stack->secagree_ipsec.alg);
|
|
TSK_FREE(stack->secagree_ipsec.ealg);
|
|
TSK_FREE(stack->secagree_ipsec.mode);
|
|
TSK_FREE(stack->secagree_ipsec.protocol);
|
|
|
|
TSK_FREE(stack->tls.ca);
|
|
TSK_FREE(stack->tls.pbk);
|
|
TSK_FREE(stack->tls.pvk);
|
|
|
|
|
|
/* DNS */
|
|
TSK_OBJECT_SAFE_FREE(stack->dns_ctx);
|
|
|
|
/* DHCP */
|
|
|
|
/* features */
|
|
|
|
/* QoS */
|
|
|
|
/* Internals. */
|
|
TSK_OBJECT_SAFE_FREE(stack->timer_mgr);
|
|
TSK_OBJECT_SAFE_FREE(stack->operations);
|
|
|
|
/* Layers */
|
|
TSK_OBJECT_SAFE_FREE(stack->layer_dialog);
|
|
TSK_OBJECT_SAFE_FREE(stack->layer_transac);
|
|
TSK_OBJECT_SAFE_FREE(stack->layer_transport);
|
|
|
|
TSK_DEBUG_INFO("STACK - DESTROYED");
|
|
}
|
|
return self;
|
|
}
|
|
|
|
static const tsk_object_def_t tsip_stack_def_s =
|
|
{
|
|
sizeof(tsip_stack_t),
|
|
_tsip_stack_create,
|
|
tsip_stack_destroy,
|
|
0,
|
|
};
|
|
const void *tsip_stack_def_t = &tsip_stack_def_s;
|