doubango/trunk/tinyNET/src/dns/tnet_dns.c

783 lines
22 KiB
C

/*
* Copyright (C) 2009 Mamadou Diop.
*
* Contact: Mamadou Diop <diopmamadou(at)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 tnet_dns.c
* @brief DNS utility functions (RFCS [1034 1035] [3401 3402 3403 3404] [3761]).
*
* @author Mamadou Diop <diopmamadou(at)yahoo.fr>
*
* @date Created: Sat Nov 8 16:54:58 2009 mdiop
*/
#include "tnet_dns.h"
#include "tnet_dns_regexp.h"
#include "tnet_dns_message.h"
#include "tnet_dns_opt.h"
#include "tnet_dns_srv.h"
#include "tnet_dns_naptr.h"
#include "tnet_types.h"
#include "tsk_memory.h"
#include "tsk_time.h"
#include "tsk_debug.h"
#include "tsk_string.h"
/* DNS cache functions */
int tnet_dns_cache_maintenance(tnet_dns_ctx_t *ctx);
int tnet_dns_cache_entry_add(tnet_dns_ctx_t *ctx, const char* qname, tnet_dns_qclass_t qclass, tnet_dns_qtype_t qtype, tnet_dns_response_t* response);
const tnet_dns_cache_entry_t* tnet_dns_cache_entry_get(tnet_dns_ctx_t *ctx, const char* qname, tnet_dns_qclass_t qclass, tnet_dns_qtype_t qtype);
/**@defgroup tnet_dns_group DNS utility functions (RFCS [1034 1035] [3401 3402 3403 3404]).
*/
/**@ingroup tnet_dns_group
* Cleanup the internal DNS cache.
* @param ctx The DNS context containing the cache to cleanup.
* The context contains the user's preference and should be created using @ref TNET_DNS_CTX_CREATE.
* @retval Zero if succeeed and non-zero error code otherwise.
*/
int tnet_dns_cache_clear(tnet_dns_ctx_t* ctx)
{
if(ctx){
tsk_safeobj_lock(ctx);
tsk_list_clear_items(ctx->cache);
tsk_safeobj_unlock(ctx);
return 0;
}
return -1;
}
/**@ingroup tnet_dns_group
* Sends DNS request over the network. The request will be sent each 500 milliseconds until @ref TNET_DNS_TIMEOUT_DEFAULT milliseconds is reached.
* @param ctx The DNS context to use. The context contains the user's preference and should be created using @ref TNET_DNS_CTX_CREATE.
* @param qname The domain name (e.g. google.com).
* @param qclass The CLASS of the query.
* @param qtype The type of the query.
* @retval The DNS response. The @a answers in the @a response are already filtered.
* MUST be destroyed using @ref TSK_OBJECT_SAFE_FREE macro.
* @sa @ref tnet_dns_query_srv, @ref tnet_dns_query_naptr_srv, @ref tnet_dns_enum.
*
* @code
* tnet_dns_ctx_t *ctx = TNET_DNS_CTX_CREATE();
* tnet_dns_response_t *response = tnet_dns_resolve(ctx, "sip2sip.info", qclass_in, qtype_srv);
* TSK_OBJECT_SAFE_FREE(response);
* TSK_OBJECT_SAFE_FREE(ctx);
* @endcode
*/
tnet_dns_response_t *tnet_dns_resolve(tnet_dns_ctx_t* ctx, const char* qname, tnet_dns_qclass_t qclass, tnet_dns_qtype_t qtype)
{
tsk_buffer_t *output = tsk_null;
tnet_dns_query_t* query = TNET_DNS_QUERY_CREATE(qname, qclass, qtype);
tnet_dns_response_t *response = tsk_null;
tsk_bool_t from_cache = tsk_false;
/* Check validity */
if(!ctx || !query){
goto bail;
}
/* Is there any DNS Server? */
if(TSK_LIST_IS_EMPTY(ctx->servers)){
TSK_DEBUG_ERROR("Failed to load DNS Servers. You can add new DNS servers by using \"tnet_dns_add_server\".");
goto bail;
}
/* Cache maintenance */
if(!TSK_LIST_IS_EMPTY(ctx->cache)){
/* Only do maintenance if the cache is not empty */
tnet_dns_cache_maintenance(ctx);
}
/* Retrieve data from cache. */
if(ctx->caching)
{
const tnet_dns_cache_entry_t *entry = tnet_dns_cache_entry_get(ctx, qname, qclass, qtype);
if(entry){
response = tsk_object_ref(((tnet_dns_cache_entry_t*)entry)->response);
from_cache = tsk_true;
goto bail;
}
}
/* Set user preference */
query->Header.RD = ctx->recursion;
/* EDNS0 */
if(ctx->edns0)
{
tnet_dns_opt_t *rr_opt = TNET_DNS_OPT_CREATE(TNET_DNS_DGRAM_SIZE_DEFAULT);
if(!query->Additionals){
query->Additionals = TSK_LIST_CREATE();
}
tsk_list_push_back_data(query->Additionals, (void**)&rr_opt);
query->Header.ARCOUNT++;
}
/* Serialize and send to the server. */
if(!(output = tnet_dns_message_serialize(query))){
TSK_DEBUG_ERROR("Failed to serialize the DNS message.");
goto bail;
}
/* ============================ */
// Send and Recaive data
/* ============================ */
{
int ret = -1;
struct timeval tv;
fd_set set;
tnet_fd_t maxFD;
uint64_t timeout = 0;
tsk_list_item_t *item;
const tnet_address_t *address;
struct sockaddr_storage server;
tnet_socket_t *localsocket4 = TNET_SOCKET_CREATE(TNET_SOCKET_HOST_ANY, TNET_SOCKET_PORT_ANY, tnet_socket_type_udp_ipv4);
tnet_socket_t *localsocket6 = TNET_SOCKET_CREATE(TNET_SOCKET_HOST_ANY, TNET_SOCKET_PORT_ANY, tnet_socket_type_udp_ipv6);
tsk_bool_t useIPv6 = TNET_SOCKET_IS_VALID(localsocket6);
tsk_safeobj_lock(ctx);
/* Check socket validity */
if(!TNET_SOCKET_IS_VALID(localsocket4)){
goto done;
}
/* Always wait 500ms before retransmission */
tv.tv_sec = 0;
tv.tv_usec = (500 * 1000);
do
{
//
// Send data (loop through all intefaces)
//
tsk_list_foreach(item, ctx->servers)
{
address = item->data;
if(!address->ip ||
(address->family != AF_INET && address->family != AF_INET6) ||
(address->family == AF_INET6 && !TNET_SOCKET_IS_VALID(localsocket6))){
continue;
}
if(tnet_sockaddr_init(address->ip, ctx->server_port, (address->family == AF_INET ? tnet_socket_type_udp_ipv4 : tnet_socket_type_udp_ipv6), &server)){
TSK_DEBUG_ERROR("Failed to initialize the DNS server address: \"%s\"", address->ip);
continue;
}
TSK_DEBUG_INFO("Send DNS query to \"%s\"", address->ip);
if(address->family == AF_INET6){
if((ret = tnet_sockfd_sendto(localsocket6->fd, (const struct sockaddr*)&server, output->data, output->size))){
// succeed?
break;
}
}
else{
if((ret = tnet_sockfd_sendto(localsocket4->fd, (const struct sockaddr*)&server, output->data, output->size))){
// succeed?
break;
}
}
}
//
// Received data
//
/* First time? ==> set timeout value */
if(!timeout){
timeout = tsk_time_epoch() + ctx->timeout;
}
/* Set FDs */
FD_ZERO(&set);
FD_SET(localsocket4->fd, &set);
if(useIPv6){
FD_SET(localsocket6->fd, &set);
maxFD = TSK_MAX(localsocket4->fd, localsocket6->fd);
}
else{
maxFD = localsocket4->fd;
}
/* wait for response */
if((ret = select(maxFD+1, &set, NULL, NULL, &tv))<0){ /* Error */
TNET_PRINT_LAST_ERROR("Select failed.");
goto done;
}
else if(ret == 0){ /* timeout ==> do nothing */
}
else{ /* there is data to read */
size_t len = 0;
void* data = 0;
tnet_fd_t active_fd;
/* Find active file descriptor */
if(FD_ISSET(localsocket4->fd, &set)){
active_fd = localsocket4->fd;
}
else if(FD_ISSET(localsocket6->fd, &set)){
active_fd = localsocket4->fd;
}
else{
TSK_DEBUG_ERROR("FD_ISSET ==> Invalid file descriptor.");
continue;
}
/* Check how how many bytes are pending */
if((ret = tnet_ioctlt(active_fd, FIONREAD, &len))<0){
TSK_DEBUG_ERROR("tnet_ioctlt failed with error code:%d", tnet_geterrno());
goto done;
}
/* Receive pending data */
data = tsk_calloc(len, sizeof(uint8_t));
if((ret = tnet_sockfd_recv(active_fd, data, len, 0))<0){
TSK_FREE(data);
TSK_DEBUG_ERROR("tnet_sockfd_recv failed with error code:%d", tnet_geterrno());
goto done;
}
/* Parse the incoming response. */
response = tnet_dns_message_deserialize(data, len);
TSK_FREE(data);
if(response)
{ /* response successfuly parsed */
if(query->Header.ID != response->Header.ID || !TNET_DNS_MESSAGE_IS_RESPONSE(response)){
/* Not same transaction id ==> continue*/
TSK_OBJECT_SAFE_FREE(response);
}
else goto done;
}
}
}
while(timeout > tsk_time_epoch());
done:
tsk_safeobj_unlock(ctx);
TSK_OBJECT_SAFE_FREE(localsocket4);
TSK_OBJECT_SAFE_FREE(localsocket6);
goto bail;
}
bail:
TSK_OBJECT_SAFE_FREE(query);
TSK_OBJECT_SAFE_FREE(output);
/* Add the result to the cache. */
if(response){
if(!from_cache && ctx->caching){
tnet_dns_cache_entry_add(ctx, qname, qclass, qtype, response);
}
}
else{
TSK_DEBUG_ERROR("Failed to contact the DNS server.");
}
return response;
}
/**@ingroup tnet_dns_group
* Gets list of URIs associated to this telephone number by using ENUM protocol (RFC 3761).
* @param ctx The DNS context.
* The context contains the user's preference and should be created using @ref TNET_DNS_CTX_CREATE.
* @param e164num A valid E.164 number (e.g. +1-800-555-5555).
* @param domain The domain name (e.g e164.arpa, e164.org, ...). If Null, default value is "e164.arpa" (IANA).
* @retval The DNS response with NAPTR RRs. The @a answers in the @a response are already filtered.
* MUST be destroyed using @ref TSK_OBJECT_SAFE_FREE macro.
* @sa @ref tnet_dns_resolve, @ref tnet_dns_enum_2.
*/
tnet_dns_response_t* tnet_dns_enum(tnet_dns_ctx_t* ctx, const char* e164num, const char* domain)
{
char e164domain[255];
tnet_dns_response_t* ret = tsk_null;
size_t e164size;
int i, j; // must be signed
e164size = strlen(e164num);
if(!ctx || !e164num || !e164size){
goto bail;
}
if(e164size /* max=15 digits + ".e164.arpa" + '+' */>=(sizeof(e164domain)-1)){
TSK_DEBUG_ERROR("%s is an invalid E.164 number.", e164num);
goto bail;
}
memset(e164domain, '\0', sizeof(e164domain));
/* RFC 3761 - 2.4. Valid Databases
1. Remove all characters with the exception of the digits. For
example, the First Well Known Rule produced the Key
"+442079460148". This step would simply remove the leading "+",
producing "442079460148".
2. Put dots (".") between each digit. Example:
4.4.2.0.7.9.4.6.0.1.4.8
3. Reverse the order of the digits. Example:
8.4.1.0.6.4.9.7.0.2.4.4
4. Append the string ".e164.arpa" to the end. Example:
8.4.1.0.6.4.9.7.0.2.4.4.e164.arpa
This domain-name is used to request NAPTR records which may contain
the end result or, if the flags field is blank, produces new keys in
the form of domain-names from the DNS.
*/
for(i = e164size-1, j=0; i>=0; i--){
if(!isdigit(e164num[i])){
continue;
}
e164domain[j++] = e164num[i];
e164domain[j++] = '.';
}
// append domain name
if(domain){
memcpy( &e164domain[j], domain, ((strlen(domain) + j) >= sizeof(e164domain)-1) ? (sizeof(e164domain)-j-1) : strlen(domain) );
}
else{
memcpy(&e164domain[j], "e164.arpa", 9);
}
/* == Performs DNS (NAPTR) lookup == */
ret = tnet_dns_resolve(ctx, e164domain, qclass_in, qtype_naptr);
bail:
return ret;
}
/**@ingroup tnet_dns_group
* Gets the internate address associated to this telephone number by using ENUM protocol (RFC 3761).
* Only terminale rules containing uris(flags="u") will be considered and the regex string will be executed on the original string for
* substitution. <br>
* <b> Parsing complex regexp will probably fail (99.99% chance). Please use @ref tnet_dns_enum if you want to use your own regexp parser. </b>
* @param ctx The DNS context.
* The context contains the user's preference and should be created using @ref TNET_DNS_CTX_CREATE.
* @param service The ENUM service (e.g. E2U+SIP).
* @param e164num A valid E.164 number (e.g. +1-800-555-5555).
* @param domain The domain name (e.g e164.arpa, e164.org, ...). If Null, default value is "e164.arpa" (IANA).
* @retval The Internet address (SIP, email, ICQ, fax, ...) associated to this service.
* MUST be freed using @ref TSK_FREE macro.
* @sa @ref tnet_dns_resolve, @ref tnet_dns_enum.
*/
char* tnet_dns_enum_2(tnet_dns_ctx_t* ctx, const char* service, const char* e164num, const char* domain)
{
tnet_dns_response_t* response;
const tsk_list_item_t* item;
char* ret = tsk_null;
const tnet_dns_rr_t* rr;
if((response = tnet_dns_enum(ctx, e164num, domain))){
if(TNET_DNS_RESPONSE_IS_SUCCESS(response)){
tsk_list_foreach(item, response->Answers){
rr = item->data;
if(rr->qtype == qtype_naptr){
const tnet_dns_naptr_t *naptr = (const tnet_dns_naptr_t*)rr;
/* RFC 3403 - 6.2 E164 Example
Both the ENUM [18] and URI Resolution [4] Applications use the 'u'
flag. This flag states that the Rule is terminal and that the output
is a URI which contains the information needed to contact that
telephone service.
*/
if( tsk_striequals(naptr->flags, "u") && tsk_striequals(naptr->services, service)){
/* RFC 3403 - 4.1 Packet Format
The fields (replacement and regexp) are also mutually exclusive. If a record is
returned that has values for both fields then it is considered to
be in error and SHOULD be either ignored or an error returned.
*/
if(naptr->regexp && naptr->replacement){
continue;
}
if((ret = tnet_dns_regex_parse(e164num, naptr->regexp))){
break;
}
}
}
}
}
else{
TSK_DEBUG_ERROR("We got an error response from the DNS server. Error code: %u", response->Header.RCODE);
}
TSK_OBJECT_SAFE_FREE(response);
}
return ret;
}
/**@ingroup tnet_dns_group
* Performs DNS SRV resolution.
* @param ctx The DNS context.
* The context contains the user's preference and should be created using @ref TNET_DNS_CTX_CREATE.
* @param service The name of the service (e.g. SIP+D2U).
* @param hostname The result containing an IP address or FQDN.
* @param port The port associated to the result.
* @retval Zero if succeed and non-zero error code otherwise.
* @sa @ref tnet_dns_resolve.
*
* @code
* tnet_dns_ctx_t *ctx = TNET_DNS_CTX_CREATE();
* char* hostname = 0;
* tnet_port_t port = 0;
*
* if(!tnet_dns_query_srv(ctx, "_sip._udp.sip2sip.info", &hostname, &port)){
* TSK_DEBUG_INFO("DNS SRV succeed ==> hostname=%s and port=%u", hostname, port);
* }
*
* TSK_FREE(hostname);
* TSK_OBJECT_SAFE_FREE(ctx);
* @endcode
*/
int tnet_dns_query_srv(tnet_dns_ctx_t *ctx, const char* service, char** hostname, tnet_port_t* port)
{
tnet_dns_response_t *response;
if(!ctx){
return -1;
}
// tnet_dns_resolve is thread-safe
if((response = tnet_dns_resolve(ctx, service, qclass_in, qtype_srv)))
{
const tsk_list_item_t *item;
const tnet_dns_rr_t* rr;
tsk_list_foreach(item, response->Answers) /* Already Filtered ==> Peek the first One */
{
rr = item->data;
if(rr->qtype == qtype_srv){
const tnet_dns_srv_t *srv = (const tnet_dns_srv_t*)rr;
tsk_strupdate(hostname, srv->target);
*port = srv->port;
break;
}
}
}
TSK_OBJECT_SAFE_FREE(response);
return (hostname && !tsk_strempty(*hostname)) ? 0 : -2;
}
/**@ingroup tnet_dns_group
* Performs DNS NAPTR followed by DNS SRV resolution.
* @param ctx The DNS context.
* The context contains the user's preference and should be created using @ref TNET_DNS_CTX_CREATE.
* @param domain The Name of the domain (e.g. google.com).
* @param service The name of the service (e.g. SIP+D2U).
* @param hostname The result containing an IP address or FQDN.
* @param port The port associated to the result.
* @retval Zero if succeed and non-zero error code otherwise.
* @sa @ref tnet_dns_resolve.
*
* @code
* tnet_dns_ctx_t *ctx = TNET_DNS_CTX_CREATE();
* char* hostname = 0;
* tnet_port_t port = 0;
*
* if(!tnet_dns_query_naptr_srv(ctx, "sip2sip.info", "SIP+D2U", &hostname, &port)){
* TSK_DEBUG_INFO("DNS NAPTR+SRV succeed ==> hostname=%s and port=%u", hostname, port);
* }
*
* TSK_FREE(hostname);
* TSK_OBJECT_SAFE_FREE(ctx);
* @endcode
*/
int tnet_dns_query_naptr_srv(tnet_dns_ctx_t *ctx, const char* domain, const char* service, char** hostname, tnet_port_t* port)
{
tnet_dns_response_t *response;
if(!ctx){
return -1;
}
// tnet_dns_resolve is thread-safe
if((response = tnet_dns_resolve(ctx, domain, qclass_in, qtype_naptr)))
{
const tsk_list_item_t *item;
const tnet_dns_rr_t* rr;
char* replacement = 0; /* e.g. _sip._udp.example.com */
char* flags = 0;/* e.g. S, A, AAAA, A6, U, P ... */
tsk_list_foreach(item, response->Answers) /* Already Filtered ==> Peek the first One */
{
rr = item->data;
if(rr->qtype == qtype_naptr){
tnet_dns_naptr_t *naptr = (tnet_dns_naptr_t*)rr;
if(tsk_striequals(service, naptr->services)){
tsk_strupdate(&replacement, naptr->replacement);
tsk_strupdate(&flags, naptr->flags);
break;
}
}
}
if(flags && replacement){
if(tsk_striequals(flags, "S")){
tnet_dns_query_srv(ctx, replacement, hostname, port);
}
else if(tsk_striequals(flags, "A") || tsk_striequals(flags, "AAAA") ||tsk_striequals(flags, "A6")){
TSK_DEBUG_WARN("Defaulting port value.");
tsk_strupdate(hostname, replacement);
*port = 5060;
}
else{
TSK_DEBUG_ERROR("DNS NAPTR query returned invalid falgs.");
}
}
else{
TSK_DEBUG_ERROR("DNS NAPTR query returned zero result.");
}
TSK_FREE(flags);
TSK_FREE(replacement);
}
TSK_OBJECT_SAFE_FREE(response);
return (hostname && *hostname && !tsk_strempty(*hostname)) ? 0 : -2;
}
// remove timedout entries
int tnet_dns_cache_maintenance(tnet_dns_ctx_t *ctx)
{
if(ctx)
{
tsk_list_item_t *item;
tsk_safeobj_lock(ctx);
again:
tsk_list_foreach(item, ctx->cache)
{
// FIXME: ttl should be from RR::ttl
tnet_dns_cache_entry_t *entry = (tnet_dns_cache_entry_t*)item->data;
if((entry ->epoch + ctx->cache_ttl) < tsk_time_epoch()){
tsk_list_remove_item_by_data(ctx->cache, entry);
goto again; /* Do not delete data while looping */
}
}
tsk_safeobj_unlock(ctx);
return 0;
}
return -1;
}
// add an entry to the cache
int tnet_dns_cache_entry_add(tnet_dns_ctx_t *ctx, const char* qname, tnet_dns_qclass_t qclass, tnet_dns_qtype_t qtype, tnet_dns_response_t* response)
{
int ret = -1;
if(ctx)
{
tnet_dns_cache_entry_t *entry;
tsk_safeobj_lock(ctx);
entry = 0;
/* Retrieve from cache */
entry = (tnet_dns_cache_entry_t*)tnet_dns_cache_entry_get(ctx, qname, qclass, qtype);
if(entry){
/* UPDATE */
TSK_OBJECT_SAFE_FREE(entry->response);
entry->response = tsk_object_ref(response);
entry->epoch = tsk_time_epoch();
ret = 0;
goto done;
}
else{
/* CREATE */
entry = TNET_DNS_CACHE_ENTRY_CREATE(qname, qclass, qtype, response);
if(entry){
tsk_list_push_back_data(ctx->cache, (void**)&entry);
ret = 0;
goto done;
}
else{
ret = -2;
goto done;
}
}
done:
tsk_safeobj_unlock(ctx);
}
return ret;
}
// get an entry from the cache
const tnet_dns_cache_entry_t* tnet_dns_cache_entry_get(tnet_dns_ctx_t *ctx, const char* qname, tnet_dns_qclass_t qclass, tnet_dns_qtype_t qtype)
{
tnet_dns_cache_entry_t *ret = tsk_null;
if(ctx)
{
tsk_list_item_t *item;
tsk_safeobj_lock(ctx);
tsk_list_foreach(item, ctx->cache){
tnet_dns_cache_entry_t *entry = (tnet_dns_cache_entry_t*)item->data;
if(entry->qtype == qtype && entry->qclass == qclass && tsk_strequals(entry->qname, qname)){
ret = entry;
break;
}
}
tsk_safeobj_unlock(ctx);
}
return ret;
}
/**
* Adds new DNS server to the list of the list of servers to query.
* @param ctx DNS context containing the user parameters. The new DNS server will be added to this context.
* @param host The IP address (or FQDN) of the dns server to add to the server.
* @retval zero if succeed and non-zero error code otherwise.
*/
int tnet_dns_add_server(tnet_dns_ctx_t *ctx, const char* host)
{
tnet_address_t *address;
if(!ctx || !host){
return -1;
}
if(!ctx->servers){
ctx->servers = TSK_LIST_CREATE();
}
if((address = TNET_ADDRESS_CREATE(host))){
address->family = tnet_get_family(host);
address->dnsserver = 1;
tsk_list_push_ascending_data(ctx->servers, (void**)&address);
return 0;
}
return -2;
}
//=================================================================================================
// [[DNS CACHE ENTRY]] object definition
//
static tsk_object_t* tnet_dns_cache_entry_create(tsk_object_t * self, va_list * app)
{
tnet_dns_cache_entry_t *entry = self;
if(entry)
{
entry->qname = tsk_strdup(va_arg(*app, const char*));
entry->qclass = va_arg(*app, tnet_dns_qtype_t);
entry->qtype = va_arg(*app, tnet_dns_qtype_t);
entry->response = tsk_object_ref(va_arg(*app, tnet_dns_response_t*));
entry->epoch = tsk_time_epoch();
}
return self;
}
static tsk_object_t* tnet_dns_cache_entry_destroy(tsk_object_t * self)
{
tnet_dns_cache_entry_t *entry = self;
if(entry){
TSK_OBJECT_SAFE_FREE(entry->response);
}
return self;
}
static const tsk_object_def_t tnet_dns_cache_entry_def_s =
{
sizeof(tnet_dns_cache_entry_t),
tnet_dns_cache_entry_create,
tnet_dns_cache_entry_destroy,
tsk_null,
};
const void *tnet_dns_cache_entry_def_t = &tnet_dns_cache_entry_def_s;
//=================================================================================================
// [[DNS CONTEXT]] object definition
//
static tsk_object_t* tnet_dns_ctx_create(tsk_object_t * self, va_list * app)
{
tnet_dns_ctx_t *ctx = self;
if(ctx)
{
ctx->timeout = TNET_DNS_TIMEOUT_DEFAULT;
ctx->recursion = tsk_true;
ctx->edns0 = tsk_true;
ctx->caching = tsk_false;
ctx->cache_ttl = TNET_DNS_CACHE_TTL;
ctx->server_port = TNET_DNS_SERVER_PORT_DEFAULT;
/* Gets all dns servers. */
ctx->servers = tnet_get_addresses_all_dnsservers();
/* Creates empty cache. */
ctx->cache = TSK_LIST_CREATE();
tsk_safeobj_init(ctx);
}
return self;
}
static tsk_object_t* tnet_dns_ctx_destroy(tsk_object_t * self)
{
tnet_dns_ctx_t *ctx = self;
if(ctx)
{
tsk_safeobj_deinit(ctx);
TSK_OBJECT_SAFE_FREE(ctx->servers);
TSK_OBJECT_SAFE_FREE(ctx->cache);
}
return self;
}
static const tsk_object_def_t tnet_dns_ctx_def_s =
{
sizeof(tnet_dns_ctx_t),
tnet_dns_ctx_create,
tnet_dns_ctx_destroy,
tsk_null,
};
const void *tnet_dns_ctx_def_t = &tnet_dns_ctx_def_s;