1838 lines
51 KiB
C
1838 lines
51 KiB
C
/*
|
|
******************************************************************************
|
|
* SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipdnsc.c,v $ $Revision: 1.68.10.8 $
|
|
* $Source: /home/interpeak/CVSRoot/ipappl/src/ipdnsc.c,v $
|
|
* $Author: jren $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation by Markus Carlstedt <markus@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
* DNS main source file.
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
|
|
#include "ipdnsc_config.h"
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#define IPCOM_USE_CLIB_PROTO
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_sysvar.h>
|
|
#include <ipcom_time.h>
|
|
|
|
#include "ipdnsc.h"
|
|
#include "ipdnsc_h.h"
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/* DNS cache type */
|
|
#define IPDNSC_CACHE_NAME 0
|
|
#define IPDNSC_CACHE_ADDR 1
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
IP_EXTERN int ipcom_dns_init_ok;
|
|
IP_EXTERN IP_PUBLIC Ipdnsc_api ipcom_dns;
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
IP_PUBLIC Ip_err ipdnsc_create(void);
|
|
IP_PUBLIC Ip_err ipdnsc_start(void);
|
|
IP_PUBLIC const char *ipdnsc_version(void);
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
const struct Ip_in6_addr ipdnsc_in6addr_any =
|
|
{{{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }}};
|
|
#endif
|
|
|
|
Ipdnsc_data ipdnsc;
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 LOCAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_ptr_name
|
|
*===========================================================================
|
|
* Description: Converts an internet address to a form suitable for
|
|
* DNS ptr queries and appends the top domain
|
|
* Parameters: buf - pointer to buffer where the result is stored
|
|
* addr - pointer to internet address in network order
|
|
* af - address family
|
|
* Returns: 0 - OK
|
|
* -1 - Fail
|
|
*/
|
|
IP_STATIC int
|
|
ipdnsc_ptr_name(Ip_u8 *buf, Ip_u32 buflen, Ip_u8 *addr, Ip_s32 family)
|
|
{
|
|
#ifdef IPCOM_USE_INET6
|
|
struct Ip_in6_addr *in6_addr;
|
|
Ip_u8 nibble;
|
|
#endif
|
|
int ret = 0, must_free = 1;
|
|
int i, must_print_tens;
|
|
Ip_u8 tmp;
|
|
char *zone;
|
|
|
|
if (buflen < 64) /* 32(hex digits) + 32(.) */
|
|
return -1;
|
|
|
|
switch(family)
|
|
{
|
|
case IP_AF_INET:
|
|
|
|
/* Reverse ipv4 address */
|
|
for (i=3; i>=0; i--)
|
|
{
|
|
must_print_tens = IP_FALSE;
|
|
|
|
/* possible hundreds digit */
|
|
tmp = (Ip_u8)(addr[i] / 100);
|
|
if (tmp)
|
|
{
|
|
*buf++ =(Ip_u8) ('0' + tmp);
|
|
must_print_tens = IP_TRUE;
|
|
|
|
}
|
|
tmp = (Ip_u8)(addr[i] % 100);
|
|
|
|
/* possible tens digit */
|
|
if ((tmp / 10) || must_print_tens)
|
|
{
|
|
*buf++ = (Ip_u8)('0' + tmp / 10);
|
|
|
|
}
|
|
/* always print ones digit */
|
|
*buf++ = (Ip_u8)('0' + tmp % 10);
|
|
|
|
*buf++ = '.';
|
|
}
|
|
|
|
/* Append the top level domain */
|
|
zone = ipcom_sysvar_get ("ipdnsc.ip4.zone", IP_NULL, IP_NULL);
|
|
if (zone == IP_NULL)
|
|
{
|
|
must_free = 0;
|
|
zone = "in-addr.arpa"; /* Default */
|
|
}
|
|
break;
|
|
|
|
#ifdef IPCOM_USE_INET6
|
|
case IP_AF_INET6:
|
|
|
|
in6_addr = (struct Ip_in6_addr *)addr;
|
|
|
|
/* Split ipv6 address in dot-separated nibbles in reverse order */
|
|
for (i=15; i>=0; i--)
|
|
{
|
|
nibble = (Ip_u8)(in6_addr->in6.addr8[i] & 0x0f);
|
|
*buf++ = (Ip_u8)(nibble > 9 ? 'a' + nibble - 10 : '0' + nibble);
|
|
*buf++ = '.';
|
|
nibble = (Ip_u8)((in6_addr->in6.addr8[i] & 0xf0) >> 4);
|
|
*buf++ = (Ip_u8)(nibble > 9 ? 'a' + nibble - 10 : '0' + nibble);
|
|
*buf++ = '.';
|
|
}
|
|
|
|
/* Append the top level domain */
|
|
zone = ipcom_sysvar_get ("ipdnsc.ip6.zone", IP_NULL, IP_NULL);
|
|
if (zone == IP_NULL)
|
|
{
|
|
must_free = 0;
|
|
zone = "ip6.int"; /* Default */
|
|
}
|
|
break;
|
|
#endif /* IPCOM_USE_INET6 */
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
if (buflen <= 64 + ipcom_strlen(zone))
|
|
ret = -1;
|
|
else
|
|
ipcom_strcpy((char *)buf, zone);
|
|
|
|
if (must_free)
|
|
ipcom_free(zone);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_encode_name
|
|
*===========================================================================
|
|
* Description: Converts a domain name to a form suitable for DNS
|
|
* queries
|
|
* Parameters: buf - pointer to buffer where the result is stored
|
|
* name - the domain name to convert
|
|
* Returns: The number of bytes in the converted name or -1 if fail
|
|
*/
|
|
IP_STATIC Ip_s32
|
|
ipdnsc_encode_name(Ip_u8 *buf, Ip_u8 *name)
|
|
{
|
|
Ip_u8 *ptr;
|
|
Ip_s32 count, tot_count = 0;
|
|
|
|
while (*name)
|
|
{
|
|
count = 0;
|
|
ptr = buf;
|
|
buf++;
|
|
while (*name && (*name != '.'))
|
|
{
|
|
*buf++ = *name++;
|
|
count++;
|
|
if (count > IPDNSC_MAXLABEL)
|
|
return -1;
|
|
}
|
|
if (*name != '\0')
|
|
name++;
|
|
*ptr = (Ip_u8)count;
|
|
tot_count = tot_count + count + 1;
|
|
}
|
|
|
|
/* Add the terminating zero */
|
|
*buf = 0;
|
|
tot_count++;
|
|
|
|
if (tot_count > IPDNSC_MAXNAME)
|
|
return -1;
|
|
|
|
return tot_count;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_decode_name
|
|
*===========================================================================
|
|
* Description: Restores a converted domain name to uncompressed and readable
|
|
* format.
|
|
* Parameters: name - the restored domain name (null terminated string).
|
|
* buf - pointer to buffer with the received name
|
|
* start - pointer to start of DNS message.
|
|
* end - pointer to end of DNS message.
|
|
* Returns: The number of bytes in the compressed name or -1 if fail
|
|
*/
|
|
IP_STATIC Ip_s32
|
|
ipdnsc_decode_name(Ip_u8 *name, Ip_u8 *buf, Ip_u8 *start, Ip_u8 *end)
|
|
{
|
|
Ip_u8 *ptr, *prev;
|
|
Ip_s32 i, len, namelen = 0, tot_len = 0, compress = 0;
|
|
|
|
if (*buf == 0 || buf >= end)
|
|
return -1;
|
|
|
|
ptr = buf;
|
|
while (*ptr && ptr < end)
|
|
{
|
|
/* Loop until we find a non-pointer */
|
|
while ((*ptr & 0xc0) == 0xc0 && ptr < end)
|
|
{
|
|
prev = ptr;
|
|
ptr = start + (IP_GET_NTOHS(ptr) & 0x3fff);
|
|
if (ptr >= prev)
|
|
return -1; /* Do not allow forward jumps (avoid loops) */
|
|
if (!compress)
|
|
{
|
|
tot_len += 2;
|
|
}
|
|
compress = 1;
|
|
}
|
|
/* Store the length of the label */
|
|
if (ptr >= end)
|
|
return -1;
|
|
len = *ptr++;
|
|
if (len > IPDNSC_MAXLABEL)
|
|
return -1;
|
|
if (!compress)
|
|
{
|
|
tot_len = tot_len + len + 1;
|
|
}
|
|
namelen = namelen + len + 1;
|
|
if (tot_len > IPDNSC_MAXNAME || namelen > IPDNSC_MAXNAME)
|
|
return -1;
|
|
/* Copy the label to name */
|
|
for (i=0; i<len; i++)
|
|
{
|
|
if (ptr >= end)
|
|
return -1;
|
|
*name++ = *ptr++;
|
|
}
|
|
*name++ = '.';
|
|
}
|
|
|
|
if (!compress)
|
|
{
|
|
/* Increment for the last zero */
|
|
tot_len++;
|
|
}
|
|
|
|
/* Null terminate the name string */
|
|
if (tot_len)
|
|
name--;
|
|
*name = 0;
|
|
return tot_len;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_parse_response
|
|
*===========================================================================
|
|
* Description: Parse a DNS name server response and create a host entry
|
|
* which will be filled in with the received data
|
|
* Parameters: dns_msg - pointer to response
|
|
* msglen - length of response.
|
|
* family - the address family
|
|
* cache_ttl - pointer to the time to cache
|
|
* num_answers - the number of answers received
|
|
* Returns: A pointer to an allocated host entry structure or NULL if failed
|
|
*/
|
|
IP_STATIC struct Ip_hostent*
|
|
ipdnsc_parse_response(Ip_u8* dns_msg, Ip_s32 msglen, Ip_s32 family, Ip_u32 *cache_ttl, Ip_s32 *num_answers)
|
|
{
|
|
Ipdnsc_hdr *dns_hdr;
|
|
struct Ip_hostent *he;
|
|
char dname[IPDNSC_MAXNAME];
|
|
Ip_u16 qtype, qclass;
|
|
Ip_s32 len, i;
|
|
Ip_u8 *ptr;
|
|
|
|
*num_answers = -1;
|
|
dns_hdr = (Ipdnsc_hdr *)dns_msg;
|
|
|
|
/* Make sure there is only one question */
|
|
if (IP_SWAP16(dns_hdr->no_ques) != 1)
|
|
return IP_NULL;
|
|
|
|
/* Make sure there was answers */
|
|
*num_answers = IP_SWAP16(dns_hdr->no_answ);
|
|
if (*num_answers == 0 ||
|
|
*num_answers >= ((IPDNSC_MAXPKT - sizeof(Ipdnsc_hdr)) / 16))
|
|
return IP_NULL;
|
|
|
|
/* Find the name */
|
|
len = ipdnsc_decode_name((Ip_u8*)dname, dns_msg + sizeof(*dns_hdr), dns_msg, dns_msg + msglen);
|
|
if (len < 0)
|
|
{
|
|
return IP_NULL;
|
|
}
|
|
|
|
/* The remaining fields in the dns message may reside on any byte alignment */
|
|
|
|
/* Get query type and query class */
|
|
ptr = dns_msg + sizeof(*dns_hdr) + len;
|
|
qtype = (Ip_u16)(IP_GET_NTOHS(ptr));
|
|
switch(qtype)
|
|
{
|
|
case IPDNSC_QTYPE_A:
|
|
if (family != IP_AF_INET)
|
|
return IP_NULL;
|
|
break;
|
|
case IPDNSC_QTYPE_AAAA:
|
|
if (family != IP_AF_INET6)
|
|
return IP_NULL;
|
|
break;
|
|
case IPDNSC_QTYPE_PTR:
|
|
break;
|
|
default:
|
|
return IP_NULL;
|
|
}
|
|
qclass = (Ip_u16)(IP_GET_NTOHS(ptr+2));
|
|
if (qclass != IPDNSC_QCLASS_INET)
|
|
return IP_NULL;
|
|
|
|
/* Create the host entry structure */
|
|
he = ipdnsc_hostent_create(family);
|
|
if (he == IP_NULL)
|
|
return IP_NULL;
|
|
|
|
/* Bump pointer to the first answer */
|
|
ptr += 4;
|
|
|
|
/* Parse the answers */
|
|
for (i=0; i<IP_SWAP16(dns_hdr->no_answ); i++)
|
|
{
|
|
Ip_u32 ttl;
|
|
Ip_u16 rrtype, rrclass, rrlen;
|
|
|
|
/* Find the name */
|
|
len = ipdnsc_decode_name((Ip_u8*)dname, ptr, dns_msg, dns_msg + msglen);
|
|
if (len < 0)
|
|
{
|
|
goto err_out;
|
|
}
|
|
|
|
/* Get query type and query class */
|
|
ptr += len;
|
|
rrtype = (Ip_u16)(IP_GET_NTOHS(ptr));
|
|
rrclass = (Ip_u16)(IP_GET_NTOHS(ptr+2));
|
|
if (rrclass != IPDNSC_QCLASS_INET)
|
|
{
|
|
goto err_out;
|
|
}
|
|
|
|
/* Get time to live */
|
|
ptr += 4;
|
|
ttl = IP_GET_NTOHL(ptr);
|
|
|
|
/* Get resource record data length */
|
|
ptr += 4;
|
|
rrlen = (Ip_u16)(IP_GET_NTOHS(ptr));
|
|
|
|
/* Advance pointer to resource record data */
|
|
ptr += 2;
|
|
|
|
switch(rrtype)
|
|
{
|
|
case IPDNSC_QTYPE_A:
|
|
case IPDNSC_QTYPE_AAAA:
|
|
if (qtype != rrtype)
|
|
{
|
|
/* Answer not relevant */
|
|
goto err_out;
|
|
}
|
|
if (rrtype == IPDNSC_QTYPE_A)
|
|
{
|
|
if (rrlen != IPDNSC_INADDRSZ)
|
|
{
|
|
/* Error in resource record data length */
|
|
goto err_out;
|
|
}
|
|
}
|
|
else if (rrtype == IPDNSC_QTYPE_AAAA)
|
|
{
|
|
if (rrlen != IPDNSC_IN6ADDRSZ)
|
|
{
|
|
/* Error in resource record data length */
|
|
goto err_out;
|
|
}
|
|
}
|
|
|
|
/* Insert the host address */
|
|
if (ipdnsc_hostent_insert_addr(he, (char *)ptr))
|
|
{
|
|
goto err_out;
|
|
}
|
|
|
|
/* Insert the host name */
|
|
if (ipdnsc_hostent_insert_name(he, dname))
|
|
{
|
|
goto err_out;
|
|
}
|
|
|
|
*cache_ttl = ttl;
|
|
|
|
break;
|
|
|
|
case IPDNSC_QTYPE_CNAME:
|
|
if (qtype != IPDNSC_QTYPE_A && qtype != IPDNSC_QTYPE_AAAA)
|
|
{
|
|
/* Answer not relevant */
|
|
goto err_out;
|
|
}
|
|
|
|
/* Insert the alias */
|
|
if (ipdnsc_hostent_insert_alias(he, dname))
|
|
{
|
|
goto err_out;
|
|
}
|
|
|
|
/* Find the primary name */
|
|
len = ipdnsc_decode_name((Ip_u8*)dname, ptr, dns_msg, dns_msg + msglen);
|
|
if ((len < 0) || (len != rrlen))
|
|
{
|
|
/* Name or length errror */
|
|
goto err_out;
|
|
}
|
|
|
|
break;
|
|
|
|
case IPDNSC_QTYPE_PTR:
|
|
if (qtype != IPDNSC_QTYPE_PTR)
|
|
{
|
|
/* Answer not relevant */
|
|
goto err_out;
|
|
}
|
|
|
|
/* Find the domain name */
|
|
len = ipdnsc_decode_name((Ip_u8*)dname, ptr, dns_msg, dns_msg + msglen);
|
|
if ((len < 0) || (len != rrlen))
|
|
{
|
|
/* Name or length errror */
|
|
goto err_out;
|
|
}
|
|
|
|
/* Insert the host name */
|
|
if (ipdnsc_hostent_insert_name(he, dname))
|
|
{
|
|
goto err_out;
|
|
}
|
|
|
|
*cache_ttl = ttl;
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Bump pointer to the next answer */
|
|
ptr += rrlen;
|
|
}
|
|
|
|
/* Sanity checking */
|
|
if (he && he->h_name == IP_NULL)
|
|
{
|
|
if (he->h_aliases[0] != 0)
|
|
{
|
|
/* Special case - CNAME record was received but no A or AAAA record */
|
|
*num_answers = 0;
|
|
}
|
|
goto err_out;
|
|
}
|
|
|
|
return he;
|
|
|
|
err_out:
|
|
|
|
ipdnsc_hostent_free(he);
|
|
return IP_NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_do_query
|
|
*===========================================================================
|
|
* Description: Handle a complete DNS query including: creation of DNS message,
|
|
* sending the message, receiveing and parsing the response.
|
|
* Parameters: dname - the name to resolve
|
|
* qtype - the query type
|
|
* family - the address family
|
|
* error - possible error code. will be set to zero if no error
|
|
* Returns: A pointer to an allocated host entry structure or NULL if failed
|
|
*/
|
|
IP_STATIC struct Ip_hostent *
|
|
ipdnsc_do_query(Ip_u8 *dname, Ip_u16 qtype, Ip_s32 family, Ipdnsc_ns_spec *dns, Ip_s32 *error,
|
|
Ip_u32 *cache_ttl)
|
|
{
|
|
struct Ip_hostent *he;
|
|
Ipdnsc_ns_addr *dns_addr;
|
|
Ip_u8 *s_buf;
|
|
Ip_u8 *r_buf;
|
|
Ip_s32 s_size, r_size = 0, err, retries, num_answers;
|
|
|
|
/* Get buffer memory. */
|
|
s_buf = ipcom_malloc(2 * IPDNSC_MAXPKT);
|
|
if (s_buf == IP_NULL)
|
|
return IP_NULL;
|
|
r_buf = s_buf + IPDNSC_MAXPKT;
|
|
|
|
/* Create the DNS query message */
|
|
s_size = ipdnsc_create_query(dname, qtype, IPDNSC_QCLASS_INET, s_buf);
|
|
if (s_size < 0)
|
|
{
|
|
*error = IPDNSC_NO_RECOVERY;
|
|
goto fail;
|
|
}
|
|
|
|
/* Send the DNS query message and wait for a response */
|
|
if (dns->primary_dns.family != 0)
|
|
dns_addr = &dns->primary_dns;
|
|
else if (dns->secondary_dns.family != 0)
|
|
dns_addr = &dns->secondary_dns;
|
|
else if (dns->tertiary_dns.family != 0)
|
|
dns_addr = &dns->tertiary_dns;
|
|
else if (dns->quaternary_dns.family != 0)
|
|
dns_addr = &dns->quaternary_dns;
|
|
else
|
|
{
|
|
*error = IPDNSC_NO_RECOVERY;
|
|
goto fail;
|
|
}
|
|
retries = dns->retry;
|
|
|
|
resend:
|
|
err = ipdnsc_send_wait(s_buf, s_size, r_buf, &r_size, dns_addr, dns->timeout);
|
|
|
|
switch(err)
|
|
{
|
|
case IPDNSC_OK:
|
|
break;
|
|
|
|
case IPDNSC_SERV_FAIL:
|
|
case IPDNSC_SERV_NOTIMPL:
|
|
case IPDNSC_SERV_REFUSED:
|
|
case IPDNSC_SERV_FORMAT:
|
|
case IPDNSC_SERV_NONAME:
|
|
/* Skip further retries */
|
|
retries = 0;
|
|
/* Fall through */
|
|
case IPDNSC_SERV_TIMEOUT:
|
|
if (retries-- > 0 )
|
|
{
|
|
/* Retry same name server */
|
|
goto resend;
|
|
}
|
|
else if (dns_addr == &dns->primary_dns && dns->secondary_dns.family != 0)
|
|
{
|
|
/* Jump to secondary name server */
|
|
dns_addr = &dns->secondary_dns;
|
|
retries = dns->retry;
|
|
goto resend;
|
|
}
|
|
else if ((dns_addr == &dns->primary_dns || dns_addr == &dns->secondary_dns) && dns->tertiary_dns.family != 0)
|
|
{
|
|
/* Jump to tertiary name server */
|
|
dns_addr = &dns->tertiary_dns;
|
|
retries = dns->retry;
|
|
goto resend;
|
|
}
|
|
else if ((dns_addr == &dns->primary_dns || dns_addr == &dns->secondary_dns || dns_addr == &dns->tertiary_dns)
|
|
&& dns->quaternary_dns.family != 0)
|
|
{
|
|
/* Jump to quaternary name server */
|
|
dns_addr = &dns->quaternary_dns;
|
|
retries = dns->retry;
|
|
goto resend;
|
|
}
|
|
else
|
|
{
|
|
/* Tried all nameservers */
|
|
if(err == IPDNSC_SERV_NONAME)
|
|
*error = IPDNSC_HOST_NOT_FOUND;
|
|
else
|
|
*error = IPDNSC_NO_RECOVERY;
|
|
}
|
|
goto fail;
|
|
case IPDNSC_ERR_GENERAL:
|
|
default:
|
|
*error = IPDNSC_NO_RECOVERY;
|
|
goto fail;
|
|
}
|
|
|
|
/* Parse the name server response */
|
|
he = ipdnsc_parse_response(r_buf, r_size, family, cache_ttl, &num_answers);
|
|
|
|
*error = (he != IP_NULL) ? IPDNSC_OK : IPDNSC_NO_RECOVERY;
|
|
|
|
/* Test the special case of no answers */
|
|
if (num_answers == 0)
|
|
{
|
|
*error = IPDNSC_NO_ADDRESS;
|
|
}
|
|
|
|
ipcom_free(s_buf);
|
|
return he;
|
|
|
|
fail:
|
|
ipcom_free(s_buf);
|
|
return IP_NULL;
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_cache_remove
|
|
*===========================================================================
|
|
* Description: Removes an entry from the DNS cache
|
|
* Parameters: index to the cache entry to remove
|
|
* Returns: 0 - OK
|
|
* -1 - Fail
|
|
*/
|
|
IP_STATIC Ip_s32
|
|
ipdnsc_cache_remove(Ipdnsc_cache *dns_cache)
|
|
{
|
|
Ipdnsc_cache *tmp;
|
|
|
|
/* Remove entry from list */
|
|
tmp = ipdnsc.cache_head;
|
|
if (tmp == dns_cache)
|
|
{
|
|
ipdnsc.cache_head = tmp->next;
|
|
}
|
|
else
|
|
{
|
|
while (tmp != IP_NULL)
|
|
{
|
|
if (tmp->next == dns_cache)
|
|
{
|
|
tmp->next = tmp->next->next;
|
|
tmp = dns_cache;
|
|
break;
|
|
}
|
|
tmp = tmp->next;
|
|
}
|
|
}
|
|
|
|
/* Free the memory */
|
|
if (tmp != IP_NULL)
|
|
{
|
|
ipdnsc_hostent_free(tmp->he);
|
|
ipcom_free(tmp);
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_cache_find_oldest
|
|
*===========================================================================
|
|
* Description: Finds the first entry in the DNS cache to expire
|
|
* Parameters: none.
|
|
* Returns: The cache entry.
|
|
*/
|
|
IP_STATIC Ipdnsc_cache *
|
|
ipdnsc_cache_find_oldest(void)
|
|
{
|
|
Ipdnsc_cache *dns_cache, *dns_oldest;
|
|
|
|
/* Search cache */
|
|
dns_cache = ipdnsc.cache_head;
|
|
dns_oldest = dns_cache;
|
|
while (dns_cache != IP_NULL)
|
|
{
|
|
/* Check if older */
|
|
if (dns_cache->expire < dns_oldest->expire)
|
|
{
|
|
dns_oldest = dns_cache;
|
|
}
|
|
dns_cache = dns_cache->next;
|
|
}
|
|
|
|
return dns_oldest;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_cache_find_expired
|
|
*===========================================================================
|
|
* Description: Finds the first expired entry in the DNS cache
|
|
* Parameters: none.
|
|
* Returns: The cache entry.
|
|
*/
|
|
IP_STATIC Ipdnsc_cache *
|
|
ipdnsc_cache_find_expired(void)
|
|
{
|
|
Ipdnsc_cache *dns_cache;
|
|
Ip_u32 now = ipdnsc_get_time();
|
|
|
|
/* Search cache */
|
|
dns_cache = ipdnsc.cache_head;
|
|
while (dns_cache != IP_NULL)
|
|
{
|
|
/* Check if expired */
|
|
if (dns_cache->expire < now)
|
|
{
|
|
return dns_cache;
|
|
}
|
|
dns_cache = dns_cache->next;
|
|
}
|
|
|
|
/* None expired */
|
|
return IP_NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_cache_clean
|
|
*===========================================================================
|
|
* Description: Remove an expired cache entry or the oldest entry.
|
|
* Parameters: none.
|
|
* Returns: 0 - OK
|
|
* -1 - Fail
|
|
*/
|
|
IP_STATIC int
|
|
ipdnsc_cache_clean(void)
|
|
{
|
|
Ipdnsc_cache *dns_cache;
|
|
|
|
dns_cache = ipdnsc_cache_find_expired();
|
|
if (dns_cache == IP_NULL)
|
|
{
|
|
dns_cache = ipdnsc_cache_find_oldest();
|
|
if (dns_cache == IP_NULL || ipdnsc_cache_remove(dns_cache) != 0)
|
|
return -1;
|
|
ipdnsc.cache_entries--;
|
|
}
|
|
else
|
|
{
|
|
while (dns_cache != IP_NULL)
|
|
{
|
|
if (ipdnsc_cache_remove(dns_cache) != 0)
|
|
return -1;
|
|
|
|
ipdnsc.cache_entries--;
|
|
dns_cache = ipdnsc_cache_find_expired();
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_cache_add
|
|
*===========================================================================
|
|
* Description: Adds a new entry to the DNS cache. Will not check for
|
|
* duplicates
|
|
* Parameters: he - pointer to the hostent structure to be added
|
|
* ttl - time to live for the new entry
|
|
* Returns: 0 - OK
|
|
* -1 - Fail
|
|
*/
|
|
IP_STATIC Ip_s32
|
|
ipdnsc_cache_add(struct Ip_hostent *he, Ip_u32 type, Ip_u32 ttl)
|
|
{
|
|
Ipdnsc_cache *dns_cache;
|
|
Ip_u32 now = ipdnsc_get_time();
|
|
|
|
if (ttl == 0)
|
|
{
|
|
/* No caching, just return ok */
|
|
return 0;
|
|
}
|
|
|
|
if (ipdnsc.cache_entries >= IPDNSC_CONF_MAX_CACHE_ENTRIES)
|
|
{
|
|
if (ipdnsc_cache_clean() != 0)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Allocate the memory */
|
|
dns_cache = ipcom_malloc(sizeof(*dns_cache));
|
|
if (dns_cache == IP_NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
dns_cache->he = ipdnsc_hostent_copy(he);
|
|
if (dns_cache->he == IP_NULL)
|
|
{
|
|
ipcom_free(dns_cache);
|
|
return -1;
|
|
}
|
|
|
|
if (ttl == 0xffffffff)
|
|
dns_cache->expire = ttl;
|
|
else
|
|
dns_cache->expire = now + ttl;
|
|
dns_cache->type = type;
|
|
|
|
/* Insert entry first in the list */
|
|
dns_cache->next = ipdnsc.cache_head;
|
|
ipdnsc.cache_head = dns_cache;
|
|
|
|
ipdnsc.cache_entries++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_cache_find_addr
|
|
*===========================================================================
|
|
* Description: Checks if an address resides in the DNS cache
|
|
* Parameters: addr - pointer to the requested address
|
|
* family - the address family
|
|
* Returns: Pointer a hostent structure including the requested address
|
|
* or IP_NULL if not found
|
|
*/
|
|
IP_STATIC struct Ip_hostent *
|
|
ipdnsc_cache_find_addr(Ip_u8 *addr, Ip_s32 family)
|
|
{
|
|
Ipdnsc_cache *dns_cache;
|
|
struct Ip_hostent *he = IP_NULL;
|
|
Ip_u32 now = ipdnsc_get_time();
|
|
|
|
/* Search cache */
|
|
dns_cache = ipdnsc.cache_head;
|
|
while (dns_cache != IP_NULL)
|
|
{
|
|
if (dns_cache->type == IPDNSC_CACHE_ADDR && dns_cache->he->h_addrtype == family)
|
|
{
|
|
if (!ipcom_memcmp(dns_cache->he->h_addr_list[0], addr, dns_cache->he->h_length))
|
|
{
|
|
/* Requested address found in the cache - check that it is still valid */
|
|
if (dns_cache->expire >= now)
|
|
{
|
|
he = ipdnsc_hostent_copy(dns_cache->he);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
dns_cache = dns_cache->next;
|
|
}
|
|
|
|
return he;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_cache_find_name
|
|
*===========================================================================
|
|
* Description: Checks if a name resides in the DNS cache
|
|
* Parameters: name - pointer to the requested name
|
|
* Returns: Pointer a hostent structure including the requested address
|
|
* or IP_NULL if not found
|
|
*/
|
|
IP_STATIC struct Ip_hostent *
|
|
ipdnsc_cache_find_name(Ip_u8 *name, Ip_s32 family)
|
|
{
|
|
Ip_s32 i;
|
|
Ipdnsc_cache *dns_cache;
|
|
struct Ip_hostent *he = IP_NULL;
|
|
Ip_u32 now = ipdnsc_get_time();
|
|
|
|
/* Search cache */
|
|
dns_cache = ipdnsc.cache_head;
|
|
while (dns_cache != IP_NULL)
|
|
{
|
|
if (dns_cache->type == IPDNSC_CACHE_NAME && dns_cache->he->h_addrtype == family)
|
|
{
|
|
if (!ipcom_strcasecmp(dns_cache->he->h_name, (char *)name))
|
|
{
|
|
/* Requested name found in the cache - check that it is still valid */
|
|
if (dns_cache->expire >= now)
|
|
{
|
|
he = ipdnsc_hostent_copy(dns_cache->he);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Scan also for aliases */
|
|
i=0;
|
|
while (dns_cache->he->h_aliases[i] != IP_NULL)
|
|
{
|
|
if (!ipcom_strcasecmp(dns_cache->he->h_aliases[i++], (char *)name))
|
|
{
|
|
/* Requested name found in the cache - check that it is still valid */
|
|
if (dns_cache->expire >= now)
|
|
{
|
|
he = ipdnsc_hostent_copy(dns_cache->he);
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
dns_cache = dns_cache->next;
|
|
}
|
|
|
|
done:
|
|
return he;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_cache_known_hosts
|
|
*===========================================================================
|
|
* Description: Add the known hosts to the cache.
|
|
* Parameters: None
|
|
* Returns: IPCOM_SUCCESS or IPCOM_ERR_XXX error code.
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipdnsc_cache_known_hosts(void)
|
|
{
|
|
struct Ip_hostent *he;
|
|
Ip_u8 ipaddr[16];
|
|
Ip_s32 family;
|
|
struct host
|
|
{
|
|
char *addr;
|
|
char *name;
|
|
};
|
|
struct host *hostp;
|
|
struct host known_hosts[] = { IPDNSC_CONF_DNS_HOSTS };
|
|
|
|
/* Add the host table to the cache */
|
|
for (hostp = &known_hosts[0]; hostp->addr != IP_NULL; hostp++)
|
|
{
|
|
family = IP_AF_INET;
|
|
/* Try an ipv4 address */
|
|
if (ipcom_inet_pton(family, hostp->addr, ipaddr) <= 0)
|
|
{
|
|
/* Try an ipv6 address */
|
|
family = IP_AF_INET6;
|
|
if (ipcom_inet_pton(family, hostp->addr, ipaddr) <= 0)
|
|
{
|
|
/* Both failed, next entry */
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* Add the host table to the cache */
|
|
he = ipdnsc_hostent_create(family);
|
|
if (he == IP_NULL)
|
|
return IPCOM_ERR_NO_MEMORY;
|
|
|
|
/* Insert the host address */
|
|
if (ipdnsc_hostent_insert_addr(he, (char *)ipaddr))
|
|
{
|
|
ipdnsc_hostent_free(he);
|
|
return IPCOM_ERR_NO_MEMORY;
|
|
}
|
|
/* Insert the host name */
|
|
if (ipdnsc_hostent_insert_name(he, hostp->name))
|
|
{
|
|
ipdnsc_hostent_free(he);
|
|
return IPCOM_ERR_NO_MEMORY;
|
|
}
|
|
|
|
ipcom_mutex_lock(ipdnsc.mutex);
|
|
/* Add the host entry to the cache */
|
|
(void)ipdnsc_cache_add(he, IPDNSC_CACHE_NAME, 0xffffffff);
|
|
/* Add the host entry to the cache */
|
|
(void)ipdnsc_cache_add(he, IPDNSC_CACHE_ADDR, 0xffffffff);
|
|
ipcom_mutex_unlock(ipdnsc.mutex);
|
|
ipdnsc_hostent_free(he);
|
|
}
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 GLOBAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_get_time
|
|
*===========================================================================
|
|
* Description: Retrives current dns time
|
|
* Parameters: None.
|
|
* Returns: Current time in seconds.
|
|
*/
|
|
IP_GLOBAL Ip_u32
|
|
ipdnsc_get_time(void)
|
|
{
|
|
#ifdef IPCOM_USE_TIME
|
|
return (Ip_u32)ipcom_time(IP_NULL);
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_create_query
|
|
*===========================================================================
|
|
* Description: Create a dns query message
|
|
* Parameters: dname - the name to query for.
|
|
* type - the query type
|
|
* class - the query class
|
|
* buffer for the generated query
|
|
* Returns: length of query message or -1 for error
|
|
*/
|
|
IP_GLOBAL Ip_s32
|
|
ipdnsc_create_query(Ip_u8 *dname, Ip_u16 type, Ip_u16 class, Ip_u8 *buf)
|
|
{
|
|
static Ip_u16 id_count = 0;
|
|
Ipdnsc_hdr *dns_hdr;
|
|
Ip_u8 *qp;
|
|
Ip_s32 qlen;
|
|
|
|
if (buf == IP_NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
dns_hdr = (Ipdnsc_hdr *)buf;
|
|
qp = (Ip_u8 *)(dns_hdr + 1);
|
|
|
|
/* Assign values to the DNS message format */
|
|
dns_hdr->id = IP_SWAP16(id_count);
|
|
id_count++;
|
|
dns_hdr->flags = IP_SWAP16(IPDNSC_RD_FLAG);
|
|
dns_hdr->no_ques = IP_SWAP16(1);
|
|
dns_hdr->no_answ = 0;
|
|
dns_hdr->no_auth = 0;
|
|
dns_hdr->no_addi = 0;
|
|
|
|
/* Convert domain name to query format */
|
|
qlen = ipdnsc_encode_name(qp, dname);
|
|
if (qlen < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
/* The remaining fields in the dns message may reside on any byte alignment */
|
|
qp += qlen;
|
|
/* Set the query type */
|
|
IP_SET_HTONS(qp, type);
|
|
|
|
/* Set the query class */
|
|
qp += 2;
|
|
IP_SET_HTONS(qp, class);
|
|
|
|
return (Ip_s32)(sizeof(*dns_hdr) + qlen + 2 + 2);
|
|
|
|
}
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_send_wait
|
|
*===========================================================================
|
|
* Description: Send a dns query and wait for the response. The supplied receive
|
|
* buffer must be large enough to hold the maximum sized DNS message
|
|
* (currently 512 bytes)
|
|
* Parameters: s_buf - send buffer
|
|
* r_buf - receive buffer
|
|
* s_size - the number of bytes to send
|
|
* r_size - the number of bytes that were received
|
|
* Returns: IPDNSC_OK
|
|
* IPDNSC_ERR_GENERAL
|
|
* IPDNSC_SERV_TIMEOUT
|
|
* IPDNSC_SERV_NONAME
|
|
* IPDNSC_SERV_FORMAT
|
|
* IPDNSC_SERV_FAIL
|
|
* IPDNSC_SERV_NOTIMPL
|
|
* IPDNSC_SERV_REFUSED
|
|
*/
|
|
IP_GLOBAL Ip_s32
|
|
ipdnsc_send_wait(Ip_u8 *s_buf, Ip_s32 s_size, Ip_u8 *r_buf, Ip_s32 *r_size, Ipdnsc_ns_addr *dns_addr, Ip_s32 timeout)
|
|
{
|
|
Ip_s32 sock, count, num, retval = IPDNSC_OK;
|
|
Ip_fd_set *read_set = IP_NULL;
|
|
struct Ip_timeval tv;
|
|
Ip_u16 s_id, r_id, s_flags, r_flags;
|
|
|
|
/* Save id and flags */
|
|
s_id = IP_SWAP16(*(Ip_u16 *)(s_buf));
|
|
s_flags = IP_SWAP16(*(Ip_u16 *)(s_buf+2));
|
|
|
|
/* Get a socket */
|
|
sock = ipcom_socket(dns_addr->family, IP_SOCK_DGRAM, 0);
|
|
if (sock < 0)
|
|
{
|
|
retval = IPDNSC_ERR_GENERAL;
|
|
return retval;
|
|
}
|
|
|
|
if (dns_addr->family == IP_AF_INET)
|
|
{
|
|
|
|
struct Ip_sockaddr_in in_addr;
|
|
|
|
/* Bind the socket */
|
|
ipcom_memset(&in_addr, 0, sizeof(struct Ip_sockaddr_in));
|
|
IPCOM_SA_LEN_SET(&in_addr, sizeof(struct Ip_sockaddr_in));
|
|
in_addr.sin_family = IP_AF_INET;
|
|
in_addr.sin_addr.s_addr = IP_INADDR_ANY;
|
|
in_addr.sin_port = 0;
|
|
if (ipcom_bind(sock, (struct Ip_sockaddr*) &in_addr, sizeof(struct Ip_sockaddr)) < 0)
|
|
{
|
|
retval = IPDNSC_ERR_GENERAL;
|
|
goto out;
|
|
}
|
|
|
|
/* Send the query */
|
|
ipcom_memset(&in_addr, 0, sizeof(struct Ip_sockaddr_in));
|
|
IPCOM_SA_LEN_SET(&in_addr, sizeof(struct Ip_sockaddr_in));
|
|
in_addr.sin_family = IP_AF_INET;
|
|
ipcom_memcpy(&in_addr.sin_addr, &dns_addr->addr.inaddr, sizeof(in_addr.sin_addr));
|
|
in_addr.sin_port = dns_addr->port;
|
|
|
|
count = (Ip_s32)ipcom_sendto(sock, s_buf, s_size, 0,
|
|
(IP_CONST struct Ip_sockaddr *) &in_addr,
|
|
sizeof(struct Ip_sockaddr_in));
|
|
|
|
|
|
}
|
|
#ifdef IPCOM_USE_INET6
|
|
else if (dns_addr->family == IP_AF_INET6)
|
|
{
|
|
|
|
struct Ip_sockaddr_in6 in_addr6;
|
|
|
|
/* Bind the socket */
|
|
ipcom_memset(&in_addr6, 0, sizeof(struct Ip_sockaddr_in6));
|
|
IPCOM_SA_LEN_SET(&in_addr6, sizeof(struct Ip_sockaddr_in6));
|
|
in_addr6.sin6_family = IP_AF_INET6;
|
|
in_addr6.sin6_addr = ipdnsc_in6addr_any;
|
|
in_addr6.sin6_port = 0;
|
|
if (ipcom_bind(sock, (struct Ip_sockaddr*)&in_addr6, sizeof(struct Ip_sockaddr_in6)) < 0)
|
|
{
|
|
retval = IPDNSC_ERR_GENERAL;
|
|
goto out;
|
|
}
|
|
|
|
/* Send the query */
|
|
ipcom_memset(&in_addr6, 0, sizeof(struct Ip_sockaddr_in6));
|
|
IPCOM_SA_LEN_SET(&in_addr6, sizeof(struct Ip_sockaddr_in6));
|
|
in_addr6.sin6_family = IP_AF_INET6;
|
|
ipcom_memcpy(&in_addr6.sin6_addr, &dns_addr->addr.inaddr6, sizeof(in_addr6.sin6_addr));
|
|
in_addr6.sin6_scope_id = dns_addr->ifindex;
|
|
in_addr6.sin6_port = dns_addr->port;
|
|
|
|
count = (Ip_s32)ipcom_sendto(sock, s_buf, s_size, 0,
|
|
(IP_CONST struct Ip_sockaddr *) &in_addr6,
|
|
sizeof(struct Ip_sockaddr_in6));
|
|
}
|
|
#endif /* IPCOM_USE_INET6 */
|
|
else
|
|
{
|
|
retval = IPDNSC_ERR_GENERAL;
|
|
goto out;
|
|
}
|
|
|
|
/* Check number of sent bytes */
|
|
if (count != s_size)
|
|
{
|
|
retval = IPDNSC_ERR_GENERAL;
|
|
goto out;
|
|
}
|
|
|
|
/* Prepare the read set */
|
|
read_set = ipcom_malloc(sizeof(Ip_fd_set));
|
|
if (read_set == IP_NULL)
|
|
{
|
|
retval = IPDNSC_ERR_GENERAL;
|
|
goto out;
|
|
}
|
|
ipcom_memset(read_set, 0, sizeof(Ip_fd_set));
|
|
|
|
/* Set the timeout */
|
|
tv.tv_sec = timeout;
|
|
tv.tv_usec = 0;
|
|
|
|
/* Wait for the response */
|
|
IP_FD_SET(sock, read_set);
|
|
num = ipcom_socketselect(sock+1, read_set, IP_NULL, IP_NULL, &tv);
|
|
if (num == IP_SOCKERR)
|
|
{
|
|
retval = IPDNSC_ERR_GENERAL;
|
|
goto out;
|
|
}
|
|
|
|
if (num == 0)
|
|
{
|
|
/* Timeout */
|
|
retval = IPDNSC_SERV_TIMEOUT;
|
|
goto out;
|
|
}
|
|
else if (num > 0 && IP_FD_ISSET(sock, read_set))
|
|
{
|
|
/* Data available */
|
|
count = (Ip_s32)ipcom_recvfrom(sock, r_buf, IPDNSC_MAXPKT, 0, IP_NULL, IP_NULL);
|
|
}
|
|
else
|
|
{
|
|
retval = IPDNSC_ERR_GENERAL;
|
|
goto out;
|
|
}
|
|
|
|
/* check received number of bytes */
|
|
if (count < (Ip_s32)sizeof(Ipdnsc_hdr))
|
|
{
|
|
retval = IPDNSC_SERV_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
/* Save id and flags */
|
|
r_id = IP_SWAP16(*(Ip_u16 *)(r_buf));
|
|
r_flags = IP_SWAP16(*(Ip_u16 *)(r_buf+2));
|
|
*r_size = count;
|
|
|
|
/* Do some basic response checks */
|
|
if (r_id != s_id)
|
|
{
|
|
/* Identifiction don't match */
|
|
retval = IPDNSC_SERV_FAIL;
|
|
goto out;
|
|
}
|
|
if (!(r_flags & IPDNSC_QR_FLAG))
|
|
{
|
|
/* This was not a response */
|
|
retval = IPDNSC_SERV_FAIL;
|
|
goto out;
|
|
}
|
|
if ((r_flags & IPDNSC_OPCODE_FLAG) != (s_flags & IPDNSC_OPCODE_FLAG))
|
|
{
|
|
/* Opcode not copied into response */
|
|
retval = IPDNSC_SERV_FAIL;
|
|
goto out;
|
|
}
|
|
if (r_flags & IPDNSC_TC_FLAG)
|
|
{
|
|
/* Can't handle truncated messages */
|
|
retval = IPDNSC_SERV_FAIL;
|
|
goto out;
|
|
}
|
|
if ((r_flags & IPDNSC_RD_FLAG) != (s_flags & IPDNSC_RD_FLAG))
|
|
{
|
|
/* RD flag not copied into response */
|
|
retval = IPDNSC_SERV_FAIL;
|
|
goto out;
|
|
}
|
|
if (r_flags & IPDNSC_ZERO_FLAG)
|
|
{
|
|
/* This field must be zero */
|
|
retval = IPDNSC_SERV_FAIL;
|
|
goto out;
|
|
}
|
|
if (r_flags & IPDNSC_RCODE_FLAG)
|
|
{
|
|
/* Server returned error */
|
|
if ((r_flags & IPDNSC_RCODE_FLAG) == IPDNSC_RCODE_FORMERR)
|
|
retval = IPDNSC_SERV_FORMAT; /* Format error */
|
|
else if ((r_flags & IPDNSC_RCODE_FLAG) == IPDNSC_RCODE_SERVFAIL)
|
|
retval = IPDNSC_SERV_FAIL; /* Server failure */
|
|
else if ((r_flags & IPDNSC_RCODE_FLAG) == IPDNSC_RCODE_NXDOMAIN)
|
|
retval = IPDNSC_SERV_NONAME; /* Name Error */
|
|
else if ((r_flags&IPDNSC_RCODE_FLAG)==IPDNSC_RCODE_YXRRSET)
|
|
retval = IPDNSC_SERV_YXRRSET;
|
|
else if ((r_flags & IPDNSC_RCODE_FLAG) == IPDNSC_RCODE_NOTIMPL)
|
|
retval = IPDNSC_SERV_NOTIMPL; /* Not Implemented */
|
|
else if ((r_flags & IPDNSC_RCODE_FLAG) == IPDNSC_RCODE_REFUSED)
|
|
retval = IPDNSC_SERV_REFUSED; /* Refused */
|
|
else
|
|
retval = IPDNSC_SERV_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
if (read_set != IP_NULL)
|
|
ipcom_free(read_set);
|
|
ipcom_socketclose(sock);
|
|
return retval;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_get_ipv6_addrconfig
|
|
*===========================================================================
|
|
* Description: Parse an IPv6 address
|
|
* Parameters: addr6 - pointer to the returned IPv6 address.
|
|
* ifindex - pointer to the returned interface index.
|
|
* addrstr - IPv6 address to parse.
|
|
* Returns: 0 = ok.
|
|
* -1 = failed.
|
|
*/
|
|
#ifdef IPCOM_USE_INET6
|
|
IP_GLOBAL Ip_s32
|
|
ipdnsc_get_ipv6_addrconfig(struct Ip_in6_addr *addr6, Ip_u32 *ifindex, char *addrstr)
|
|
{
|
|
char *ifname;
|
|
|
|
/* Get the address */
|
|
if (ipcom_inet_pton(IP_AF_INET6, addrstr, addr6) != 1)
|
|
return -1;
|
|
|
|
/* Get the interface */
|
|
*ifindex = 0;
|
|
ifname = ipcom_strchr(addrstr, IPDNSC_IF_DELIMITER);
|
|
if (ifname != IP_NULL && *ifname != '\0')
|
|
{
|
|
ifname++;
|
|
if (ipcom_isdigit(*ifname))
|
|
{
|
|
*ifindex = (Ip_u32)ipcom_atoi(ifname);
|
|
}
|
|
else
|
|
{
|
|
*ifindex = ipcom_if_nametoindex(ifname);
|
|
if (*ifindex == 0)
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* IPCOM_USE_INET6 */
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_default
|
|
*===========================================================================
|
|
* Description: Get the default DNS server information.
|
|
* Parameters: dns - pointer to the DNS server structure.
|
|
* Returns: 0 = ok.
|
|
* -1 = failed.
|
|
*/
|
|
IP_GLOBAL Ip_s32
|
|
ipdnsc_default(Ipdnsc_ns_spec *dns)
|
|
{
|
|
Ip_u16 port_n, i;
|
|
char *ptr;
|
|
Ipdnsc_ns_addr *ns_addr;
|
|
|
|
ipcom_memset(dns, 0, sizeof(*dns));
|
|
|
|
/* Configure DNS server address/ports */
|
|
port_n = (Ip_u16)ipcom_sysvar_get_as_int ("ipdnsc.port", IPDNSC_DEFAULT_DNS_PORT);
|
|
port_n = ip_htons(port_n);
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
switch (i)
|
|
{
|
|
case 0:
|
|
ptr = ipcom_sysvar_get ("ipdnsc.primaryns", IP_NULL, IP_NULL);
|
|
ns_addr = &dns->primary_dns;
|
|
break;
|
|
case 1:
|
|
ptr = ipcom_sysvar_get ("ipdnsc.secondaryns", IP_NULL, IP_NULL);
|
|
ns_addr = &dns->secondary_dns;
|
|
break;
|
|
case 2:
|
|
ptr = ipcom_sysvar_get ("ipdnsc.tertiaryns", IP_NULL, IP_NULL);
|
|
ns_addr = &dns->tertiary_dns;
|
|
break;
|
|
case 3:
|
|
ptr = ipcom_sysvar_get ("ipdnsc.quaternaryns", IP_NULL, IP_NULL);
|
|
ns_addr = &dns->quaternary_dns;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
if (ptr != IP_NULL)
|
|
{
|
|
ns_addr->family = IP_AF_INET;
|
|
if (ipcom_inet_pton (ns_addr->family,
|
|
ptr,
|
|
&ns_addr->addr) != 1)
|
|
{
|
|
#ifdef IPCOM_USE_INET6
|
|
ns_addr->family = IP_AF_INET6;
|
|
if (ipdnsc_get_ipv6_addrconfig(&ns_addr->addr.inaddr6, &ns_addr->ifindex, ptr) < 0)
|
|
#endif
|
|
/* Bad address */
|
|
ns_addr->family = 0;
|
|
}
|
|
ipcom_free(ptr);
|
|
}
|
|
ns_addr->port = port_n;
|
|
}
|
|
|
|
/* Configure other DNS parameters */
|
|
dns->timeout = ipcom_sysvar_get_as_int ("ipdnsc.timeout", IPDNSC_DEFAULT_DNS_TIMEOUT);
|
|
dns->retry = ipcom_sysvar_get_as_int ("ipdnsc.retries", IPDNSC_DEFAULT_DNS_RETRY);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_resolve_name
|
|
*===========================================================================
|
|
* Description: Resolve a name.
|
|
* Parameters: name - name to resolve.
|
|
* family - address family.
|
|
* use_cache - check to cache before performing name lookup.
|
|
* dns - pointer to the DNS structure.
|
|
* error - type of error if lookup fails.
|
|
* Returns: pointer to the hostent structure or IP_NULL if failed.
|
|
*/
|
|
IP_GLOBAL struct Ip_hostent*
|
|
ipdnsc_resolve_name(Ip_u8 *name, Ip_s32 family, Ip_s32 use_cache, Ipdnsc_ns_spec *dns, Ip_s32 *error_num)
|
|
{
|
|
Ip_u16 qtype;
|
|
Ip_u32 ttl;
|
|
struct Ip_hostent *he;
|
|
Ip_u8 hostname[IPDNSC_MAXNAME];
|
|
char *domain;
|
|
Ip_size_t len;
|
|
Ip_bool initiated;
|
|
|
|
ipcom_mutex_lock(ipdnsc.mutex);
|
|
initiated = ipdnsc.init;
|
|
ipcom_mutex_unlock(ipdnsc.mutex);
|
|
if (initiated == IP_FALSE)
|
|
{
|
|
*error_num = IPDNSC_NO_RECOVERY;
|
|
he = IP_NULL;
|
|
goto done;
|
|
}
|
|
|
|
if (error_num == IP_NULL)
|
|
{
|
|
he = IP_NULL;
|
|
goto done;
|
|
}
|
|
|
|
if (dns == IP_NULL)
|
|
{
|
|
*error_num = IPDNSC_NO_RECOVERY;
|
|
he = IP_NULL;
|
|
goto done;
|
|
}
|
|
|
|
/* Check family */
|
|
switch(family)
|
|
{
|
|
case IP_AF_INET:
|
|
qtype = IPDNSC_QTYPE_A;
|
|
break;
|
|
|
|
case IP_AF_INET6:
|
|
qtype = IPDNSC_QTYPE_AAAA;
|
|
break;
|
|
|
|
default:
|
|
*error_num = IPDNSC_NO_RECOVERY;
|
|
he = IP_NULL;
|
|
goto done;
|
|
}
|
|
|
|
/* Copy name to local buf */
|
|
len = ipcom_strlen((char *)name);
|
|
if (len <= 0 || len > (IPDNSC_MAXNAME-1))
|
|
{
|
|
*error_num = IPDNSC_NO_RECOVERY;
|
|
he = IP_NULL;
|
|
goto done;
|
|
}
|
|
ipcom_memcpy(hostname, name, len);
|
|
hostname[len] = '\0';
|
|
|
|
/* Check for fully qualified domain name */
|
|
if (hostname[len-1] == '.')
|
|
{
|
|
/* Remove the last dot */
|
|
hostname[len-1] = '\0';
|
|
}
|
|
else if (ipcom_strcasecmp((char *)hostname, "localhost") != 0 && ipcom_strcasecmp((char *)hostname, "localhost6") != 0)
|
|
{
|
|
/* Append our domain if no dots */
|
|
if (!ipcom_strchr((char *)hostname, '.'))
|
|
{
|
|
domain = ipcom_sysvar_get("ipdnsc.domainname", IP_NULL, IP_NULL);
|
|
if (domain != IP_NULL && *domain != '\0')
|
|
{
|
|
hostname[len++] = '.';
|
|
if ((len + ipcom_strlen(domain)) > (IPDNSC_MAXNAME-2))
|
|
{
|
|
ipcom_free (domain);
|
|
*error_num = IPDNSC_NO_RECOVERY;
|
|
he = IP_NULL;
|
|
goto done;
|
|
}
|
|
ipcom_strcpy((char *)&hostname[len], domain);
|
|
hostname[len + ipcom_strlen(domain)] = '\0';
|
|
ipcom_free (domain);
|
|
}
|
|
else
|
|
{
|
|
if (domain != IP_NULL)
|
|
ipcom_free (domain);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Check the cache */
|
|
if (use_cache)
|
|
{
|
|
ipcom_mutex_lock(ipdnsc.mutex);
|
|
he = ipdnsc_cache_find_name(hostname, family);
|
|
ipcom_mutex_unlock(ipdnsc.mutex);
|
|
if (he != IP_NULL)
|
|
{
|
|
*error_num = IPDNSC_OK;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* Do the query */
|
|
he = ipdnsc_do_query(hostname, qtype, family, dns, error_num, &ttl);
|
|
if (he == IP_NULL)
|
|
goto done;
|
|
|
|
/* Add the host entry to the cache */
|
|
if (use_cache)
|
|
{
|
|
ipcom_mutex_lock(ipdnsc.mutex);
|
|
(void)ipdnsc_cache_add(he, IPDNSC_CACHE_NAME, ttl);
|
|
ipcom_mutex_unlock(ipdnsc.mutex);
|
|
}
|
|
|
|
done:
|
|
return he;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_resolve_addr
|
|
*===========================================================================
|
|
* Description: Resolve an address.
|
|
* Parameters: addr - pointer to the address.
|
|
* family - address family.
|
|
* use_cache - check to cache before performing name lookup.
|
|
* dns - pointer to the DNS structure.
|
|
* error - type of error if lookup fails.
|
|
* Returns: pointer to the hostent structure or IP_NULL if failed.
|
|
*/
|
|
IP_GLOBAL struct Ip_hostent*
|
|
ipdnsc_resolve_addr(Ip_u8 *addr, Ip_s32 family, Ip_s32 use_cache, Ipdnsc_ns_spec *dns, Ip_s32 *error_num)
|
|
{
|
|
struct Ip_hostent *he;
|
|
Ip_u8 ptr_name[128];
|
|
Ip_u32 ttl;
|
|
Ip_bool initiated;
|
|
Ip_u16 qtype;
|
|
|
|
ipcom_mutex_lock(ipdnsc.mutex);
|
|
initiated = ipdnsc.init;
|
|
ipcom_mutex_unlock(ipdnsc.mutex);
|
|
if (initiated == IP_FALSE)
|
|
{
|
|
*error_num = IPDNSC_NO_RECOVERY;
|
|
he = IP_NULL;
|
|
goto done;
|
|
}
|
|
|
|
if (error_num == IP_NULL)
|
|
{
|
|
he = IP_NULL;
|
|
goto done;
|
|
}
|
|
|
|
if (dns == IP_NULL)
|
|
{
|
|
*error_num = IPDNSC_NO_RECOVERY;
|
|
he = IP_NULL;
|
|
goto done;
|
|
}
|
|
|
|
/* Check family */
|
|
switch(family)
|
|
{
|
|
case IP_AF_INET:
|
|
case IP_AF_INET6:
|
|
qtype = IPDNSC_QTYPE_PTR;
|
|
break;
|
|
|
|
default:
|
|
*error_num = IPDNSC_NO_RECOVERY;
|
|
he = IP_NULL;
|
|
goto done;
|
|
}
|
|
|
|
/* Check the cache */
|
|
if (use_cache)
|
|
{
|
|
ipcom_mutex_lock(ipdnsc.mutex);
|
|
he = ipdnsc_cache_find_addr(addr, family);
|
|
ipcom_mutex_unlock(ipdnsc.mutex);
|
|
if (he != IP_NULL)
|
|
{
|
|
*error_num = IPDNSC_OK;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* Convert address to special domain name */
|
|
if (ipdnsc_ptr_name(ptr_name, sizeof(ptr_name), addr, family))
|
|
{
|
|
*error_num = IPDNSC_NO_RECOVERY;
|
|
he = IP_NULL;
|
|
goto done;
|
|
}
|
|
|
|
/* Do the query */
|
|
he = ipdnsc_do_query(ptr_name, qtype, family, dns, error_num, &ttl);
|
|
if (he == IP_NULL)
|
|
goto done;
|
|
|
|
/* Insert the host address in the host address structure */
|
|
if (ipdnsc_hostent_insert_addr(he, (char *)addr))
|
|
{
|
|
ipdnsc_hostent_free(he);
|
|
*error_num = IPDNSC_NO_RECOVERY;
|
|
he = IP_NULL;
|
|
goto done;
|
|
}
|
|
|
|
/* Add the host entry to the cache */
|
|
if (use_cache)
|
|
{
|
|
ipcom_mutex_lock(ipdnsc.mutex);
|
|
(void)ipdnsc_cache_add(he, IPDNSC_CACHE_ADDR, ttl);
|
|
ipcom_mutex_unlock(ipdnsc.mutex);
|
|
}
|
|
|
|
done:
|
|
return he;
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 11 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_cache_flush
|
|
*===========================================================================
|
|
* Description: See ipdnsc.h for description.
|
|
* Parameters:
|
|
* Returns:
|
|
*/
|
|
IP_PUBLIC void
|
|
ipdnsc_cache_flush(void)
|
|
{
|
|
ipcom_mutex_lock(ipdnsc.mutex);
|
|
while (ipdnsc.cache_head != IP_NULL)
|
|
{
|
|
(void)ipdnsc_cache_remove(ipdnsc.cache_head);
|
|
ipdnsc.cache_entries--;
|
|
}
|
|
ip_assert(ipdnsc.cache_entries == 0);
|
|
ipcom_mutex_unlock(ipdnsc.mutex);
|
|
(void)ipdnsc_cache_known_hosts();
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_create
|
|
*===========================================================================
|
|
* Description: DNS client create function.
|
|
* Parameters: None.
|
|
* Returns: IPCOM_SUCCESS
|
|
*/
|
|
IP_PUBLIC Ip_err
|
|
ipdnsc_create(void)
|
|
{
|
|
ipcom_memset(&ipdnsc, 0, sizeof(ipdnsc));
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_start
|
|
*===========================================================================
|
|
* Description: DNS client start function.
|
|
* Parameters: None.
|
|
* Returns: IPCOM_SUCCESS or IPCOM_ERR_XXX error code.
|
|
*/
|
|
IP_PUBLIC Ip_err
|
|
ipdnsc_start(void)
|
|
{
|
|
Ip_err retval;
|
|
|
|
if (ipcom_mutex_create(&ipdnsc.mutex) != IPCOM_SUCCESS)
|
|
return IPCOM_ERR_NO_MEMORY;
|
|
|
|
/* Initialise DNS variables */
|
|
ipcom_mutex_lock(ipdnsc.mutex);
|
|
ipdnsc.cache_head = IP_NULL;
|
|
ipcom_mutex_unlock(ipdnsc.mutex);
|
|
|
|
retval = ipdnsc_cache_known_hosts();
|
|
if (retval != IPCOM_SUCCESS)
|
|
{
|
|
(void)ipcom_mutex_delete(&ipdnsc.mutex);
|
|
return retval;
|
|
}
|
|
|
|
ipcom_mutex_lock(ipdnsc.mutex);
|
|
ipdnsc.init = IP_TRUE;
|
|
|
|
ipcom_dns_init_ok = 1;
|
|
ipcom_dns.getipnodebyname = ipdnsc_getipnodebyname;
|
|
ipcom_dns.getipnodebyaddr = ipdnsc_getipnodebyaddr;
|
|
ipcom_dns.freehostent = ipdnsc_freehostent;
|
|
ipcom_dns.cache_flush = ipdnsc_cache_flush;
|
|
|
|
ipcom_mutex_unlock(ipdnsc.mutex);
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipdnsc_version
|
|
*===========================================================================
|
|
* Description: Get DNS client version string.
|
|
* Parameters: None.
|
|
* Returns: Pointer to the version string.
|
|
*/
|
|
IP_PUBLIC const char *
|
|
ipdnsc_version(void)
|
|
{
|
|
return "@(#) IPDNSC $Name: VXWORKS_ITER40_2019102207 $ - INTERPEAK_COPYRIGHT_STRING";
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|
|
|