Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4332 lines
96 KiB

/*++
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
//