Files
m241Neelima/components/ip_net2-6.9/ipappl/src/ipdnsc_update.c
T
2026-07-30 11:19:44 +05:30

4075 lines
113 KiB
C

/*
******************************************************************************
* SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipdnsc_update.c,v $ $Revision: 1.7.10.7 $
* $Source: /home/interpeak/CVSRoot/ipappl/src/ipdnsc_update.c,v $
* $Author: jren $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Markus Carlstedt <markus@interpeak.se>
******************************************************************************
*/
/*
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1996-1999 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* \file
* \brief
* Based on the Dynamic DNS reference implementation by Viraj Bais
* &lt;viraj_bais@ccm.fm.intel.com>
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
* DNS client update source file.
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#include "ipdnsc_config.h"
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#define IPCOM_USE_CLIB_PROTO
#include <ipcom_clib.h>
#include <ipcom_sysvar.h>
#ifdef OPENSSL_FIPS
#include <openssl/fips.h>
#endif
#ifdef IPDNSC_INCLUDE_MD5
#ifdef IPMCRYPTO
#include <ip/openssl/md5.h>
#else
#include <openssl/md5.h>
#endif
#endif
#include "ipdnsc.h"
#include "ipdnsc_h.h"
/*
****************************************************************************
* 4 DEFINES
****************************************************************************
*/
#define IPDNSC_RR_NS_HAVE_V4 0x01
#define IPDNSC_RR_NS_HAVE_V6 0x02
#define IPDNSC_CMPRSFLGS 0xc0 /* Flag bits indicating name compression. */
#define IPDNSC_TYPE_ELT 0x40 /* EDNS0 extended label type */
#define IPDNSC_LABELTYPE_BITSTRING 0x41
/* DST algorithm codes */
#define IPDNSC_ALG_HMAC_MD5 157
#define IPDNSC_KEY_TYPE_AUTH_ONLY 0x4000 /* Key usable for authentication */
#define IPDNSC_KEY_PROT_ANY 255
#define IPDNSC_TSIG_FUDGE 300
/* These are used used in functions dst_sign_data and dst_verify_data */
#define IPDNSC_SIG_MODE_INIT 1 /* initialize digest */
#define IPDNSC_SIG_MODE_UPDATE 2 /* add data to digest */
#define IPDNSC_SIG_MODE_FINAL 4 /* generate/verify signature */
#define IPDNSC_SIG_MODE_ALL (IPDNSC_SIG_MODE_INIT|IPDNSC_SIG_MODE_UPDATE|IPDNSC_SIG_MODE_FINAL)
/* error codes from dst routines */
#define IPDNSC_SIGN_INIT_FAILURE (-23)
#define IPDNSC_SIGN_UPDATE_FAILURE (-24)
#define IPDNSC_SIGN_FINAL_FAILURE (-25)
#define IPDNSC_VERIFY_INIT_FAILURE (-26)
#define IPDNSC_VERIFY_UPDATE_FAILURE (-27)
#define IPDNSC_VERIFY_FINAL_FAILURE (-28)
#define IPDNSC_MISSING_KEY_OR_SIGNATURE (-30)
#define IPDNSC_UNSUPPORTED_KEYALG (-31)
#define ShrinkBuf(buflen, len) if ((buflen = buflen - (len)) < 0) return (-2);
/*
****************************************************************************
* 5 TYPES
****************************************************************************
*/
#ifdef IPDNSC_INCLUDE_MD5
#define IPDNSC_HMAC_LEN 64
#define IPDNSC_HMAC_IPAD 0x36
#define IPDNSC_HMAC_OPAD 0x5c
typedef struct Ipdnsc_hmac_key_struct{
Ip_u8 hk_ipad[IPDNSC_HMAC_LEN];
Ip_u8 hk_opad[IPDNSC_HMAC_LEN];
}
Ipdnsc_hmac_key;
typedef struct Ipdnsc_dst_func_struct {
Ip_s32 (*sign)(const Ip_s32 mode, void *in_key, void **context, const Ip_u8 *data, const Ip_s32 len, Ip_u8 *signature, const Ip_s32 sig_len);
Ip_s32 (*verify)(const Ip_s32 mode, void *in_key, void **context, const Ip_u8 *data, const Ip_s32 len, const Ip_u8 *signature, const Ip_s32 sig_len);
void *(*destroy)(void *in_key);
/* conversion functions */
Ip_s32 (*to_dns_key)(const void *in_key, Ip_u8 *out, const Ip_s32 out_len);
Ip_s32 (*from_dns_key)(void *in_key, const Ip_u8 *str, const Ip_s32 str_len);
}
Ipdnsc_dst_func;
typedef struct Ipdnsc_dst_key_struct {
char *dk_key_name; /* name of the key */
Ip_s32 dk_key_size; /* this is the size of the key in bits */
Ip_s32 dk_proto; /* what protocols this key can be used for */
Ip_s32 dk_alg; /* algorithm number from key record */
Ip_u32 dk_flags; /* and the flags of the public key */
Ip_u16 dk_id; /* identifier of the key */
void *dk_KEY_struct; /* pointer to key in crypto pkg fmt */
Ipdnsc_dst_func *dk_func; /* point to crypto specific function table */
}
Ipdnsc_dst_key;
#endif
/*
* General DNS message structure. It is caller allocated and has no dynamic data.
* It is mainly used in parsing DNS response packet.It includes the general info
* for the response from the server.
*/
typedef struct Ipdnsc_general_msg_struct {
const Ip_u8 *begin; /* the beginning of response data */
const Ip_u8 *end; /* the end of response data */
Ip_u16 id; /* identification */
Ip_u16 flags; /* response flag */
Ip_u16 counts[IPDNSC_SECTION_MAX]; /* the number of RRs in each section */
const Ip_u8 *sections[IPDNSC_SECTION_MAX]; /* point the begin of data in the packet for each section */
Ipdnsc_section current_section; /* current section in parsed */
Ip_s32 current_rrnum; /* current number of record in parsed */
const Ip_u8 *msg_ptr; /* point to current record to be parsed now */
}
Ipdnsc_general_msg;
/* This is a parsed record. It is caller allocated and has no dynamic data. */
typedef struct Ipdnsc_record_struct {
char name[IPDNSC_MAXDNAME];
Ip_u16 type;
Ip_u16 rr_class;
Ip_u32 ttl;
Ip_u16 rdlength;
const Ip_u8 *rdata;
}
Ipdnsc_record;
/*
****************************************************************************
* 6 EXTERN PROTOTYPES
****************************************************************************
*/
IP_EXTERN const char *ipdnsc_update_rmsg[IPDNSC_RCODE_MAX + 1];
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
#ifdef IPDNSC_INCLUDE_MD5
IP_STATIC Ip_s32 ipdnsc_dst_hmac_md5_sign(const Ip_s32 mode, void *in_key, void **context, const Ip_u8 *data,
const Ip_s32 len, Ip_u8 *signature, const Ip_s32 sig_len);
IP_STATIC Ip_s32 ipdnsc_dst_hmac_md5_verify(const Ip_s32 mode, void *in_key, void **context, const Ip_u8 *data,
const Ip_s32 len, const Ip_u8 *signature, const Ip_s32 sig_len);
IP_STATIC void *ipdnsc_dst_hmac_md5_free_key_structure(void *in_key);
IP_STATIC Ip_s32 ipdnsc_dst_hmac_md5_to_dns_key(const void *in_key, Ip_u8 *out_str, const Ip_s32 out_len);
IP_STATIC Ip_s32 ipdnsc_dst_buffer_to_hmac_md5(void *in_key, const Ip_u8 *key, const Ip_s32 keylen);
#endif
IP_GLOBAL Ip_s32 ipdnsc_samename(const char *a, const char *b);
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
IP_STATIC const char ipdnsc_digits[] = "0123456789";
IP_STATIC const Ip_u8 ipdnsc_digitvalue[256] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*16*/
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*32*/
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*48*/
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*64*/
0xFF, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*80*/
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*96*/
0xFF, 10, 11, 12, 13, 14, 15, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*112*/
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*128*/
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /*256*/
};
#ifdef IPDNSC_INCLUDE_MD5
IP_STATIC const char ipdnsc_base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
IP_STATIC const char ipdnsc_pad64 = '=';
IP_STATIC Ipdnsc_dst_func ipdnsc_dst_func_table[2]={
{
ipdnsc_dst_hmac_md5_sign,
ipdnsc_dst_hmac_md5_verify,
ipdnsc_dst_hmac_md5_free_key_structure,
ipdnsc_dst_hmac_md5_to_dns_key,
ipdnsc_dst_buffer_to_hmac_md5
},
{
IP_NULL,
IP_NULL,
IP_NULL,
IP_NULL,
IP_NULL
}
};
#endif
/*
****************************************************************************
* 9 LOCAL FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipdnsc_makecanon
*===========================================================================
* Description: make a canonical copy of domain name "src"
* Parameters:
* Returns: 0(ok) or -1(fail).
* notes:
* \code
* foo -> foo.
* foo. -> foo.
* foo.. -> foo.
* foo\. -> foo\..
* foo\\. -> foo\\.
* \endcode
*/
IP_STATIC Ip_s32
ipdnsc_makecanon(const char *src, char *dst, Ip_size_t dstsize)
{
Ip_size_t n = ipcom_strlen(src);
if (n + sizeof(".") > dstsize)
{ /* Note: sizeof == 2 */
return (-1);
}
ipcom_strcpy(dst, src);
while (n >= 1U && dst[n - 1] == '.') /* Ends in "." */
if (n >= 2U && dst[n - 2] == '\\' && /* Ends in "\." */
(n < 3U || dst[n - 3] != '\\')) /* But not "\\." */
break;
else
dst[--n] = '\0';
dst[n++] = '.';
dst[n] = '\0';
return (0);
}
/*
*===========================================================================
* ipdnsc_labellen
*===========================================================================
* Description:
* Parameters:
* Returns: -1(fail) or 1(ok)
*/
IP_STATIC Ip_s32
ipdnsc_labellen(const Ip_u8 *lp)
{
Ip_s32 bitlen;
Ip_u8 l = *lp;
if ((l & IPDNSC_CMPRSFLGS) == IPDNSC_CMPRSFLGS)
/* should be avoided by the caller */
return(-1);
if ((l & IPDNSC_CMPRSFLGS) == IPDNSC_TYPE_ELT)
{
if (l == IPDNSC_LABELTYPE_BITSTRING)
{
if ((bitlen = *(lp + 1)) == 0)
bitlen = 256;
return((bitlen + 7 ) / 8 + 1);
}
return(-1); /* unknwon ELT */
}
return (l);
}
/*
*===========================================================================
* ipdnsc_special
*===========================================================================
* Description: Thinking in noninternationalized USASCII (per the DNS spec),
* is this characted special ("in need of quoting") ?
* Parameters:
* Returns: boolean
*/
IP_STATIC Ip_s32
ipdnsc_special(Ip_s32 ch)
{
switch (ch) {
case 0x22: /* '"' */
case 0x2E: /* '.' */
case 0x3B: /* ';' */
case 0x5C: /* '\\' */
case 0x28: /* '(' */
case 0x29: /* ')' */
/* Special modifiers in zone files. */
case 0x40: /* '@' */
case 0x24: /* '$' */
return (1);
default:
return (0);
}
}
/*
*===========================================================================
* ipdnsc_mklower
*===========================================================================
* Description: Thinking in noninternationalized USASCII (per the DNS spec),
* convert this character to lower case if it's upper case.
* Parameters:
* Returns: boolean
*/
IP_STATIC Ip_s32
ipdnsc_mklower(Ip_s32 ch)
{
if (ch >= 0x41 && ch <= 0x5A)
return (ch + 0x20);
return (ch);
}
/*
*===========================================================================
* ipdnsc_name_skip
*===========================================================================
* Description: Advance *ptrptr to skip over the compressed name it points at.
* Parameters:
* Returns: 0 on success, -1 on failure.
*/
IP_STATIC Ip_s32
ipdnsc_name_skip(const Ip_u8 **ptrptr, const Ip_u8 *eom)
{
const Ip_u8 *cp;
Ip_u32 n;
Ip_s32 l;
cp = *ptrptr;
while (cp < eom && (n = *cp++) != 0)
{
/* Check for indirection. */
switch (n & IPDNSC_CMPRSFLGS)
{
case 0: /* normal case, n == len */
cp += n;
continue;
case IPDNSC_TYPE_ELT: /* EDNS0 extended label */
if ((l = ipdnsc_labellen(cp - 1)) < 0)
return(-1);
cp += l;
continue;
case IPDNSC_CMPRSFLGS: /* indirection */
cp++;
break;
default: /* illegal type */
return (-1);
}
break;
}
if (cp > eom)
return (-1);
*ptrptr = cp;
return (0);
}
/*
*===========================================================================
* ipdnsc_skipname
*===========================================================================
* Description: Skip over a compressed domain name.
* Parameters:
* Returns: offset on success, -1 on failure.
*/
IP_STATIC Ip_s32
ipdnsc_skipname(const Ip_u8 *ptr, const Ip_u8 *eom)
{
const Ip_u8 *saveptr = ptr;
if (ipdnsc_name_skip(&ptr, eom) == -1)
return (-1);
return (Ip_s32)(ptr - saveptr);
}
/*
*===========================================================================
* ipdnsc_skiprr
*===========================================================================
* Description: Skip over a compressed domain name.
* Parameters:
* Returns: offset on success, -1 on failure.
*/
IP_STATIC Ip_s32
ipdnsc_skiprr(const Ip_u8 *ptr, const Ip_u8 *eom, Ipdnsc_section section, Ip_s32 count)
{
const Ip_u8 *optr = ptr;
Ip_s32 ret;
for (; count > 0; count--)
{
Ip_s32 rdlength;
ret = ipdnsc_skipname(ptr, eom);
if (ret < 0)
return -1;
/* Name + Type + Class */
ptr += ret + 2 + 2;
if (section != IPDNSC_SECTION_QD)
{
if (ptr + 4 + 2 > eom)
return -1;
ptr += 4; /*TTL*/
rdlength = IP_GET_HTONS(ptr);
ptr += 2;
ptr += rdlength; /*RData*/
}
}
if (ptr > eom)
return -1;
return (Ip_s32)(ptr - optr);
}
/*
*===========================================================================
* ipdnsc_getstr_str
*===========================================================================
* Description: get a white spae delimited string from memory. Process quoted strings
* and escapes
* Parameters:
* Returns: length or -1 on error. Returned string may contain nulls.
*/
IP_STATIC Ip_s32
ipdnsc_getstr_str(char *buf, Ip_s32 size, Ip_u8 **startpp, Ip_u8 *endp)
{
char *cp;
Ip_s32 c, c1 = 0;
Ip_s32 inquote = 0;
Ip_s32 seen_quote = 0;
Ip_s32 escape = 0;
Ip_s32 dig = 0;
for (cp = buf; *startpp <= endp; ) {
if ((c = **startpp) == '\0')
break;
/* leading white space */
if ((cp == buf) && !seen_quote && ipcom_isspace(c)) {
(*startpp)++;
continue;
}
switch (c) {
case '\\':
if (!escape) {
escape = 1;
dig = 0;
c1 = 0;
(*startpp)++;
continue;
}
goto do_escape;
case '"':
if (!escape) {
inquote = !inquote;
seen_quote = 1;
(*startpp)++;
continue;
}
/* fall through */
default:
do_escape:
if (escape) {
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
c1 = c1 * 10 + (Ip_s32)(ipcom_strchr(ipdnsc_digits, c) - ipdnsc_digits);
if (++dig == 3) {
c = c1 &0xff;
break;
}
(*startpp)++;
continue;
}
escape = 0;
} else if (!inquote && ipcom_isspace(c))
goto done;
if (cp >= buf+size-1)
goto done;
*cp++ = (Ip_s8)c;
(*startpp)++;
}
}
done:
*cp = '\0';
return (Ip_s32)((cp == buf)? (seen_quote? 0: -1): (cp - buf));
}
/*
*===========================================================================
* ipdnsc_getword_str
*===========================================================================
* Description: Get a whitespace delimited word from a string (not file)
* into buf. modify the start pointer to point after the word in the string.
* Parameters:
* Returns:
*/
IP_STATIC Ip_s32
ipdnsc_getword_str(char *buf, Ip_u32 size, Ip_u8 **startpp, Ip_u8 *endp)
{
char *cp;
Ip_s32 c;
for (cp = buf; *startpp <= endp; )
{
c = **startpp;
if (ipcom_isspace(c) || c == '\0')
{
if (cp != buf) /* trailing whitespace */
break;
else
{ /* leading whitespace */
(*startpp)++;
continue;
}
}
(*startpp)++;
if (cp >= buf+size-1)
break;
*cp++ = (Ip_u8)c;
}
*cp = '\0';
return (cp != buf);
}
/*
*===========================================================================
* ipdnsc_getnum_str
*===========================================================================
* Description: Get a whitespace delimited base 10 number from a string (not file) into buf
* update the start pointer to point after the number in the string.
* Parameters:
* Returns:
*/
IP_STATIC Ip_s32
ipdnsc_getnum_str(Ip_u8 **startpp, Ip_u8 *endp)
{
Ip_s32 c, n, m=0;
Ip_s32 seendigit = 0;
for (n = 0; *startpp <= endp; )
{
c = **startpp;
if (ipcom_isspace(c) || c == '\0')
{
if (seendigit) /* trailing whitespace */
break;
else
{ /* leading whitespace */
(*startpp)++;
continue;
}
}
if (c == ';')
{
while ((*startpp <= endp) && ((c = **startpp) != '\n'))
(*startpp)++;
if (seendigit)
break;
continue;
}
if (!ipcom_isdigit(c))
{
if (c == ')' && seendigit)
{
(*startpp)--;
break;
}
return (-1);
}
(*startpp)++;
n = n * 10 + (c - '0');
seendigit = 1;
}
return (n + m);
}
/*
*===========================================================================
* ipdnsc_dn_find
*===========================================================================
* Description: Search for the counted-label name in an array of compressed names.
* Parameters:
* Returns: offset from msg if found, or -1.
* notes: dnptrs is the pointer to the first name on the list,
* not the pointer to the start of the message.
*/
IP_STATIC Ip_s32
ipdnsc_dn_find(const Ip_u8 *domain, const Ip_u8 *msg, const Ip_u8 * const *dnptrs, const Ip_u8 * const *lastdnptr, Ip_s32 *error)
{
const Ip_u8 *dn, *cp, *sp;
const Ip_u8 * const *cpp;
Ip_u32 n;
for (cpp = dnptrs; cpp < lastdnptr; cpp++) {
sp = *cpp;
/*
* terminate search on:
* root label
* compression pointer
* unusable offset
*/
while (*sp != 0 && (*sp & IPDNSC_CMPRSFLGS) == 0 && (sp - msg) < 0x4000) {
dn = domain;
cp = sp;
while ((n = *cp++) != 0) {
/*
* check for indirection
*/
switch (n & IPDNSC_CMPRSFLGS) {
case 0: /* normal case, n == len */
n = ipdnsc_labellen(cp - 1); /* XXX */
if (n != *dn++)
goto next;
for (; n > 0; n--)
if (ipdnsc_mklower(*dn++) != ipdnsc_mklower(*cp++))
goto next;
/* Is next root for both ? */
if (*dn == '\0' && *cp == '\0')
return (Ip_s32)(sp - msg);
if (*dn)
continue;
goto next;
case IPDNSC_CMPRSFLGS: /* indirection */
cp = msg + (((n & 0x3f) << 8) | *cp);
break;
default: /* illegal type */
*error = IPDNSC_UPDATE_ERR_MSGSIZE;
return (-1);
}
}
next: ;
sp += *sp + 1;
}
}
*error = IPDNSC_UPDATE_ERR_INVALID;
return (-1);
}
/*
*===========================================================================
* ipdnsc_encode_bitsring
*===========================================================================
* Description:
* Parameters:
* Returns:
*/
IP_STATIC Ip_s32
ipdnsc_encode_bitsring(const char **bp, const char *end, Ip_u8 **labelp,Ip_u8 ** dst, const Ip_u8 *eom, Ip_s32 *error)
{
const char *cp = *bp;
const char *beg_blen;
Ip_u8 *tp;
char c, *end_blen = IP_NULL;
Ip_s32 value = 0, count = 0, tbcount = 0, blen = 0, afterslash = 0;
beg_blen = end_blen = IP_NULL;
/* a bitstring must contain at least 2 characters */
if (end - cp < 2)
goto error_EINVAL;
/* XXX: currently, only hex strings are supported */
if (*cp++ != 'x')
goto error_EINVAL;
if (!ipcom_isxdigit((*cp) & 0xff)) /* reject '\[x/BLEN]' */
goto error_EINVAL;
for (tp = *dst + 1; cp < end && tp < eom; cp++) {
switch((c = *cp)) {
case ']': /* end of the bitstring */
if (afterslash) {
if (beg_blen == IP_NULL)
goto error_EINVAL;
blen = (Ip_s32)ipcom_strtol(beg_blen, &end_blen, 10);
if (*end_blen != ']')
goto error_EINVAL;
}
if (count)
*tp++ = (Ip_u8)((value << 4) & 0xff);
cp++; /* skip ']' */
goto done;
case '/':
afterslash = 1;
break;
default:
if (afterslash) {
if (!ipcom_isdigit(c&0xff))
goto error_EINVAL;
if (beg_blen == IP_NULL) {
if (c == '0') {
/* blen never begings with 0 */
goto error_EINVAL;
}
beg_blen = cp;
}
} else {
if (!ipcom_isxdigit(c&0xff))
goto error_EINVAL;
value <<= 4;
value += ipdnsc_digitvalue[(Ip_s32)c];
count += 4;
tbcount += 4;
if (tbcount > 256)
goto error_EINVAL;
if (count == 8) {
*tp++ = (Ip_u8)value;
count = 0;
}
}
break;
}
}
done:
if (cp >= end || tp >= eom)
goto error_EMSGSIZE;
/*
* bit length validation:
* If a <length> is present, the number of digits in the <bit-data>
* MUST be just sufficient to contain the number of bits specified
* by the <length>. If there are insignificant bits in a final
* hexadecimal or octal digit, they MUST be zero.
* RFC2673, Section 3.2.
*/
if (blen > 0) {
Ip_s32 traillen;
if (((blen + 3) & ~3) != tbcount)
goto error_EINVAL;
traillen = tbcount - blen; /* between 0 and 3 */
if (((value << (8 - traillen)) & 0xff) != 0)
goto error_EINVAL;
}
else
blen = tbcount;
if (blen == 256)
blen = 0;
/* encode the type and the significant bit fields */
**labelp = IPDNSC_LABELTYPE_BITSTRING;
**dst = (Ip_u8)blen;
*bp = cp;
*dst = tp;
return (0);
error_EINVAL:
*error = IPDNSC_UPDATE_ERR_INVALID;
return (-1);
error_EMSGSIZE:
*error = IPDNSC_UPDATE_ERR_MSGSIZE;
return (-1);
}
/*
*===========================================================================
* ipdnsc_decode_bitstring
*===========================================================================
* Description:
* Parameters:
* Returns: offset or -1 on fail
*/
IP_STATIC Ip_s32
ipdnsc_decode_bitstring(const Ip_u8 **cpp, char *dn, const char *eom)
{
const Ip_u8 *cp = *cpp;
char *beg = dn, tc;
Ip_s32 b, blen, plen, i;
if ((blen = (*cp & 0xff)) == 0)
blen = 256;
plen = (blen + 3) / 4;
plen += (Ip_s32)(sizeof("\\[x/]") + (blen > 99 ? 3 : (blen > 9) ? 2 : 1));
if (dn + plen >= eom)
return(-1);
cp++;
i = ipcom_sprintf(dn, "\\[x");
if (i < 0)
return (-1);
dn += i;
for (b = blen; b > 7; b -= 8, cp++)
{
i = ipcom_sprintf(dn, "%02x", *cp & 0xff);
if (i < 0)
return (-1);
dn += i;
}
if (b > 4)
{
tc = *cp++;
i = ipcom_sprintf(dn, "%02x", tc & (0xff << (8 - b)));
if (i < 0)
return (-1);
dn += i;
}
else if (b > 0)
{
tc = *cp++;
i = ipcom_sprintf(dn, "%1x", ((tc >> 4) & 0x0f) & (0x0f << (4 - b)));
if (i < 0)
return (-1);
dn += i;
}
i = ipcom_sprintf(dn, "/%d]", blen);
if (i < 0)
return (-1);
dn += i;
*cpp = cp;
return (Ip_s32)(dn - beg);
}
/*
*===========================================================================
* ipdnsc_samedomain
*===========================================================================
* Description: Check whether a name belongs to a domain.
* Parameters: a - the domain whose ancestory is being verified
* b - the potential ancestor we're checking against
* Returns: boolean - is a at or below b
* Notes:
* Trailing dots are first removed from name and domain.
* Always compare complete subdomains, not only whether the
* domain name is the trailing string of the given name.
*
* "host.foobar.top" lies in "foobar.top" and in "top" and in ""
* but NOT in "bar.top"
*/
IP_STATIC Ip_s32
ipdnsc_samedomain(const char *a, const char *b)
{
Ip_size_t la, lb;
Ip_s32 diff, i, escaped;
const char *cp;
la = ipcom_strlen(a);
lb = ipcom_strlen(b);
/* Ignore a trailing label separator (i.e. an unescaped dot) in 'a'. */
if (la != 0U && a[la - 1] == '.')
{
escaped = 0;
/* Note this loop doesn't get executed if la==1. */
for (i = (Ip_s32)(la - 2); i >= 0; i--)
if (a[i] == '\\')
{
if (escaped)
escaped = 0;
else
escaped = 1;
}
else
break;
if (!escaped)
la--;
}
/* Ignore a trailing label separator (i.e. an unescaped dot) in 'b'. */
if (lb != 0U && b[lb - 1] == '.')
{
escaped = 0;
/* note this loop doesn't get executed if lb==1 */
for (i = (Ip_s32)(lb - 2); i >= 0; i--)
if (b[i] == '\\') {
if (escaped)
escaped = 0;
else
escaped = 1;
} else
break;
if (!escaped)
lb--;
}
/* lb == 0 means 'b' is the root domain, so 'a' must be in 'b'. */
if (lb == 0U)
return (1);
/* 'b' longer than 'a' means 'a' can't be in 'b'. */
if (lb > la)
return (0);
/* 'a' and 'b' being equal at this point indicates sameness. */
if (lb == la)
return (ipcom_strncasecmp(a, b, lb) == 0);
/* Ok, we know la > lb. */
diff = (Ip_s32)(la - lb);
/*
* If 'a' is only 1 character longer than 'b', then it can't be
* a subdomain of 'b' (because of the need for the '.' label
* separator).
*/
if (diff < 2)
return (0);
/*
* If the character before the last 'lb' characters of 'b'
* isn't '.', then it can't be a match (this lets us avoid
* having "foobar.com" match "bar.com").
*/
if (a[diff - 1] != '.')
return (0);
/*
* We're not sure about that '.', however. It could be escaped
* and thus not a really a label separator.
*/
escaped = 0;
for (i = diff - 2; i >= 0; i--)
if (a[i] == '\\') {
if (escaped)
escaped = 0;
else
escaped = 1;
} else
break;
if (escaped)
return (0);
/* Now compare aligned trailing substring. */
cp = a + diff;
return (ipcom_strncasecmp(cp, b, lb) == 0);
}
#ifdef IPDNSC_INCLUDE_MD5
/*
*===========================================================================
* ipdnsc_base64_decodestring
*===========================================================================
* skips all whitespace anywhere.
converts characters, four at a time, starting at (or after)
src from base - 64 numbers into three 8 bit bytes in the target area.
it returns the number of data bytes stored at the target, or -1 on error.
*/
IP_STATIC Ip_s32
ipdnsc_base64_decodestring(Ip_u8 const *src, Ip_u8 *target, Ip_size_t targsize)
{
Ip_s32 tarindex, state, ch;
char *pos;
state = 0;
tarindex = 0;
while ((ch = *src++) != '\0') {
if (ipcom_isspace(ch)) /* Skip whitespace anywhere. */
continue;
if (ch == ipdnsc_pad64)
break;
pos = ipcom_strchr(ipdnsc_base64, ch);
if (pos == 0) /* A non-base64 character. */
return (-1);
switch (state) {
case 0:
if (target) {
if ((Ip_size_t)tarindex >= targsize)
return (-1);
target[tarindex] = (Ip_u8)((pos - ipdnsc_base64) << 2);
}
state = 1;
break;
case 1:
if (target) {
if ((Ip_size_t)tarindex + 1 >= targsize)
return (-1);
target[tarindex] |= (Ip_u8)((pos - ipdnsc_base64) >> 4);
target[tarindex+1] = (Ip_u8)(((pos - ipdnsc_base64) & 0x0f)
<< 4 );
}
tarindex++;
state = 2;
break;
case 2:
if (target) {
if ((Ip_size_t)tarindex + 1 >= targsize)
return (-1);
target[tarindex] |= (Ip_u8)((pos - ipdnsc_base64) >> 2);
target[tarindex+1] = (Ip_u8)(((pos - ipdnsc_base64) & 0x03)
<< 6);
}
tarindex++;
state = 3;
break;
case 3:
if (target) {
if ((Ip_size_t)tarindex >= targsize)
return (-1);
target[tarindex] |= (Ip_u8)(pos - ipdnsc_base64);
}
tarindex++;
state = 0;
break;
default:
return -1;
}
}
/*
* We are done decoding Base-64 chars. Let's see if we ended
* on a byte boundary, and/or with erroneous trailing characters.
*/
if (ch == ipdnsc_pad64) { /* We got a pad char. */
ch = *src++; /* Skip it, get next. */
switch (state) {
case 0: /* Invalid = in first position */
case 1: /* Invalid = in second position */
return (-1);
case 2: /* Valid, means one byte of info */
/* Skip any number of spaces. */
for (; ch != '\0'; ch = *src++)
if (!ipcom_isspace(ch))
break;
/* Make sure there is another trailing = sign. */
if (ch != ipdnsc_pad64)
return (-1);
ch = *src++; /* Skip the = */
/* Fall through to "single trailing =" case. */
/* FALLTHROUGH */
case 3: /* Valid, means two bytes of info */
/*
* We know this char is an =. Is there anything but
* whitespace after it?
*/
for (; ch != '\0'; ch = *src++)
if (!ipcom_isspace(ch))
return (-1);
/*
* Now make sure for cases 2 and 3 that the "extra"
* bits that slopped past the last full byte were
* zeros. If we don't check them, they become a
* subliminal channel.
*/
if (target && target[tarindex] != 0)
return (-1);
}
}
else {
/*
* We ended by seeing the end of the string. Make sure we
* have no partial bytes lying around.
*/
if (state != 0)
return (-1);
}
return (tarindex);
}
/*
*===========================================================================
* ipdnsc_find_tsig
*===========================================================================
* Description: find tsig from msg.
* Parameters:
* Returns: the point of tsig or IP_NULL.
*/
IP_STATIC Ip_u8 *
ipdnsc_find_tsig(Ip_u8 *msg, Ip_u8 *eom)
{
Ipdnsc_hdr *hp = (Ipdnsc_hdr *)msg;
Ip_s32 n, type, headlen = sizeof(Ipdnsc_hdr);
Ip_u8 *start, *cp = msg;
if (msg == IP_NULL || eom == IP_NULL || msg > eom)
return (IP_NULL);
if (cp + headlen >= eom)
return (IP_NULL);
if (hp->no_addi == 0)
return (IP_NULL);
cp += headlen;
n = ipdnsc_skiprr(cp, eom, IPDNSC_SECTION_QD, ipcom_ntohs(hp->no_ques));
if (n < 0)
return (IP_NULL);
cp += n;
n = ipdnsc_skiprr(cp, eom, IPDNSC_SECTION_AN, ipcom_ntohs(hp->no_answ));
if (n < 0)
return (IP_NULL);
cp += n;
n = ipdnsc_skiprr(cp, eom, IPDNSC_SECTION_NS, ipcom_ntohs(hp->no_auth));
if (n < 0)
return (IP_NULL);
cp += n;
n = ipdnsc_skiprr(cp, eom, IPDNSC_SECTION_AR, ipcom_ntohs(hp->no_addi) - 1);
if (n < 0)
return (IP_NULL);
cp += n;
start = cp;
n = ipdnsc_skipname(cp, eom);
if (n < 0)
return (IP_NULL);
cp += n;
if (cp + 2 >= eom)
return (IP_NULL);
type = IP_GET_HTONS(cp);
cp += 2;
if (type != IPDNSC_QTYPE_TSIG)
return (IP_NULL);
return (start);
}
/*
*===========================================================================
* ipdnsc_name_ntol
*===========================================================================
* Description: Convert a network strings labels into all lowercase.
* Parameters:
* Returns: Number of bytes written to buffer, or -1 on fail
* notes: Enforces label and domain length limits.
*/
IP_STATIC Ip_s32
ipdnsc_name_ntol(const Ip_u8 *src, Ip_u8 *dst, Ip_size_t dstsiz)
{
const Ip_u8 *cp;
Ip_u8 *dn, *eom, c;
Ip_u32 n;
Ip_s32 l;
cp = src;
dn = dst;
eom = dst + dstsiz;
if (dn >= eom)
return (-1);
while ((n = *cp++) != 0)
{
if ((n & IPDNSC_CMPRSFLGS) == IPDNSC_CMPRSFLGS)
/* Some kind of compression pointer. */
return (-1);
*dn++ = (Ip_u8)n;
if ((l = ipdnsc_labellen(cp - 1)) < 0)
return (-1);
if (dn + l >= eom)
return (-1);
for ((void)IP_NULL; l > 0; l--)
{
c = *cp++;
if (ipcom_isupper(c))
*dn++ = (Ip_u8)ipcom_tolower(c);
else
*dn++ = c;
}
}
*dn++ = '\0';
return (Ip_s32)(dn - dst);
}
#endif
/*
*===========================================================================
* ipdnsc_name_pton
*===========================================================================
* Description: Convert a ascii string into an encoded domain name as per RFC1035.
* Parameters:
* Returns: -1 if it fails
* 1 if string was fully qualified
* 0 is string was not fully qualified
* notes: Enforces label and domain length limits.
*/
IP_STATIC Ip_s32
ipdnsc_name_pton(const char *src, Ip_u8 *dst, Ip_size_t dstsiz, Ip_s32 *error)
{
Ip_u8 *label, *bp, *eom;
Ip_s32 c, n, escaped;
char *cp;
escaped = 0;
bp = dst;
eom = dst + dstsiz;
label = bp++;
while ((c = *src++) != 0) {
if (escaped) {
if (c == '[') { /* start a bit string label */
if ((cp = ipcom_strchr(src, ']')) == IP_NULL)
goto error_EINVAL;
if ((ipdnsc_encode_bitsring(&src, cp + 2, &label, &bp, eom, error)) != 0)
return(-1);
escaped = 0;
label = bp++;
if ((c = *src++) == 0)
goto done;
else if (c != '.')
goto error_EINVAL;
continue;
}
else if ((cp = ipcom_strchr(ipdnsc_digits, c)) != IP_NULL) {
n = (Ip_s32)(cp - ipdnsc_digits) * 100;
if ((c = *src++) == 0 || (cp = ipcom_strchr(ipdnsc_digits, c)) == IP_NULL)
goto error_EMSGSIZE;
n += (Ip_s32)(cp - ipdnsc_digits) * 10;
if ((c = *src++) == 0 || (cp = ipcom_strchr(ipdnsc_digits, c)) == IP_NULL)
goto error_EMSGSIZE;
n += (Ip_s32)(cp - ipdnsc_digits);
if (n > 255)
goto error_EMSGSIZE;
c = n;
}
escaped = 0;
}
else if (c == '\\') {
escaped = 1;
continue;
}
else if (c == '.') {
c = (Ip_s32)(bp - label - 1);
if ((c & IPDNSC_CMPRSFLGS) != 0)/* Label too big. */
goto error_EMSGSIZE;
if (label >= eom)
goto error_EMSGSIZE;
*label = (Ip_u8)c;
/* Fully qualified */
if (*src == '\0') {
if (c != 0) {
if (bp >= eom)
goto error_EMSGSIZE;
*bp++ = '\0';
}
if ((bp - dst) > IPDNSC_MAXCDNAME)
goto error_EMSGSIZE;
return (1);
}
if (c == 0 || *src == '.')
goto error_EMSGSIZE;
label = bp++;
continue;
}
if (bp >= eom)
goto error_EMSGSIZE;
*bp++ = (Ip_u8)c;
}
c = (Ip_s32)(bp - label - 1);
if ((c & IPDNSC_CMPRSFLGS) != 0)/* Label too big. */
goto error_EMSGSIZE;
done:
if (label >= eom)
goto error_EMSGSIZE;
*label = (Ip_u8)c;
if (c != 0) {
if (bp >= eom)
goto error_EMSGSIZE;
*bp++ = 0;
}
if ((bp - dst) > IPDNSC_MAXCDNAME)/* src too big */
goto error_EMSGSIZE;
return (0);
error_EINVAL:
*error = IPDNSC_UPDATE_ERR_INVALID;
return (-1);
error_EMSGSIZE:
*error = IPDNSC_UPDATE_ERR_MSGSIZE;
return (-1);
}
/*
*===========================================================================
* ipdnsc_name_ntop
*===========================================================================
* Description: Convert an encoded domain name to printable ascii as per RFC1035.
* Parameters:
* Returns: Number of bytes written to buffer, or -1 on fail
* notes: The root is returned as "."
* All other domains are returned in non absolute form
*/
IP_STATIC Ip_s32
ipdnsc_name_ntop(const Ip_u8 *src, char *dst, Ip_size_t dstsiz)
{
const Ip_u8 *cp;
char *dn, *eom;
Ip_u8 c;
Ip_u32 n;
Ip_s32 l;
cp = src;
dn = dst;
eom = dst + dstsiz;
while ((n = *cp++) != 0)
{
if ((n & IPDNSC_CMPRSFLGS) == IPDNSC_CMPRSFLGS)
/* Some kind of compression pointer. */
return (-1);
if (dn != dst)
{
if (dn >= eom)
return (-1);
*dn++ = '.';
}
if ((l = ipdnsc_labellen(cp - 1)) < 0)
return(-1);
if (dn + l >= eom)
return (-1);
if ((n & IPDNSC_CMPRSFLGS) == IPDNSC_TYPE_ELT)
{
Ip_s32 m;
if (n != IPDNSC_LABELTYPE_BITSTRING)
return(-1);
if ((m = ipdnsc_decode_bitstring(&cp, dn, eom)) < 0)
return(-1);
dn += m;
continue;
}
for (; l > 0; l--)
{
c = *cp++;
if (ipdnsc_special(c))
{
if (dn + 1 >= eom)
return (-1);
*dn++ = '\\';
*dn++ = (char)c;
}
else if (!(c > 0x20 && c < 0x7f))
{
if (dn + 3 >= eom)
return (-1);
*dn++ = '\\';
*dn++ = ipdnsc_digits[c / 100];
*dn++ = ipdnsc_digits[(c % 100) / 10];
*dn++ = ipdnsc_digits[c % 10];
}
else
{
if (dn >= eom)
return (-1);
*dn++ = (char)c;
}
}
}
if (dn == dst)
{
if (dn >= eom)
return (-1);
*dn++ = '.';
}
if (dn >= eom)
return (-1);
*dn++ = '\0';
return (Ip_s32)(dn - dst);
}
/*
*===========================================================================
* ipdnsc_name_pack
*===========================================================================
* Description: Pack domain name 'domain' into 'comp_dn'.
* Parameters:
* Returns: Size of the compressed name, or -1.
* notes:
* 'dnptrs' is an array of pointers to previous compressed names.
* dnptrs[0] is a pointer to the beginning of the message. The array ends with IP_NULL.
* 'lastdnptr' is a pointer to the end of the array pointed to by 'dnptrs'.
* Side effects:
* The list of pointers in dnptrs is updated for labels inserted into
* the message as we compress the name. If 'dnptr' is IP_NULL, we don't
* try to compress names. If 'lastdnptr' is IP_NULL, we don't update the
* list.
*/
IP_STATIC Ip_s32
ipdnsc_name_pack(const Ip_u8 *src, Ip_u8 *dst, Ip_s32 dstsiz, const Ip_u8 **dnptrs, const Ip_u8 **lastdnptr, Ip_s32 *error)
{
Ip_u8 *dstp;
const Ip_u8 **cpp, **lpp, *eob, *msg, *srcp;
Ip_s32 n, l, first = 1;
srcp = src;
dstp = dst;
eob = dstp + dstsiz;
lpp = cpp = IP_NULL;
if (dnptrs != IP_NULL)
{
if ((msg = *dnptrs++) != IP_NULL)
{
for (cpp = dnptrs; *cpp != IP_NULL; cpp++)
(void)IP_NULL;
lpp = cpp; /* end of list to search */
}
}
else
msg = IP_NULL;
/* make sure the domain we are about to add is legal */
l = 0;
do {
Ip_s32 l0;
n = *srcp;
if ((n & IPDNSC_CMPRSFLGS) == IPDNSC_CMPRSFLGS)
goto error_EMSGSIZE;
if ((l0 = ipdnsc_labellen(srcp)) < 0)
goto error_EINVAL;
l += l0 + 1;
if (l > IPDNSC_MAXCDNAME)
goto error_EMSGSIZE;
srcp += l0 + 1;
} while (n != 0);
/* from here on we need to reset compression pointer array on error */
srcp = src;
do {
/* Look to see if we can use pointers. */
n = *srcp;
if (n != 0 && msg != IP_NULL) {
l = ipdnsc_dn_find(srcp, msg, (const Ip_u8 * const *)dnptrs, (const Ip_u8 * const *)lpp, error);
if (l >= 0) {
if (dstp + 1 >= eob) {
goto cleanup;
}
*dstp++ = (Ip_u8)((l >> 8) | IPDNSC_CMPRSFLGS);
*dstp++ = (Ip_u8)(l % 256);
return (Ip_s32)(dstp - dst);
}
/* Not found, save it. */
if (lastdnptr != IP_NULL && cpp < lastdnptr - 1 &&
(dstp - msg) < 0x4000 && first) {
*cpp++ = dstp;
*cpp = IP_NULL;
first = 0;
}
}
/* copy label to buffer */
if ((n & IPDNSC_CMPRSFLGS) == IPDNSC_CMPRSFLGS) {
/* Should not happen. */
goto cleanup;
}
n = ipdnsc_labellen(srcp);
if (dstp + 1 + n >= eob) {
goto cleanup;
}
ipcom_memcpy(dstp, srcp, n + 1);
srcp += n + 1;
dstp += n + 1;
} while (n != 0);
if (dstp > eob) {
cleanup:
if (msg != IP_NULL)
*lpp = IP_NULL;
*error = IPDNSC_UPDATE_ERR_MSGSIZE;
return (-1);
}
return (Ip_s32)(dstp - dst);
error_EINVAL:
*error = IPDNSC_UPDATE_ERR_INVALID;
return (-1);
error_EMSGSIZE:
*error = IPDNSC_UPDATE_ERR_MSGSIZE;
return (-1);
}
/*
*===========================================================================
* ipdnsc_name_unpack
*===========================================================================
* Description: Unpack a domain name from a message, source may be compressed.
* Parameters:
* Returns: -1 if it fails, or consumed octets if it succeeds.
*/
IP_STATIC Ip_s32
ipdnsc_name_unpack(const Ip_u8 *msg, const Ip_u8 *eom, const Ip_u8 *src, Ip_u8 *dst, Ip_size_t dstsiz)
{
const Ip_u8 *srcp, *dstlim;
Ip_u8 *dstp;
Ip_s32 n, len, checked, l;
len = -1;
checked = 0;
dstp = dst;
srcp = src;
dstlim = dst + dstsiz;
if (srcp < msg || srcp >= eom)
return (-1);
/* Fetch next label in domain name. */
while ((n = *srcp++) != 0)
{
/* Check for indirection. */
switch (n & IPDNSC_CMPRSFLGS)
{
case 0:
case IPDNSC_TYPE_ELT:
/* Limit checks. */
if ((l = ipdnsc_labellen(srcp - 1)) < 0)
return(-1);
if (dstp + l + 1 >= dstlim || srcp + l >= eom)
return (-1);
checked += l + 1;
*dstp++ = (Ip_u8)n;
ipcom_memcpy(dstp, srcp, l);
dstp += l;
srcp += l;
break;
case IPDNSC_CMPRSFLGS:
if (srcp >= eom)
return (-1);
if (len < 0)
len = (Ip_s32)(srcp - src + 1);
srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff));
if (srcp < msg || srcp >= eom)
return (-1);
checked += 2;
/*
* Check for loops in the compressed name;
* if we've looked at the whole message,
* there must be a loop.
*/
if (checked >= eom - msg)
return (-1);
break;
default:
return (-1); /* flag error */
}
}
*dstp = '\0';
if (len < 0)
len = (Ip_s32)(srcp - src);
return (len);
}
/*
*===========================================================================
* ipdnsc_name_compress
*===========================================================================
* Description: Compress a domain name into wire format, using compression pointers.
* Parameters:
* Returns: Number of bytes consumed in `dst' or -1 .
* notes:
* 'dnptrs' is an array of pointers to previous compressed names.
* dnptrs[0] is a pointer to the beginning of the message.
* The list ends with IP_NULL. 'lastdnptr' is a pointer to the end of the
* array pointed to by 'dnptrs'. Side effect is to update the list of
* pointers for labels inserted into the message as we compress the name.
* If 'dnptr' is IP_NULL, we don't try to compress names. If 'lastdnptr'
* is IP_NULL, we don't update the list.
*/
IP_STATIC Ip_s32
ipdnsc_name_compress(const char *src, Ip_u8 *dst, Ip_size_t dstsiz, Ip_u8 **dnptrs, Ip_u8 **lastdnptr, Ip_s32 *error)
{
Ip_u8 tmp[IPDNSC_MAXCDNAME];
if (ipdnsc_name_pton(src, tmp, IPDNSC_MAXCDNAME, error) == -1)
return (-1);
return ipdnsc_name_pack(tmp, dst, (Ip_s32)dstsiz, (const Ip_u8 **)dnptrs, (const Ip_u8 **)lastdnptr, error);
}
/*
*===========================================================================
* ipdnsc_name_uncompress
*===========================================================================
* Description: Expand compressed domain name to presentation format.
* Parameters:
* Returns: Number of bytes read out of `src', or -1.
* note: Root domain returns as "." not "".
*/
IP_STATIC Ip_s32
ipdnsc_name_uncompress(const Ip_u8 *msg, const Ip_u8 *eom, const Ip_u8 *src,
char *dst, Ip_size_t dstsiz)
{
Ip_u8 tmp[IPDNSC_MAXCDNAME];
Ip_s32 n;
if ((n = ipdnsc_name_unpack(msg, eom, src, tmp, IPDNSC_MAXCDNAME)) == -1)
return (-1);
if (ipdnsc_name_ntop(tmp, dst, dstsiz) == -1)
return (-1);
return (n);
}
/*
*===========================================================================
* ipdnsc_expand
*===========================================================================
* Description: Expand compressed domain name 'src' to full domain name.
* Parameters:
* 'msg' is a pointer to the begining of the message,
* 'eom' points to the first location after the message,
* 'dst' is a pointer to a buffer of size 'dstsiz' for the result.
* Returns: size of compressed name or -1 if there was an error.
*/
IP_STATIC Ip_s32
ipdnsc_expand(const Ip_u8 *msg, const Ip_u8 *eom, const Ip_u8 *src, char *dst, Ip_s32 dstsiz)
{
Ip_s32 n = ipdnsc_name_uncompress(msg, eom, src, dst, (Ip_size_t)dstsiz);
if (n > 0 && dst[0] == '.')
dst[0] = '\0';
return (n);
}
/*
*===========================================================================
* ipdnsc_setsection
*===========================================================================
* Description: update current_section and current_rrnum in g_msg.
* Parameters: g_msg -- A pointer to a data structure filled in by ipdnsc_initparse.
* Returns:
*/
IP_STATIC void
ipdnsc_setsection(Ipdnsc_general_msg *g_msg, Ipdnsc_section sect)
{
g_msg->current_section = sect;
if (sect == IPDNSC_SECTION_MAX)
{
g_msg->current_rrnum = -1;
g_msg->msg_ptr = IP_NULL;
}
else
{
g_msg->current_rrnum = 0;
g_msg->msg_ptr = g_msg->sections[(int)sect];
}
}
/*
*===========================================================================
* ipdnsc_initparse
*===========================================================================
* Description: the first routine you must call before you use the other name
* server library routines.
* Parameters: r_buf -- A pointer to the beginning of the response message buffer.
r_size -- The size of the message buffer.
g_msg -- A pointer to a data structure filled in by ipdnsc_initparse.
* Returns: 0 on success and -1 if it fails to parse the message buffer.
*/
IP_STATIC Ip_s32
ipdnsc_initparse(const Ip_u8 *r_buf, Ip_s32 r_size, Ipdnsc_general_msg *g_msg)
{
const Ip_u8 *ptr = r_buf;
const Ip_u8 *end = r_buf + r_size;
Ip_s32 i;
if (r_size < (Ip_s32)sizeof(Ipdnsc_hdr))
return -1;
/* initialize g_msg */
g_msg->begin = ptr;
g_msg->end = end;
g_msg->id = IP_SWAP16(*(Ip_u16 *)(ptr));
g_msg->flags = IP_SWAP16(*(Ip_u16 *)(ptr+2));
ptr += 4;
for (i = 0; i < IPDNSC_SECTION_MAX; i++)
{
g_msg->counts[i] = IP_GET_NTOHS(ptr);
ptr +=2;
}
for (i = 0; i < IPDNSC_SECTION_MAX; i++)
if (g_msg->counts[i] == 0)
g_msg->sections[i] = IP_NULL;
else
{
Ip_s32 b = ipdnsc_skiprr(ptr, end, (Ipdnsc_section)i, g_msg->counts[i]);
if (b < 0)
return (-1);
g_msg->sections[i] = ptr;
ptr += b;
}
if (ptr != end)
return -1;
ipdnsc_setsection(g_msg, IPDNSC_SECTION_MAX);
return (0);
}
/*
*===========================================================================
* ipdnsc_parserr
*===========================================================================
* Description: parse record from g_msg.
* Parameters:
* Returns: 0 on success and -1 if it fails to parse the message buffer.
*/
IP_STATIC Ip_s32
ipdnsc_parserr(Ipdnsc_general_msg *g_msg, Ipdnsc_section section, Ip_s32 rrnum, Ipdnsc_record *rr)
{
Ip_s32 b,tmp;
/* Make section right. */
tmp = section;
if (tmp < 0 || section >= IPDNSC_SECTION_MAX)
return -1;
/* If section is changed, makes msg_ptr point to the begin of this section */
if (section != g_msg->current_section)
ipdnsc_setsection(g_msg, section);
/* Make rrnum right. */
if (rrnum == -1)
rrnum = g_msg->current_rrnum;
if (rrnum < 0 || rrnum >= g_msg->counts[(Ip_s32)section])
return -1;
if (rrnum < g_msg->current_rrnum)
ipdnsc_setsection(g_msg, section);
/* If rrnum > 0, move msg_ptr point to the next record */
if (rrnum > g_msg->current_rrnum)
{
b = ipdnsc_skiprr(g_msg->msg_ptr, g_msg->end, section, rrnum - g_msg->current_rrnum);
if (b < 0)
return (-1);
g_msg->msg_ptr += b;
g_msg->current_rrnum = rrnum;
}
/* parse one record, rr_ptr is the begin of this record. */
b = ipdnsc_expand(g_msg->begin, g_msg->end, g_msg->msg_ptr, rr->name, IPDNSC_MAXDNAME);
if (b < 0)
return (-1);
g_msg->msg_ptr += b;
if (g_msg->msg_ptr + 2 + 2 > g_msg->end)
return (-1);
rr->type = IP_GET_NTOHS(g_msg->msg_ptr);
g_msg->msg_ptr += 2;
rr->rr_class = IP_GET_NTOHS(g_msg->msg_ptr);
g_msg->msg_ptr += 2;
if (section == IPDNSC_SECTION_QD)
{
rr->ttl = 0;
rr->rdlength = 0;
rr->rdata = IP_NULL;
}
else
{
if (g_msg->msg_ptr + 4 + 2 > g_msg->end)
return (-1);
rr->ttl = IP_GET_NTOHL(g_msg->msg_ptr);
g_msg->msg_ptr += 4;
rr->rdlength = IP_GET_NTOHS(g_msg->msg_ptr);
g_msg->msg_ptr += 2;
if (g_msg->msg_ptr + rr->rdlength > g_msg->end)
return (-1);
rr->rdata = g_msg->msg_ptr;
g_msg->msg_ptr += rr->rdlength;
}
if (++g_msg->current_rrnum > g_msg->counts[(Ip_s32)section])
ipdnsc_setsection(g_msg, (Ipdnsc_section)((Ip_s32)section + 1));
return (0);
}
/*
*===========================================================================
* ipdnsc_do_query_ext
*===========================================================================
* Description: Handle a complete DNS query including:
* sending the message, receiveing and parsing the response.
* Parameters: dname - the name to resolve
* qtype - the query type
*
* error - possible error code. will be set to zero if no error
* Returns: <0 unsuccess
>0 the number of answer, CNAME or DNAME found.
=0 other answer
*/
IP_STATIC Ip_s32
ipdnsc_do_query_ext(char *dname, Ip_u16 qclass, Ip_u16 qtype, Ip_u8 *r_buf, Ip_s32 *r_size,
Ipdnsc_ns_spec *dns, Ipdnsc_general_msg *g_msg, Ip_s32 *error)
{
Ipdnsc_ns_addr *dns_addr;
Ip_u8 *s_buf;
Ip_s32 s_size, err, retries, i, nanswer;
Ipdnsc_record rr;
/* Get buffer memory. */
s_buf = ipcom_malloc(IPDNSC_MAXPKT);
if (s_buf == IP_NULL)
{
*error = IPDNSC_UPDATE_ERR_GENERAL;
return -1;
}
/* Create the DNS query message */
s_size = ipdnsc_create_query((Ip_u8*)dname, qtype, qclass, s_buf);
if (s_size < 0)
{
*error = IPDNSC_UPDATE_ERR_GENERAL;
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_UPDATE_ERR_GENERAL;
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:
case IPDNSC_SERV_NONAME:
break;
case IPDNSC_SERV_FAIL:
case IPDNSC_SERV_NOTIMPL:
case IPDNSC_SERV_REFUSED:
/* 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 */
*error = IPDNSC_UPDATE_ERR_GENERAL;
}
goto fail;
case IPDNSC_ERR_GENERAL:
case IPDNSC_SERV_FORMAT:
default:
*error = IPDNSC_UPDATE_ERR_GENERAL;
goto fail;
}
/* Parse the server response */
if (ipdnsc_initparse(r_buf, *r_size, g_msg) < 0)
goto fail;
nanswer = 0;
for (i = 0; i < g_msg->counts[IPDNSC_SECTION_AN]; i++)
{
if (ipdnsc_parserr(g_msg, IPDNSC_SECTION_AN, i, &rr) < 0)
goto fail;
if (rr.rr_class == qclass && (rr.type == IPDNSC_QTYPE_CNAME || rr.type == IPDNSC_QTYPE_DNAME))
nanswer++;
}
ipcom_free(s_buf);
return nanswer;
fail:
ipcom_free(s_buf);
return -1;
}
/*
*===========================================================================
* ipdnsc_find_ns
*===========================================================================
* Description: find name server from nsrrsp.
* Parameters:
* Returns:
*/
IP_STATIC Ipdnsc_record_ns *
ipdnsc_find_ns(Ipdnsc_record_nsset *nsrrsp, const char *dname)
{
Ipdnsc_record_ns *nsrr;
for (nsrr = nsrrsp->head; nsrr != IP_NULL; nsrr = nsrr->next)
if (ipdnsc_samename(nsrr->name, dname) == 1)
return (nsrr);
return (IP_NULL);
}
/*
*===========================================================================
* ipdnsc_save_a
*===========================================================================
* Description: save address to nsrr.
* Parameters:
* Returns:
*/
IP_STATIC Ip_s32
ipdnsc_save_a(Ipdnsc_general_msg *msg, Ipdnsc_section sect, const char *owner, Ip_u16 qclass, Ipdnsc_record_ns *nsrr)
{
Ip_s32 i;
Ip_u16 port_n;
for (i = 0; i < msg->counts[sect]; i++)
{
Ipdnsc_record rr;
Ipdnsc_record_addr *arr;
if (ipdnsc_parserr(msg, sect, i, &rr) < 0)
return (-1);
if ((rr.type != IPDNSC_QTYPE_A && rr.type != IPDNSC_QTYPE_AAAA) ||
(rr.rdlength != IPDNSC_INADDRSZ && rr.rdlength != IPDNSC_IN6ADDRSZ) ||
rr.rr_class != qclass || ipdnsc_samename(rr.name, owner) != 1)
continue;
arr = ipcom_malloc(sizeof(Ipdnsc_record_addr));
if (arr == IP_NULL)
return (-1);
ipcom_memset(arr, 0, sizeof(Ipdnsc_record_addr));
switch (rr.type)
{
case IPDNSC_QTYPE_A:
arr->ns_addr.family = IP_AF_INET;
/* Configure DNS server address/ports */
port_n = (Ip_u16)ipcom_sysvar_get_as_int ("ipdnsc.port", IPDNSC_DEFAULT_DNS_PORT);
arr->ns_addr.port = ip_htons(port_n);
ipcom_memcpy(&(arr->ns_addr.addr.inaddr.s_addr), rr.rdata, IPDNSC_INADDRSZ);
nsrr->flags |= IPDNSC_RR_NS_HAVE_V4;
break;
#ifdef IPCOM_USE_INET6
case IPDNSC_QTYPE_AAAA:
arr->ns_addr.family = IP_AF_INET6;
/* Configure DNS server address/ports */
port_n = (Ip_u16)ipcom_sysvar_get_as_int ("ipdnsc.port", IPDNSC_DEFAULT_DNS_PORT);
arr->ns_addr.port = ip_htons(port_n);
ipcom_memcpy(&(arr->ns_addr.addr.inaddr6), rr.rdata, IPDNSC_IN6ADDRSZ);
arr->ns_addr.ifindex = 0;
nsrr->flags |= IPDNSC_RR_NS_HAVE_V6;
break;
#endif
default:
ipcom_free(arr);
continue;
}
/* append arr to nsrr */
arr->next = nsrr->rr_addrs;
nsrr->rr_addrs = arr;
}
return (0);
}
/*
*===========================================================================
* ipdnsc_save_ns
*===========================================================================
* Description: save name server to nsrrsp.
* Parameters:
* Returns:
*/
IP_STATIC Ip_s32
ipdnsc_save_ns(Ipdnsc_general_msg *msg, Ipdnsc_section sect, const char *owner, Ip_u16 qclass,Ipdnsc_record_nsset *nsrrsp)
{
Ip_s32 i;
for (i = 0; i < msg->counts[sect]; i++)
{
char tname[IPDNSC_MAXDNAME];
const Ip_u8 *rdata;
Ipdnsc_record_ns *nsrr;
Ipdnsc_record rr;
if (ipdnsc_parserr(msg, sect, i, &rr) < 0)
return (-1);
if (rr.type!= IPDNSC_QTYPE_NS || rr.rr_class != qclass || ipdnsc_samename(rr.name, owner) != 1)
continue;
nsrr = ipdnsc_find_ns(nsrrsp, rr.name);
if (nsrr == IP_NULL)
{
nsrr = ipcom_malloc(sizeof(Ipdnsc_record_ns));
if (nsrr == IP_NULL)
return (-1);
ipcom_memset(nsrr, 0, sizeof(Ipdnsc_record_ns));
rdata = rr.rdata;
if (ipdnsc_name_uncompress(msg->begin, msg->end, rdata, tname, IPDNSC_MAXDNAME) < 0)
{
ipcom_free(nsrr);
return (-1);
}
nsrr->name = ipcom_strdup(tname);
if (nsrr->name == IP_NULL)
{
ipcom_free(nsrr);
return (-1);
}
/* append nsrr to the nsrrsp */
nsrr->next = nsrrsp->head;
nsrrsp->head = nsrr;
}
if (ipdnsc_save_a(msg, IPDNSC_SECTION_AR, nsrr->name, qclass, nsrr) < 0)
return (-1);
}
return (0);
}
/*
*===========================================================================
* ipdnsc_free_nsrr
*===========================================================================
* Description: free nsrr from nsrrsp.
* Parameters:
* Returns:
*/
IP_STATIC void
ipdnsc_free_nsrr(Ipdnsc_record_nsset *nsrrsp, Ipdnsc_record_ns *nsrr)
{
Ipdnsc_record_addr *arr, *arr_n;
Ipdnsc_record_ns *nsrr_free, *nsrr_prev;
if (nsrrsp == IP_NULL || nsrr == IP_NULL)
return;
arr = nsrr->rr_addrs;
while (arr != IP_NULL)
{
arr_n = arr->next;
ipcom_free(arr);
arr = arr_n;
}
nsrr->rr_addrs = IP_NULL;
ipcom_free(nsrr->name);
/* search nsrr from nsrrsp */
if (nsrr == nsrrsp->head)
{
nsrrsp->head = nsrr->next;
ipcom_free(nsrr);
nsrr = IP_NULL;
return;
}
nsrr_prev = nsrrsp->head;
nsrr_free = nsrrsp->head->next;
for (; nsrr_free != IP_NULL;)
{
if (nsrr_free == nsrr)
{
nsrr_prev->next = nsrr->next;
ipcom_free(nsrr);
nsrr = IP_NULL;
return;
}
nsrr_prev = nsrr_free;
nsrr_free = nsrr_free->next;
}
}
/*
*===========================================================================
* ipdnsc_add_addrs
*===========================================================================
* Description: add addreass to addrs.
* Parameters:
* Returns:
*/
IP_STATIC Ip_s32
ipdnsc_add_addrs(Ipdnsc_record_ns *nsrr, Ipdnsc_ns_addr *addrs, Ip_s32 *total)
{
Ipdnsc_record_addr *arr;
Ip_s32 n = 0;
for (arr = nsrr->rr_addrs; arr != IP_NULL; arr = arr->next)
{
*addrs++ = arr->ns_addr;
n++;
(*total)++;
/* The number of address is 3. */
if (*total >= IPDNSC_MAX_NAMESERVER)
break;
}
return (n);
}
#ifdef IPDNSC_INCLUDE_MD5
/*
*===========================================================================
* ipdnsc_dst_sign_data
*===========================================================================
* Description:
* An incremental signing function. Data is signed in steps.
* First the context must be initialized (IPDNSC_SIG_MODE_INIT).
* Then data is hashed (IPDNSC_SIG_MODE_UPDATE). Finally the signature
* itself is created (IPDNSC_SIG_MODE_FINAL). This function can be called
* once with INIT, UPDATE and FINAL modes all set, or it can be
* called separately with a different mode set for each step. The
* UPDATE step can be repeated.
* Parameters
* mode A bit mask used to specify operation(s) to be performed.
* IPDNSC_SIG_MODE_INIT 1 Initialize digest
* IPDNSC_SIG_MODE_UPDATE 2 Add data to digest
* IPDNSC_SIG_MODE_FINAL 4 Generate signature
* from signature
* IPDNSC_SIG_MODE_ALL (IPDNSC_SIG_MODE_INIT,IPDNSC_SIG_MODE_UPDATE,IPDNSC_SIG_MODE_FINAL)
* data Data to be signed.
* len The length in bytes of data to be signed.
* in_key Contains a private key to sign with.
* KEY structures should be handled (created, converted,
* compared, stored, freed) by the DST.
* signature
* The location to which the signature will be written.
* sig_len Length of the signature field in bytes.
* Return
* 0 Successfull INIT or Update operation
* >0 success FINAL (sign) operation
* <0 failure
*/
IP_STATIC Ip_s32
ipdnsc_dst_sign_data(const Ip_s32 mode, Ipdnsc_dst_key *in_key, void **context,
const Ip_u8 *data, const Ip_s32 len,
Ip_u8 *signature, const Ip_s32 sig_len)
{
if (mode & IPDNSC_SIG_MODE_FINAL &&
(in_key->dk_KEY_struct == IP_NULL || signature == IP_NULL))
return (IPDNSC_MISSING_KEY_OR_SIGNATURE);
if (in_key->dk_func && in_key->dk_func->sign)
return (in_key->dk_func->sign(mode, (void*)in_key, context, data, len, signature, sig_len));
return 0;
}
/*
*===========================================================================
* ipdnsc_dst_verify_data
*===========================================================================
* Description:
* An incremental verify function. Data is verified in steps.
* First the context must be initialized (IPDNSC_SIG_MODE_INIT).
* Then data is hashed (IPDNSC_SIG_MODE_UPDATE). Finally the signature
* is verified (IPDNSC_SIG_MODE_FINAL). This function can be called
* once with INIT, UPDATE and FINAL modes all set, or it can be
* called separately with a different mode set for each step. The
* UPDATE step can be repeated.
* Parameters
* mode Operations to perform this time.
* IPDNSC_SIG_MODE_INIT 1 Initialize digest
* IPDNSC_SIG_MODE_UPDATE 2 add data to digest
* IPDNSC_SIG_MODE_FINAL 4 verify signature
* IPDNSC_SIG_MODE_ALL
* (IPDNSC_SIG_MODE_INIT,IPDNSC_SIG_MODE_UPDATE,IPDNSC_SIG_MODE_FINAL)
* data Data to pass through the hash function.
* len Length of the data in bytes.
* in_key Key for verification.
* signature Location of signature.
* sig_len Length of the signature in bytes.
* Returns
* 0 Verify success
* Non-Zero Verify Failure
*/
IP_STATIC Ip_s32
ipdnsc_dst_verify_data(const Ip_s32 mode, Ipdnsc_dst_key *in_key, void **context,
const Ip_u8 *data, const Ip_s32 len,
const Ip_u8 *signature, const Ip_s32 sig_len)
{
if (mode & IPDNSC_SIG_MODE_FINAL &&
(in_key->dk_KEY_struct == IP_NULL || signature == IP_NULL))
return (IPDNSC_MISSING_KEY_OR_SIGNATURE);
if (in_key->dk_func == IP_NULL || in_key->dk_func->verify == IP_NULL)
return (IPDNSC_UNSUPPORTED_KEYALG);
return (in_key->dk_func->verify(mode, (void*)in_key, context, data, len, signature, sig_len));
}
/*
*===========================================================================
* ipdnsc_sign
*===========================================================================
* Description: sign
* Parameters:
* msg message to be sent
* msglen input - length of message
* output - length of signed message
* msgsize length of buffer containing message
* k tsig key used for signing
* sig a buffer to hold the generated signature
* siglen input - length of signature buffer
* output - length of signature
* Errors:
* - bad input data (-1)
* - bad key / sign failed (IPDNSC_UPDATE_ERR_TSIG_BADKEY)
* - not enough space (IPDNSC_UPDATE_ERR_TSIG_NO_SPACE)
*/
IP_STATIC Ip_s32
ipdnsc_sign(Ip_u8 *msg, Ip_s32 *msglen, Ip_s32 msgsize, void *k,Ip_u8 *sig, Ip_s32 *siglen, Ip_s32 *error)
{
Ipdnsc_hdr *hp = (Ipdnsc_hdr *)msg;
Ipdnsc_dst_key *key = (Ipdnsc_dst_key *)k;
Ip_u8 *cp, *cp2, *eob, *lenp, *alg;
Ip_u32 timesigned;
Ip_u8 name[IPDNSC_MAXCDNAME];
void *ctx;
Ip_u8 buf[IPDNSC_MAXCDNAME];
Ip_s32 n;
if (msg == IP_NULL || msglen == IP_NULL || key == IP_NULL || sig == IP_NULL || siglen == IP_NULL)
return (-1);
cp = msg + *msglen;
eob = msg + msgsize;
/* Name. */
n = ipdnsc_name_pton(key->dk_key_name, name, IPDNSC_MAXCDNAME, error);
if (n != -1)
n = ipdnsc_name_pack(name, cp, (Ip_s32)(eob - cp),IP_NULL, IP_NULL, error);
if (n < 0)
return IPDNSC_UPDATE_ERR_TSIG_NO_SPACE;
cp += n;
/* Type, class, ttl, length (not filled in yet). */
if ((cp + 2 + 2 + 4 + 2) > eob)
return IPDNSC_UPDATE_ERR_TSIG_NO_SPACE;
IP_SET_HTONS(cp, IPDNSC_QTYPE_TSIG);
cp += 2;
IP_SET_HTONS(cp, IPDNSC_QCLASS_ANY);
cp += 2;
IP_SET_HTONL(cp, 0); /* TTL */
cp += 4;
lenp = cp;
cp += 2;
/* Alg. */
if (key->dk_alg != IPDNSC_ALG_HMAC_MD5)
return IPDNSC_UPDATE_ERR_TSIG_BADKEY;
n = ipdnsc_name_compress(IPDNSC_TSIG_ALG_HMAC_MD5, cp, eob - cp, IP_NULL, IP_NULL, error);
if (n < 0)
return (IPDNSC_UPDATE_ERR_TSIG_NO_SPACE);
alg = cp;
cp += n;
/* Time. */
if ((cp + 2 + 4 + 2) > eob)
return IPDNSC_UPDATE_ERR_TSIG_NO_SPACE;
IP_SET_HTONS(cp, 0);
cp += 2;
timesigned = ipdnsc_get_time();
IP_SET_HTONL(cp, timesigned);
cp += 4;
IP_SET_HTONS(cp, IPDNSC_TSIG_FUDGE);
cp += 2;
/* Compute the signature. */
(void)ipdnsc_dst_sign_data(IPDNSC_SIG_MODE_INIT, key, &ctx, IP_NULL, 0, IP_NULL, 0);
/* Digest the query signature, if this is a response. */
/* Digest the message. */
(void)ipdnsc_dst_sign_data(IPDNSC_SIG_MODE_UPDATE, key, &ctx, msg, *msglen, IP_NULL, 0);
/* Digest the key name. */
if ((n = ipdnsc_name_ntol(name, buf, IPDNSC_MAXCDNAME)) < 0)
return -1;
(void)ipdnsc_dst_sign_data(IPDNSC_SIG_MODE_UPDATE, key, &ctx, buf, n, IP_NULL, 0);
/* Digest the class and TTL. */
cp2 = buf;
IP_SET_HTONS(cp2, IPDNSC_QCLASS_ANY);
cp2 += 2;
IP_SET_HTONL(cp2, 0);
cp2 += 4;
(void)ipdnsc_dst_sign_data(IPDNSC_SIG_MODE_UPDATE, key, &ctx, buf, (Ip_s32)(cp2-buf), IP_NULL, 0);
/* Digest the algorithm. */
if ((n = ipdnsc_name_ntol(alg, buf, IPDNSC_MAXCDNAME)) < 0)
return -1;
(void)ipdnsc_dst_sign_data(IPDNSC_SIG_MODE_UPDATE, key, &ctx, buf, n, IP_NULL, 0);
/* Digest the time signed, fudge, error, and other data */
cp2 = buf;
IP_SET_HTONS(cp2, 0); /* Top 16 bits of time */
cp2 += 2;
IP_SET_HTONL(cp2, timesigned);
cp2 += 4;
IP_SET_HTONS(cp2, IPDNSC_TSIG_FUDGE);
cp2 += 2;
IP_SET_HTONS(cp2, 0); /* Error */
cp2 += 2;
IP_SET_HTONS(cp2, 0); /* Other data length */
cp2 += 2;
(void)ipdnsc_dst_sign_data(IPDNSC_SIG_MODE_UPDATE, key, &ctx, buf, (Ip_s32)(cp2-buf), IP_NULL, 0);
if ((n = ipdnsc_dst_sign_data(IPDNSC_SIG_MODE_FINAL, key, &ctx, IP_NULL, 0, sig, *siglen)) < 0)
return IPDNSC_UPDATE_ERR_TSIG_BADKEY;
*siglen = n;
/* Add the signature. */
if ((cp + 2 + (*siglen)) > eob)
return IPDNSC_UPDATE_ERR_TSIG_NO_SPACE;
IP_SET_HTONS(cp, *siglen);
cp += 2;
ipcom_memcpy(cp, sig, *siglen);
cp += (*siglen);
/* The original message ID & error. */
if ((cp + 2 + 2) > eob)
return IPDNSC_UPDATE_ERR_TSIG_NO_SPACE;
IP_SET_HTONS(cp, ipcom_ntohs(hp->id)); /* already in network order */
cp += 2;
IP_SET_HTONS(cp, 0);
cp += 2;
/* Other data. */
if ((cp + 2) > eob)
return IPDNSC_UPDATE_ERR_TSIG_NO_SPACE;
IP_SET_HTONS(cp, 0); /* Other data length */
cp += 2;
/* Go back and fill in the length. */
IP_SET_HTONS(lenp, cp - lenp - 2);
lenp += 2;
hp->no_addi = ipcom_htons((Ip_u16)(ipcom_ntohs(hp->no_addi) + 1));
*msglen = (Ip_s32)(cp - msg);
return (0);
}
/*
*===========================================================================
* ipdnsc_verify
*===========================================================================
* Description:
* Parameters:
*\li statp res stuff
*\li msg received message
*\li msglen length of message
*\li key tsig key used for verifying.
*\li querysig (response), the signature in the query
*\li querysiglen (response), the length of the signature in the query
*\li sig (query), a buffer to hold the signature
*\li siglen (query), input - length of signature buffer
* output - length of signature
*
* Errors:
*\li - bad input (-1)
*\li - invalid dns message (IPDNSC_UPDATE_ERR_TSIG_BADFORMAT)
*\li - TSIG is not present (IPDNSC_UPDATE_ERR_NO_TSIG)
*\li - key doesn't match (IPDNSC_UPDATE_ERR_TSIG_SERVER_BADKEY)
*\li - TSIG verification fails with BADKEY (-ns_r_badkey)
*\li - TSIG verification fails with BADSIG (-ns_r_badsig)
*\li - TSIG verification fails with BADTIME (-ns_r_badtime)
*\li - TSIG verification succeeds, error set to BAKEY (ns_r_badkey)
*\li - TSIG verification succeeds, error set to BADSIG (ns_r_badsig)
*\li - TSIG verification succeeds, error set to BADTIME (ns_r_badtime)
*/
IP_STATIC Ip_s32
ipdnsc_verify(Ip_u8 *msg, Ip_s32 *msglen, void *k,
const Ip_u8 *querysig, Ip_s32 querysiglen, Ip_u8 *sig, Ip_s32 *siglen, Ip_time_t *timesigned, Ip_s32 *error)
{
Ipdnsc_hdr *hp = (Ipdnsc_hdr *)msg;
Ipdnsc_dst_key *key = (Ipdnsc_dst_key *)k;
Ip_u8 *cp = msg, *eom;
char name[IPDNSC_MAXDNAME];
char alg[IPDNSC_MAXDNAME];
Ip_u8 *recstart, *rdatastart;
Ip_u8 *sigstart, *otherstart;
Ip_s32 n;
Ip_s32 resp_error;
Ip_u16 type, length;
Ip_u16 fudge, sigfieldlen, otherfieldlen;
if (msg == IP_NULL || msglen == IP_NULL || *msglen < 0)
return IPDNSC_UPDATE_ERR_INVALID;
eom = msg + *msglen;
recstart = ipdnsc_find_tsig(msg, eom);
if (recstart == IP_NULL)
return (IPDNSC_UPDATE_ERR_NO_TSIG);
cp = recstart;
/* Read the key name. */
n = ipdnsc_expand(msg, eom, cp, name, IPDNSC_MAXDNAME);
if (n < 0)
return (IPDNSC_UPDATE_ERR_TSIG_BADFORMAT);
cp += n;
/* Read the type. */
if ((cp + 2*2 + 4 + 2) > eom)
return (IPDNSC_UPDATE_ERR_TSIG_BADFORMAT);
type = IP_GET_HTONS(cp);
cp += 2;
if (type != IPDNSC_QTYPE_TSIG)
return IPDNSC_UPDATE_ERR_NO_TSIG;
/* Skip the class and TTL, save the length. */
cp += 2 + 4;
length = IP_GET_HTONS(cp);
cp += 2;
if (eom - cp != length)
return IPDNSC_UPDATE_ERR_TSIG_BADFORMAT;
/* Read the algorithm name. */
rdatastart = cp;
n = ipdnsc_expand(msg, eom, cp, alg, IPDNSC_MAXDNAME);
if (n < 0)
return IPDNSC_UPDATE_ERR_TSIG_BADFORMAT;
if (ipdnsc_samename(alg, IPDNSC_TSIG_ALG_HMAC_MD5) != 1)
return IPDNSC_UPDATE_ERR_TSIG_SERVER_BADKEY;
cp += n;
/* Read the time signed and fudge. */
if ((cp + 2 + 4 + 2) > eom)
return (IPDNSC_UPDATE_ERR_TSIG_BADFORMAT);
cp += 2;
*timesigned = IP_GET_HTONL(cp);
cp += 4;
fudge = IP_GET_HTONS(cp);
cp += 2;
/* Read the signature. */
if ((cp + 2) > eom)
return (IPDNSC_UPDATE_ERR_TSIG_BADFORMAT);
sigfieldlen = IP_GET_HTONS(cp);
cp += 2;
if ((cp + sigfieldlen) > eom)
return (IPDNSC_UPDATE_ERR_TSIG_BADFORMAT);
sigstart = cp;
cp += sigfieldlen;
/* Skip id and read error. */
if ((cp + 2*2) > eom)
return (IPDNSC_UPDATE_ERR_TSIG_BADFORMAT);
cp += 2;
resp_error = IP_GET_HTONS(cp);
cp += 2;
/* Parse the other data. */
if ((cp + 2) > eom)
return (IPDNSC_UPDATE_ERR_TSIG_BADFORMAT);
otherfieldlen = IP_GET_HTONS(cp);
cp += 2;
if ((cp + otherfieldlen) > eom)
return (IPDNSC_UPDATE_ERR_TSIG_BADFORMAT);
otherstart = cp;
cp += otherfieldlen;
if (cp != eom)
return (IPDNSC_UPDATE_ERR_TSIG_BADFORMAT);
/* Verify that the key used is OK. */
if (key != IP_NULL)
{
if (key->dk_alg != IPDNSC_ALG_HMAC_MD5)
return IPDNSC_UPDATE_ERR_TSIG_SERVER_BADKEY;
if (resp_error != IPDNSC_RCODE_BADSIG && resp_error != IPDNSC_RCODE_BADKEY)
{
if (ipdnsc_samename(key->dk_key_name, name) != 1)
return IPDNSC_UPDATE_ERR_TSIG_SERVER_BADKEY;
}
}
hp->no_addi = ipcom_htons((Ip_u16)(ipcom_ntohs(hp->no_addi) - 1));
/*
* Do the verification.
*/
if (key != IP_NULL && resp_error != IPDNSC_RCODE_BADSIG && resp_error != IPDNSC_RCODE_BADKEY)
{
void *ctx;
Ip_u8 buf[IPDNSC_MAXDNAME];
Ip_u8 buf2[IPDNSC_MAXDNAME];
/* Digest the query signature, if this is a response. */
(void)ipdnsc_dst_verify_data(IPDNSC_SIG_MODE_INIT, key, &ctx, IP_NULL, 0, IP_NULL, 0);
if (querysiglen > 0 && querysig != IP_NULL)
{
Ip_u16 len_n = ipcom_htons((Ip_u16)querysiglen);
(void)ipdnsc_dst_verify_data(IPDNSC_SIG_MODE_UPDATE, key, &ctx, (Ip_u8 *)&len_n, 2, IP_NULL, 0);
(void)ipdnsc_dst_verify_data(IPDNSC_SIG_MODE_UPDATE, key, &ctx, querysig, querysiglen, IP_NULL, 0);
}
/* Digest the message. */
(void)ipdnsc_dst_verify_data(IPDNSC_SIG_MODE_UPDATE, key, &ctx, msg, (Ip_s32)(recstart - msg), IP_NULL, 0);
/* Digest the key name. */
if ((n = ipdnsc_name_pton(name, buf2, IPDNSC_MAXCDNAME, error)) < 0)
return (-1);
if ((n = ipdnsc_name_ntol(buf2, buf, IPDNSC_MAXCDNAME)) < 0)
return (-1);
(void)ipdnsc_dst_verify_data(IPDNSC_SIG_MODE_UPDATE, key, &ctx, buf, n, IP_NULL, 0);
/* Digest the class and TTL. */
(void)ipdnsc_dst_verify_data(IPDNSC_SIG_MODE_UPDATE, key, &ctx,
recstart + ipdnsc_skipname(recstart, eom) + 2, 2 + 4, IP_NULL, 0);
/* Digest the algorithm. */
if ((n = ipdnsc_name_pton(alg, buf2, IPDNSC_MAXCDNAME, error)) < 0)
return (-1);
if ((n = ipdnsc_name_ntol(buf2, buf, IPDNSC_MAXCDNAME)) < 0)
return (-1);
(void)ipdnsc_dst_verify_data(IPDNSC_SIG_MODE_UPDATE, key, &ctx, buf, n, IP_NULL, 0);
/* Digest the time signed and fudge. */
(void)ipdnsc_dst_verify_data(IPDNSC_SIG_MODE_UPDATE, key, &ctx,
rdatastart + ipdnsc_skipname(rdatastart, eom), 2 + 4 + 2, IP_NULL, 0);
/* Digest the error and other data. */
(void)ipdnsc_dst_verify_data(IPDNSC_SIG_MODE_UPDATE, key, &ctx,
otherstart - 2 - 2, otherfieldlen + 2 + 2, IP_NULL, 0);
if ((n = ipdnsc_dst_verify_data(IPDNSC_SIG_MODE_FINAL, key, &ctx, IP_NULL, 0, sigstart, sigfieldlen)) < 0)
return IPDNSC_UPDATE_ERR_TSIG_SERVER_BADKEY;
if (sig != IP_NULL && siglen != IP_NULL)
{
if (*siglen < sigfieldlen)
return (IPDNSC_UPDATE_ERR_TSIG_NO_SPACE);
ipcom_memcpy(sig, sigstart, sigfieldlen);
*siglen = sigfieldlen;
}
}
else
{
if (sigfieldlen > 0)
return (IPDNSC_UPDATE_ERR_TSIG_BADFORMAT);
if (sig != IP_NULL && siglen != IP_NULL)
*siglen = 0;
}
/* Reset the counter, since we still need to check for badtime. */
hp->no_addi = ipcom_htons((Ip_u16)(ipcom_ntohs(hp->no_addi) + 1));
/* Verify the time. */
if (IP_ABS((*timesigned) - (Ip_time_t)ipdnsc_get_time()) > fudge)
return IPDNSC_UPDATE_ERR_TSIG_SERVER_BADTIME;
return resp_error;
}
/*
*===========================================================================
* ipdnsc_dst_s_put_int16
*===========================================================================
* Description: Take a 16 bit integer and store the value in a two byte
* character string. The integer is assumed to be in network
* order and the string is returned in host order.
* Parameters: buf Storage for a two byte character string.
* val 16 bit integer.
* Returns:
*/
IP_STATIC void
ipdnsc_dst_s_put_int16(Ip_u8 *buf, const Ip_u16 val)
{
buf[0] = (Ip_u8)(val >> 8);
buf[1] = (Ip_u8)(val);
}
/*
*===========================================================================
* ipdnsc_dst_s_id_calc
*===========================================================================
* Description: calculates a checksum used in dst for an id. takes an array of bytes and a length.
* Parameters:
* Returns: a 16 bit checksum.
*/
IP_STATIC Ip_u16
ipdnsc_dst_s_id_calc(const Ip_u8 *key, const Ip_s32 keysize)
{
Ip_u32 ac;
const Ip_u8 *kp = key;
Ip_s32 size = keysize;
if (!key || (keysize <= 0))
return (0xffffU);
for (ac = 0; size > 1; size -= 2, kp += 2)
ac += ((*kp) << 8) + *(kp + 1);
if (size > 0)
ac += ((*kp) << 8);
ac += (ac >> 16) & 0xffff;
return (Ip_u16)(ac & 0xffff);
}
/*
*===========================================================================
* ipdnsc_dst_s_dns_key_id
*===========================================================================
* Description: calculate DNSSEC footprint from KEY record rdata
* Parameters:
* Input:
* dns_key_rdata: the raw data in wire format
* rdata_len: the size of the input data
* Output:
* the key footprint/id calculated from the key data
* Returns:
*/
IP_STATIC Ip_u16
ipdnsc_dst_s_dns_key_id(const Ip_u8 *dns_key_rdata, const Ip_s32 rdata_len)
{
if (!dns_key_rdata)
return 0;
/* compute id */
if (dns_key_rdata[3] == IPDNSC_ALG_HMAC_MD5)
return 0;
else
/* compute a checksum on the key part of the key rr */
return ipdnsc_dst_s_id_calc(dns_key_rdata, rdata_len);
}
/*
*===========================================================================
* ipdnsc_dst_free_key
*===========================================================================
* Description: Release all data structures pointed to by a key structure.
* Parameters: f_key Key structure to be freed.
* Returns
*/
IP_STATIC Ipdnsc_dst_key *
ipdnsc_dst_free_key(Ipdnsc_dst_key *f_key)
{
if (f_key == IP_NULL)
return (f_key);
if (f_key->dk_func->destroy != IP_NULL)
f_key->dk_KEY_struct = f_key->dk_func->destroy(f_key->dk_KEY_struct);
if (f_key->dk_KEY_struct != IP_NULL)
{
ipcom_free(f_key->dk_KEY_struct);
f_key->dk_KEY_struct = IP_NULL;
}
if (f_key->dk_key_name != IP_NULL)
ipcom_free(f_key->dk_key_name);
ipcom_free(f_key);
return (IP_NULL);
}
/*
*===========================================================================
* ipdnsc_dst_key_to_dns_key
*===========================================================================
* Description: encode a public key into DNS KEY wire format
* Parameters
* key Key structure to encode.
* out_storage Location to write the encoded key to.
* out_len Size of the output array.
* Returns
* <0 Failure
* >=0 Number of bytes written to out_storage
*/
IP_STATIC Ip_s32
ipdnsc_dst_key_to_dns_key(const void *in_key, Ip_u8 *out_storage, const Ip_s32 out_len)
{
Ip_u16 val;
Ip_s32 loc = 0;
Ip_s32 enc_len = 0;
Ipdnsc_dst_key *key = (Ipdnsc_dst_key*)in_key;
if (key == IP_NULL)
return (-1);
ipcom_memset(out_storage, 0, out_len);
val = (Ip_u16)(key->dk_flags & 0xffff);
ipdnsc_dst_s_put_int16(out_storage, val);
loc += 2;
out_storage[loc++] = (Ip_u8) key->dk_proto;
out_storage[loc++] = (Ip_u8) key->dk_alg;
if (key->dk_flags > 0xffff)
{
/* Extended flags */
val = (Ip_u16)((key->dk_flags >> 16) & 0xffff);
ipdnsc_dst_s_put_int16(&out_storage[loc], val);
loc += 2;
}
if (key->dk_KEY_struct == IP_NULL)
return (loc);
if (key->dk_func && key->dk_func->to_dns_key)
{
enc_len = key->dk_func->to_dns_key((void*)key, (Ip_u8 *) &out_storage[loc], out_len - loc);
if (enc_len > 0)
return (enc_len + loc);
else
return (-1);
}
return (-1);
}
/*
*===========================================================================
* Ipdnsc_dst_buffer_to_key
*===========================================================================
* Description: encode a string of raw data into a DST key
* Parameters:
* alg The algorithm (HMAC only)
* key A pointer to the data
* keylen The length of the data
* Returns
* IP_NULL an error occurred
* NON-IP_NULL the DST key
*/
IP_STATIC Ipdnsc_dst_key *
ipdnsc_dst_buffer_to_key(const char *key_name, /* name of the key */
const Ip_s32 alg, /* algorithm */
const Ip_s32 flags, /* dns flags */
const Ip_s32 protocol, /* dns protocol */
const Ip_u8 *key_buf, /* key in dns wire fmt */
const Ip_u32 key_len, /* size of key */
Ip_s32 *error)
{
Ipdnsc_dst_key *dkey = IP_NULL;
Ip_s32 dnslen;
Ip_u8 dns[2048];
if (alg != IPDNSC_ALG_HMAC_MD5)
{
*error = IPDNSC_UPDATE_ERR_NOTSUPPORT_ALG;
return IP_NULL;
}
if (key_name == IP_NULL || key_buf == IP_NULL)
return IP_NULL;
dkey = (Ipdnsc_dst_key *)ipcom_malloc(sizeof(Ipdnsc_dst_key));
if (dkey == IP_NULL)
return IP_NULL;
ipcom_memset(dkey, 0, sizeof(Ipdnsc_dst_key));
dkey->dk_key_name = ipcom_strdup(key_name);
if (dkey->dk_key_name == IP_NULL)
{
ipcom_free(dkey);
return IP_NULL;
}
dkey->dk_alg = alg;
dkey->dk_flags = flags;
dkey->dk_proto = protocol;
dkey->dk_KEY_struct = IP_NULL;
dkey->dk_key_size = -1;
dkey->dk_func = &(ipdnsc_dst_func_table[0]);
if (dkey->dk_func->from_dns_key((void*)dkey, key_buf, key_len) < 0)
{
return (ipdnsc_dst_free_key(dkey));
}
dnslen = ipdnsc_dst_key_to_dns_key(dkey, dns, sizeof(dns));
dkey->dk_id = ipdnsc_dst_s_dns_key_id(dns, dnslen);
return (dkey);
}
/*
*===========================================================================
* ipdnsc_dst_hmac_md5_sign
*===========================================================================
* Description:
* Call HMAC signing functions to sign a block of data.
* There are three steps to signing, INIT (initialize structures),
* UPDATE (hash (more) data), FINAL (generate a signature). This
* routine performs one or more of these steps.
* Parameters
* mode SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL.
* priv_key key to use for signing.
* context the context to be used in this digest
* data data to be signed.
* len length in bytes of data.
* signature location to store signature.
* sig_len size of the signature location
* returns
* N Success on SIG_MODE_FINAL = returns signature length in bytes
* 0 Success on SIG_MODE_INIT and UPDATE
* <0 Failure
*/
IP_STATIC Ip_s32
ipdnsc_dst_hmac_md5_sign(const Ip_s32 mode, void *in_key, void **context,
const Ip_u8 *data, const Ip_s32 len,
Ip_u8 *signature, const Ip_s32 sig_len)
{
Ipdnsc_hmac_key *key;
Ipdnsc_dst_key *d_key = (Ipdnsc_dst_key*)in_key;
Ip_s32 sign_len = 0;
MD5_CTX *ctx = IP_NULL;
#ifdef OPENSSL_FIPS
if (FIPS_mode())
{
ipcom_printf("IPDNSC : MD5 is not supported when FIPS mode is enabled!\n");
return (-1);
}
#endif
if (d_key == IP_NULL || d_key->dk_KEY_struct == IP_NULL)
return (-1);
if (mode & IPDNSC_SIG_MODE_INIT)
ctx = (MD5_CTX *) ipcom_malloc(sizeof(*ctx));
else if (context)
ctx = (MD5_CTX *) *context;
if (ctx == IP_NULL)
return (-1);
key = (Ipdnsc_hmac_key *) d_key->dk_KEY_struct;
if (mode & IPDNSC_SIG_MODE_INIT)
{
MD5_Init(ctx);
MD5_Update(ctx, key->hk_ipad, IPDNSC_HMAC_LEN);
}
if ((mode & IPDNSC_SIG_MODE_UPDATE) && (data && len > 0))
MD5_Update(ctx, data, len);
if (mode & IPDNSC_SIG_MODE_FINAL)
{
if (signature == IP_NULL || sig_len < MD5_DIGEST_LENGTH)
{
ipcom_free(ctx);
return (IPDNSC_SIGN_FINAL_FAILURE);
}
MD5_Final(signature, ctx);
/* perform outer MD5 */
MD5_Init(ctx);
MD5_Update(ctx, key->hk_opad, IPDNSC_HMAC_LEN);
MD5_Update(ctx, signature, MD5_DIGEST_LENGTH);
MD5_Final(signature, ctx);
sign_len = MD5_DIGEST_LENGTH;
ipcom_free(ctx);
}
else
{
if (context == IP_NULL)
{
ipcom_free(ctx);
return (-1);
}
*context = (void *) ctx;
}
return (sign_len);
}
/*
*===========================================================================
* ipdnsc_dst_hmac_md5_verify
*===========================================================================
* Description:
* Calls HMAC verification routines. There are three steps to
* verification, INIT (initialize structures), UPDATE (hash (more) data),
* FINAL (generate a signature). This routine performs one or more of
* these steps.
* Parameters
* mode SIG_MODE_INIT, SIG_MODE_UPDATE and/or SIG_MODE_FINAL.
* dkey key to use for verify.
* data data signed.
* len length in bytes of data.
* signature signature.
* sig_len length in bytes of signature.
* returns
* 0 Success
* <0 Failure
*/
IP_STATIC Ip_s32
ipdnsc_dst_hmac_md5_verify(const Ip_s32 mode, void *in_key, void **context,
const Ip_u8 *data, const Ip_s32 len,
const Ip_u8 *signature, const Ip_s32 sig_len)
{
Ipdnsc_hmac_key *key;
Ipdnsc_dst_key *d_key = (Ipdnsc_dst_key*)in_key;
MD5_CTX *ctx = IP_NULL;
#ifdef OPENSSL_FIPS
if (FIPS_mode())
{
ipcom_printf("IPDNSC : MD5 is not supported when FIPS mode is enabled!\n");
return (-1);
}
#endif
if (d_key == IP_NULL || d_key->dk_KEY_struct == IP_NULL)
return (-1);
if (mode & IPDNSC_SIG_MODE_INIT)
ctx = (MD5_CTX *) ipcom_malloc(sizeof(*ctx));
else if (context)
ctx = (MD5_CTX *) *context;
if (ctx == IP_NULL)
return (-1);
key = (Ipdnsc_hmac_key *) d_key->dk_KEY_struct;
if (mode & IPDNSC_SIG_MODE_INIT)
{
MD5_Init(ctx);
MD5_Update(ctx, key->hk_ipad, IPDNSC_HMAC_LEN);
}
if ((mode & IPDNSC_SIG_MODE_UPDATE) && (data && len > 0))
MD5_Update(ctx, data, len);
if (mode & IPDNSC_SIG_MODE_FINAL)
{
Ip_u8 digest[MD5_DIGEST_LENGTH];
if (signature == IP_NULL || key == IP_NULL || sig_len != MD5_DIGEST_LENGTH)
{
ipcom_free(ctx);
return (IPDNSC_VERIFY_FINAL_FAILURE);
}
MD5_Final(digest, ctx);
/* perform outer MD5 */
MD5_Init(ctx);
MD5_Update(ctx, key->hk_opad, IPDNSC_HMAC_LEN);
MD5_Update(ctx, digest, MD5_DIGEST_LENGTH);
MD5_Final(digest, ctx);
ipcom_free(ctx);
if (ipcom_memcmp(digest, signature, MD5_DIGEST_LENGTH) != 0)
return (IPDNSC_VERIFY_FINAL_FAILURE);
}
else
{
if (context == IP_NULL)
{
ipcom_free(ctx);
return (-1);
}
*context = (void *) ctx;
}
return (0);
}
/*
*===========================================================================
* ipdnsc_dst_hmac_md5_free_key_structure
*===========================================================================
* Description: free key
* Parameters
* Returns
*/
IP_STATIC void *
ipdnsc_dst_hmac_md5_free_key_structure(void *in_key)
{
ipcom_free(in_key);
in_key = IP_NULL;
return IP_NULL;
}
/*
*===========================================================================
* ipdnsc_dst_hmac_md5_to_dns_key
*===========================================================================
* Description: extract hmac key from DST_KEY structure
* Parameters: (intput) in_key: HMAC-MD5 key
* (output) out_str: buffer to write to
out_len: size of output buffer
* returns: number of bytes written to output buffer
*/
IP_STATIC Ip_s32
ipdnsc_dst_hmac_md5_to_dns_key(const void *in_key, Ip_u8 *out_str, const Ip_s32 out_len)
{
Ipdnsc_hmac_key *hkey;
Ipdnsc_dst_key *dkey = (Ipdnsc_dst_key*)in_key;
Ip_s32 i;
if (dkey == IP_NULL || dkey->dk_KEY_struct == IP_NULL || out_len <= dkey->dk_key_size || out_str == IP_NULL)
return (-1);
hkey = (Ipdnsc_hmac_key *) dkey->dk_KEY_struct;
for (i = 0; i < dkey->dk_key_size; i++)
out_str[i] = hkey->hk_ipad[i] ^ IPDNSC_HMAC_IPAD;
return (i);
}
/*
*===========================================================================
* Ipdnsc_dst_buffer_to_hmac_md5
*===========================================================================
* Description:
* Converts key from raw data to an HMAC Key
* This function gets in a pointer to the data
* Parameters
* hkey the HMAC key to be filled in
* key the key in raw format
* keylen the length of the key
* Return
* 0 Success
* <0 Failure
*/
IP_STATIC Ip_s32
ipdnsc_dst_buffer_to_hmac_md5(void *in_key, const Ip_u8 *key, const Ip_s32 keylen)
{
Ipdnsc_hmac_key *hkey = IP_NULL;
Ipdnsc_dst_key *dkey = (Ipdnsc_dst_key*)in_key;
MD5_CTX ctx;
Ip_s32 i, local_keylen = keylen;
Ip_u8 tk[MD5_DIGEST_LENGTH];
#ifdef OPENSSL_FIPS
if (FIPS_mode())
{
ipcom_printf("IPDNSC : MD5 is not supported when FIPS mode is enabled!\n");
return (-1);
}
#endif
if (dkey == IP_NULL || key == IP_NULL || keylen < 0)
return (-1);
if ((hkey = (Ipdnsc_hmac_key *) ipcom_malloc(sizeof(Ipdnsc_hmac_key))) == IP_NULL)
return (-2);
ipcom_memset(hkey->hk_ipad, 0, sizeof(hkey->hk_ipad));
ipcom_memset(hkey->hk_opad, 0, sizeof(hkey->hk_opad));
/* if key is longer than IPDNSC_HMAC_LEN bytes reset it to key=MD5(key) */
if (keylen > IPDNSC_HMAC_LEN) {
MD5_Init(&ctx);
MD5_Update(&ctx, key, keylen);
MD5_Final(tk, &ctx);
ipcom_memset((void *) &ctx, 0, sizeof(ctx));
key = tk;
local_keylen = MD5_DIGEST_LENGTH;
}
/* start out by storing key in pads */
ipcom_memcpy(hkey->hk_ipad, key, local_keylen);
ipcom_memcpy(hkey->hk_opad, key, local_keylen);
/* XOR key with hk_ipad and opad values */
for (i = 0; i < IPDNSC_HMAC_LEN; i++) {
hkey->hk_ipad[i] ^= IPDNSC_HMAC_IPAD;
hkey->hk_opad[i] ^= IPDNSC_HMAC_OPAD;
}
dkey->dk_key_size = local_keylen;
dkey->dk_KEY_struct = (void *) hkey;
return (1);
}
#endif
/*
****************************************************************************
* 10 GLOBAL FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipdnsc_samename
*===========================================================================
* Description: determine whether domain name "a" is the same as domain name "b"
* Parameters:
* Returns: -1 on error or 0 if names differ or 1 if names are the same
*/
IP_GLOBAL Ip_s32
ipdnsc_samename(const char *a, const char *b)
{
char ta[IPDNSC_MAXDNAME];
char tb[IPDNSC_MAXDNAME];
if (ipdnsc_makecanon(a, ta, IPDNSC_MAXDNAME) < 0 ||
ipdnsc_makecanon(b, tb, IPDNSC_MAXDNAME) < 0)
return (-1);
if (ipcom_strcasecmp(ta, tb) == 0)
return (1);
else
return (0);
}
/*
*===========================================================================
* ipdnsc_free_nsrr
*===========================================================================
* Description: free nsrr from nsrrsp.
* Parameters:
* Returns:
*/
IP_GLOBAL void
ipdnsc_free_nsrrset(Ipdnsc_record_nsset *nsrrsp)
{
Ipdnsc_record_ns *nsrr, *nsrr_n;
nsrr = nsrrsp->head;
while (nsrr != IP_NULL)
{
nsrr_n = nsrr->next;
ipdnsc_free_nsrr(nsrrsp, nsrr);
nsrr = nsrr_n;
}
return;
}
/*
*===========================================================================
* ipdnsc_mkupdate
*===========================================================================
* Description: Form dnsc update packets.
* Parameters:
* Returns: the size of the resulting packet if no error
* -1 if error in reading a word/number in rdata portion for update packets
* -2 if length of buffer passed is insufficient
* -3 if zone section is not the first section in
* the linked list, or section order has a problem
* -4 on a number overflow
* -5 unknown operation or no records
*/
IP_GLOBAL Ip_s32
ipdnsc_mkupdate(Ipdnsc_updrec *rrecp_in, Ip_u8 *buf, Ip_s32 buflen, Ip_s32 *error)
{
IP_STATIC Ip_u16 id_count = 0;
Ipdnsc_updrec *rrecp, *rrecp_start = rrecp_in;
Ipdnsc_hdr *dns_hdr;
Ip_u8 *cp, *sp2, *startp, *endp;
Ip_s32 i, soanum, multiline;
struct Ip_in_addr ina;
#ifdef IPCOM_USE_INET6
struct Ip_in6_addr in6a;
#endif
char buf2[IPDNSC_MAXDNAME];
Ipdnsc_section section;
Ip_u32 numrrs = 0;
Ip_u32 counts[IPDNSC_SECTION_MAX]; /* number of items in each section */
Ip_u16 rtype, rclass;
Ip_u32 n1, rttl;
Ip_u8 *dnptrs[20], **dpp, **lastdnptr;
Ip_s32 qlen;
if (buf == IP_NULL)
return (-1);
ipcom_memset(buf, 0, buflen);
/* Assign values to the DNS message format */
dns_hdr = (Ipdnsc_hdr *) buf;
dns_hdr->id = IP_SWAP16(id_count);
id_count++;
dns_hdr->flags = IP_SWAP16(IPDNSC_UP_FLAG);
/* format DNS update data message */
cp = (Ip_u8 *)(dns_hdr + 1);
buflen -= (Ip_s32)sizeof(Ipdnsc_hdr);
dpp = dnptrs;
*dpp++ = buf;
*dpp++ = IP_NULL;
lastdnptr = dnptrs + sizeof(dnptrs) / sizeof(dnptrs[0]);
if (rrecp_start == IP_NULL)
return (-5);
else if (rrecp_start->r_section != IPDNSC_SECTION_ZN)
return (-3);
ipcom_memset(counts, 0, sizeof(counts));
for (rrecp = rrecp_start; rrecp; rrecp = rrecp->samezone_next)
{
numrrs++;
section = rrecp->r_section;
if (section >= IPDNSC_SECTION_MAX)
return (-1);
counts[section]++;
for (i = section + 1; i < IPDNSC_SECTION_MAX; i++)
if (counts[i])
return (-3);
rtype = rrecp->r_type;
rclass = rrecp->r_class;
rttl = rrecp->r_ttl;
/* overload class and type */
if (section == IPDNSC_SECTION_PR)
{
rttl = 0;
switch (rrecp->r_opcode)
{
case IPDNSC_RCODE_YXDOMAIN:
rclass = IPDNSC_QCLASS_ANY;
rtype = IPDNSC_QTYPE_ANY;
rrecp->r_size = 0;
break;
case IPDNSC_RCODE_NXDOMAIN:
rclass = IPDNSC_QCLASS_NONE;
rtype = IPDNSC_QTYPE_ANY;
rrecp->r_size = 0;
break;
case IPDNSC_RCODE_NXRRSET:
rclass = IPDNSC_QCLASS_NONE;
rrecp->r_size = 0;
break;
case IPDNSC_RCODE_YXRRSET:
if (rrecp->r_size == 0)
rclass = IPDNSC_QCLASS_ANY;
break;
default:
ipcom_printf("ipdnsc_mkupdate: [section Prerequisite] incorrect opcode: %d\n",rrecp->r_opcode);
return (-1);
}
}
else if (section == IPDNSC_SECTION_UP)
{
switch (rrecp->r_opcode)
{
case IPDNSC_UPOP_DELETE:
rclass = (Ip_u16)((rrecp->r_size == 0) ? IPDNSC_QCLASS_ANY : IPDNSC_QCLASS_NONE);
rrecp->r_ttl = 0;
break;
case IPDNSC_UPOP_ADD:
break;
default:
ipcom_printf("ipdnsc_mkupdate: [section update]incorrect opcode: %d\n", rrecp->r_opcode);
return (-1);
}
}
/* Convert domain name to query format */
if ((qlen = ipdnsc_name_compress(rrecp->r_dname, cp, buflen, dnptrs, lastdnptr, error)) < 0)
return (-1);
/* The remaining fields in the dns message may reside on any byte alignment */
cp += qlen;
/* Set the query type */
IP_SET_HTONS(cp, rtype);
cp += 2;
/* Set the query class */
IP_SET_HTONS(cp, rclass);
cp += 2;
ShrinkBuf(buflen,(qlen + 2*2));
if (section == IPDNSC_SECTION_ZN)
{
if (numrrs != 1 || rrecp->r_type != IPDNSC_QTYPE_SOA)
return (-3);
continue;
}
ShrinkBuf(buflen,(4 + 2));
IP_SET_HTONL(cp, rttl);
cp += 4;
sp2 = cp; /* save pointer to data length field(2 bytes) */
cp += 2;
if (rrecp->r_size == 0)
{
if (section == IPDNSC_SECTION_UP && rclass != IPDNSC_QCLASS_ANY)
return (-1);
else
{
IP_SET_HTONS(sp2, 0);
sp2 += 2;
continue;
}
}
startp = rrecp->r_data;
endp = startp + rrecp->r_size - 1;
/* XXX this should be done centrally. */
switch (rrecp->r_type)
{
case IPDNSC_QTYPE_A:
if (!ipdnsc_getword_str(buf2, IPDNSC_MAXDNAME, &startp, endp))
return (-1);
if (ipcom_inet_pton(IP_AF_INET, buf2, &ina) <= 0)
return (-1);
n1 = ip_ntohl(ina.s_addr);
ShrinkBuf(buflen,4);
IP_SET_HTONL(cp,n1);
cp += 4;
break;
case IPDNSC_QTYPE_CNAME:
case IPDNSC_QTYPE_MB:
case IPDNSC_QTYPE_MG:
case IPDNSC_QTYPE_MR:
case IPDNSC_QTYPE_NS:
case IPDNSC_QTYPE_PTR:
case IPDNSC_QTYPE_DNAME:
if (!ipdnsc_getword_str(buf2, IPDNSC_MAXDNAME, &startp, endp))
return (-1);
if ((qlen = ipdnsc_name_compress(buf2, cp, buflen, dnptrs, lastdnptr, error)) < 0)
return (-1);
cp += qlen;
ShrinkBuf(buflen,qlen);
break;
case IPDNSC_QTYPE_MINFO:
case IPDNSC_QTYPE_SOA:
case IPDNSC_QTYPE_RP:
for (i = 0; i < 2; i++) {
if (!ipdnsc_getword_str(buf2, IPDNSC_MAXDNAME, &startp, endp))
return (-1);
if ((qlen = ipdnsc_name_compress(buf2, cp, buflen, dnptrs, lastdnptr, error)) < 0)
return (-1);
cp += qlen;
ShrinkBuf(buflen,qlen);
}
if (rrecp->r_type == IPDNSC_QTYPE_SOA) {
ShrinkBuf(buflen,(5 * 4));
while (ipcom_isspace(*startp) || !*startp)
startp++;
if (*startp == '(') {
multiline = 1;
startp++;
} else
multiline = 0;
/* serial, refresh, retry, expire, minimum */
for (i = 0; i < 5; i++) {
soanum = ipdnsc_getnum_str(&startp, endp);
if (soanum < 0)
return (-1);
IP_SET_HTONL(cp,soanum);
cp += 4;
}
if (multiline) {
while (ipcom_isspace(*startp) || !*startp)
startp++;
if (*startp != ')')
return (-1);
}
}
break;
case IPDNSC_QTYPE_MX:
case IPDNSC_QTYPE_AFSDB:
case IPDNSC_QTYPE_RT:
if ((qlen = ipdnsc_getnum_str(&startp, endp)) < 0)
return (-1);
ShrinkBuf(buflen,2);
IP_SET_HTONS(cp,qlen);
cp += 2;
if (!ipdnsc_getword_str(buf2, IPDNSC_MAXDNAME, &startp, endp))
return (-1);
if ((qlen = ipdnsc_name_compress(buf2, cp, buflen, dnptrs, lastdnptr, error)) < 0)
return (-1);
cp += qlen;
ShrinkBuf(buflen,qlen);
break;
case IPDNSC_QTYPE_SRV:
if ((qlen = ipdnsc_getnum_str(&startp, endp)) < 0)
return (-1);
ShrinkBuf(buflen,2);
IP_SET_HTONS(cp,qlen);
cp += 2;
if ((qlen = ipdnsc_getnum_str(&startp, endp)) < 0)
return (-1);
ShrinkBuf(buflen,2);
IP_SET_HTONS(cp,qlen);
cp += 2;
if ((qlen = ipdnsc_getnum_str(&startp, endp)) < 0)
return (-1);
ShrinkBuf(buflen,2);
IP_SET_HTONS(cp,qlen);
cp += 2;
if (!ipdnsc_getword_str(buf2, IPDNSC_MAXDNAME, &startp, endp))
return (-1);
if ((qlen = ipdnsc_name_compress(buf2, cp, buflen, IP_NULL, IP_NULL, error)) < 0)
return (-1);
cp += qlen;
ShrinkBuf(buflen,qlen);
break;
case IPDNSC_QTYPE_PX:
if ((qlen = ipdnsc_getnum_str(&startp, endp)) < 0)
return (-1);
IP_SET_HTONS(cp,qlen);
cp += 2;
ShrinkBuf(buflen,2);
for (i = 0; i < 2; i++) {
if (!ipdnsc_getword_str(buf2, IPDNSC_MAXDNAME, &startp, endp))
return (-1);
if ((qlen = ipdnsc_name_compress(buf2, cp, buflen, dnptrs, lastdnptr, error)) < 0)
return (-1);
cp += qlen;
ShrinkBuf(buflen,qlen);
}
break;
case IPDNSC_QTYPE_HINFO:
for (i = 0; i < 2; i++) {
if ((qlen = ipdnsc_getstr_str(buf2, IPDNSC_MAXDNAME, &startp, endp)) < 0)
return (-1);
if (qlen > 255)
return (-1);
ShrinkBuf(buflen,(qlen+1));
*cp++ = (Ip_u8)qlen;
ipcom_memcpy(cp, buf2, qlen);
cp += qlen;
}
break;
case IPDNSC_QTYPE_TXT:
for (;;) {
if ((qlen = ipdnsc_getstr_str(buf2, IPDNSC_MAXDNAME, &startp, endp)) < 0) {
if (cp != (sp2 + 2))
break;
return (-1);
}
if (qlen > 255)
return (-1);
ShrinkBuf(buflen,(qlen+1));
*cp++ = (Ip_u8)qlen;
ipcom_memcpy(cp, buf2, qlen);
cp += qlen;
}
break;
case IPDNSC_QTYPE_X25:
/* RFC1183 */
if ((qlen = ipdnsc_getstr_str(buf2, IPDNSC_MAXDNAME, &startp, endp)) < 0)
return (-1);
if (qlen > 255)
return (-1);
ShrinkBuf(buflen, qlen+1);
*cp++ = (Ip_u8)qlen;
ipcom_memcpy(cp, buf2, qlen);
cp += qlen;
break;
case IPDNSC_QTYPE_ISDN:
/* RFC1183 */
if ((qlen = ipdnsc_getstr_str(buf2, IPDNSC_MAXDNAME, &startp, endp)) < 0)
return (-1);
if ((qlen > 255) || (qlen == 0))
return (-1);
ShrinkBuf(buflen,(qlen+1));
*cp++ = (Ip_u8)qlen;
ipcom_memcpy(cp, buf2, qlen);
cp += qlen;
if ((qlen = ipdnsc_getstr_str(buf2, IPDNSC_MAXDNAME, &startp, endp)) < 0)
qlen = 0;
if (qlen > 255)
return (-1);
ShrinkBuf(buflen,(qlen+1));
*cp++ = (Ip_u8)qlen;
ipcom_memcpy(cp, buf2, qlen);
cp += qlen;
break;
#ifdef IPCOM_USE_INET6
case IPDNSC_QTYPE_AAAA:
if (!ipdnsc_getword_str(buf2, IPDNSC_MAXDNAME, &startp, endp))
return (-1);
if (ipcom_inet_pton(IP_AF_INET6, buf2, &in6a) <= 0)
return (-1);
ShrinkBuf(buflen,16); /* IPv6 Address Size */
ipcom_memcpy(cp, &in6a, 16);
cp += 16;
break;
#endif
case IPDNSC_QTYPE_NAPTR:
/* Order Preference Flags Service Replacement Regexp */
/* Order */
qlen = ipdnsc_getnum_str(&startp, endp);
if (qlen < 0 || qlen > 65535)
return (-1);
ShrinkBuf(buflen,2);
IP_SET_HTONS(cp,qlen);
cp += 2;
/* Preference */
qlen = ipdnsc_getnum_str(&startp, endp);
if (qlen < 0 || qlen > 65535)
return (-1);
ShrinkBuf(buflen,2);
IP_SET_HTONS(cp,qlen);
cp += 2;
/* Flags */
if ((qlen = ipdnsc_getstr_str(buf2, IPDNSC_MAXDNAME,&startp, endp)) < 0)
return (-1);
if (qlen > 255)
return (-1);
ShrinkBuf(buflen,(qlen+1));
*cp++ = (Ip_u8)qlen;
ipcom_memcpy(cp, buf2, qlen);
cp += qlen;
/* Service Classes */
if ((qlen = ipdnsc_getstr_str(buf2, IPDNSC_MAXDNAME, &startp, endp)) < 0)
return (-1);
if (qlen > 255)
return (-1);
ShrinkBuf(buflen,(qlen+1));
*cp++ = (Ip_u8)qlen;
ipcom_memcpy(cp, buf2, qlen);
cp += qlen;
/* Pattern */
if ((qlen = ipdnsc_getstr_str(buf2, IPDNSC_MAXDNAME,&startp, endp)) < 0)
return (-1);
if (qlen > 255)
return (-1);
ShrinkBuf(buflen,(qlen+1));
*cp++ = (Ip_u8)qlen;
ipcom_memcpy(cp, buf2, qlen);
cp += qlen;
/* Replacement */
if (!ipdnsc_getword_str(buf2, IPDNSC_MAXDNAME, &startp, endp))
return (-1);
if ((qlen = ipdnsc_name_compress(buf2, cp, buflen, IP_NULL, IP_NULL, error)) < 0)
return (-1);
cp += qlen;
ShrinkBuf(buflen,qlen);
break;
default:
*error = IPDNSC_UPDATE_ERR_NOTSUPPORT_TYPE;
return (-1);
} /*switch*/
qlen = (Ip_u16)((cp - sp2) - 2);
IP_SET_HTONS(sp2,qlen);
sp2 += 2;
} /*for*/
dns_hdr->no_ques = ipcom_htons((Ip_u16)counts[0]);
dns_hdr->no_answ = ipcom_htons((Ip_u16)counts[1]);
dns_hdr->no_auth = ipcom_htons((Ip_u16)counts[2]);
dns_hdr->no_addi = ipcom_htons((Ip_u16)counts[3]);
return (Ip_s32)(cp - buf);
}
/*
*===========================================================================
* ipdnsc_get_soa
*===========================================================================
* Description: get SOA
* Parameters:
* Returns:
*/
IP_GLOBAL Ip_s32
ipdnsc_get_soa(const char *dname, Ip_u16 qclass, char *zname,
Ip_size_t zsize, char *mname, Ip_size_t msize, Ipdnsc_record_nsset *nsrrsp, Ip_s32 *error)
{
char tname[IPDNSC_MAXDNAME];
Ip_s32 n, i, ancount, nscount;
Ipdnsc_section sect;
Ip_u32 rcode;
Ipdnsc_ns_spec dns;
Ipdnsc_general_msg g_msg;
Ip_u8 *r_buf;
Ip_s32 r_size = IPDNSC_MAXPKT;
/* Configure the DNS */
if (ipdnsc_default(&dns))
{
*error = IPDNSC_UPDATE_ERR_GENERAL;
return -1;
}
/* Get buffer memory. */
r_buf = ipcom_malloc(IPDNSC_MAXPKT);
if (r_buf == IP_NULL)
return -1;
/*
* Find closest enclosing SOA, even if it's for the root zone.
*/
/* First canonicalize dname (exactly one unescaped trailing "."). */
if (ipdnsc_makecanon(dname, tname, IPDNSC_MAXDNAME) < 0)
goto cleanup;
dname = tname;
/* Now grovel the subdomains, hunting for an SOA answer or auth. */
for (;;)
{
/* Leading or inter-label '.' are skipped here. */
while (*dname == '.')
dname++;
/* Is there an SOA? */
ipcom_memset(r_buf, 0, IPDNSC_MAXPKT);
ipcom_memset(&g_msg, 0, sizeof(Ipdnsc_general_msg));
n = ipdnsc_do_query_ext((char*)dname, qclass, IPDNSC_QTYPE_SOA, r_buf, &r_size, &dns, &g_msg, error);
if (n < 0)
goto cleanup;
if (n > 0)
{
/* ipdnsc_do_query_soa: CNAME or DNAME found */
sect = IPDNSC_SECTION_MAX;
n = 0;
}
else
{
rcode = g_msg.flags & IPDNSC_RCODE_FLAG;
ancount = g_msg.counts[IPDNSC_SECTION_AN];
nscount = g_msg.counts[IPDNSC_SECTION_NS];
if (ancount > 0 && rcode == IPDNSC_RCODE_NOERROR)
{ sect = IPDNSC_SECTION_AN; n = ancount; }
else if (nscount > 0)
{ sect = IPDNSC_SECTION_NS; n = nscount; }
else
{sect = IPDNSC_SECTION_MAX; n = 0;}
}
for (i = 0; i < n; i++)
{
const char *t;
const Ip_u8 *rdata;
Ipdnsc_record rr;
ipcom_memset(&rr, 0, sizeof(Ipdnsc_record));
if (ipdnsc_parserr(&g_msg, sect, i, &rr) < 0)
goto cleanup;
if (rr.type == IPDNSC_QTYPE_CNAME|| rr.type == IPDNSC_QTYPE_DNAME)
break;
if (rr.type != IPDNSC_QTYPE_SOA|| rr.rr_class != qclass)
continue;
t = (rr.name[0] != '\0') ? rr.name : ".";
switch (sect)
{
case IPDNSC_SECTION_AN:
if (ipdnsc_samedomain(dname, t) == 0)
{
*error = IPDNSC_UPDATE_ERR_EPROTOTYPE;
goto cleanup;
}
break;
case IPDNSC_SECTION_NS:
if (ipdnsc_samename(dname, t) == 1 || ipdnsc_samedomain(dname, t) == 0)
{
*error = IPDNSC_UPDATE_ERR_EPROTOTYPE;
goto cleanup;
}
break;
default:
break;
}
if (ipcom_strlen(t) + 1 > zsize)
{
*error = IPDNSC_UPDATE_ERR_MSGSIZE;
goto cleanup;
}
ipcom_strcpy(zname, t);
rdata = rr.rdata;
if (ipdnsc_name_uncompress(r_buf, g_msg.end, rdata, mname, msize) < 0)
goto cleanup;
if (ipdnsc_save_ns(&g_msg, IPDNSC_SECTION_NS, zname, qclass, nsrrsp) < 0)
{
goto cleanup;
}
ipcom_free(r_buf);
return (0);
}
/* If we're out of labels, then not even "." has an SOA! */
if (*dname == '\0')
break;
/* Find label-terminating "."; top of loop will skip it. */
while (*dname != '.')
{
if (*dname == '\\')
if (*++dname == '\0')
{
*error = IPDNSC_UPDATE_ERR_MSGSIZE;
goto cleanup;
}
dname++;
}
}
cleanup:
if (r_buf != IP_NULL)
ipcom_free(r_buf);
return (-1);
}
/*
*===========================================================================
* ipdnsc_get_ns
*===========================================================================
* Description: get name server.
* Parameters:
* Returns: 0 ok or -1 fail
*/
IP_GLOBAL Ip_s32
ipdnsc_get_ns(char *zname, Ip_u16 qclass, Ipdnsc_record_nsset *nsrrsp, Ip_s32 *error)
{
Ip_s32 n;
Ipdnsc_ns_spec dns;
Ipdnsc_general_msg g_msg;
Ip_u8 *r_buf;
Ip_s32 r_size = IPDNSC_MAXPKT;
/* Configure the DNS */
if (ipdnsc_default(&dns))
{
*error = IPDNSC_UPDATE_ERR_GENERAL;
return -1;
}
/* Get buffer memory. */
r_buf = ipcom_malloc(IPDNSC_MAXPKT);
if (r_buf == IP_NULL)
return -1;
/* Go and get the NS RRs for this zone. */
n = ipdnsc_do_query_ext(zname, qclass, IPDNSC_QTYPE_NS, r_buf, &r_size, &dns, &g_msg, error);
if (n != 0)
{
ipcom_free(r_buf);
return (-1);
}
/* Remember the NS RRs and associated A RRs that came back. */
if (ipdnsc_save_ns(&g_msg, IPDNSC_SECTION_AN, zname, qclass, nsrrsp) < 0)
{
ipcom_free(r_buf);
return (-1);
}
ipcom_free(r_buf);
return (0);
}
/*
*===========================================================================
* ipdnsc_get_glue
*===========================================================================
* Description:
* Parameters:
* Returns:
*/
IP_GLOBAL Ip_s32
ipdnsc_get_glue(Ip_u16 qclass, Ipdnsc_record_nsset *nsrrsp, Ip_s32 *error)
{
Ipdnsc_record_ns *nsrr, *nsrr_n;
Ipdnsc_ns_spec dns;
Ipdnsc_general_msg g_msg;
Ip_u8 *r_buf;
Ip_s32 r_size = IPDNSC_MAXPKT;
/* Configure the DNS */
if (ipdnsc_default(&dns))
{
*error = IPDNSC_UPDATE_ERR_GENERAL;
return -1;
}
/* Get buffer memory. */
r_buf = ipcom_malloc(IPDNSC_MAXPKT);
if (r_buf == IP_NULL)
{
*error = IPDNSC_UPDATE_ERR_GENERAL;
return -1;
}
/* Go and get the A RRs for each empty NS RR on our list. */
for (nsrr = nsrrsp->head; nsrr != IP_NULL; nsrr = nsrr_n)
{
Ip_s32 n;
nsrr_n = nsrr->next;
if ((nsrr->flags & IPDNSC_RR_NS_HAVE_V4) == 0)
{
n = ipdnsc_do_query_ext(nsrr->name, qclass, IPDNSC_QTYPE_A, r_buf, &r_size, &dns, &g_msg, error);
if (n < 0)
goto cleanup;
if (ipdnsc_save_a(&g_msg, IPDNSC_SECTION_AN, nsrr->name, qclass, nsrr) < 0)
goto cleanup;
}
if ((nsrr->flags & IPDNSC_RR_NS_HAVE_V6) == 0)
{
n = ipdnsc_do_query_ext(nsrr->name, qclass, IPDNSC_QTYPE_AAAA, r_buf, &r_size, &dns, &g_msg, error);
if (n < 0)
goto cleanup;
if (ipdnsc_save_a(&g_msg, IPDNSC_SECTION_AN, nsrr->name, qclass, nsrr) < 0)
goto cleanup;
}
/* If it's still empty, it's just chaff. */
if (nsrr->rr_addrs == IP_NULL)
{
ipcom_printf("ipdnsc_get_glue: No valid nameserver found in processing record '%s'. \n", nsrr->name);
ipdnsc_free_nsrr(nsrrsp, nsrr);
}
}
ipcom_free(r_buf);
return (0);
cleanup:
ipcom_free(r_buf);
return (-1);
}
/*
*===========================================================================
* ipdnsc_satisfy
*===========================================================================
* Description:
* Parameters:
* Returns:
*/
IP_GLOBAL Ip_s32
ipdnsc_satisfy(const char *mname, Ipdnsc_record_nsset *nsrrsp, Ipdnsc_ns_addr *addrs)
{
Ipdnsc_record_ns *nsrr;
Ip_s32 n, x;
Ip_s32 naddrs = IPDNSC_MAX_NAMESERVER; /* The number of address is 3 */
n = 0;
nsrr = ipdnsc_find_ns(nsrrsp, mname);
if (nsrr != IP_NULL)
{
x = ipdnsc_add_addrs(nsrr, addrs, &n);
addrs += x;
naddrs -= x;
}
for (nsrr = nsrrsp->head; nsrr != IP_NULL && naddrs > 0; nsrr = nsrr->next)
if (ipdnsc_samename(nsrr->name, mname) != 1)
{
x = ipdnsc_add_addrs(nsrr, addrs, &n);
addrs += x;
naddrs -= x;
}
return (n);
}
/*
*===========================================================================
* ipdnsc_send_update
*===========================================================================
* Description: send update packet to server
* Parameters:
* Returns
*/
IP_GLOBAL Ip_s32
ipdnsc_send_update(Ip_u8 *s_buf, Ip_s32 s_buflen, Ip_u8 *r_buf, Ip_s32 *r_buflen,
Ipdnsc_ns_addr *dns_addr, Ip_u32 dns_count, Ip_s32 *error)
{
Ip_s32 timeout, retry, retries, err;
/* check received number of bytes */
if (*r_buflen < (Ip_s32)sizeof(Ipdnsc_hdr))
{
*error = IPDNSC_ERR_GENERAL;
return IPDNSC_OK;
}
/* Get other DNS parameters */
timeout = ipcom_sysvar_get_as_int ("ipdnsc.timeout", IPDNSC_DEFAULT_DNS_TIMEOUT);
retry = ipcom_sysvar_get_as_int ("ipdnsc.retries", IPDNSC_DEFAULT_DNS_RETRY);
retries = retry;
/* Send request, RETRY times, or until successful. */
resend:
err = ipdnsc_send_wait(s_buf, s_buflen, r_buf, r_buflen, dns_addr, timeout);
switch(err)
{
case IPDNSC_OK:
break;
case IPDNSC_SERV_FAIL:
case IPDNSC_SERV_NOTIMPL:
case IPDNSC_SERV_REFUSED:
/* Skip further retries */
retries = 0;
/* Fall through */
case IPDNSC_SERV_TIMEOUT:
if (retries-- > 0 )
{
/* Retry same name server */
goto resend;
}
else if (dns_count > 0)
{
/* Jump to secondary name server */
dns_addr++;
dns_count--;
retries = retry;
goto resend;
}
else
{
/* Tried all nameservers */
*error = IPDNSC_UPDATE_ERR_GENERAL;
}
ipcom_printf("ipdnsc_send_update():: Timeout in sending packets to server.\n");
goto fail;
case IPDNSC_SERV_NONAME:
*error = IPDNSC_HOST_NOT_FOUND;
goto fail;
case IPDNSC_SERV_YXRRSET:
*error = IPDNSC_UPDATE_ERR_YXRRSET;
goto fail;
case IPDNSC_ERR_GENERAL:
case IPDNSC_SERV_FORMAT:
default:
*error = IPDNSC_UPDATE_ERR_GENERAL;
goto fail;
}
fail:
return err;
}
/*
*===========================================================================
* ipdnsc_send_update_signed
*===========================================================================
* Description: send update packet to server with key signed.
* Parameters:
* Returns
*/
IP_GLOBAL Ip_s32
ipdnsc_send_update_signed(const Ip_u8 *s_buf, Ip_s32 s_buflen, Ip_u8 *r_buf, Ip_s32 *r_buflen,
Ipdnsc_ns_addr *dns_addr, Ip_u32 dns_count, Ipdnsc_tsig_key *key, Ip_s32 *error)
{
#ifndef IPDNSC_INCLUDE_MD5
IPCOM_UNUSED_ARG(s_buf);
IPCOM_UNUSED_ARG(s_buflen);
IPCOM_UNUSED_ARG(r_buf);
IPCOM_UNUSED_ARG(r_buflen);
IPCOM_UNUSED_ARG(dns_addr);
IPCOM_UNUSED_ARG(dns_count);
IPCOM_UNUSED_ARG(key);
*error = IPDNSC_UPDATE_ERR_MD5_DISABLED;
ipcom_printf(" ipdnsc_send_update_signed():: failed for MD5 is disabled. \n");
return IPDNSC_ERR_GENERAL;
#else
Ipdnsc_dst_key *dstkey;
Ip_u8 *new_s_buf, *secret;
Ip_s32 new_s_buflen, bufsize, siglen, secretlen;
Ip_u8 sig[64];
Ip_time_t tsig_time;
Ip_s32 ret;
if (ipdnsc_samename(key->alg, IPDNSC_TSIG_ALG_HMAC_MD5) != 1)
{
*error = IPDNSC_UPDATE_ERR_NOTSUPPORT_ALG;
return IPDNSC_ERR_GENERAL;
}
bufsize = s_buflen + 1024;
new_s_buf = (Ip_u8 *)ipcom_malloc(bufsize);
if (new_s_buf == IP_NULL)
return IPDNSC_ERR_GENERAL;
ipcom_memcpy(new_s_buf, s_buf, s_buflen);
new_s_buflen = s_buflen;
/* Decode secret string(key->data) to the base64 secret. Then it will be the binary
equivalent with the secret of server's public key */
secretlen = (key->len * 3) / 4;
secret = (Ip_u8 *)ipcom_malloc(secretlen+1);
if (secret == IP_NULL)
{
ipcom_free(new_s_buf);
return IPDNSC_ERR_GENERAL;
}
secretlen = ipdnsc_base64_decodestring(key->secret, secret, secretlen);
if (secretlen < 0)
{
ipcom_printf(" ipdnsc_send_update_signed():: could not create key from %s: bad base64 encoding. \n", key->secret);
ipcom_free(secret);
ipcom_free(new_s_buf);
return IPDNSC_ERR_GENERAL;
}
dstkey = ipdnsc_dst_buffer_to_key(key->name, IPDNSC_ALG_HMAC_MD5, IPDNSC_KEY_TYPE_AUTH_ONLY, IPDNSC_KEY_PROT_ANY, secret, secretlen, error);
ipcom_free(secret);
if (dstkey == IP_NULL)
{
ipcom_free(new_s_buf);
return IPDNSC_ERR_GENERAL;
}
siglen = sizeof(sig);
ret = ipdnsc_sign(new_s_buf, &new_s_buflen, bufsize, dstkey, sig, &siglen, error);
if (ret < 0)
{
ipcom_free(new_s_buf);
ipdnsc_dst_free_key(dstkey);
*error = ret;
return IPDNSC_ERR_GENERAL;
}
ret = ipdnsc_send_update(new_s_buf, new_s_buflen, r_buf, r_buflen, dns_addr, dns_count, error);
if (ret < 0)
{
ipcom_free(new_s_buf);
ipdnsc_dst_free_key(dstkey);
return (ret);
}
ret = ipdnsc_verify(r_buf, r_buflen, dstkey, sig, siglen, IP_NULL, IP_NULL, &tsig_time, error);
if (ret != 0)
{
if (ret > 0)
ipcom_printf(" ipdnsc_send_update_signed():: server rejected TSIG for %s. Error code is %d. \n",ipdnsc_update_rmsg[ret&0x1F], *error);
else
{
*error = ret;
ipcom_printf(" ipdnsc_send_update_signed():: TSIG invalid.\n");
}
ipcom_free(new_s_buf);
ipdnsc_dst_free_key(dstkey);
return IPDNSC_ERR_GENERAL;
}
ipcom_free(new_s_buf);
ipdnsc_dst_free_key(dstkey);
return IPDNSC_OK;
#endif
}
/*
****************************************************************************
* 11 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/