3807 lines
84 KiB
C
3807 lines
84 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1995-2001 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
name.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Domain Name System (DNS) Library
|
|||
|
|
|||
|
DNS name routines.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Jim Gilroy (jamesg) October 1995
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
jamesg Jan 1997 UTF-8, Unicode conversions
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
|
|||
|
#include "local.h"
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// DNS name cannonicalization
|
|||
|
//
|
|||
|
// Flags to form cannonical name.
|
|||
|
//
|
|||
|
|
|||
|
#define DNS_CANONICALIZING_FLAGS ( LCMAP_LOWERCASE )
|
|||
|
|
|||
|
//
|
|||
|
// Comparison flags -- args to compare string
|
|||
|
//
|
|||
|
// These flags are what DS uses when calling CompareString.
|
|||
|
// They are defined in ntdsapi.h.
|
|||
|
//
|
|||
|
// Note: these NORM_IGNOREXYZ flags which directory uses
|
|||
|
// for compare, actually STOP downcasing of these in LCMapString
|
|||
|
// -- international folks need to give us the correct deal here
|
|||
|
//
|
|||
|
// We will only use the IGNORECASE flag because this is the
|
|||
|
// only one which we can use in LCMapString().
|
|||
|
// We want to say that if two names compare equal that you
|
|||
|
// can register either one and lookup the other and get the
|
|||
|
// result. In other words they are equal throughout the
|
|||
|
// client-server system.
|
|||
|
//
|
|||
|
|
|||
|
#if 0
|
|||
|
#define DS_DEFAULT_LOCALE_COMPARE_FLAGS (NORM_IGNORECASE | \
|
|||
|
NORM_IGNOREKANATYPE | \
|
|||
|
NORM_IGNORENONSPACE | \
|
|||
|
NORM_IGNOREWIDTH)
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
#define DNS_CANONICAL_COMPARE_FLAGS ( NORM_IGNORECASE )
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Locale to canonically downcase in.
|
|||
|
//
|
|||
|
// Need to disambiguate to a universal standard so that every DNS
|
|||
|
// server interprets these the same way.
|
|||
|
//
|
|||
|
// In Win2K we used US English.
|
|||
|
// Sublang: US English (0x04) Lang: English (0x09)
|
|||
|
// (note sublang US english actually 0x1, but sublang starts at
|
|||
|
// bit 10)
|
|||
|
//
|
|||
|
// #define DNS_CANONICAL_LOCALE ( 0x0409 )
|
|||
|
//
|
|||
|
// For Whistler invariant locale is created; It is actually the
|
|||
|
// same as US English for downcasing -- US English has no
|
|||
|
// exceptions to the default case conversion table.
|
|||
|
//
|
|||
|
|
|||
|
#define DNS_CANONICAL_LOCALE ( LOCALE_INVARIANT )
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// DNS Character properties for validation
|
|||
|
//
|
|||
|
// DCR: combine char validation and file tables
|
|||
|
// Probably could be combined with file character
|
|||
|
// lookup, by simply merging bit fields appropriately.
|
|||
|
// At this point time, however, no need to disturb
|
|||
|
// file lookup, which is working fine.
|
|||
|
//
|
|||
|
// Character attributes bitfields
|
|||
|
//
|
|||
|
|
|||
|
#define B_RFC 0x00000001
|
|||
|
#define B_NUMBER 0x00000002
|
|||
|
#define B_UPPER 0x00000004
|
|||
|
#define B_NON_RFC 0x00000008
|
|||
|
|
|||
|
#define B_UTF8_TRAIL 0x00000010
|
|||
|
#define B_UTF8_FIRST_TWO 0x00000020
|
|||
|
#define B_UTF8_FIRST_THREE 0x00000040
|
|||
|
#define B_UTF8_PAIR 0x00000080
|
|||
|
|
|||
|
#define B_DOT 0x00000800
|
|||
|
#define B_SPECIAL 0x00001000
|
|||
|
#define B_LEADING_ONLY 0x00004000
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Generic characters
|
|||
|
//
|
|||
|
|
|||
|
#define DC_RFC (B_RFC)
|
|||
|
#define DC_LOWER (B_RFC)
|
|||
|
#define DC_UPPER (B_UPPER | B_RFC)
|
|||
|
#define DC_NUMBER (B_NUMBER | B_RFC)
|
|||
|
#define DC_NON_RFC (B_NON_RFC)
|
|||
|
|
|||
|
#define DC_UTF8_TRAIL (B_UTF8_TRAIL)
|
|||
|
#define DC_UTF8_1ST_2 (B_UTF8_FIRST_TWO)
|
|||
|
#define DC_UTF8_1ST_3 (B_UTF8_FIRST_THREE)
|
|||
|
#define DC_UTF8_PAIR (B_UTF8_PAIR)
|
|||
|
|
|||
|
//
|
|||
|
// Special characters
|
|||
|
// * valid as single label wildcard
|
|||
|
// _ leading SRV record domain names
|
|||
|
// / in classless in-addr
|
|||
|
//
|
|||
|
|
|||
|
#define DC_DOT (B_SPECIAL | B_DOT)
|
|||
|
|
|||
|
#define DC_ASTERISK (B_SPECIAL | B_LEADING_ONLY)
|
|||
|
|
|||
|
#define DC_UNDERSCORE (B_SPECIAL | B_LEADING_ONLY)
|
|||
|
|
|||
|
#define DC_BACKSLASH (B_SPECIAL)
|
|||
|
|
|||
|
//
|
|||
|
// More special
|
|||
|
// These have no special validations, but have special file
|
|||
|
// properties, so define to keep table in shape for merge with
|
|||
|
// file chars.
|
|||
|
//
|
|||
|
|
|||
|
#define DC_NULL (0)
|
|||
|
|
|||
|
#define DC_OCTAL (B_NON_RFC)
|
|||
|
#define DC_RETURN (B_NON_RFC)
|
|||
|
#define DC_NEWLINE (B_NON_RFC)
|
|||
|
#define DC_TAB (B_NON_RFC)
|
|||
|
#define DC_BLANK (B_NON_RFC)
|
|||
|
#define DC_QUOTE (B_NON_RFC)
|
|||
|
#define DC_SLASH (B_NON_RFC)
|
|||
|
#define DC_OPEN_PAREN (B_NON_RFC)
|
|||
|
#define DC_CLOSE_PAREN (B_NON_RFC)
|
|||
|
#define DC_COMMENT (B_NON_RFC)
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// DNS character table
|
|||
|
//
|
|||
|
// These routines handle the name conversion issues relating to
|
|||
|
// writing names and strings in flat ANSI files
|
|||
|
// -- special file characters
|
|||
|
// -- quoted string
|
|||
|
// -- character quotes for special characters and unprintable chars
|
|||
|
//
|
|||
|
// The character to char properties table allows simple mapping of
|
|||
|
// a character to its properties saving us a bunch of compare\branch
|
|||
|
// instructions in parsing file names\strings.
|
|||
|
//
|
|||
|
// See nameutil.h for specific properties.
|
|||
|
//
|
|||
|
|
|||
|
DWORD DnsCharPropertyTable[] =
|
|||
|
{
|
|||
|
// control chars 0-31 must be octal in all circumstances
|
|||
|
// end-of-line and tab characters are special
|
|||
|
|
|||
|
DC_NULL, // zero special on read, some RPC strings NULL terminated
|
|||
|
|
|||
|
DC_OCTAL, DC_OCTAL, DC_OCTAL, DC_OCTAL,
|
|||
|
DC_OCTAL, DC_OCTAL, DC_OCTAL, DC_OCTAL,
|
|||
|
|
|||
|
DC_TAB, // tab
|
|||
|
DC_NEWLINE, // line feed
|
|||
|
DC_OCTAL,
|
|||
|
DC_OCTAL,
|
|||
|
DC_RETURN, // carriage return
|
|||
|
DC_OCTAL,
|
|||
|
DC_OCTAL,
|
|||
|
|
|||
|
DC_OCTAL, DC_OCTAL, DC_OCTAL, DC_OCTAL,
|
|||
|
DC_OCTAL, DC_OCTAL, DC_OCTAL, DC_OCTAL,
|
|||
|
DC_OCTAL, DC_OCTAL, DC_OCTAL, DC_OCTAL,
|
|||
|
DC_OCTAL, DC_OCTAL, DC_OCTAL, DC_OCTAL,
|
|||
|
|
|||
|
DC_BLANK, // blank, special char but needs octal quote
|
|||
|
|
|||
|
DC_NON_RFC, // !
|
|||
|
DC_QUOTE, // " always must be quoted
|
|||
|
DC_NON_RFC, // #
|
|||
|
DC_NON_RFC, // $
|
|||
|
DC_NON_RFC, // %
|
|||
|
DC_NON_RFC, // &
|
|||
|
DC_NON_RFC, // '
|
|||
|
|
|||
|
DC_OPEN_PAREN, // ( datafile line extension
|
|||
|
DC_CLOSE_PAREN, // ) datafile line extension
|
|||
|
DC_ASTERISK, // *
|
|||
|
DC_NON_RFC, // +
|
|||
|
DC_NON_RFC, // ,
|
|||
|
DC_RFC, // - RFC for hostname
|
|||
|
DC_DOT, // . must quote in names
|
|||
|
DC_BACKSLASH, // /
|
|||
|
|
|||
|
// 0 - 9 RFC for hostname
|
|||
|
|
|||
|
DC_NUMBER, DC_NUMBER, DC_NUMBER, DC_NUMBER,
|
|||
|
DC_NUMBER, DC_NUMBER, DC_NUMBER, DC_NUMBER,
|
|||
|
DC_NUMBER, DC_NUMBER,
|
|||
|
|
|||
|
DC_NON_RFC, // :
|
|||
|
DC_COMMENT, // ; datafile comment
|
|||
|
DC_NON_RFC, // <
|
|||
|
DC_NON_RFC, // =
|
|||
|
DC_NON_RFC, // >
|
|||
|
DC_NON_RFC, // ?
|
|||
|
DC_NON_RFC, // @
|
|||
|
|
|||
|
// A - Z RFC for hostname
|
|||
|
|
|||
|
DC_UPPER, DC_UPPER, DC_UPPER, DC_UPPER,
|
|||
|
DC_UPPER, DC_UPPER, DC_UPPER, DC_UPPER,
|
|||
|
DC_UPPER, DC_UPPER, DC_UPPER, DC_UPPER,
|
|||
|
DC_UPPER, DC_UPPER, DC_UPPER, DC_UPPER,
|
|||
|
DC_UPPER, DC_UPPER, DC_UPPER, DC_UPPER,
|
|||
|
DC_UPPER, DC_UPPER, DC_UPPER, DC_UPPER,
|
|||
|
DC_UPPER, DC_UPPER,
|
|||
|
|
|||
|
DC_NON_RFC, // [
|
|||
|
DC_SLASH, // \ always must be quoted
|
|||
|
DC_NON_RFC, // ]
|
|||
|
DC_NON_RFC, // ^
|
|||
|
DC_UNDERSCORE, // _
|
|||
|
DC_NON_RFC, // `
|
|||
|
|
|||
|
// a - z RFC for hostname
|
|||
|
|
|||
|
DC_LOWER, DC_LOWER, DC_LOWER, DC_LOWER,
|
|||
|
DC_LOWER, DC_LOWER, DC_LOWER, DC_LOWER,
|
|||
|
DC_LOWER, DC_LOWER, DC_LOWER, DC_LOWER,
|
|||
|
DC_LOWER, DC_LOWER, DC_LOWER, DC_LOWER,
|
|||
|
DC_LOWER, DC_LOWER, DC_LOWER, DC_LOWER,
|
|||
|
DC_LOWER, DC_LOWER, DC_LOWER, DC_LOWER,
|
|||
|
DC_LOWER, DC_LOWER,
|
|||
|
|
|||
|
DC_NON_RFC, // {
|
|||
|
DC_NON_RFC, // |
|
|||
|
DC_NON_RFC, // }
|
|||
|
DC_NON_RFC, // ~
|
|||
|
DC_OCTAL, // 0x7f DEL code
|
|||
|
|
|||
|
// UTF8 trail bytes
|
|||
|
// - chars 0x80 <= X < 0xc0
|
|||
|
// - mask [10xx xxxx]
|
|||
|
//
|
|||
|
// Lead UTF8 character determines count of bytes in conversion.
|
|||
|
// Trail characters fill out conversion.
|
|||
|
|
|||
|
DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
|
|||
|
DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
|
|||
|
DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
|
|||
|
DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
|
|||
|
DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
|
|||
|
DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
|
|||
|
DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
|
|||
|
DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
|
|||
|
|
|||
|
DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
|
|||
|
DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
|
|||
|
DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
|
|||
|
DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
|
|||
|
DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
|
|||
|
DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
|
|||
|
DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
|
|||
|
DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
|
|||
|
|
|||
|
// UTF8_1ST_OF_2
|
|||
|
// - chars > 0xc0 to 0xdf
|
|||
|
// - mask [110x xxxx]
|
|||
|
//
|
|||
|
// Converting unicode chars > 7 bits <= 11 bits (from 0x80 to 0x7ff)
|
|||
|
// consists of first of two char followed by one trail bytes
|
|||
|
|
|||
|
DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2,
|
|||
|
DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2,
|
|||
|
DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2,
|
|||
|
DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2,
|
|||
|
DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2,
|
|||
|
DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2,
|
|||
|
DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2,
|
|||
|
DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2,
|
|||
|
|
|||
|
// UTF8_1ST_OF_3
|
|||
|
// - chars > 0xe0
|
|||
|
// - mask [1110 xxxx]
|
|||
|
//
|
|||
|
// Converting unicode > 11 bits (0x7ff)
|
|||
|
// consists of first of three char followed by two trail bytes
|
|||
|
|
|||
|
DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3,
|
|||
|
DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3,
|
|||
|
DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3,
|
|||
|
DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3,
|
|||
|
DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3,
|
|||
|
DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3,
|
|||
|
DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3,
|
|||
|
DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
Dns_VerifyValidFileCharPropertyTable(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Verify haven't broken lookup table.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
ASSERT( DnsCharPropertyTable[0] == DC_NULL );
|
|||
|
ASSERT( DnsCharPropertyTable['\t'] == DC_TAB );
|
|||
|
ASSERT( DnsCharPropertyTable['\n'] == DC_NEWLINE );
|
|||
|
ASSERT( DnsCharPropertyTable['\r'] == DC_RETURN );
|
|||
|
ASSERT( DnsCharPropertyTable[' '] == DC_BLANK );
|
|||
|
ASSERT( DnsCharPropertyTable['"'] == DC_QUOTE );
|
|||
|
ASSERT( DnsCharPropertyTable['('] == DC_OPEN_PAREN );
|
|||
|
ASSERT( DnsCharPropertyTable[')'] == DC_CLOSE_PAREN );
|
|||
|
ASSERT( DnsCharPropertyTable['*'] == DC_ASTERISK );
|
|||
|
ASSERT( DnsCharPropertyTable['-'] == DC_RFC );
|
|||
|
ASSERT( DnsCharPropertyTable['.'] == DC_DOT );
|
|||
|
ASSERT( DnsCharPropertyTable['/'] == DC_BACKSLASH );
|
|||
|
ASSERT( DnsCharPropertyTable['0'] == DC_NUMBER );
|
|||
|
ASSERT( DnsCharPropertyTable['9'] == DC_NUMBER );
|
|||
|
ASSERT( DnsCharPropertyTable[';'] == DC_COMMENT );
|
|||
|
ASSERT( DnsCharPropertyTable['A'] == DC_UPPER );
|
|||
|
ASSERT( DnsCharPropertyTable['Z'] == DC_UPPER );
|
|||
|
ASSERT( DnsCharPropertyTable['\\'] == DC_SLASH );
|
|||
|
ASSERT( DnsCharPropertyTable['_'] == DC_UNDERSCORE );
|
|||
|
ASSERT( DnsCharPropertyTable['a'] == DC_LOWER );
|
|||
|
ASSERT( DnsCharPropertyTable['z'] == DC_LOWER );
|
|||
|
ASSERT( DnsCharPropertyTable[0x7f] == DC_OCTAL );
|
|||
|
ASSERT( DnsCharPropertyTable[0x80] == DC_UTF8_TRAIL );
|
|||
|
ASSERT( DnsCharPropertyTable[0xbf] == DC_UTF8_TRAIL );
|
|||
|
ASSERT( DnsCharPropertyTable[0xc0] == DC_UTF8_1ST_2 );
|
|||
|
ASSERT( DnsCharPropertyTable[0xdf] == DC_UTF8_1ST_2 );
|
|||
|
ASSERT( DnsCharPropertyTable[0xe0] == DC_UTF8_1ST_3 );
|
|||
|
ASSERT( DnsCharPropertyTable[0xff] == DC_UTF8_1ST_3 );
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Validation routine flags
|
|||
|
//
|
|||
|
|
|||
|
#define DNSVAL_ALLOW_LEADING_UNDERSCORE 0x00010000
|
|||
|
#define DNSVAL_ALLOW_ASTERISK 0x00020000
|
|||
|
#define DNSVAL_ALLOW_BACKSLASH 0x00040000
|
|||
|
|
|||
|
//
|
|||
|
// Validation bit flags
|
|||
|
//
|
|||
|
|
|||
|
#define DNS_BIT_NAME_FQDN 0x00000001
|
|||
|
#define DNS_BIT_NAME_SINGLE_LABEL 0x00000002
|
|||
|
#define DNS_BIT_NAME_DOTTED 0x00000004
|
|||
|
#define DNS_BIT_NAME_ROOT 0x00000008
|
|||
|
|
|||
|
#define DNS_BIT_NAME_CONTAINS_UPPER 0x00000010
|
|||
|
#define DNS_BIT_NAME_NUMERIC 0x00000100
|
|||
|
#define DNS_BIT_NAME_NUMERIC_LABEL 0x00000200
|
|||
|
#define DNS_BIT_NAME_NUMERIC_FIRST_LABEL 0x00000400
|
|||
|
|
|||
|
#define DNS_BIT_NAME_UNDERSCORE 0x00001000
|
|||
|
#define DNS_BIT_NAME_WILDCARD 0x00002000
|
|||
|
#define DNS_BIT_NAME_BACKSLASH 0x00004000
|
|||
|
#define DNS_BIT_NAME_NON_RFC_ASCII 0x00008000
|
|||
|
#define DNS_BIT_NAME_MULTIBYTE 0x00010000
|
|||
|
#define DNS_BIT_NAME_BINARY_LABEL 0x00020000
|
|||
|
|
|||
|
#define DNS_BIT_NAME_INVALID 0x80000000
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
#if 0
|
|||
|
//
|
|||
|
// Old validation -- retired
|
|||
|
//
|
|||
|
// Downcase and validation table
|
|||
|
//
|
|||
|
// DCR: table lookup for all DNS char properties
|
|||
|
// especially RFC, non-RFC, invalid
|
|||
|
//
|
|||
|
|
|||
|
typedef struct _Dns_ValidationChar
|
|||
|
{
|
|||
|
CHAR chDown;
|
|||
|
UCHAR fNonRfc;
|
|||
|
}
|
|||
|
DNS_VALIDATION_CHAR;
|
|||
|
|
|||
|
#define NON_RFC (1)
|
|||
|
#define EXTENDED_CHAR (0x80)
|
|||
|
|
|||
|
|
|||
|
DNS_VALIDATION_CHAR
|
|||
|
Dns_ValidateDowncaseChar(
|
|||
|
IN CHAR ch
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Validates character
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ch -- character to validate
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Validation character -- downcased character and flag
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DNS_VALIDATION_CHAR val;
|
|||
|
|
|||
|
// default to normal character
|
|||
|
|
|||
|
val.chDown = ch;
|
|||
|
val.fNonRfc = 0;
|
|||
|
|
|||
|
//
|
|||
|
// break out character tests
|
|||
|
// - attempt most likely to least likely
|
|||
|
// - but also working down to simplify tests
|
|||
|
//
|
|||
|
|
|||
|
if ( (UCHAR)ch >= 'a' )
|
|||
|
{
|
|||
|
if ( (UCHAR)ch <= 'z' )
|
|||
|
{
|
|||
|
return( val );
|
|||
|
}
|
|||
|
val.fNonRfc = NON_RFC;
|
|||
|
if ( ch & 0x80 )
|
|||
|
{
|
|||
|
val.fNonRfc = EXTENDED_CHAR;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
else if ( (UCHAR)ch >= 'A' )
|
|||
|
{
|
|||
|
if ( (UCHAR)ch <= 'Z' )
|
|||
|
{
|
|||
|
val.chDown = ch + 0x20;
|
|||
|
return( val );
|
|||
|
}
|
|||
|
val.fNonRfc = NON_RFC;
|
|||
|
}
|
|||
|
|
|||
|
else if ( (UCHAR)ch >= '0' )
|
|||
|
{
|
|||
|
if ( (UCHAR)ch <= '9' )
|
|||
|
{
|
|||
|
return( val );
|
|||
|
}
|
|||
|
val.fNonRfc = NON_RFC;
|
|||
|
}
|
|||
|
|
|||
|
else if ( (UCHAR)ch > ' ' )
|
|||
|
{
|
|||
|
if ( (UCHAR)ch == '-' )
|
|||
|
{
|
|||
|
return( val );
|
|||
|
}
|
|||
|
val.fNonRfc = NON_RFC;
|
|||
|
}
|
|||
|
|
|||
|
// blank or below is flat error
|
|||
|
|
|||
|
else
|
|||
|
{
|
|||
|
val.chDown = 0;
|
|||
|
val.fNonRfc = NON_RFC;
|
|||
|
}
|
|||
|
|
|||
|
return( val );
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Name validation
|
|||
|
//
|
|||
|
// DCR: name validation by bitfield
|
|||
|
//
|
|||
|
// An interesting approach to validation, would be to expose
|
|||
|
// a set of properties about a name.
|
|||
|
// Caller could then specify allowable set (we'd give packages)
|
|||
|
// and we'd give back actual set.
|
|||
|
// Low level routines would do nothing but return bit field of
|
|||
|
// property set.
|
|||
|
//
|
|||
|
// Properties would include:
|
|||
|
// - RFC
|
|||
|
// - contains numeric
|
|||
|
// - contains upper
|
|||
|
// - all numeric
|
|||
|
// - first label numeric
|
|||
|
//
|
|||
|
// - utf8 multibyte
|
|||
|
// - underscore
|
|||
|
// - other non-RFC
|
|||
|
// - unprintable
|
|||
|
// - non-utf8 high (i.e. requires binary label)
|
|||
|
//
|
|||
|
// - FQDN
|
|||
|
// - single label
|
|||
|
// - root
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
DNS_STATUS
|
|||
|
validateDnsNamePrivate(
|
|||
|
IN LPCSTR pszName,
|
|||
|
IN DWORD dwFlag,
|
|||
|
OUT PDWORD pLabelCount,
|
|||
|
OUT PDWORD pResultFlag
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Verifies name is valid DNS name.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pszName -- DNS name (standard dotted form) to check
|
|||
|
|
|||
|
dwFlags -- validation flags
|
|||
|
- DNSVAL_ALLOW_LEADING_UNDERSCORE
|
|||
|
- DNSVAL_ALLOW_BACKSLASH
|
|||
|
- DNSVAL_ALLOW_ASTERIK
|
|||
|
|
|||
|
pLabelCount -- addr to recv label count
|
|||
|
|
|||
|
pResultFlag -- addr to recv result flag
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS -- completely RFC compliant name
|
|||
|
DNS_ERROR_NON_RFC_NAME -- syntax valid, but not standard RFC name
|
|||
|
DNS_ERROR_INVALID_NAME_CHAR -- syntax valid, but invalid characters
|
|||
|
ERROR_INVALID_NAME -- name completely useless, bogus, toast
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PUCHAR pch = (PUCHAR)pszName;
|
|||
|
UCHAR ch;
|
|||
|
DWORD charProp;
|
|||
|
DWORD labelCount = 0;
|
|||
|
DWORD trailCount = 0;
|
|||
|
INT labelCharCount = 0;
|
|||
|
INT labelNumberCount = 0;
|
|||
|
DWORD flag;
|
|||
|
BOOL fqdn = FALSE;
|
|||
|
BOOL fnonRfc = FALSE;
|
|||
|
BOOL finvalidChar = FALSE;
|
|||
|
BOOL fnameNonNumeric = FALSE;
|
|||
|
BOOL flabelNonNumeric = FALSE;
|
|||
|
DNS_STATUS status;
|
|||
|
|
|||
|
|
|||
|
DNSDBG( TRACE, ( "validateNamePrivate()\n" ));
|
|||
|
|
|||
|
if ( !pch )
|
|||
|
{
|
|||
|
goto InvalidName;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// validations
|
|||
|
// - name length (255)
|
|||
|
// - label length (63)
|
|||
|
// - UTF8 encoding correct
|
|||
|
// - no unprintable characters
|
|||
|
//
|
|||
|
|
|||
|
while ( 1 )
|
|||
|
{
|
|||
|
// get next character and properties
|
|||
|
|
|||
|
ch = *pch++;
|
|||
|
charProp = DnsCharPropertyTable[ ch ];
|
|||
|
|
|||
|
// inc label count
|
|||
|
// - do here for simplicity, dec in "." case below
|
|||
|
|
|||
|
labelCharCount++;
|
|||
|
|
|||
|
//
|
|||
|
// simplify UTF8 -- just get it out of the way
|
|||
|
// need to do first or else need trailCount==0 checks
|
|||
|
// on all other paths
|
|||
|
//
|
|||
|
|
|||
|
if ( ch >= 0x80 )
|
|||
|
{
|
|||
|
DWORD tempStatus;
|
|||
|
|
|||
|
tempStatus = Dns_ValidateUtf8Byte(
|
|||
|
ch,
|
|||
|
& trailCount );
|
|||
|
if ( tempStatus != ERROR_SUCCESS )
|
|||
|
{
|
|||
|
DNSDBG( READ, (
|
|||
|
"ERROR: Name UTF8 trail count check at %c\n", ch ));
|
|||
|
goto InvalidName;
|
|||
|
}
|
|||
|
fnonRfc = TRUE;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// trail count check
|
|||
|
// - all ASCII chars, must not be in middle of UTF8
|
|||
|
//
|
|||
|
|
|||
|
if ( trailCount )
|
|||
|
{
|
|||
|
DNSDBG( READ, (
|
|||
|
"ERROR: Name failed trail count check at %c\n", ch ));
|
|||
|
goto InvalidName;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// full RFC -- continue
|
|||
|
//
|
|||
|
|
|||
|
if ( charProp & B_RFC )
|
|||
|
{
|
|||
|
if ( charProp & B_NUMBER )
|
|||
|
{
|
|||
|
labelNumberCount++;
|
|||
|
}
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// label termination: dot or NULL
|
|||
|
//
|
|||
|
|
|||
|
if ( ch == '.' || ch == 0 )
|
|||
|
{
|
|||
|
labelCharCount--;
|
|||
|
|
|||
|
// FQDN termination
|
|||
|
// - termination with no bytes in label
|
|||
|
//
|
|||
|
// two cases:
|
|||
|
// 1) terminate on NULL char
|
|||
|
// - standard FQDN "foo.bar."
|
|||
|
// - but empty name invalid
|
|||
|
// 2) terminate on dot
|
|||
|
// - only "." root valid
|
|||
|
// - all other ".." or ".xyz" cases invalid
|
|||
|
|
|||
|
if ( labelCharCount == 0 )
|
|||
|
{
|
|||
|
fqdn = TRUE;
|
|||
|
|
|||
|
if ( ch == 0 )
|
|||
|
{
|
|||
|
if ( labelCount )
|
|||
|
{
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
}
|
|||
|
else if ( pch == pszName+1 && *pch == 0 )
|
|||
|
{
|
|||
|
// root
|
|||
|
// - set flags for validity
|
|||
|
// - skip final length check
|
|||
|
|
|||
|
fnameNonNumeric = TRUE;
|
|||
|
flabelNonNumeric = TRUE;
|
|||
|
goto DoneRoot;
|
|||
|
}
|
|||
|
DNSDBG( READ, (
|
|||
|
"ERROR: Name (%s) failed check\n",
|
|||
|
pszName ));
|
|||
|
goto InvalidName;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// read non-empty label
|
|||
|
// - label length validity
|
|||
|
// - detect non-numeric labels
|
|||
|
// (easier to handle numeric name check, by detecting non-numeric)
|
|||
|
//
|
|||
|
|
|||
|
if ( labelCharCount > DNS_MAX_LABEL_LENGTH )
|
|||
|
{
|
|||
|
DNSDBG( READ, ( "ERROR: Name failed label count check\n" ));
|
|||
|
goto InvalidName;
|
|||
|
}
|
|||
|
|
|||
|
if ( labelNumberCount != labelCharCount )
|
|||
|
{
|
|||
|
fnameNonNumeric = TRUE;
|
|||
|
if ( labelCount == 0 )
|
|||
|
{
|
|||
|
flabelNonNumeric = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// count label
|
|||
|
// - stop if NULL terminator
|
|||
|
// - otherwise, reset for next label and continue
|
|||
|
|
|||
|
labelCount++;
|
|||
|
if ( ch == 0 )
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
labelCharCount = 0;
|
|||
|
labelNumberCount = 0;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// non-RFC
|
|||
|
// - currently accepting only "_" as allowable as part of
|
|||
|
// microsoft acceptable non-RFC set
|
|||
|
//
|
|||
|
// however DNS server must be able to read *, \, etc
|
|||
|
// it gets called through Dns_CreateStandardDnsName()
|
|||
|
//
|
|||
|
// note, could tighten this up with special flag, but since
|
|||
|
// this only speeds case with invalid chars, there's not much
|
|||
|
// point; underscore is likely to see significant use
|
|||
|
//
|
|||
|
|
|||
|
// underscore
|
|||
|
// - can be valid as part of SRV domain name
|
|||
|
// - otherwise non-RFC
|
|||
|
|
|||
|
if ( ch == '_' )
|
|||
|
{
|
|||
|
if ( labelCharCount == 1 &&
|
|||
|
(*pch && *pch!= '.') &&
|
|||
|
(dwFlag & DNSVAL_ALLOW_LEADING_UNDERSCORE) )
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
fnonRfc = TRUE;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
// backslash
|
|||
|
// - used to denote classless in-addr domains
|
|||
|
// - so valid even as zone name on server
|
|||
|
// - otherwise completely invalid
|
|||
|
|
|||
|
else if ( ch == '/' )
|
|||
|
{
|
|||
|
if ( dwFlag & DNSVAL_ALLOW_BACKSLASH )
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// asterisk
|
|||
|
// - valid only as single-byte first label in wildcard name
|
|||
|
// - otherwise completely invalid
|
|||
|
|
|||
|
else if ( ch == '*' )
|
|||
|
{
|
|||
|
if ( labelCount == 0 &&
|
|||
|
labelCharCount == 1 &&
|
|||
|
( *pch==0 || *pch=='.') &&
|
|||
|
(dwFlag & DNSVAL_ALLOW_ASTERISK) )
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// anything else is complete junk
|
|||
|
//
|
|||
|
// JENHANCE: if desired, could break out printable\non
|
|||
|
|
|||
|
fnonRfc = TRUE;
|
|||
|
finvalidChar = TRUE;
|
|||
|
DNSDBG( READ, ( "ERROR: Name character %c failed check\n", ch ));
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
Done:
|
|||
|
|
|||
|
// verify total name length
|
|||
|
// to fit in wire 255 limit:
|
|||
|
// - FQDN can be up to 254
|
|||
|
// - non-FQDN can be up to 253
|
|||
|
|
|||
|
pch--;
|
|||
|
DNS_ASSERT( pch > pszName );
|
|||
|
labelCharCount = (INT)(pch - pszName);
|
|||
|
|
|||
|
if ( !fqdn )
|
|||
|
{
|
|||
|
labelCharCount++;
|
|||
|
}
|
|||
|
if ( labelCharCount >= DNS_MAX_NAME_LENGTH )
|
|||
|
{
|
|||
|
DNSDBG( READ, ( "ERROR: Name failed final length check\n" ));
|
|||
|
goto InvalidName;
|
|||
|
}
|
|||
|
|
|||
|
DoneRoot:
|
|||
|
|
|||
|
//
|
|||
|
// return flags
|
|||
|
//
|
|||
|
// JENHANCE: all returns from validateNamePrivate() could come
|
|||
|
// as result flag; then charset issues could be separated
|
|||
|
// out by higher level routine
|
|||
|
//
|
|||
|
|
|||
|
*pLabelCount = labelCount;
|
|||
|
|
|||
|
flag = 0;
|
|||
|
if ( fqdn )
|
|||
|
{
|
|||
|
flag |= DNS_BIT_NAME_FQDN;
|
|||
|
}
|
|||
|
if ( ! fnameNonNumeric )
|
|||
|
{
|
|||
|
flag |= DNS_BIT_NAME_NUMERIC;
|
|||
|
}
|
|||
|
if ( ! flabelNonNumeric )
|
|||
|
{
|
|||
|
flag |= DNS_BIT_NAME_NUMERIC_FIRST_LABEL;
|
|||
|
}
|
|||
|
*pResultFlag = flag;
|
|||
|
|
|||
|
//
|
|||
|
// return status
|
|||
|
// ERROR_SUCCESS -- full RFC name
|
|||
|
// DNS_ERROR_NON_RFC_NAME -- MS extended and '_' names
|
|||
|
// DNS_ERROR_INVALID_NAME_CHAR -- syntaxtically valid, but bad chars
|
|||
|
// ERROR_INVALID_NAME -- syntaxtically invalid name
|
|||
|
//
|
|||
|
|
|||
|
status = ERROR_SUCCESS;
|
|||
|
|
|||
|
if ( finvalidChar )
|
|||
|
{
|
|||
|
status = DNS_ERROR_INVALID_NAME_CHAR;
|
|||
|
}
|
|||
|
else if ( fnonRfc )
|
|||
|
{
|
|||
|
status = DNS_ERROR_NON_RFC_NAME;
|
|||
|
}
|
|||
|
|
|||
|
DNSDBG( READ, (
|
|||
|
"Leave validateNamePrivate(), status = %d\n",
|
|||
|
status ));
|
|||
|
|
|||
|
return( status );
|
|||
|
|
|||
|
|
|||
|
InvalidName:
|
|||
|
|
|||
|
DNSDBG( READ, (
|
|||
|
"Leave validateNamePrivate(), status = ERROR_INVALID_NAME\n" ));
|
|||
|
|
|||
|
*pLabelCount = 0;
|
|||
|
*pResultFlag = 0;
|
|||
|
return( ERROR_INVALID_NAME );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DNS_STATUS
|
|||
|
Dns_ValidateName_UTF8(
|
|||
|
IN LPCSTR pszName,
|
|||
|
IN DNS_NAME_FORMAT Format
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Verifies name is valid DNS name.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pszName -- DNS name (standard dotted form) to check
|
|||
|
|
|||
|
Format -- required format of DNS name
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS -- completely RFC compliant name
|
|||
|
DNS_ERROR_NON_RFC_NAME -- syntax valid, but not standard RFC name
|
|||
|
DNS_ERROR_NUMERIC_NAME -- syntax valid, but numeric label violation
|
|||
|
DNS_ERROR_INVALID_NAME_CHAR -- syntax valid, but invalid characters
|
|||
|
ERROR_INVALID_NAME -- name completely useless, bogus, toast
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DNS_STATUS status;
|
|||
|
DWORD labelCount;
|
|||
|
BOOL isFqdn;
|
|||
|
DWORD flag = 0;
|
|||
|
DWORD resultFlag = 0;
|
|||
|
|
|||
|
|
|||
|
DNSDBG( TRACE, (
|
|||
|
"Dns_ValidateName_UTF8()\n"
|
|||
|
"\tname = %s\n"
|
|||
|
"\tformat = %d\n",
|
|||
|
pszName,
|
|||
|
Format
|
|||
|
));
|
|||
|
|
|||
|
if ( !pszName )
|
|||
|
{
|
|||
|
return( ERROR_INVALID_NAME );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// special casing?
|
|||
|
//
|
|||
|
// SRV records can have leading underscores
|
|||
|
// wildcards can have first label "*"
|
|||
|
// backslash ok in classless in-addr domains
|
|||
|
//
|
|||
|
|
|||
|
switch( Format )
|
|||
|
{
|
|||
|
#if 0
|
|||
|
case DnsNameServerZonePrivate:
|
|||
|
|
|||
|
flag = DNSVAL_ALLOW_BACKSLASH | DNSVAL_ALLOW_LEADING_UNDERSCORE;
|
|||
|
#endif
|
|||
|
|
|||
|
case DnsNameWildcard:
|
|||
|
|
|||
|
flag = DNSVAL_ALLOW_ASTERISK;
|
|||
|
break;
|
|||
|
|
|||
|
case DnsNameSrvRecord:
|
|||
|
|
|||
|
flag = DNSVAL_ALLOW_LEADING_UNDERSCORE;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// do validation
|
|||
|
//
|
|||
|
// return immediately on invalid name, so type
|
|||
|
// specific returns do not overwrite this error
|
|||
|
//
|
|||
|
|
|||
|
status = validateDnsNamePrivate(
|
|||
|
pszName,
|
|||
|
flag,
|
|||
|
& labelCount,
|
|||
|
& resultFlag
|
|||
|
);
|
|||
|
if ( status == ERROR_INVALID_NAME )
|
|||
|
{
|
|||
|
return( status );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// do name type specific validation
|
|||
|
//
|
|||
|
|
|||
|
switch( Format )
|
|||
|
{
|
|||
|
// domain name -- any valid non-numeric DNS name
|
|||
|
|
|||
|
case DnsNameDomain:
|
|||
|
|
|||
|
if ( resultFlag & DNS_BIT_NAME_NUMERIC )
|
|||
|
{
|
|||
|
return( DNS_ERROR_NUMERIC_NAME );
|
|||
|
}
|
|||
|
return( status );
|
|||
|
|
|||
|
// domain name label -- any valid single-label DNS name
|
|||
|
|
|||
|
case DnsNameDomainLabel:
|
|||
|
|
|||
|
if ( labelCount != 1 || resultFlag & DNS_BIT_NAME_FQDN )
|
|||
|
{
|
|||
|
return( ERROR_INVALID_NAME );
|
|||
|
}
|
|||
|
return( status );
|
|||
|
|
|||
|
// hostname full -- non-numeric hostname label
|
|||
|
|
|||
|
case DnsNameHostnameFull:
|
|||
|
|
|||
|
if ( resultFlag & DNS_BIT_NAME_NUMERIC_FIRST_LABEL )
|
|||
|
{
|
|||
|
return( DNS_ERROR_NUMERIC_NAME );
|
|||
|
}
|
|||
|
return( status );
|
|||
|
|
|||
|
// hostname label -- single label and non-numeric
|
|||
|
|
|||
|
case DnsNameHostnameLabel:
|
|||
|
|
|||
|
if ( labelCount != 1 || resultFlag & DNS_BIT_NAME_FQDN )
|
|||
|
{
|
|||
|
return( ERROR_INVALID_NAME );
|
|||
|
}
|
|||
|
if ( resultFlag & DNS_BIT_NAME_NUMERIC_FIRST_LABEL )
|
|||
|
{
|
|||
|
return( DNS_ERROR_NUMERIC_NAME );
|
|||
|
}
|
|||
|
return( status );
|
|||
|
|
|||
|
//
|
|||
|
// wildcard -- single "*" as first label
|
|||
|
// if *.???? then must revalidate the rest of the name as
|
|||
|
// "*" has probably resulted in validation error
|
|||
|
// if "*" then consider this successful
|
|||
|
//
|
|||
|
|
|||
|
case DnsNameWildcard:
|
|||
|
|
|||
|
if ( *pszName == '*' )
|
|||
|
{
|
|||
|
return( status );
|
|||
|
}
|
|||
|
return( ERROR_INVALID_NAME );
|
|||
|
|
|||
|
//
|
|||
|
// SRV label -- validate leading underscore
|
|||
|
//
|
|||
|
|
|||
|
case DnsNameSrvRecord:
|
|||
|
|
|||
|
if ( *pszName == '_' )
|
|||
|
{
|
|||
|
return( status );
|
|||
|
}
|
|||
|
return( ERROR_INVALID_NAME );
|
|||
|
|
|||
|
//
|
|||
|
// unknown format validation
|
|||
|
//
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
return( ERROR_INVALID_PARAMETER );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DNS_STATUS
|
|||
|
Dns_ValidateName_W(
|
|||
|
IN LPCWSTR pwszName,
|
|||
|
IN DNS_NAME_FORMAT Format
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Verifies name is valid DNS name.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pwszName -- DNS name (standard dotted form) to check
|
|||
|
|
|||
|
Format -- required format of DNS name
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS -- if completely compliant name
|
|||
|
DNS_ERROR_NON_RFC_NAME -- if not standard RFC name
|
|||
|
ERROR_INVALID_NAME -- if name completely useless, bogus, toast
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD nameLength = MAX_PATH;
|
|||
|
CHAR nameBuffer[ MAX_PATH ] = {0}; // init for prefix
|
|||
|
|
|||
|
//
|
|||
|
// convert name to UTF8
|
|||
|
// - if can't convert, then can't fit into buffer
|
|||
|
// so must be invalid name on length grounds
|
|||
|
//
|
|||
|
|
|||
|
if ( ! Dns_NameCopy(
|
|||
|
nameBuffer,
|
|||
|
& nameLength, // avail buf length
|
|||
|
(PCHAR) pwszName,
|
|||
|
0, // unknown length
|
|||
|
DnsCharSetUnicode, // unicode in
|
|||
|
DnsCharSetUtf8 // UTF8 out
|
|||
|
) )
|
|||
|
{
|
|||
|
return( ERROR_INVALID_NAME );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// validate name in UTF8 format
|
|||
|
|
|||
|
return Dns_ValidateName_UTF8(
|
|||
|
(LPCSTR) nameBuffer,
|
|||
|
Format );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DNS_STATUS
|
|||
|
Dns_ValidateName_A(
|
|||
|
IN LPCSTR pszName,
|
|||
|
IN DNS_NAME_FORMAT Format
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Verifies name is valid DNS name.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pszName -- DNS name (standard dotted form) to check
|
|||
|
|
|||
|
Format -- required format of DNS name
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS -- if completely compliant name
|
|||
|
DNS_ERROR_NON_RFC_NAME -- if not standard RFC name
|
|||
|
ERROR_INVALID_NAME -- if name completely useless, bogus, toast
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD nameLength = MAX_PATH;
|
|||
|
CHAR nameBuffer[ MAX_PATH ];
|
|||
|
|
|||
|
//
|
|||
|
// convert name to UTF8
|
|||
|
// - if can't convert, then can't fit into buffer
|
|||
|
// so must be invalid name on length grounds
|
|||
|
//
|
|||
|
|
|||
|
if ( ! Dns_NameCopy(
|
|||
|
nameBuffer,
|
|||
|
& nameLength, // avail buf length
|
|||
|
(PCHAR) pszName,
|
|||
|
0, // unknown length
|
|||
|
DnsCharSetAnsi, // unicode in
|
|||
|
DnsCharSetUtf8 // UTF8 out
|
|||
|
) )
|
|||
|
{
|
|||
|
return( ERROR_INVALID_NAME );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// validate name in UTF8 format
|
|||
|
|
|||
|
return Dns_ValidateName_UTF8(
|
|||
|
(LPCSTR) nameBuffer,
|
|||
|
Format );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DNS_STATUS
|
|||
|
Dns_ValidateDnsString_UTF8(
|
|||
|
IN LPCSTR pszString
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Verifies string is valid DNS string.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pszString -- DNS string (standard dotted form) to check
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS -- if completely compliant string
|
|||
|
ERROR_INVALID_DATA -- otherwise
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PUCHAR pch = (PUCHAR) pszString;
|
|||
|
UCHAR ch;
|
|||
|
DWORD trailCount = 0;
|
|||
|
|
|||
|
DNSDBG( TRACE, ( "Dns_ValidateDnsString_UTF8()\n" ));
|
|||
|
|
|||
|
if ( !pszString )
|
|||
|
{
|
|||
|
return( ERROR_INVALID_DATA );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// validations
|
|||
|
// - string length (255)
|
|||
|
// - UTF8 chars valid
|
|||
|
// - no unprintable characters
|
|||
|
//
|
|||
|
|
|||
|
while ( ch = *pch++ )
|
|||
|
{
|
|||
|
if ( ch & 0x80 )
|
|||
|
{
|
|||
|
DWORD status;
|
|||
|
status = Dns_ValidateUtf8Byte(
|
|||
|
ch,
|
|||
|
& trailCount );
|
|||
|
if ( status != ERROR_SUCCESS )
|
|||
|
{
|
|||
|
return( status );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
else if ( ch < ' ' )
|
|||
|
{
|
|||
|
return( ERROR_INVALID_DATA );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// verify string length ok
|
|||
|
|
|||
|
if ( pch - pszString > DNS_MAX_NAME_LENGTH )
|
|||
|
{
|
|||
|
return( ERROR_INVALID_DATA );
|
|||
|
}
|
|||
|
return( ERROR_SUCCESS );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DNS_STATUS
|
|||
|
Dns_ValidateDnsString_W(
|
|||
|
IN LPCWSTR pszString
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Verifies string is valid DNS string.
|
|||
|
|
|||
|
Not sure there's any need to UNICODE string routine.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pszString -- DNS string
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS -- if completely compliant string
|
|||
|
ERROR_INVALID_DATA -- otherwise
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
INT count;
|
|||
|
CHAR stringUtf8[ DNS_MAX_NAME_BUFFER_LENGTH ];
|
|||
|
DWORD bufLength = DNS_MAX_NAME_BUFFER_LENGTH;
|
|||
|
|
|||
|
DNSDBG( TRACE, ( "Dns_ValidateDnsString_W()\n" ));
|
|||
|
|
|||
|
if ( !pszString )
|
|||
|
{
|
|||
|
return( ERROR_INVALID_DATA );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// need to convert to unicode in order to test UTF8 (wire) length
|
|||
|
// - buffer (twice max length) can hold any valid
|
|||
|
// coversion of unicode name within max length
|
|||
|
//
|
|||
|
|
|||
|
count = wcslen( pszString );
|
|||
|
if ( count > DNS_MAX_NAME_LENGTH )
|
|||
|
{
|
|||
|
return( ERROR_INVALID_DATA );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// convert, then test
|
|||
|
//
|
|||
|
|
|||
|
if ( ! Dns_StringCopy(
|
|||
|
stringUtf8,
|
|||
|
& bufLength,
|
|||
|
(LPSTR) pszString,
|
|||
|
(WORD) count,
|
|||
|
DnsCharSetUnicode, // unicode in
|
|||
|
DnsCharSetUtf8 // UTF8 out
|
|||
|
) )
|
|||
|
{
|
|||
|
return( ERROR_INVALID_DATA );
|
|||
|
}
|
|||
|
|
|||
|
return Dns_ValidateDnsString_UTF8( stringUtf8 );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Name cannonicalization
|
|||
|
//
|
|||
|
// Currently, clients downcase (when extended) in their locale to go to wire.
|
|||
|
// On server end however all names are cannonicalized.
|
|||
|
//
|
|||
|
// DCR: cannonicalize completely on client end?
|
|||
|
// Ideally client would do complete cannonicallization on its end.
|
|||
|
// The only issue is whether there are locale specific issues where
|
|||
|
// downcasing would be different and yield substaintially different result
|
|||
|
//
|
|||
|
|
|||
|
#define MAX_DNS_DOWN_CASE_BUF_LEN 512
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
Dns_MakeCanonicalNameW(
|
|||
|
OUT PWSTR pBuffer,
|
|||
|
IN DWORD BufLength,
|
|||
|
IN PWSTR pwsString,
|
|||
|
IN DWORD StringLength
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Create cannonical unicode DNS name.
|
|||
|
|
|||
|
This name is downcased and ambiguities converted to standard
|
|||
|
DNS characters.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pBuffer -- buffer to recv canon name
|
|||
|
|
|||
|
BufLength -- length of buffer; if 0, buffer MUST have adequate length
|
|||
|
|
|||
|
pwsString -- ptr to string to copy
|
|||
|
|
|||
|
StringLength -- string length, if known
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Count of characters converted INCLUDING NULL terminator.
|
|||
|
Zero on error.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD inLength = StringLength;
|
|||
|
|
|||
|
//
|
|||
|
// verify adequate buffer length
|
|||
|
//
|
|||
|
// DCR: should allow non-null terminated canonicalizations?
|
|||
|
//
|
|||
|
// note: we allow and convert non-null terminated name
|
|||
|
// the result will not necessarily be NULL terminated
|
|||
|
// if buffer is exactly equal to string length
|
|||
|
//
|
|||
|
|
|||
|
if ( inLength == 0 )
|
|||
|
{
|
|||
|
inLength = wcslen( pwsString );
|
|||
|
inLength++;
|
|||
|
}
|
|||
|
|
|||
|
if ( BufLength < inLength )
|
|||
|
{
|
|||
|
DNSDBG( ANY, (
|
|||
|
"ERROR: insufficient cannon buffer len = %d\n"
|
|||
|
"\tstring = %S, len = %d\n",
|
|||
|
BufLength,
|
|||
|
pwsString,
|
|||
|
inLength ));
|
|||
|
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|||
|
return( 0 );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// convert name
|
|||
|
// - downcase with canonicalizing rules
|
|||
|
//
|
|||
|
|
|||
|
inLength = LCMapStringW(
|
|||
|
DNS_CANONICAL_LOCALE,
|
|||
|
DNS_CANONICALIZING_FLAGS,
|
|||
|
pwsString,
|
|||
|
inLength,
|
|||
|
pBuffer,
|
|||
|
BufLength
|
|||
|
);
|
|||
|
|
|||
|
#if DBG
|
|||
|
if ( inLength == 0 )
|
|||
|
{
|
|||
|
DNS_STATUS status = GetLastError();
|
|||
|
|
|||
|
DNSDBG( ANY, (
|
|||
|
"Canonicalization failed => %d\n"
|
|||
|
"\tin %S\n",
|
|||
|
status,
|
|||
|
pwsString ));
|
|||
|
|
|||
|
SetLastError( status );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//
|
|||
|
// DCR: warning this print can blow on non-null terminated conversions
|
|||
|
//
|
|||
|
|
|||
|
DNSDBG( READ, (
|
|||
|
"Canonicalized name at %p\n"
|
|||
|
"\tin %S\n"
|
|||
|
"\tout %S\n",
|
|||
|
pwsString,
|
|||
|
pwsString,
|
|||
|
(PWSTR) pBuffer ));
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
return( inLength );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
Dns_MakeCanonicalNameInPlaceW(
|
|||
|
IN PWCHAR pwString,
|
|||
|
IN DWORD StringLength
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
In place cannonicalization of name.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pwString -- ptr to string to copy
|
|||
|
|
|||
|
StringLength -- length of string
|
|||
|
if zero string assumed to be NULL terminated, in this case
|
|||
|
canonicalization includes NULL terminator
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Count of characters converted -- including NULL terminator if
|
|||
|
StringLength is unspecified
|
|||
|
Zero on error.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD nameLength = StringLength;
|
|||
|
WCHAR tempBuffer[ DNS_MAX_NAME_BUFFER_LENGTH_UNICODE ] = { 0 }; // init for prefix
|
|||
|
|
|||
|
DNSDBG( READ, (
|
|||
|
"Dns_MakeCanonicalNameInPlace()\n"
|
|||
|
"\tpwString = %S\n"
|
|||
|
"\tlength = %d\n",
|
|||
|
pwString,
|
|||
|
StringLength ));
|
|||
|
|
|||
|
// if length unknown, must be NULL terminated string
|
|||
|
|
|||
|
if ( nameLength == 0 )
|
|||
|
{
|
|||
|
nameLength = (DWORD) wcslen( pwString );
|
|||
|
nameLength++;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// cannonicalize (downcase and cleanup)
|
|||
|
// - copy string to temp buffer
|
|||
|
// - then cannonicalize into original buffer
|
|||
|
//
|
|||
|
|
|||
|
if ( nameLength <= DNS_MAX_NAME_BUFFER_LENGTH_UNICODE )
|
|||
|
{
|
|||
|
wcsncpy( tempBuffer, pwString, nameLength );
|
|||
|
|
|||
|
return Dns_MakeCanonicalNameW(
|
|||
|
pwString, // write back to original string
|
|||
|
nameLength, // length of buffer
|
|||
|
tempBuffer, // input is temp copy
|
|||
|
nameLength // input length
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
INT
|
|||
|
Dns_DowncaseNameLabel(
|
|||
|
OUT PCHAR pchResult,
|
|||
|
//OUT PDWORD pNameProperty,
|
|||
|
IN PCHAR pchLabel,
|
|||
|
IN DWORD cchLabel,
|
|||
|
IN DWORD dwFlag
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Create a downcased version of DNS name.
|
|||
|
|
|||
|
This is UTF8 only routine for use by DNS server to
|
|||
|
validate and downcase label during node creation.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pchResult -- resulting downcased label;
|
|||
|
MUST have MAX_LABEL_BUFFER_LENGTH
|
|||
|
|
|||
|
//pNameProperty -- name properties of result
|
|||
|
// ResultLength -- ptr to DWORD to recieve resulting length
|
|||
|
|
|||
|
pchLabel -- ptr to label
|
|||
|
|
|||
|
cchLabel -- count of bytes in label
|
|||
|
|
|||
|
dwFlag -- flag indicating what names are acceptable
|
|||
|
strict RFC => DNS_ALLOW_RFC_NAMES_ONLY
|
|||
|
non RFC names => DNS_ALLOW_NONRFC_NAMES
|
|||
|
UTF8 extended => DNS_ALLOW_MULTIBYTE_NAMES
|
|||
|
anything => DNS_ALLOW_ALL_NAMES
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
If extended name -- length of converted name.
|
|||
|
Zero if success.
|
|||
|
(-1) on error.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
UCHAR ch;
|
|||
|
PUCHAR pchout = pchResult;
|
|||
|
PUCHAR pch = pchLabel;
|
|||
|
DWORD count = cchLabel;
|
|||
|
DWORD charProp;
|
|||
|
DWORD trailCount = 0;
|
|||
|
DWORD property = 0;
|
|||
|
|
|||
|
|
|||
|
DNSDBG( TRACE, (
|
|||
|
"Dns_DowncaseNameLabel( %.*s )\n"
|
|||
|
"\tflag = %08x\n",
|
|||
|
cchLabel,
|
|||
|
pchLabel,
|
|||
|
dwFlag ));
|
|||
|
|
|||
|
if ( count == 0 || count > DNS_MAX_LABEL_LENGTH )
|
|||
|
{
|
|||
|
goto InvalidName;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// copy each character
|
|||
|
// - downcasing upper case chars
|
|||
|
// - detecting invalid chars (unprintable, blank, dot)
|
|||
|
//
|
|||
|
|
|||
|
while ( count-- )
|
|||
|
{
|
|||
|
// get next character and properties
|
|||
|
|
|||
|
ch = *pch++;
|
|||
|
*pchout++ = ch;
|
|||
|
charProp = DnsCharPropertyTable[ ch ];
|
|||
|
|
|||
|
// trail count check
|
|||
|
// check this first to avoid trail count check on all
|
|||
|
// other char types
|
|||
|
//
|
|||
|
// DEVNOTE: note this screens binary labels
|
|||
|
|
|||
|
if ( trailCount )
|
|||
|
{
|
|||
|
if ( (charProp & B_UTF8_TRAIL) )
|
|||
|
{
|
|||
|
trailCount--;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
DNSDBG( READ, (
|
|||
|
"ERROR: Name failed trail count check at %c\n", ch ));
|
|||
|
property |= DNS_BIT_NAME_BINARY_LABEL;
|
|||
|
}
|
|||
|
|
|||
|
// full RFC
|
|||
|
// - map upper case to lower case
|
|||
|
// - continue
|
|||
|
|
|||
|
if ( charProp & B_RFC )
|
|||
|
{
|
|||
|
if ( charProp & B_UPPER )
|
|||
|
{
|
|||
|
--pchout;
|
|||
|
*pchout++ = ch + 0x20;
|
|||
|
}
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// check for extended chars
|
|||
|
// - trail characters should have been caught above
|
|||
|
// - doing this first so can make single trailCount
|
|||
|
// check for all other ASCII chars
|
|||
|
|
|||
|
if ( ch >= 0x80 )
|
|||
|
{
|
|||
|
DWORD tempStatus;
|
|||
|
tempStatus = Dns_ValidateUtf8Byte(
|
|||
|
ch,
|
|||
|
& trailCount );
|
|||
|
if ( tempStatus != ERROR_SUCCESS )
|
|||
|
{
|
|||
|
DNSDBG( READ, (
|
|||
|
"ERROR: Name UTF8 trail count check at %c\n", ch ));
|
|||
|
goto InvalidName;
|
|||
|
}
|
|||
|
property |= DNS_BIT_NAME_MULTIBYTE;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// non-RFC
|
|||
|
// - currently accepting only "_" as allowable as part of
|
|||
|
// microsoft acceptable non-RFC set
|
|||
|
//
|
|||
|
// however DNS server must be able to read *, \, etc
|
|||
|
// as these can be part of valid label
|
|||
|
//
|
|||
|
// note, could tighten this up with special flag, but since
|
|||
|
// this only speeds case with invalid chars, there's not much
|
|||
|
// point; underscore is likely to see significant use
|
|||
|
//
|
|||
|
|
|||
|
// underscore
|
|||
|
// - can be valid as leading label as part of SRV domain name
|
|||
|
// - otherwise non-RFC
|
|||
|
|
|||
|
if ( ch == '_' )
|
|||
|
{
|
|||
|
if ( count == cchLabel - 1 )
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
property |= DNS_BIT_NAME_UNDERSCORE;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
// backslash
|
|||
|
// - used to denote classless in-addr domains
|
|||
|
// must have leading and following chars
|
|||
|
// - otherwise completely invalid
|
|||
|
|
|||
|
else if ( ch == '/' )
|
|||
|
{
|
|||
|
if ( count != 0 && count != cchLabel-1 )
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// asterisk
|
|||
|
// - valid only as single-byte first label in wildcard name
|
|||
|
// - otherwise completely invalid
|
|||
|
|
|||
|
else if ( ch == '*' )
|
|||
|
{
|
|||
|
if ( count == 0 )
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// anything else is complete junk
|
|||
|
// currently only acceptable if allow binary labels
|
|||
|
//
|
|||
|
// JENHANCE: could break out non-RFC (printable\non)
|
|||
|
|
|||
|
property |= DNS_BIT_NAME_BINARY_LABEL;
|
|||
|
DNSDBG( READ, ( "ERROR: Name character %c failed check\n", ch ));
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// fill out name properties
|
|||
|
//
|
|||
|
// JENHANCE: full property fill out
|
|||
|
//
|
|||
|
// currently only property we're returning is multibyte name issue
|
|||
|
// as that's all the server needs to check
|
|||
|
//
|
|||
|
// if save more properties then test becomes something like this
|
|||
|
// if ( (property & dwFlags) != (property & SUPPORTED_CHECK_FLAGS) )
|
|||
|
//
|
|||
|
|
|||
|
#if 0
|
|||
|
//*pNameProperty = property;
|
|||
|
|
|||
|
if ( (property & dwFlags) != property )
|
|||
|
{
|
|||
|
goto InvalidName;
|
|||
|
}
|
|||
|
|
|||
|
if ( property & DNS_BIT_NAME_MULTIBYTE )
|
|||
|
{
|
|||
|
goto Extended;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
// standard RFC name -- skip the detail parsing
|
|||
|
|
|||
|
if ( property == 0 )
|
|||
|
{
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
|
|||
|
// other chars invalid unless allowing all
|
|||
|
|
|||
|
if ( property & DNS_BIT_NAME_BINARY_LABEL )
|
|||
|
{
|
|||
|
if ( dwFlag != DNS_ALLOW_ALL_NAMES )
|
|||
|
{
|
|||
|
goto InvalidName;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// multibyte
|
|||
|
// - do extended downcase if multibyte
|
|||
|
// - do nothing if binary
|
|||
|
// - for strict this is invalid
|
|||
|
|
|||
|
if ( property & DNS_BIT_NAME_MULTIBYTE )
|
|||
|
{
|
|||
|
if ( dwFlag == DNS_ALLOW_MULTIBYTE_NAMES ||
|
|||
|
dwFlag == DNS_ALLOW_ALL_NAMES )
|
|||
|
{
|
|||
|
goto Extended;
|
|||
|
}
|
|||
|
#if 0
|
|||
|
if ( dwFlag != DNS_BINARY_LABELS )
|
|||
|
{
|
|||
|
goto InvalidName;
|
|||
|
}
|
|||
|
#endif
|
|||
|
goto InvalidName;
|
|||
|
}
|
|||
|
|
|||
|
// underscore valid unless completely strict
|
|||
|
|
|||
|
if ( property & DNS_BIT_NAME_UNDERSCORE )
|
|||
|
{
|
|||
|
if ( dwFlag == DNS_ALLOW_RFC_NAMES_ONLY )
|
|||
|
{
|
|||
|
goto InvalidName;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Done:
|
|||
|
|
|||
|
//
|
|||
|
// NULL terminate, return success.
|
|||
|
//
|
|||
|
|
|||
|
*pchout = 0;
|
|||
|
return( 0 );
|
|||
|
|
|||
|
|
|||
|
Extended:
|
|||
|
|
|||
|
//
|
|||
|
// DCR: better approach to extended names
|
|||
|
// 1) cannonicalize upfront
|
|||
|
// - do whole name in one pass
|
|||
|
// - no need to upcase here, similar to validateName() routine
|
|||
|
// 2) cannonicalize here
|
|||
|
// - detect extended
|
|||
|
// - cannonicalize here
|
|||
|
// - single recursion into routine like validateName()
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// extended character encountered
|
|||
|
// - convert to unicode
|
|||
|
// - downcase
|
|||
|
// - convert back to UTF8
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// DCR_PERF: optimize for names where extended already downcased
|
|||
|
//
|
|||
|
// DCR_PERF: should wrap this code into UTF8 cannon routine
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
//if ( ! (dwFlags & DNS_ALLOW_ALREADY_EXTENDED_DOWN) )
|
|||
|
{
|
|||
|
DWORD length;
|
|||
|
WCHAR unicodeString[ DNS_MAX_LABEL_BUFFER_LENGTH ];
|
|||
|
#if DBG
|
|||
|
WCHAR originalName[ DNS_MAX_LABEL_BUFFER_LENGTH ];
|
|||
|
WCHAR downName[ DNS_MAX_LABEL_BUFFER_LENGTH ];
|
|||
|
WCHAR cannonDownName[ DNS_MAX_LABEL_BUFFER_LENGTH ];
|
|||
|
#endif
|
|||
|
|
|||
|
DNSDBG( READ, (
|
|||
|
"Extended character encountered downcasing string %*.s\n"
|
|||
|
"\tconverting to unicode for case conversion\n",
|
|||
|
cchLabel,
|
|||
|
pchLabel ));
|
|||
|
|
|||
|
length = Dns_Utf8ToUnicode(
|
|||
|
pchResult,
|
|||
|
cchLabel,
|
|||
|
unicodeString,
|
|||
|
DNS_MAX_LABEL_BUFFER_LENGTH
|
|||
|
);
|
|||
|
DNSDBG( READ, (
|
|||
|
"Unicode converted string %.*S\n"
|
|||
|
"\tlength = %d\n"
|
|||
|
"\tlast error = %d\n",
|
|||
|
length,
|
|||
|
unicodeString,
|
|||
|
length,
|
|||
|
GetLastError() ));
|
|||
|
|
|||
|
if ( length == 0 )
|
|||
|
{
|
|||
|
DNSDBG( READ, (
|
|||
|
"Rejecting invalid UTF8 string %.*s\n"
|
|||
|
"\tFailed conversion to unicode OR conversion created\n"
|
|||
|
"\tinvalid unicode string\n",
|
|||
|
cchLabel,
|
|||
|
pchResult ));
|
|||
|
goto InvalidName;
|
|||
|
}
|
|||
|
|
|||
|
// no possible conversion of valid length UTF8, can
|
|||
|
// overflow unicode buffer
|
|||
|
|
|||
|
ASSERT( length <= DNS_MAX_LABEL_LENGTH );
|
|||
|
|
|||
|
Dns_MakeCanonicalNameInPlaceW( unicodeString, length );
|
|||
|
#if 1
|
|||
|
DNSDBG( READ, (
|
|||
|
"Canonical unicode name %.*S\n"
|
|||
|
"\tlength = %d\n",
|
|||
|
length,
|
|||
|
unicodeString,
|
|||
|
length ));
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// reconvert to UTF8
|
|||
|
// - mapping to UTF8 is just math, so only error
|
|||
|
// is possibly overflowing UTF8 max label buffer
|
|||
|
// - catch this error is character count changes
|
|||
|
// note, that also must catch case where write fills
|
|||
|
// 64 byte buffer eliminating NULL terminator
|
|||
|
//
|
|||
|
|
|||
|
length = Dns_UnicodeToUtf8(
|
|||
|
unicodeString,
|
|||
|
length,
|
|||
|
pchResult,
|
|||
|
DNS_MAX_LABEL_BUFFER_LENGTH
|
|||
|
);
|
|||
|
DNSDBG( READ, (
|
|||
|
"UTF8 downcased string %.*s\n"
|
|||
|
"\tlength = %d\n",
|
|||
|
length,
|
|||
|
pchResult,
|
|||
|
length ));
|
|||
|
|
|||
|
if ( length != cchLabel )
|
|||
|
{
|
|||
|
DNSDBG( ANY, (
|
|||
|
"Downcasing UTF8 label %.*s, changed character count!\n"
|
|||
|
"\tfrom %d to %d\n"
|
|||
|
"\tResult name %.*s\n"
|
|||
|
"\tlast error = %d\n",
|
|||
|
cchLabel,
|
|||
|
pchLabel,
|
|||
|
cchLabel,
|
|||
|
length,
|
|||
|
length,
|
|||
|
pchResult,
|
|||
|
GetLastError() ));
|
|||
|
|
|||
|
if ( length == 0 || length > DNS_MAX_LABEL_LENGTH )
|
|||
|
{
|
|||
|
DNSDBG( ANY, (
|
|||
|
"Failed conversion of downcased unicode string %S\n"
|
|||
|
"\tback into UTF8.\n",
|
|||
|
unicodeString ));
|
|||
|
goto InvalidName;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// NULL terminate, return length to indicate extended name
|
|||
|
//
|
|||
|
|
|||
|
pchResult[ length ] = 0;
|
|||
|
return( (INT)length );
|
|||
|
}
|
|||
|
|
|||
|
// no UTF8 multi-byte allowed -- drop through to invalid name return
|
|||
|
|
|||
|
|
|||
|
InvalidName:
|
|||
|
|
|||
|
// return (-1) for error
|
|||
|
|
|||
|
DNSDBG( READ, (
|
|||
|
"Dns_DowncaseNameLabel() found label to be invalid.\n"
|
|||
|
"\tlabel = %.*s\n"
|
|||
|
"\tcount = %d\n"
|
|||
|
"\tproperty = %08x\n",
|
|||
|
cchLabel,
|
|||
|
pchLabel,
|
|||
|
count,
|
|||
|
property ));
|
|||
|
|
|||
|
return( -1 );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
LPSTR
|
|||
|
Dns_CreateStandardDnsNameCopy(
|
|||
|
IN PCHAR pchName,
|
|||
|
IN DWORD cchName,
|
|||
|
IN DWORD dwFlag
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Makes copy of DNS name in valid "standard form"
|
|||
|
- downcased
|
|||
|
- no trailing dot (to avoid confusing DS)
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pchName -- ptr DNS name in UTF8
|
|||
|
|
|||
|
cchName -- count of chars in name; may be NULL
|
|||
|
|
|||
|
dwFlag -- strict checking flags; currently ignored
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Ptr to copy of DNS name.
|
|||
|
NULL on invalid name.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PCHAR pszcopy = NULL;
|
|||
|
DNS_STATUS status;
|
|||
|
DWORD length;
|
|||
|
|
|||
|
DNSDBG( TRACE, ( "Dns_CreateStandardDnsName()\n" ));
|
|||
|
DNSDBG( READ, (
|
|||
|
"Dns_CreateStandardDnsName()\n"
|
|||
|
"\tpchName = %.*s\n"
|
|||
|
"\tcchName = %d\n",
|
|||
|
cchName,
|
|||
|
pchName,
|
|||
|
cchName ));
|
|||
|
|
|||
|
if ( !pchName )
|
|||
|
{
|
|||
|
status = ERROR_INVALID_NAME;
|
|||
|
goto Failed;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// ASCII string?
|
|||
|
//
|
|||
|
|
|||
|
if ( Dns_IsStringAsciiEx( pchName, cchName ) )
|
|||
|
{
|
|||
|
//
|
|||
|
// make copy
|
|||
|
//
|
|||
|
|
|||
|
pszcopy = Dns_CreateStringCopy( pchName, cchName );
|
|||
|
if ( !pszcopy )
|
|||
|
{
|
|||
|
status = DNS_ERROR_NO_MEMORY;
|
|||
|
goto Failed;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// validate, check against strict criteria
|
|||
|
//
|
|||
|
// no validation until relax criteria
|
|||
|
//
|
|||
|
// DCR: name validation within Dns_CreateStandardNameCopy()
|
|||
|
// accept anything except INVALID_NAME
|
|||
|
// flags return FQDN info
|
|||
|
//
|
|||
|
#if 0
|
|||
|
status = Dns_ValidateName_UTF8( pszcopy, DnsNameDomain );
|
|||
|
if ( status == ERROR_INVALID_NAME )
|
|||
|
{
|
|||
|
goto Failed;
|
|||
|
}
|
|||
|
#endif
|
|||
|
//
|
|||
|
// downcase
|
|||
|
// remove any trailing dot, except for root name
|
|||
|
//
|
|||
|
|
|||
|
_strlwr( pszcopy );
|
|||
|
length = strlen( pszcopy );
|
|||
|
if ( length == 0 )
|
|||
|
{
|
|||
|
status = ERROR_INVALID_NAME;
|
|||
|
goto Failed;
|
|||
|
}
|
|||
|
length--;
|
|||
|
if ( length > 0 && pszcopy[length] == '.' )
|
|||
|
{
|
|||
|
pszcopy[length] = 0;
|
|||
|
}
|
|||
|
|
|||
|
DNSDBG( READ, (
|
|||
|
"Standard DNS name copy of %.*s is %s\n",
|
|||
|
cchName,
|
|||
|
pchName,
|
|||
|
pszcopy ));
|
|||
|
return( pszcopy );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// unicode name
|
|||
|
//
|
|||
|
|
|||
|
else
|
|||
|
{
|
|||
|
WCHAR unicodeName[ DNS_MAX_NAME_BUFFER_LENGTH ];
|
|||
|
WCHAR cannonicalName[ DNS_MAX_NAME_BUFFER_LENGTH ];
|
|||
|
DWORD unicodeBufferLength;
|
|||
|
|
|||
|
//
|
|||
|
// convert to unicode
|
|||
|
// - buf length is in bytes
|
|||
|
//
|
|||
|
|
|||
|
unicodeBufferLength = DNS_MAX_NAME_BUFFER_LENGTH * 2;
|
|||
|
|
|||
|
length = Dns_NameCopy(
|
|||
|
(PSTR) unicodeName,
|
|||
|
& unicodeBufferLength,
|
|||
|
pchName,
|
|||
|
cchName,
|
|||
|
DnsCharSetUtf8,
|
|||
|
DnsCharSetUnicode
|
|||
|
);
|
|||
|
if ( length == 0 )
|
|||
|
{
|
|||
|
DNSDBG( ANY, (
|
|||
|
"ERROR conversion of name %.*s to unicode failed!\n",
|
|||
|
cchName,
|
|||
|
pchName ));
|
|||
|
status = ERROR_INVALID_NAME;
|
|||
|
goto Failed;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// make cannonical name
|
|||
|
// - buf length is in unicode characters
|
|||
|
// - output length is in unicode chars
|
|||
|
|
|||
|
length = Dns_MakeCanonicalNameW(
|
|||
|
cannonicalName,
|
|||
|
length / 2,
|
|||
|
unicodeName,
|
|||
|
dwFlag );
|
|||
|
|
|||
|
ASSERT( length != 0 );
|
|||
|
if ( length == 0 )
|
|||
|
{
|
|||
|
status = ERROR_INVALID_NAME;
|
|||
|
goto Failed;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// allocate UTF8 converted copy
|
|||
|
// - this conversion should never fail
|
|||
|
// - string length is unicode chars
|
|||
|
//
|
|||
|
|
|||
|
pszcopy = Dns_StringCopyAllocate(
|
|||
|
(PSTR) cannonicalName,
|
|||
|
length,
|
|||
|
DnsCharSetUnicode, // unicode in
|
|||
|
DnsCharSetUtf8 // UTF8 out
|
|||
|
);
|
|||
|
if ( !pszcopy )
|
|||
|
{
|
|||
|
status = DNS_ERROR_NO_MEMORY;
|
|||
|
goto Failed;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// validate, check against strict criteria
|
|||
|
//
|
|||
|
// no validation until relax criteria
|
|||
|
//
|
|||
|
// DCR: name validation within Dns_CreateStandardNameCopy()
|
|||
|
// accept anything except INVALID_NAME
|
|||
|
// flags return FQDN info
|
|||
|
//
|
|||
|
#if 0
|
|||
|
status = Dns_ValidateName_UTF8( pszcopy, DnsNameDomain );
|
|||
|
if ( status == ERROR_INVALID_NAME )
|
|||
|
{
|
|||
|
goto Failed;
|
|||
|
}
|
|||
|
#endif
|
|||
|
return( pszcopy );
|
|||
|
}
|
|||
|
|
|||
|
Failed:
|
|||
|
|
|||
|
FREE_HEAP( pszcopy );
|
|||
|
SetLastError( status );
|
|||
|
return( NULL );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Public compare functions
|
|||
|
//
|
|||
|
|
|||
|
#if DNSWIN95
|
|||
|
|
|||
|
//
|
|||
|
// Running 9x - exposing for debug
|
|||
|
//
|
|||
|
|
|||
|
#define WIN9X_FLAG_START_VAL (0x1111)
|
|||
|
|
|||
|
BOOL g_fWin9x = WIN9X_FLAG_START_VAL;
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
Dns_IsWin9x(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Are we on Win9x
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if on Win9x
|
|||
|
FALSE otherwise
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// if not yet queried, query for system version
|
|||
|
// Win9x (and Win3.1) have high bit set, regardless of major version
|
|||
|
//
|
|||
|
// for Win9x set locale to system default, as we'll use the CRT
|
|||
|
// functions to do name comparisons (CompareStringW() is a non-implemented
|
|||
|
// stub on Win9x
|
|||
|
// - LC_ALL sets for all CRT functions
|
|||
|
// - "" sets to system default code page
|
|||
|
//
|
|||
|
|
|||
|
if ( g_fWin9x == WIN9X_FLAG_START_VAL )
|
|||
|
{
|
|||
|
if ( GetVersion() & 0x80000000 )
|
|||
|
{
|
|||
|
g_fWin9x = TRUE;
|
|||
|
setlocale( LC_ALL, "" );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
g_fWin9x = FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return( g_fWin9x );
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
Dns_NameCompare_A(
|
|||
|
IN PCSTR pName1,
|
|||
|
IN PCSTR pName2
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Compare two DNS names.
|
|||
|
|
|||
|
Can not use stricmp() because of the possiblity of names with
|
|||
|
trailing dots.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pName1 - ptr to first DNS name string (dotted format)
|
|||
|
pName2 - ptr to second DNS name string (dotted format)
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if names equal.
|
|||
|
FALSE otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
INT len1;
|
|||
|
INT len2;
|
|||
|
INT result;
|
|||
|
|
|||
|
//
|
|||
|
// flat out match
|
|||
|
// - this is possible with owner names and possibly other fields
|
|||
|
//
|
|||
|
|
|||
|
if ( pName1 == pName2 )
|
|||
|
{
|
|||
|
return( TRUE );
|
|||
|
}
|
|||
|
|
|||
|
if ( !pName1 || !pName2 )
|
|||
|
{
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// if lengths NOT equal, then
|
|||
|
// they must be within one and longer string must have trailing dot
|
|||
|
// - in this case save
|
|||
|
//
|
|||
|
|
|||
|
len1 = strlen( pName1 );
|
|||
|
len2 = strlen( pName2 );
|
|||
|
|
|||
|
if ( len2 != len1 )
|
|||
|
{
|
|||
|
if ( len2 == len1+1 )
|
|||
|
{
|
|||
|
if ( pName2[len1] != '.' )
|
|||
|
{
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
// len1 is comparable length
|
|||
|
}
|
|||
|
else if ( len2+1 == len1 )
|
|||
|
{
|
|||
|
if ( pName1[len2] != '.' )
|
|||
|
{
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
// len1 is set to comparable length
|
|||
|
len1 = len2;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// compare only comparable length of string
|
|||
|
//
|
|||
|
|
|||
|
result = CompareStringA(
|
|||
|
//LOCALE_SYSTEM_DEFAULT,
|
|||
|
DNS_CANONICAL_LOCALE,
|
|||
|
DNS_CANONICAL_COMPARE_FLAGS,
|
|||
|
pName1,
|
|||
|
len1,
|
|||
|
pName2,
|
|||
|
len1 );
|
|||
|
|
|||
|
if ( result == CSTR_EQUAL )
|
|||
|
{
|
|||
|
return( TRUE );
|
|||
|
}
|
|||
|
|
|||
|
// not equal or error
|
|||
|
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
Dns_NameCompare_W(
|
|||
|
IN PCWSTR pName1,
|
|||
|
IN PCWSTR pName2
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Compare two (Wide) DNS names.
|
|||
|
|
|||
|
Note: this is unicode aware, it assumes names in WCHAR string
|
|||
|
format. Can not use stricmp() because of the possiblity of names
|
|||
|
with trailing dots.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pName1 - ptr to first DNS name string (dotted format)
|
|||
|
pName2 - ptr to second DNS name string (dotted format)
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if names equal.
|
|||
|
FALSE otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
INT len1;
|
|||
|
INT len2;
|
|||
|
INT result;
|
|||
|
|
|||
|
//
|
|||
|
// flat out match
|
|||
|
// - this is possible with owner names and possibly other fields
|
|||
|
//
|
|||
|
|
|||
|
if ( pName1 == pName2 )
|
|||
|
{
|
|||
|
return( TRUE );
|
|||
|
}
|
|||
|
|
|||
|
if ( !pName1 || !pName2 )
|
|||
|
{
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// if lengths NOT equal, then
|
|||
|
// they must be within one and longer string must have trailing dot
|
|||
|
// - in this case save
|
|||
|
//
|
|||
|
|
|||
|
len1 = wcslen( pName1 );
|
|||
|
len2 = wcslen( pName2 );
|
|||
|
|
|||
|
if ( len2 != len1 )
|
|||
|
{
|
|||
|
if ( len2 == len1+1 )
|
|||
|
{
|
|||
|
if ( pName2[len1] != L'.' )
|
|||
|
{
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
// len1 is comparable length
|
|||
|
}
|
|||
|
else if ( len2+1 == len1 )
|
|||
|
{
|
|||
|
if ( pName1[len2] != L'.' )
|
|||
|
{
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
// len1 is set to comparable length
|
|||
|
len1 = len2;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// compare only comparable length of string
|
|||
|
//
|
|||
|
|
|||
|
#if DNSWIN95
|
|||
|
//
|
|||
|
// Win9x does not currently support CompareStringW()
|
|||
|
//
|
|||
|
|
|||
|
if ( Dns_IsWin9x() )
|
|||
|
{
|
|||
|
return( !_wcsnicmp( pName1, pName2, len1 ) );
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
result = CompareStringW(
|
|||
|
//LOCALE_SYSTEM_DEFAULT,
|
|||
|
DNS_CANONICAL_LOCALE,
|
|||
|
DNS_CANONICAL_COMPARE_FLAGS,
|
|||
|
pName1,
|
|||
|
len1,
|
|||
|
pName2,
|
|||
|
len1 );
|
|||
|
|
|||
|
if ( result == CSTR_EQUAL )
|
|||
|
{
|
|||
|
return( TRUE );
|
|||
|
}
|
|||
|
|
|||
|
// not equal or error
|
|||
|
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
Dns_NameCompare_UTF8(
|
|||
|
IN PCSTR pName1,
|
|||
|
IN PCSTR pName2
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Compare two DNS names.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pName1 - ptr to first DNS name string (dotted format)
|
|||
|
pName2 - ptr to second DNS name string (dotted format)
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if names equal.
|
|||
|
FALSE otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
WCHAR nameBuffer1[ DNS_MAX_NAME_BUFFER_LENGTH ];
|
|||
|
WCHAR nameBuffer2[ DNS_MAX_NAME_BUFFER_LENGTH ];
|
|||
|
DWORD bufLen;
|
|||
|
|
|||
|
//
|
|||
|
// flat out match
|
|||
|
// - this is possible with owner names and possibly other fields
|
|||
|
//
|
|||
|
|
|||
|
if ( pName1 == pName2 )
|
|||
|
{
|
|||
|
return( TRUE );
|
|||
|
}
|
|||
|
|
|||
|
if ( !pName1 || !pName2 )
|
|||
|
{
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// if strings pure ASCII, then use ANSI version
|
|||
|
//
|
|||
|
|
|||
|
if ( Dns_IsStringAscii( (PCHAR)pName1 ) &&
|
|||
|
Dns_IsStringAscii( (PCHAR)pName2 ) )
|
|||
|
{
|
|||
|
return Dns_NameCompare_A( pName1, pName2 );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// otherwise must take names back to unicode to compare
|
|||
|
//
|
|||
|
|
|||
|
bufLen = DNS_MAX_NAME_LENGTH;
|
|||
|
|
|||
|
if ( ! Dns_NameCopy(
|
|||
|
(PCHAR) nameBuffer1,
|
|||
|
& bufLen,
|
|||
|
(PCHAR) pName1,
|
|||
|
0, // length unknown
|
|||
|
DnsCharSetUtf8,
|
|||
|
DnsCharSetUnicode
|
|||
|
) )
|
|||
|
{
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
|
|||
|
bufLen = DNS_MAX_NAME_LENGTH;
|
|||
|
|
|||
|
if ( ! Dns_NameCopy(
|
|||
|
(PCHAR) nameBuffer2,
|
|||
|
& bufLen,
|
|||
|
(PCHAR) pName2,
|
|||
|
0, // length unknown
|
|||
|
DnsCharSetUtf8,
|
|||
|
DnsCharSetUnicode
|
|||
|
) )
|
|||
|
{
|
|||
|
return( FALSE );
|
|||
|
}
|
|||
|
|
|||
|
return Dns_NameCompare_W( nameBuffer1, nameBuffer2 );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
Dns_NameComparePrivate(
|
|||
|
IN PCSTR pName1,
|
|||
|
IN PCSTR pName2,
|
|||
|
IN DNS_CHARSET CharSet
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Compare two DNS names.
|
|||
|
|
|||
|
This is simply helpful utility to avoid coding the wide\narrow
|
|||
|
test in the code in a hundred different places.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pName1 - ptr to first DNS name string (dotted format)
|
|||
|
pName2 - ptr to second DNS name string (dotted format)
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if names equal.
|
|||
|
FALSE otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
if ( CharSet == DnsCharSetUnicode )
|
|||
|
{
|
|||
|
return Dns_NameCompare_W(
|
|||
|
(PCWSTR) pName1,
|
|||
|
(PCWSTR) pName2 );
|
|||
|
}
|
|||
|
else if ( CharSet == DnsCharSetAnsi )
|
|||
|
{
|
|||
|
return Dns_NameCompare_A(
|
|||
|
pName1,
|
|||
|
pName2 );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return Dns_NameCompare_UTF8(
|
|||
|
pName1,
|
|||
|
pName2 );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Advanced name comparison
|
|||
|
// Includes hierarchial name relationship.
|
|||
|
//
|
|||
|
|
|||
|
DNS_NAME_COMPARE_STATUS
|
|||
|
Dns_NameCompareEx(
|
|||
|
IN PCSTR pszNameLeft,
|
|||
|
IN PCSTR pszNameRight,
|
|||
|
IN DWORD dwReserved,
|
|||
|
IN DNS_CHARSET CharSet
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Advanced compare of DNS names, including hierarchial relationship.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pszNameLeft -- left name
|
|||
|
|
|||
|
pszNameRight -- right name
|
|||
|
|
|||
|
dwReserved -- reserved for future use (type of comparison)
|
|||
|
|
|||
|
CharSet -- char set of names
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
DnsNameCompareInvalid -- one of the names was invalid
|
|||
|
DnsNameCompareEqual -- names are equal
|
|||
|
DnsNameCompareLeftParent -- left is ancestor of right name
|
|||
|
DnsNameCompareRightParent -- right is ancestor of left name
|
|||
|
DnsNameCompareNotEqual -- name not equal, no hierarchial relationship
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DNS_NAME_COMPARE_STATUS result;
|
|||
|
DNS_STATUS status;
|
|||
|
INT lengthLeft;
|
|||
|
INT lengthRight;
|
|||
|
INT lengthDiff;
|
|||
|
INT compareResult;
|
|||
|
DWORD bufLength;
|
|||
|
WCHAR nameLeft[ DNS_MAX_NAME_BUFFER_LENGTH ];
|
|||
|
WCHAR nameRight[ DNS_MAX_NAME_BUFFER_LENGTH ];
|
|||
|
|
|||
|
|
|||
|
DNSDBG( TRACE, (
|
|||
|
"Dns_NameCompareEx( %s%S, %s%S )\n",
|
|||
|
(CharSet==DnsCharSetUnicode) ? "" : pszNameLeft,
|
|||
|
(CharSet==DnsCharSetUnicode) ? pszNameLeft : "",
|
|||
|
(CharSet==DnsCharSetUnicode) ? "" : pszNameRight,
|
|||
|
(CharSet==DnsCharSetUnicode) ? pszNameRight : ""
|
|||
|
));
|
|||
|
|
|||
|
//
|
|||
|
// implementation note
|
|||
|
// there's a lot of inefficiency here, because there are
|
|||
|
// two different character sets required for
|
|||
|
// validation -- UTF8 to check packet limits
|
|||
|
// downcasing\comparison -- unicode for case insensitivity
|
|||
|
//
|
|||
|
// obviously there are much more efficient paths through this
|
|||
|
// morass for particular special cases (ASCII names: downcase
|
|||
|
// in ANSI, validate, compare); but since this is not perf
|
|||
|
// path code we'll take the approach
|
|||
|
// - convert to unicode
|
|||
|
// - validate (which will convert at copy to UTF8)
|
|||
|
// - downcase unicode
|
|||
|
// - compare unicode
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// validate args
|
|||
|
//
|
|||
|
|
|||
|
if ( ! pszNameLeft || ! pszNameRight )
|
|||
|
{
|
|||
|
goto Invalid;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// copy convert to unicode
|
|||
|
// - downcasing and compare will be done in unicode
|
|||
|
// - return lengths are bytes converted, convert to string lengths
|
|||
|
// - Dns_NameCopy() returns zero for invalid convert
|
|||
|
//
|
|||
|
|
|||
|
bufLength = DNS_MAX_NAME_BUFFER_LENGTH * 2;
|
|||
|
|
|||
|
lengthLeft = (INT) Dns_NameCopy(
|
|||
|
(PCHAR) nameLeft,
|
|||
|
& bufLength,
|
|||
|
(LPSTR) pszNameLeft,
|
|||
|
0, // string NULL terminated
|
|||
|
CharSet, // char set in
|
|||
|
DnsCharSetUnicode // unicode out
|
|||
|
);
|
|||
|
if ( lengthLeft == 0 )
|
|||
|
{
|
|||
|
goto Invalid;
|
|||
|
}
|
|||
|
lengthLeft = (lengthLeft/2) - 1;
|
|||
|
ASSERT( lengthLeft >= 0 );
|
|||
|
|
|||
|
bufLength = DNS_MAX_NAME_BUFFER_LENGTH * 2;
|
|||
|
|
|||
|
lengthRight = (INT) Dns_NameCopy(
|
|||
|
(PCHAR) nameRight,
|
|||
|
& bufLength,
|
|||
|
(LPSTR) pszNameRight,
|
|||
|
0, // string NULL terminated
|
|||
|
CharSet, // char set in
|
|||
|
DnsCharSetUnicode // unicode out
|
|||
|
);
|
|||
|
if ( lengthRight == 0 )
|
|||
|
{
|
|||
|
goto Invalid;
|
|||
|
}
|
|||
|
lengthRight = (lengthRight/2) - 1;
|
|||
|
ASSERT( lengthRight >= 0 );
|
|||
|
|
|||
|
//
|
|||
|
// cannonicalize names
|
|||
|
//
|
|||
|
|
|||
|
Dns_MakeCanonicalNameInPlaceW( nameLeft, lengthLeft );
|
|||
|
Dns_MakeCanonicalNameInPlaceW( nameRight, lengthRight );
|
|||
|
|
|||
|
//
|
|||
|
// validate names
|
|||
|
// - must screen empty string or we can fault below
|
|||
|
//
|
|||
|
|
|||
|
status = Dns_ValidateName_W( nameLeft, DnsNameDomain );
|
|||
|
if ( ERROR_SUCCESS != status &&
|
|||
|
DNS_ERROR_NON_RFC_NAME != status )
|
|||
|
{
|
|||
|
goto Invalid;
|
|||
|
}
|
|||
|
|
|||
|
status = Dns_ValidateName_W( nameRight, DnsNameDomain );
|
|||
|
if ( ERROR_SUCCESS != status &&
|
|||
|
DNS_ERROR_NON_RFC_NAME != status )
|
|||
|
{
|
|||
|
goto Invalid;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// add trailing dots
|
|||
|
//
|
|||
|
// we need to either add or remove trailing dots to make comparisons
|
|||
|
// the advantage of adding them is that then, the root name does
|
|||
|
// not require any special casing -- the root is the ancestor of
|
|||
|
// every name
|
|||
|
//
|
|||
|
|
|||
|
if ( nameLeft[ lengthLeft-1 ] != (WORD)'.')
|
|||
|
{
|
|||
|
nameLeft[ lengthLeft++ ] = (WORD) '.';
|
|||
|
nameLeft[ lengthLeft ] = (WORD) 0;
|
|||
|
}
|
|||
|
if ( nameRight[ lengthRight-1 ] != (WORD)'.')
|
|||
|
{
|
|||
|
nameRight[ lengthRight++ ] = (WORD) '.';
|
|||
|
nameRight[ lengthRight ] = (WORD) 0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// compare equal length strings
|
|||
|
//
|
|||
|
|
|||
|
result = DnsNameCompareNotEqual;
|
|||
|
|
|||
|
lengthDiff = (INT)lengthLeft - (INT)lengthRight;
|
|||
|
|
|||
|
if ( lengthLeft == lengthRight )
|
|||
|
{
|
|||
|
compareResult = wcscmp( nameLeft, nameRight );
|
|||
|
if ( compareResult == 0 )
|
|||
|
{
|
|||
|
result = DnsNameCompareEqual;
|
|||
|
}
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// strings not equal
|
|||
|
// - compare smaller string of length X
|
|||
|
// to last X characters of larger string
|
|||
|
// - also must make sure starting at label boundary
|
|||
|
//
|
|||
|
// note: strstr() is NOT useful for this work, because it
|
|||
|
// compares useless for this work because it is finding the
|
|||
|
// first match -- a little thought would indicate that this
|
|||
|
// will fail in several obvious cases
|
|||
|
//
|
|||
|
|
|||
|
// right string longer
|
|||
|
// - need to sign change diff to make it offset in right string
|
|||
|
|
|||
|
else if ( lengthDiff < 0 )
|
|||
|
{
|
|||
|
lengthDiff = -lengthDiff;
|
|||
|
|
|||
|
if ( nameRight[ lengthDiff-1 ] != L'.' )
|
|||
|
{
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
compareResult = wcscmp( nameLeft, nameRight+lengthDiff );
|
|||
|
if ( compareResult == 0 )
|
|||
|
{
|
|||
|
result = DnsNameCompareLeftParent;
|
|||
|
}
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
|
|||
|
// left string longer
|
|||
|
// - lengthDiff is offset into left string to start compare
|
|||
|
|
|||
|
else
|
|||
|
{
|
|||
|
if ( nameLeft[ lengthDiff-1 ] != L'.' )
|
|||
|
{
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
compareResult = wcscmp( nameLeft+lengthDiff, nameRight );
|
|||
|
if ( compareResult == 0 )
|
|||
|
{
|
|||
|
result = DnsNameCompareRightParent;
|
|||
|
}
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
|
|||
|
Done:
|
|||
|
|
|||
|
DNSDBG( TRACE, (
|
|||
|
"Leave DnsNameCompareEx() result = %d\n",
|
|||
|
result ));
|
|||
|
|
|||
|
return( result );
|
|||
|
|
|||
|
Invalid:
|
|||
|
|
|||
|
DNSDBG( ANY, (
|
|||
|
"ERROR: Invalid name to Dns_NameCompareEx()\n" ));
|
|||
|
|
|||
|
return( DnsNameCompareInvalid );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Random name utilities
|
|||
|
//
|
|||
|
|
|||
|
PCHAR
|
|||
|
_fastcall
|
|||
|
Dns_GetDomainName(
|
|||
|
IN PCSTR pszName
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Get domain name of DNS name.
|
|||
|
|
|||
|
Note, this assumes name already in UTF-8
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pszName - standard dotted DNS name
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Ptr to domain name of pszName.
|
|||
|
NULL if pszName is in root domain.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
CHAR ch;
|
|||
|
|
|||
|
//
|
|||
|
// find next "." in name, then return ptr to next character
|
|||
|
//
|
|||
|
|
|||
|
while( ch = *pszName++ )
|
|||
|
{
|
|||
|
if ( ch == '.' )
|
|||
|
{
|
|||
|
if ( *pszName )
|
|||
|
{
|
|||
|
return( (PCHAR)pszName );
|
|||
|
}
|
|||
|
return( NULL );
|
|||
|
}
|
|||
|
}
|
|||
|
return( NULL );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
PWSTR
|
|||
|
_fastcall
|
|||
|
Dns_GetDomainName_W(
|
|||
|
IN PCWSTR pwsName
|
|||
|
)
|
|||
|
{
|
|||
|
PWSTR pdomain;
|
|||
|
|
|||
|
//
|
|||
|
// find next "." in name, then return ptr to next character
|
|||
|
//
|
|||
|
|
|||
|
pdomain = wcschr( pwsName, L'.' );
|
|||
|
|
|||
|
if ( pdomain && *(++pdomain) )
|
|||
|
{
|
|||
|
return( pdomain );
|
|||
|
}
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
PCHAR
|
|||
|
_fastcall
|
|||
|
Dns_GetTldForName(
|
|||
|
IN PCSTR pszName
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Get domain name of DNS name.
|
|||
|
|
|||
|
Note, this assumes name already in UTF-8
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pszName - standard dotted DNS name
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Ptr to domain name of pszName.
|
|||
|
NULL if pszName is in root domain.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PSTR pdomain = (PSTR) pszName;
|
|||
|
PSTR ptld = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// find last domain name in name
|
|||
|
//
|
|||
|
|
|||
|
while ( pdomain = Dns_GetDomainName(pdomain) )
|
|||
|
{
|
|||
|
ptld = (PSTR) pdomain;
|
|||
|
}
|
|||
|
return ptld;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
_fastcall
|
|||
|
Dns_IsNameShort(
|
|||
|
IN PCSTR pszName
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Determine if a name is a multi-label DNS name.
|
|||
|
|
|||
|
This is a test of whether name at least one non-terminal dot.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pszName - standard dotted DNS name
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if multiple labels.
|
|||
|
FALSE otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
INT nameLen;
|
|||
|
|
|||
|
// trailing domain? -- done
|
|||
|
|
|||
|
if ( Dns_GetDomainName( pszName ) )
|
|||
|
{
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
// otherwise test for valid label
|
|||
|
|
|||
|
nameLen = strlen( pszName );
|
|||
|
if ( nameLen <= DNS_MAX_LABEL_LENGTH )
|
|||
|
{
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
nameLen--;
|
|||
|
if ( nameLen == DNS_MAX_LABEL_LENGTH &&
|
|||
|
pszName[nameLen] == '.')
|
|||
|
{
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
_fastcall
|
|||
|
Dns_IsNameFQDN(
|
|||
|
IN PCSTR pszName
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Determine if a name is a fully qualified DNS name (FQDN).
|
|||
|
|
|||
|
This is a test of whether name has trailing dot.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pszName - standard dotted DNS name
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if FQDN.
|
|||
|
FALSE otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD nameLen = strlen( pszName );
|
|||
|
|
|||
|
if ( nameLen == 0 )
|
|||
|
{
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
if ( pszName[nameLen - 1] == '.' )
|
|||
|
{
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
_fastcall
|
|||
|
Dns_GetNameAttributes(
|
|||
|
IN PCSTR pszName
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Determine the attributes that a name has.
|
|||
|
|
|||
|
Note, this assumes name already in UTF-8
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pszName - standard dotted DNS name
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
DWORD with possible flags:
|
|||
|
|
|||
|
DNS_NAME_IS_FQDN
|
|||
|
DNS_NAME_SINGLE_LABEL
|
|||
|
DNS_NAME_MULTI_LABEL
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD dwAttributes = DNS_NAME_UNKNOWN;
|
|||
|
|
|||
|
if ( Dns_IsNameFQDN( pszName ) )
|
|||
|
{
|
|||
|
dwAttributes = DNS_NAME_IS_FQDN;
|
|||
|
}
|
|||
|
|
|||
|
if ( Dns_IsNameShort( pszName ) )
|
|||
|
{
|
|||
|
dwAttributes |= DNS_NAME_SINGLE_LABEL;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
dwAttributes |= DNS_NAME_MULTI_LABEL;
|
|||
|
}
|
|||
|
|
|||
|
return dwAttributes;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DNS_STATUS
|
|||
|
Dns_ValidateAndCategorizeDnsNameEx(
|
|||
|
IN PCHAR pchName,
|
|||
|
IN DWORD cchNameLength,
|
|||
|
OUT PDWORD pLabelCount
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Determine type of name.
|
|||
|
|
|||
|
Three types:
|
|||
|
1) FQDN -- dot on end, signifies full DNS name, never appended
|
|||
|
|
|||
|
2) dotted -- dot in name; probably FQDN, but may need to be appended
|
|||
|
(as in file store)
|
|||
|
|
|||
|
3) single part -- single part name (not FQDN), always appended with zone
|
|||
|
or default domain name
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pchName -- ptr to name
|
|||
|
|
|||
|
cchNameLength -- name length
|
|||
|
|
|||
|
pLabelCount -- address to receive label count
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
DNS_STATUS_FQDN
|
|||
|
DNS_STATUS_DOTTED_NAME
|
|||
|
DNS_STATUS_SINGLE_PART_NAME
|
|||
|
DNS_ERROR_INVALID_NAME on non-DNS name
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
register PCHAR pch;
|
|||
|
register CHAR ch;
|
|||
|
PCHAR pchstop;
|
|||
|
BOOL fdotted = FALSE;
|
|||
|
DWORD labelCount = 0;
|
|||
|
DWORD charCount = 0;
|
|||
|
DNS_STATUS status = DNS_STATUS_SINGLE_PART_NAME;
|
|||
|
|
|||
|
//
|
|||
|
// name length for string
|
|||
|
//
|
|||
|
|
|||
|
if ( cchNameLength == 0 )
|
|||
|
{
|
|||
|
cchNameLength = strlen( pchName );
|
|||
|
}
|
|||
|
if ( cchNameLength > DNS_MAX_NAME_LENGTH ||
|
|||
|
cchNameLength == 0 )
|
|||
|
{
|
|||
|
goto InvalidName;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// run through name
|
|||
|
//
|
|||
|
|
|||
|
pch = pchName;
|
|||
|
pchstop = pch + cchNameLength;
|
|||
|
|
|||
|
while ( pch < pchstop )
|
|||
|
{
|
|||
|
ch = *pch++;
|
|||
|
if ( ch == '.' )
|
|||
|
{
|
|||
|
if ( charCount > DNS_MAX_LABEL_LENGTH )
|
|||
|
{
|
|||
|
goto InvalidName;
|
|||
|
}
|
|||
|
if ( charCount > 0 )
|
|||
|
{
|
|||
|
labelCount++;
|
|||
|
charCount = 0;
|
|||
|
status = DNS_STATUS_DOTTED_NAME;
|
|||
|
continue;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// only valid zero label name is "."
|
|||
|
if ( pch == pchstop &&
|
|||
|
pch-1 == pchName )
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
goto InvalidName;
|
|||
|
}
|
|||
|
}
|
|||
|
else if ( ch == 0 )
|
|||
|
{
|
|||
|
DNS_ASSERT( FALSE );
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
// regular character
|
|||
|
charCount++;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// handle last label
|
|||
|
// - if count, then boost label count
|
|||
|
// - if zero and previously had dot, then string
|
|||
|
// ended in dot and is FQDN
|
|||
|
//
|
|||
|
|
|||
|
if ( charCount > 0 )
|
|||
|
{
|
|||
|
if ( charCount > DNS_MAX_LABEL_LENGTH )
|
|||
|
{
|
|||
|
goto InvalidName;
|
|||
|
}
|
|||
|
labelCount++;
|
|||
|
}
|
|||
|
else if ( status == DNS_STATUS_DOTTED_NAME )
|
|||
|
{
|
|||
|
status = DNS_STATUS_FQDN;
|
|||
|
}
|
|||
|
|
|||
|
// return label count
|
|||
|
|
|||
|
if ( pLabelCount )
|
|||
|
{
|
|||
|
*pLabelCount = labelCount;
|
|||
|
}
|
|||
|
|
|||
|
DNSDBG( TRACE, (
|
|||
|
"Leave Dns_ValidateAndCategorizeNameEx()\n"
|
|||
|
"\tstatus = %d\n"
|
|||
|
"\tlabel count = %d\n",
|
|||
|
status,
|
|||
|
labelCount ));
|
|||
|
|
|||
|
return( status );
|
|||
|
|
|||
|
|
|||
|
InvalidName:
|
|||
|
|
|||
|
if ( pLabelCount )
|
|||
|
{
|
|||
|
*pLabelCount = 0;
|
|||
|
}
|
|||
|
|
|||
|
DNSDBG( TRACE, (
|
|||
|
"Leave Dns_ValidateAndCategorizeNameEx()\n"
|
|||
|
"\tstatus = ERROR_INVALID_NAME\n" ));
|
|||
|
|
|||
|
return( DNS_ERROR_INVALID_NAME );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
Dns_NameLabelCount(
|
|||
|
IN PCSTR pszName
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Return name label count.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pszName -- ptr to name
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Label count if valid name.
|
|||
|
Zero on root name or error.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD labelCount = 0;
|
|||
|
DNS_STATUS status;
|
|||
|
|
|||
|
//
|
|||
|
// call real routine
|
|||
|
//
|
|||
|
|
|||
|
status = Dns_ValidateAndCategorizeDnsNameEx(
|
|||
|
(PCHAR) pszName,
|
|||
|
0,
|
|||
|
& labelCount );
|
|||
|
|
|||
|
if ( status == DNS_ERROR_INVALID_NAME )
|
|||
|
{
|
|||
|
labelCount = 0;
|
|||
|
}
|
|||
|
|
|||
|
return( labelCount );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
PSTR
|
|||
|
Dns_NameAppend_A(
|
|||
|
OUT PCHAR pNameBuffer,
|
|||
|
IN DWORD BufferLength,
|
|||
|
IN PSTR pszName,
|
|||
|
IN PSTR pszDomain
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Write appended name to buffer (ANSI or UTF8).
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pNameBuffer -- name buffer to write to
|
|||
|
|
|||
|
BufferLength -- buffer length
|
|||
|
|
|||
|
pszName -- name to append domain to
|
|||
|
|
|||
|
pszDomain -- domain name
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Ptr to buffer with appended domain name.
|
|||
|
NULL on invalid (too long) name.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD length1;
|
|||
|
DWORD length2;
|
|||
|
DWORD totalLength;
|
|||
|
|
|||
|
DNSDBG( TRACE, ( "Dns_NameAppend_A()\n" ));
|
|||
|
|
|||
|
//
|
|||
|
// appending NULL domain?
|
|||
|
//
|
|||
|
|
|||
|
if ( !pszDomain )
|
|||
|
{
|
|||
|
totalLength = strlen( pszName );
|
|||
|
if ( totalLength >= BufferLength )
|
|||
|
{
|
|||
|
return( NULL );
|
|||
|
}
|
|||
|
RtlCopyMemory(
|
|||
|
pNameBuffer,
|
|||
|
pszName,
|
|||
|
totalLength );
|
|||
|
|
|||
|
pNameBuffer[ totalLength ] = 0;
|
|||
|
|
|||
|
return( pNameBuffer );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// get name lengths -- make sure we fit length
|
|||
|
//
|
|||
|
|
|||
|
length1 = strlen( pszName );
|
|||
|
if ( pszName[ length1-1] == '.' )
|
|||
|
{
|
|||
|
length1--;
|
|||
|
}
|
|||
|
|
|||
|
length2 = strlen( pszDomain );
|
|||
|
|
|||
|
totalLength = length1 + length2 + 1;
|
|||
|
if ( totalLength >= BufferLength )
|
|||
|
{
|
|||
|
return( NULL );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// copy to buffer
|
|||
|
//
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
pNameBuffer,
|
|||
|
pszName,
|
|||
|
length1 );
|
|||
|
|
|||
|
pNameBuffer[ length1 ] = '.';
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
& pNameBuffer[length1+1],
|
|||
|
pszDomain,
|
|||
|
length2 );
|
|||
|
|
|||
|
pNameBuffer[ totalLength ] = 0;
|
|||
|
|
|||
|
return( pNameBuffer );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
PWSTR
|
|||
|
Dns_NameAppend_W(
|
|||
|
OUT PWCHAR pNameBuffer,
|
|||
|
IN DWORD BufferLength,
|
|||
|
IN PWSTR pwsName,
|
|||
|
IN PWSTR pwsDomain
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Write appended name to buffer (unicode).
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pNameBuffer -- name buffer to write to
|
|||
|
|
|||
|
BufferLength -- buffer length in WCHAR
|
|||
|
|
|||
|
pwsName -- name to append domain to
|
|||
|
|
|||
|
pwsDomain -- domain name
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Ptr to buffer with appended domain name.
|
|||
|
NULL on invalid (too long) name.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD length1;
|
|||
|
DWORD length2;
|
|||
|
DWORD totalLength;
|
|||
|
|
|||
|
DNSDBG( TRACE, ( "Dns_NameAppend_W()\n" ));
|
|||
|
|
|||
|
//
|
|||
|
// appending NULL domain?
|
|||
|
//
|
|||
|
|
|||
|
if ( !pwsDomain )
|
|||
|
{
|
|||
|
totalLength = wcslen( pwsName );
|
|||
|
if ( totalLength >= BufferLength )
|
|||
|
{
|
|||
|
return( NULL );
|
|||
|
}
|
|||
|
RtlCopyMemory(
|
|||
|
pNameBuffer,
|
|||
|
pwsName,
|
|||
|
totalLength*sizeof(WCHAR) );
|
|||
|
|
|||
|
pNameBuffer[ totalLength ] = 0;
|
|||
|
|
|||
|
return( pNameBuffer );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// get name lengths -- make sure we fit length
|
|||
|
//
|
|||
|
|
|||
|
length1 = wcslen( pwsName );
|
|||
|
if ( pwsName[ length1-1] == '.' )
|
|||
|
{
|
|||
|
length1--;
|
|||
|
}
|
|||
|
|
|||
|
length2 = wcslen( pwsDomain );
|
|||
|
|
|||
|
totalLength = length1 + length2 + 1;
|
|||
|
if ( totalLength >= BufferLength )
|
|||
|
{
|
|||
|
return( NULL );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// copy to buffer
|
|||
|
//
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
pNameBuffer,
|
|||
|
pwsName,
|
|||
|
length1*sizeof(WCHAR) );
|
|||
|
|
|||
|
pNameBuffer[ length1 ] = '.';
|
|||
|
|
|||
|
RtlCopyMemory(
|
|||
|
& pNameBuffer[length1+1],
|
|||
|
pwsDomain,
|
|||
|
length2*sizeof(WCHAR) );
|
|||
|
|
|||
|
pNameBuffer[ totalLength ] = 0;
|
|||
|
|
|||
|
return( pNameBuffer );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
PSTR
|
|||
|
Dns_SplitHostFromDomainName_A(
|
|||
|
IN PSTR pszName
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Split host name from domain name.
|
|||
|
|
|||
|
Combines getting domain name and splitting
|
|||
|
off host name.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pszName - standard dotted DNS name
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Ptr to domain name of pszName.
|
|||
|
NULL if pszName is in root domain.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PSTR pnameDomain;
|
|||
|
|
|||
|
//
|
|||
|
// get domain name
|
|||
|
// if exists, NULL terminate host name part
|
|||
|
//
|
|||
|
|
|||
|
pnameDomain = Dns_GetDomainName( (PCSTR)pszName );
|
|||
|
if ( pnameDomain )
|
|||
|
{
|
|||
|
if ( pnameDomain <= pszName )
|
|||
|
{
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
*(pnameDomain-1) = 0;
|
|||
|
}
|
|||
|
|
|||
|
return pnameDomain;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
_fastcall
|
|||
|
Dns_IsNameNumeric_A(
|
|||
|
IN PCSTR pszName
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Determine if numeric name.
|
|||
|
|
|||
|
Note, this assumes name already in UTF-8
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pszName - standard dotted DNS name
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if all numeric.
|
|||
|
FALSE otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
CHAR ch;
|
|||
|
BOOL fnumeric = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// check if everything in name is numeric
|
|||
|
// - dotted names can be numeric
|
|||
|
// - "." not numeric
|
|||
|
//
|
|||
|
|
|||
|
while( ch = *pszName++ )
|
|||
|
{
|
|||
|
if ( ch >= '0' && ch <= '9' )
|
|||
|
{
|
|||
|
fnumeric = TRUE;
|
|||
|
continue;
|
|||
|
}
|
|||
|
else if ( ch == '.' )
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
return fnumeric;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Wrappers for most common name conversions
|
|||
|
//
|
|||
|
|
|||
|
DWORD
|
|||
|
Dns_NameCopyWireToUnicode(
|
|||
|
OUT PWCHAR pBufferUnicode,
|
|||
|
IN PCSTR pszNameWire
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Convert name from wire to unicode.
|
|||
|
|
|||
|
Simple wrapper on Dns_NameCopy for common operation:
|
|||
|
- unicode to wire
|
|||
|
- NULL terminated name
|
|||
|
- standard length buffer
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pBufferUnicode -- unicode result buffer
|
|||
|
|
|||
|
pszNameWire - name in wire format
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Count of bytes copied if successful.
|
|||
|
Zero on error -- name too long or conversion error.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD bufferLength = DNS_MAX_NAME_BUFFER_LENGTH_UNICODE;
|
|||
|
|
|||
|
//
|
|||
|
// copy name back to unicode
|
|||
|
//
|
|||
|
|
|||
|
return Dns_NameCopy(
|
|||
|
(PCHAR) pBufferUnicode,
|
|||
|
& bufferLength,
|
|||
|
(PCHAR) pszNameWire,
|
|||
|
0, // null terminated
|
|||
|
DnsCharSetWire,
|
|||
|
DnsCharSetUnicode );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
Dns_NameCopyUnicodeToWire(
|
|||
|
OUT PCHAR pBufferWire,
|
|||
|
IN PCWSTR pwsNameUnicode
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Convert name from unicode to wire.
|
|||
|
|
|||
|
Simple wrapper on Dns_NameCopy for common operation:
|
|||
|
- unicode to wire
|
|||
|
- NULL terminated name
|
|||
|
- standard length buffer
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pBufferWire -- wire format result buffer
|
|||
|
|
|||
|
pwsNameUnicode - name in unicode
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
|
|||
|
Count of bytes copied if successful.
|
|||
|
Zero on error -- name too long or conversion error.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD bufferLength = DNS_MAX_NAME_BUFFER_LENGTH;
|
|||
|
|
|||
|
//
|
|||
|
// copy name to wire format
|
|||
|
//
|
|||
|
|
|||
|
return Dns_NameCopy(
|
|||
|
pBufferWire,
|
|||
|
& bufferLength,
|
|||
|
(PCHAR) pwsNameUnicode,
|
|||
|
0, // null terminated
|
|||
|
DnsCharSetUnicode,
|
|||
|
DnsCharSetWire );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
Dns_NameCopyStandard_W(
|
|||
|
OUT PWCHAR pBuffer,
|
|||
|
IN PCWSTR pwsNameUnicode
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Copy unicode name.
|
|||
|
|
|||
|
Simple wrapper on Dns_NameCopy for common operation:
|
|||
|
- unicode to unicode
|
|||
|
- NULL terminated name
|
|||
|
- standard length buffer
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pBuffer -- wire format result buffer
|
|||
|
|
|||
|
pwsNameUnicode - name in unicode
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Count of bytes copied if successful.
|
|||
|
Zero on error -- name too long or conversion error.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD bufferLength = DNS_MAX_NAME_BUFFER_LENGTH_UNICODE;
|
|||
|
|
|||
|
//
|
|||
|
// copy name
|
|||
|
//
|
|||
|
|
|||
|
return Dns_NameCopy(
|
|||
|
(PCHAR) pBuffer,
|
|||
|
& bufferLength,
|
|||
|
(PCHAR) pwsNameUnicode,
|
|||
|
0, // null terminated
|
|||
|
DnsCharSetUnicode,
|
|||
|
DnsCharSetUnicode );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
Dns_NameCopyStandard_A(
|
|||
|
OUT PCHAR pBuffer,
|
|||
|
IN PCSTR pszName
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Convert name from unicode to wire.
|
|||
|
|
|||
|
Simple wrapper on Dns_NameCopy for common operation:
|
|||
|
- unicode to wire
|
|||
|
- NULL terminated name
|
|||
|
- standard length buffer
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pBuffer -- wire format result buffer
|
|||
|
|
|||
|
pszName - name in narrow char set
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Count of bytes copied if successful.
|
|||
|
Zero on error -- name too long or conversion error.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD bufferLength = DNS_MAX_NAME_BUFFER_LENGTH;
|
|||
|
|
|||
|
//
|
|||
|
// copy name
|
|||
|
//
|
|||
|
|
|||
|
return Dns_NameCopy(
|
|||
|
pBuffer,
|
|||
|
& bufferLength,
|
|||
|
(PCHAR) pszName,
|
|||
|
0, // null terminated
|
|||
|
DnsCharSetUtf8,
|
|||
|
DnsCharSetUtf8 );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// End name.c
|
|||
|
//
|
|||
|
|
|||
|
|