/* ****************************************************************************** * 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 ****************************************************************************** */ /* * 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 #include #ifdef OPENSSL_FIPS #include #endif #ifdef IPDNSC_INCLUDE_MD5 #ifdef IPMCRYPTO #include #else #include #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 is present, the number of digits in the * MUST be just sufficient to contain the number of bits specified * by the . 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 **************************************************************************** */