4075 lines
113 KiB
C
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
|
|
* <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
|
|
****************************************************************************
|
|
*/
|
|
|