|
|
/*++
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 ];
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 ); 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; } DNSDBG( READ, ( "Unicode converted string %.*S\n" "\tlength = %d\n" "\tlast error = %d\n", length, unicodeString, length, GetLastError() ));
// 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
//
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_GetDomainNameA( 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_GetDomainNameW( 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; }
PSTR _fastcall Dns_GetTldForNameA( IN PCSTR pszName ) /*++
Routine Description:
Get domain name of DNS name.
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_GetDomainNameA(pdomain) ) { ptld = (PSTR) pdomain; } return ptld; }
PWSTR _fastcall Dns_GetTldForNameW( IN PCWSTR pszName ) { PWSTR pdomain = (PWSTR) pszName; PWSTR ptld = NULL;
//
// find last domain name in name
//
while ( pdomain = Dns_GetDomainNameW(pdomain) ) { ptld = pdomain; } return ptld; }
BOOL _fastcall Dns_IsNameShortA( 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_GetDomainNameA( 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_IsNameShortW( IN PCWSTR pszName ) { INT nameLen;
// trailing domain? -- done
if ( Dns_GetDomainNameW( pszName ) ) { return FALSE; }
// otherwise test for valid label
nameLen = wcslen( pszName ); if ( nameLen <= DNS_MAX_LABEL_LENGTH ) { return TRUE; } nameLen--; if ( nameLen == DNS_MAX_LABEL_LENGTH && pszName[nameLen] == L'.') { return TRUE; } return FALSE; }
BOOL _fastcall Dns_IsNameNumericA( 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; }
BOOL _fastcall Dns_IsNameNumericW( IN PCWSTR pszName ) { WCHAR 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; }
BOOL _fastcall Dns_IsNameFQDN_A( 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; } }
BOOL _fastcall Dns_IsNameFQDN_W( IN PCWSTR pszName ) { DWORD nameLen = wcslen( pszName );
if ( nameLen == 0 ) { return FALSE; }
if ( pszName[nameLen - 1] == L'.' ) { return TRUE; } else { return FALSE; } }
DWORD _fastcall Dns_GetNameAttributesA( 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 attributes = DNS_NAME_UNKNOWN;
if ( Dns_IsNameFQDN_A( pszName ) ) { attributes = DNS_NAME_IS_FQDN; }
if ( Dns_IsNameShortA( pszName ) ) { attributes |= DNS_NAME_SINGLE_LABEL; } else { attributes |= DNS_NAME_MULTI_LABEL; }
return attributes; }
DWORD _fastcall Dns_GetNameAttributesW( IN PCWSTR pszName ) { DWORD attributes = DNS_NAME_UNKNOWN;
if ( Dns_IsNameFQDN_W( pszName ) ) { attributes = DNS_NAME_IS_FQDN; }
if ( Dns_IsNameShortW( pszName ) ) { attributes |= DNS_NAME_SINGLE_LABEL; } else { attributes |= DNS_NAME_MULTI_LABEL; }
return attributes; }
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 ); }
DNS_STATUS Dns_ValidateAndCategorizeDnsNameA( IN PCSTR pszName, 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:
pszName -- ptr to name
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
--*/ { //
// call real function
//
return Dns_ValidateAndCategorizeDnsNameEx( (PCHAR) pszName, 0, // NULL terminated
pLabelCount ); }
DNS_STATUS Dns_ValidateAndCategorizeDnsNameW( IN PCWSTR pszName, 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:
pszName -- ptr to name
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 PWCHAR pch; register WCHAR ch; PWCHAR pchstop; BOOL fdotted = FALSE; DWORD nameLength; DWORD labelCount = 0; DWORD charCount = 0; DNS_STATUS status = DNS_STATUS_SINGLE_PART_NAME;
//
// name length for string
//
nameLength = wcslen( pszName );
if ( nameLength > DNS_MAX_NAME_LENGTH || nameLength == 0 ) { goto InvalidName; }
//
// run through name
//
pch = (PWSTR) pszName; pchstop = pch + nameLength;
while ( pch < pchstop ) { ch = *pch++; if ( ch == L'.' ) { 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 == pszName ) { 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_ValidateAndCategorizeNameW()\n" "\tstatus = %d\n" "\tlabel count = %d\n", status, labelCount ));
return( status );
InvalidName:
if ( pLabelCount ) { *pLabelCount = 0; }
DNSDBG( TRACE, ( "Leave Dns_ValidateAndCategorizeNameW()\n" "\tstatus = ERROR_INVALID_NAME\n" ));
return( DNS_ERROR_INVALID_NAME ); }
DWORD Dns_NameLabelCountA( 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 ); }
DWORD Dns_NameLabelCountW( IN PCWSTR 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_ValidateAndCategorizeDnsNameW( pszName, & 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( %A, %A )\n", pszName, pszDomain ));
//
// 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 ( !length1 ) { return NULL; } 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( %S, %S )\n", pwsName, pwsDomain ));
//
// 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 ( !length1 ) { return NULL; } if ( pwsName[ length1-1] == L'.' ) { length1--; }
length2 = wcslen( pwsDomain ); totalLength = length1 + length2 + 1; if ( totalLength >= BufferLength ) { return( NULL ); }
//
// copy to buffer
//
RtlCopyMemory( pNameBuffer, pwsName, length1*sizeof(WCHAR) );
pNameBuffer[ length1 ] = L'.';
RtlCopyMemory( & pNameBuffer[length1+1], pwsDomain, length2*sizeof(WCHAR) );
pNameBuffer[ totalLength ] = 0;
return( pNameBuffer ); }
PSTR Dns_SplitHostFromDomainNameA( 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_GetDomainNameA( (PCSTR)pszName ); if ( pnameDomain ) { if ( pnameDomain <= pszName ) { return NULL; } *(pnameDomain-1) = 0; }
return pnameDomain; }
PWSTR Dns_SplitHostFromDomainNameW( IN PWSTR pszName ) { PWSTR pnameDomain;
//
// get domain name
// if exists, NULL terminate host name part
//
pnameDomain = Dns_GetDomainNameW( (PCWSTR)pszName ); if ( pnameDomain ) { if ( pnameDomain <= pszName ) { return NULL; } *(pnameDomain-1) = 0; }
return pnameDomain; }
BOOL Dns_CopyNameLabelA( OUT PCHAR pBuffer, IN PCSTR pszName ) /*++
Routine Description:
Copy first label in name.
Arguments:
pBuffer - buffer to hold label copy; must be DNS_MAX_LABEL_BUFFER_LENGTH
pszName - standard dotted DNS name
Return Value:
TRUE on successful copy. FALSE on bad name. Copy buffer will still be valid.
--*/ { CHAR ch; PCHAR plabel = pBuffer; PCHAR pstop; BOOL retval = TRUE;
if ( !pszName ) { retval = FALSE; goto Done; } pstop = plabel + DNS_MAX_LABEL_LENGTH;
//
// find next "." in name, then return ptr to next character
//
while( ch = *pszName++ ) { if ( ch == '.' ) { if ( plabel == pBuffer ) { *plabel++ = ch; } break; } if ( plabel >= pstop ) { retval = FALSE; break; } *plabel++ = ch; }
Done:
*plabel = 0; return( retval ); }
BOOL Dns_CopyNameLabelW( OUT PWCHAR pBuffer, IN PCWSTR pszName ) { WCHAR ch; PWCHAR plabel = pBuffer; PWCHAR pstop; BOOL retval = TRUE;
if ( !pszName ) { retval = FALSE; goto Done; } pstop = plabel + DNS_MAX_LABEL_LENGTH;
//
// find next "." in name, then return ptr to next character
//
while( ch = *pszName++ ) { if ( ch == '.' ) { if ( plabel == pBuffer ) { *plabel++ = ch; } break; } if ( plabel >= pstop ) { retval = FALSE; break; } *plabel++ = ch; }
Done:
*plabel = 0; return( retval ); }
//
// 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 ); }
//
// Temp fix ups for functions exposed in dnslib.h
//
// DCR: delete when clean build with dnslib.h functions removed
//
PSTR _fastcall Dns_GetDomainName( IN PCSTR pszName ) { return Dns_GetDomainNameA( pszName ); }
PWSTR _fastcall Dns_GetDomainName_W( IN PCWSTR pwsName ) { return Dns_GetDomainNameW( pwsName ); }
PCHAR _fastcall Dns_GetTldForName( IN PCSTR pszName ) { return Dns_GetTldForNameA( pszName ); }
BOOL _fastcall Dns_IsNameShort( IN PCSTR pszName ) { return Dns_IsNameShortA( pszName ); }
BOOL _fastcall Dns_IsNameFQDN( IN PCSTR pszName ) { return Dns_IsNameFQDN_A( pszName ); }
DWORD Dns_NameLabelCount( IN PCSTR pszName ) { return Dns_NameLabelCountA( pszName ); }
DWORD _fastcall Dns_GetNameAttributes( IN PCSTR pszName ) { return Dns_GetNameAttributesA( pszName ); }
//
// End name.c
//
|