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

  1. /*++
  2. Copyright (c) 1995-2001 Microsoft Corporation
  3. Module Name:
  4. name.c
  5. Abstract:
  6. Domain Name System (DNS) Library
  7. DNS name routines.
  8. Author:
  9. Jim Gilroy (jamesg) October 1995
  10. Revision History:
  11. jamesg Jan 1997 UTF-8, Unicode conversions
  12. --*/
  13. #include "local.h"
  14. //
  15. // DNS name cannonicalization
  16. //
  17. // Flags to form cannonical name.
  18. //
  19. #define DNS_CANONICALIZING_FLAGS ( LCMAP_LOWERCASE )
  20. //
  21. // Comparison flags -- args to compare string
  22. //
  23. // These flags are what DS uses when calling CompareString.
  24. // They are defined in ntdsapi.h.
  25. //
  26. // Note: these NORM_IGNOREXYZ flags which directory uses
  27. // for compare, actually STOP downcasing of these in LCMapString
  28. // -- international folks need to give us the correct deal here
  29. //
  30. // We will only use the IGNORECASE flag because this is the
  31. // only one which we can use in LCMapString().
  32. // We want to say that if two names compare equal that you
  33. // can register either one and lookup the other and get the
  34. // result. In other words they are equal throughout the
  35. // client-server system.
  36. //
  37. #if 0
  38. #define DS_DEFAULT_LOCALE_COMPARE_FLAGS (NORM_IGNORECASE | \
  39. NORM_IGNOREKANATYPE | \
  40. NORM_IGNORENONSPACE | \
  41. NORM_IGNOREWIDTH)
  42. #endif
  43. #define DNS_CANONICAL_COMPARE_FLAGS ( NORM_IGNORECASE )
  44. //
  45. // Locale to canonically downcase in.
  46. //
  47. // Need to disambiguate to a universal standard so that every DNS
  48. // server interprets these the same way.
  49. //
  50. // In Win2K we used US English.
  51. // Sublang: US English (0x04) Lang: English (0x09)
  52. // (note sublang US english actually 0x1, but sublang starts at
  53. // bit 10)
  54. //
  55. // #define DNS_CANONICAL_LOCALE ( 0x0409 )
  56. //
  57. // For Whistler invariant locale is created; It is actually the
  58. // same as US English for downcasing -- US English has no
  59. // exceptions to the default case conversion table.
  60. //
  61. #define DNS_CANONICAL_LOCALE ( LOCALE_INVARIANT )
  62. //
  63. // DNS Character properties for validation
  64. //
  65. // DCR: combine char validation and file tables
  66. // Probably could be combined with file character
  67. // lookup, by simply merging bit fields appropriately.
  68. // At this point time, however, no need to disturb
  69. // file lookup, which is working fine.
  70. //
  71. // Character attributes bitfields
  72. //
  73. #define B_RFC 0x00000001
  74. #define B_NUMBER 0x00000002
  75. #define B_UPPER 0x00000004
  76. #define B_NON_RFC 0x00000008
  77. #define B_UTF8_TRAIL 0x00000010
  78. #define B_UTF8_FIRST_TWO 0x00000020
  79. #define B_UTF8_FIRST_THREE 0x00000040
  80. #define B_UTF8_PAIR 0x00000080
  81. #define B_DOT 0x00000800
  82. #define B_SPECIAL 0x00001000
  83. #define B_LEADING_ONLY 0x00004000
  84. //
  85. // Generic characters
  86. //
  87. #define DC_RFC (B_RFC)
  88. #define DC_LOWER (B_RFC)
  89. #define DC_UPPER (B_UPPER | B_RFC)
  90. #define DC_NUMBER (B_NUMBER | B_RFC)
  91. #define DC_NON_RFC (B_NON_RFC)
  92. #define DC_UTF8_TRAIL (B_UTF8_TRAIL)
  93. #define DC_UTF8_1ST_2 (B_UTF8_FIRST_TWO)
  94. #define DC_UTF8_1ST_3 (B_UTF8_FIRST_THREE)
  95. #define DC_UTF8_PAIR (B_UTF8_PAIR)
  96. //
  97. // Special characters
  98. // * valid as single label wildcard
  99. // _ leading SRV record domain names
  100. // / in classless in-addr
  101. //
  102. #define DC_DOT (B_SPECIAL | B_DOT)
  103. #define DC_ASTERISK (B_SPECIAL | B_LEADING_ONLY)
  104. #define DC_UNDERSCORE (B_SPECIAL | B_LEADING_ONLY)
  105. #define DC_BACKSLASH (B_SPECIAL)
  106. //
  107. // More special
  108. // These have no special validations, but have special file
  109. // properties, so define to keep table in shape for merge with
  110. // file chars.
  111. //
  112. #define DC_NULL (0)
  113. #define DC_OCTAL (B_NON_RFC)
  114. #define DC_RETURN (B_NON_RFC)
  115. #define DC_NEWLINE (B_NON_RFC)
  116. #define DC_TAB (B_NON_RFC)
  117. #define DC_BLANK (B_NON_RFC)
  118. #define DC_QUOTE (B_NON_RFC)
  119. #define DC_SLASH (B_NON_RFC)
  120. #define DC_OPEN_PAREN (B_NON_RFC)
  121. #define DC_CLOSE_PAREN (B_NON_RFC)
  122. #define DC_COMMENT (B_NON_RFC)
  123. //
  124. // DNS character table
  125. //
  126. // These routines handle the name conversion issues relating to
  127. // writing names and strings in flat ANSI files
  128. // -- special file characters
  129. // -- quoted string
  130. // -- character quotes for special characters and unprintable chars
  131. //
  132. // The character to char properties table allows simple mapping of
  133. // a character to its properties saving us a bunch of compare\branch
  134. // instructions in parsing file names\strings.
  135. //
  136. // See nameutil.h for specific properties.
  137. //
  138. DWORD DnsCharPropertyTable[] =
  139. {
  140. // control chars 0-31 must be octal in all circumstances
  141. // end-of-line and tab characters are special
  142. DC_NULL, // zero special on read, some RPC strings NULL terminated
  143. DC_OCTAL, DC_OCTAL, DC_OCTAL, DC_OCTAL,
  144. DC_OCTAL, DC_OCTAL, DC_OCTAL, DC_OCTAL,
  145. DC_TAB, // tab
  146. DC_NEWLINE, // line feed
  147. DC_OCTAL,
  148. DC_OCTAL,
  149. DC_RETURN, // carriage return
  150. DC_OCTAL,
  151. DC_OCTAL,
  152. DC_OCTAL, DC_OCTAL, DC_OCTAL, DC_OCTAL,
  153. DC_OCTAL, DC_OCTAL, DC_OCTAL, DC_OCTAL,
  154. DC_OCTAL, DC_OCTAL, DC_OCTAL, DC_OCTAL,
  155. DC_OCTAL, DC_OCTAL, DC_OCTAL, DC_OCTAL,
  156. DC_BLANK, // blank, special char but needs octal quote
  157. DC_NON_RFC, // !
  158. DC_QUOTE, // " always must be quoted
  159. DC_NON_RFC, // #
  160. DC_NON_RFC, // $
  161. DC_NON_RFC, // %
  162. DC_NON_RFC, // &
  163. DC_NON_RFC, // '
  164. DC_OPEN_PAREN, // ( datafile line extension
  165. DC_CLOSE_PAREN, // ) datafile line extension
  166. DC_ASTERISK, // *
  167. DC_NON_RFC, // +
  168. DC_NON_RFC, // ,
  169. DC_RFC, // - RFC for hostname
  170. DC_DOT, // . must quote in names
  171. DC_BACKSLASH, // /
  172. // 0 - 9 RFC for hostname
  173. DC_NUMBER, DC_NUMBER, DC_NUMBER, DC_NUMBER,
  174. DC_NUMBER, DC_NUMBER, DC_NUMBER, DC_NUMBER,
  175. DC_NUMBER, DC_NUMBER,
  176. DC_NON_RFC, // :
  177. DC_COMMENT, // ; datafile comment
  178. DC_NON_RFC, // <
  179. DC_NON_RFC, // =
  180. DC_NON_RFC, // >
  181. DC_NON_RFC, // ?
  182. DC_NON_RFC, // @
  183. // A - Z RFC for hostname
  184. DC_UPPER, DC_UPPER, DC_UPPER, DC_UPPER,
  185. DC_UPPER, DC_UPPER, DC_UPPER, DC_UPPER,
  186. DC_UPPER, DC_UPPER, DC_UPPER, DC_UPPER,
  187. DC_UPPER, DC_UPPER, DC_UPPER, DC_UPPER,
  188. DC_UPPER, DC_UPPER, DC_UPPER, DC_UPPER,
  189. DC_UPPER, DC_UPPER, DC_UPPER, DC_UPPER,
  190. DC_UPPER, DC_UPPER,
  191. DC_NON_RFC, // [
  192. DC_SLASH, // \ always must be quoted
  193. DC_NON_RFC, // ]
  194. DC_NON_RFC, // ^
  195. DC_UNDERSCORE, // _
  196. DC_NON_RFC, // `
  197. // a - z RFC for hostname
  198. DC_LOWER, DC_LOWER, DC_LOWER, DC_LOWER,
  199. DC_LOWER, DC_LOWER, DC_LOWER, DC_LOWER,
  200. DC_LOWER, DC_LOWER, DC_LOWER, DC_LOWER,
  201. DC_LOWER, DC_LOWER, DC_LOWER, DC_LOWER,
  202. DC_LOWER, DC_LOWER, DC_LOWER, DC_LOWER,
  203. DC_LOWER, DC_LOWER, DC_LOWER, DC_LOWER,
  204. DC_LOWER, DC_LOWER,
  205. DC_NON_RFC, // {
  206. DC_NON_RFC, // |
  207. DC_NON_RFC, // }
  208. DC_NON_RFC, // ~
  209. DC_OCTAL, // 0x7f DEL code
  210. // UTF8 trail bytes
  211. // - chars 0x80 <= X < 0xc0
  212. // - mask [10xx xxxx]
  213. //
  214. // Lead UTF8 character determines count of bytes in conversion.
  215. // Trail characters fill out conversion.
  216. DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
  217. DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
  218. DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
  219. DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
  220. DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
  221. DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
  222. DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
  223. DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
  224. DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
  225. DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
  226. DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
  227. DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
  228. DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
  229. DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
  230. DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
  231. DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL, DC_UTF8_TRAIL,
  232. // UTF8_1ST_OF_2
  233. // - chars > 0xc0 to 0xdf
  234. // - mask [110x xxxx]
  235. //
  236. // Converting unicode chars > 7 bits <= 11 bits (from 0x80 to 0x7ff)
  237. // consists of first of two char followed by one trail bytes
  238. DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2,
  239. DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2,
  240. DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2,
  241. DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2,
  242. DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2,
  243. DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2,
  244. DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2,
  245. DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2, DC_UTF8_1ST_2,
  246. // UTF8_1ST_OF_3
  247. // - chars > 0xe0
  248. // - mask [1110 xxxx]
  249. //
  250. // Converting unicode > 11 bits (0x7ff)
  251. // consists of first of three char followed by two trail bytes
  252. DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3,
  253. DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3,
  254. DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3,
  255. DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3,
  256. DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3,
  257. DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3,
  258. DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3,
  259. DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3, DC_UTF8_1ST_3
  260. };
  261. VOID
  262. Dns_VerifyValidFileCharPropertyTable(
  263. VOID
  264. )
  265. /*++
  266. Routine Description:
  267. Verify haven't broken lookup table.
  268. Arguments:
  269. None
  270. Return Value:
  271. None
  272. --*/
  273. {
  274. ASSERT( DnsCharPropertyTable[0] == DC_NULL );
  275. ASSERT( DnsCharPropertyTable['\t'] == DC_TAB );
  276. ASSERT( DnsCharPropertyTable['\n'] == DC_NEWLINE );
  277. ASSERT( DnsCharPropertyTable['\r'] == DC_RETURN );
  278. ASSERT( DnsCharPropertyTable[' '] == DC_BLANK );
  279. ASSERT( DnsCharPropertyTable['"'] == DC_QUOTE );
  280. ASSERT( DnsCharPropertyTable['('] == DC_OPEN_PAREN );
  281. ASSERT( DnsCharPropertyTable[')'] == DC_CLOSE_PAREN );
  282. ASSERT( DnsCharPropertyTable['*'] == DC_ASTERISK );
  283. ASSERT( DnsCharPropertyTable['-'] == DC_RFC );
  284. ASSERT( DnsCharPropertyTable['.'] == DC_DOT );
  285. ASSERT( DnsCharPropertyTable['/'] == DC_BACKSLASH );
  286. ASSERT( DnsCharPropertyTable['0'] == DC_NUMBER );
  287. ASSERT( DnsCharPropertyTable['9'] == DC_NUMBER );
  288. ASSERT( DnsCharPropertyTable[';'] == DC_COMMENT );
  289. ASSERT( DnsCharPropertyTable['A'] == DC_UPPER );
  290. ASSERT( DnsCharPropertyTable['Z'] == DC_UPPER );
  291. ASSERT( DnsCharPropertyTable['\\'] == DC_SLASH );
  292. ASSERT( DnsCharPropertyTable['_'] == DC_UNDERSCORE );
  293. ASSERT( DnsCharPropertyTable['a'] == DC_LOWER );
  294. ASSERT( DnsCharPropertyTable['z'] == DC_LOWER );
  295. ASSERT( DnsCharPropertyTable[0x7f] == DC_OCTAL );
  296. ASSERT( DnsCharPropertyTable[0x80] == DC_UTF8_TRAIL );
  297. ASSERT( DnsCharPropertyTable[0xbf] == DC_UTF8_TRAIL );
  298. ASSERT( DnsCharPropertyTable[0xc0] == DC_UTF8_1ST_2 );
  299. ASSERT( DnsCharPropertyTable[0xdf] == DC_UTF8_1ST_2 );
  300. ASSERT( DnsCharPropertyTable[0xe0] == DC_UTF8_1ST_3 );
  301. ASSERT( DnsCharPropertyTable[0xff] == DC_UTF8_1ST_3 );
  302. };
  303. //
  304. // Validation routine flags
  305. //
  306. #define DNSVAL_ALLOW_LEADING_UNDERSCORE 0x00010000
  307. #define DNSVAL_ALLOW_ASTERISK 0x00020000
  308. #define DNSVAL_ALLOW_BACKSLASH 0x00040000
  309. //
  310. // Validation bit flags
  311. //
  312. #define DNS_BIT_NAME_FQDN 0x00000001
  313. #define DNS_BIT_NAME_SINGLE_LABEL 0x00000002
  314. #define DNS_BIT_NAME_DOTTED 0x00000004
  315. #define DNS_BIT_NAME_ROOT 0x00000008
  316. #define DNS_BIT_NAME_CONTAINS_UPPER 0x00000010
  317. #define DNS_BIT_NAME_NUMERIC 0x00000100
  318. #define DNS_BIT_NAME_NUMERIC_LABEL 0x00000200
  319. #define DNS_BIT_NAME_NUMERIC_FIRST_LABEL 0x00000400
  320. #define DNS_BIT_NAME_UNDERSCORE 0x00001000
  321. #define DNS_BIT_NAME_WILDCARD 0x00002000
  322. #define DNS_BIT_NAME_BACKSLASH 0x00004000
  323. #define DNS_BIT_NAME_NON_RFC_ASCII 0x00008000
  324. #define DNS_BIT_NAME_MULTIBYTE 0x00010000
  325. #define DNS_BIT_NAME_BINARY_LABEL 0x00020000
  326. #define DNS_BIT_NAME_INVALID 0x80000000
  327. #if 0
  328. //
  329. // Old validation -- retired
  330. //
  331. // Downcase and validation table
  332. //
  333. // DCR: table lookup for all DNS char properties
  334. // especially RFC, non-RFC, invalid
  335. //
  336. typedef struct _Dns_ValidationChar
  337. {
  338. CHAR chDown;
  339. UCHAR fNonRfc;
  340. }
  341. DNS_VALIDATION_CHAR;
  342. #define NON_RFC (1)
  343. #define EXTENDED_CHAR (0x80)
  344. DNS_VALIDATION_CHAR
  345. Dns_ValidateDowncaseChar(
  346. IN CHAR ch
  347. )
  348. /*++
  349. Routine Description:
  350. Validates character
  351. Arguments:
  352. ch -- character to validate
  353. Return Value:
  354. Validation character -- downcased character and flag
  355. --*/
  356. {
  357. DNS_VALIDATION_CHAR val;
  358. // default to normal character
  359. val.chDown = ch;
  360. val.fNonRfc = 0;
  361. //
  362. // break out character tests
  363. // - attempt most likely to least likely
  364. // - but also working down to simplify tests
  365. //
  366. if ( (UCHAR)ch >= 'a' )
  367. {
  368. if ( (UCHAR)ch <= 'z' )
  369. {
  370. return( val );
  371. }
  372. val.fNonRfc = NON_RFC;
  373. if ( ch & 0x80 )
  374. {
  375. val.fNonRfc = EXTENDED_CHAR;
  376. }
  377. }
  378. else if ( (UCHAR)ch >= 'A' )
  379. {
  380. if ( (UCHAR)ch <= 'Z' )
  381. {
  382. val.chDown = ch + 0x20;
  383. return( val );
  384. }
  385. val.fNonRfc = NON_RFC;
  386. }
  387. else if ( (UCHAR)ch >= '0' )
  388. {
  389. if ( (UCHAR)ch <= '9' )
  390. {
  391. return( val );
  392. }
  393. val.fNonRfc = NON_RFC;
  394. }
  395. else if ( (UCHAR)ch > ' ' )
  396. {
  397. if ( (UCHAR)ch == '-' )
  398. {
  399. return( val );
  400. }
  401. val.fNonRfc = NON_RFC;
  402. }
  403. // blank or below is flat error
  404. else
  405. {
  406. val.chDown = 0;
  407. val.fNonRfc = NON_RFC;
  408. }
  409. return( val );
  410. }
  411. #endif
  412. //
  413. // Name validation
  414. //
  415. // DCR: name validation by bitfield
  416. //
  417. // An interesting approach to validation, would be to expose
  418. // a set of properties about a name.
  419. // Caller could then specify allowable set (we'd give packages)
  420. // and we'd give back actual set.
  421. // Low level routines would do nothing but return bit field of
  422. // property set.
  423. //
  424. // Properties would include:
  425. // - RFC
  426. // - contains numeric
  427. // - contains upper
  428. // - all numeric
  429. // - first label numeric
  430. //
  431. // - utf8 multibyte
  432. // - underscore
  433. // - other non-RFC
  434. // - unprintable
  435. // - non-utf8 high (i.e. requires binary label)
  436. //
  437. // - FQDN
  438. // - single label
  439. // - root
  440. //
  441. DNS_STATUS
  442. validateDnsNamePrivate(
  443. IN LPCSTR pszName,
  444. IN DWORD dwFlag,
  445. OUT PDWORD pLabelCount,
  446. OUT PDWORD pResultFlag
  447. )
  448. /*++
  449. Routine Description:
  450. Verifies name is valid DNS name.
  451. Arguments:
  452. pszName -- DNS name (standard dotted form) to check
  453. dwFlags -- validation flags
  454. - DNSVAL_ALLOW_LEADING_UNDERSCORE
  455. - DNSVAL_ALLOW_BACKSLASH
  456. - DNSVAL_ALLOW_ASTERIK
  457. pLabelCount -- addr to recv label count
  458. pResultFlag -- addr to recv result flag
  459. Return Value:
  460. ERROR_SUCCESS -- completely RFC compliant name
  461. DNS_ERROR_NON_RFC_NAME -- syntax valid, but not standard RFC name
  462. DNS_ERROR_INVALID_NAME_CHAR -- syntax valid, but invalid characters
  463. ERROR_INVALID_NAME -- name completely useless, bogus, toast
  464. --*/
  465. {
  466. PUCHAR pch = (PUCHAR)pszName;
  467. UCHAR ch;
  468. DWORD charProp;
  469. DWORD labelCount = 0;
  470. DWORD trailCount = 0;
  471. INT labelCharCount = 0;
  472. INT labelNumberCount = 0;
  473. DWORD flag;
  474. BOOL fqdn = FALSE;
  475. BOOL fnonRfc = FALSE;
  476. BOOL finvalidChar = FALSE;
  477. BOOL fnameNonNumeric = FALSE;
  478. BOOL flabelNonNumeric = FALSE;
  479. DNS_STATUS status;
  480. DNSDBG( TRACE, ( "validateNamePrivate()\n" ));
  481. if ( !pch )
  482. {
  483. goto InvalidName;
  484. }
  485. //
  486. // validations
  487. // - name length (255)
  488. // - label length (63)
  489. // - UTF8 encoding correct
  490. // - no unprintable characters
  491. //
  492. while ( 1 )
  493. {
  494. // get next character and properties
  495. ch = *pch++;
  496. charProp = DnsCharPropertyTable[ ch ];
  497. // inc label count
  498. // - do here for simplicity, dec in "." case below
  499. labelCharCount++;
  500. //
  501. // simplify UTF8 -- just get it out of the way
  502. // need to do first or else need trailCount==0 checks
  503. // on all other paths
  504. //
  505. if ( ch >= 0x80 )
  506. {
  507. DWORD tempStatus;
  508. tempStatus = Dns_ValidateUtf8Byte(
  509. ch,
  510. & trailCount );
  511. if ( tempStatus != ERROR_SUCCESS )
  512. {
  513. DNSDBG( READ, (
  514. "ERROR: Name UTF8 trail count check at %c\n", ch ));
  515. goto InvalidName;
  516. }
  517. fnonRfc = TRUE;
  518. continue;
  519. }
  520. //
  521. // trail count check
  522. // - all ASCII chars, must not be in middle of UTF8
  523. //
  524. if ( trailCount )
  525. {
  526. DNSDBG( READ, (
  527. "ERROR: Name failed trail count check at %c\n", ch ));
  528. goto InvalidName;
  529. }
  530. //
  531. // full RFC -- continue
  532. //
  533. if ( charProp & B_RFC )
  534. {
  535. if ( charProp & B_NUMBER )
  536. {
  537. labelNumberCount++;
  538. }
  539. continue;
  540. }
  541. //
  542. // label termination: dot or NULL
  543. //
  544. if ( ch == '.' || ch == 0 )
  545. {
  546. labelCharCount--;
  547. // FQDN termination
  548. // - termination with no bytes in label
  549. //
  550. // two cases:
  551. // 1) terminate on NULL char
  552. // - standard FQDN "foo.bar."
  553. // - but empty name invalid
  554. // 2) terminate on dot
  555. // - only "." root valid
  556. // - all other ".." or ".xyz" cases invalid
  557. if ( labelCharCount == 0 )
  558. {
  559. fqdn = TRUE;
  560. if ( ch == 0 )
  561. {
  562. if ( labelCount )
  563. {
  564. goto Done;
  565. }
  566. }
  567. else if ( pch == pszName+1 && *pch == 0 )
  568. {
  569. // root
  570. // - set flags for validity
  571. // - skip final length check
  572. fnameNonNumeric = TRUE;
  573. flabelNonNumeric = TRUE;
  574. goto DoneRoot;
  575. }
  576. DNSDBG( READ, (
  577. "ERROR: Name (%s) failed check\n",
  578. pszName ));
  579. goto InvalidName;
  580. }
  581. //
  582. // read non-empty label
  583. // - label length validity
  584. // - detect non-numeric labels
  585. // (easier to handle numeric name check, by detecting non-numeric)
  586. //
  587. if ( labelCharCount > DNS_MAX_LABEL_LENGTH )
  588. {
  589. DNSDBG( READ, ( "ERROR: Name failed label count check\n" ));
  590. goto InvalidName;
  591. }
  592. if ( labelNumberCount != labelCharCount )
  593. {
  594. fnameNonNumeric = TRUE;
  595. if ( labelCount == 0 )
  596. {
  597. flabelNonNumeric = TRUE;
  598. }
  599. }
  600. // count label
  601. // - stop if NULL terminator
  602. // - otherwise, reset for next label and continue
  603. labelCount++;
  604. if ( ch == 0 )
  605. {
  606. break;
  607. }
  608. labelCharCount = 0;
  609. labelNumberCount = 0;
  610. continue;
  611. }
  612. //
  613. // non-RFC
  614. // - currently accepting only "_" as allowable as part of
  615. // microsoft acceptable non-RFC set
  616. //
  617. // however DNS server must be able to read *, \, etc
  618. // it gets called through Dns_CreateStandardDnsName()
  619. //
  620. // note, could tighten this up with special flag, but since
  621. // this only speeds case with invalid chars, there's not much
  622. // point; underscore is likely to see significant use
  623. //
  624. // underscore
  625. // - can be valid as part of SRV domain name
  626. // - otherwise non-RFC
  627. if ( ch == '_' )
  628. {
  629. if ( labelCharCount == 1 &&
  630. (*pch && *pch!= '.') &&
  631. (dwFlag & DNSVAL_ALLOW_LEADING_UNDERSCORE) )
  632. {
  633. continue;
  634. }
  635. fnonRfc = TRUE;
  636. continue;
  637. }
  638. // backslash
  639. // - used to denote classless in-addr domains
  640. // - so valid even as zone name on server
  641. // - otherwise completely invalid
  642. else if ( ch == '/' )
  643. {
  644. if ( dwFlag & DNSVAL_ALLOW_BACKSLASH )
  645. {
  646. continue;
  647. }
  648. }
  649. // asterisk
  650. // - valid only as single-byte first label in wildcard name
  651. // - otherwise completely invalid
  652. else if ( ch == '*' )
  653. {
  654. if ( labelCount == 0 &&
  655. labelCharCount == 1 &&
  656. ( *pch==0 || *pch=='.') &&
  657. (dwFlag & DNSVAL_ALLOW_ASTERISK) )
  658. {
  659. continue;
  660. }
  661. }
  662. // anything else is complete junk
  663. //
  664. // JENHANCE: if desired, could break out printable\non
  665. fnonRfc = TRUE;
  666. finvalidChar = TRUE;
  667. DNSDBG( READ, ( "ERROR: Name character %c failed check\n", ch ));
  668. continue;
  669. }
  670. Done:
  671. // verify total name length
  672. // to fit in wire 255 limit:
  673. // - FQDN can be up to 254
  674. // - non-FQDN can be up to 253
  675. pch--;
  676. DNS_ASSERT( pch > pszName );
  677. labelCharCount = (INT)(pch - pszName);
  678. if ( !fqdn )
  679. {
  680. labelCharCount++;
  681. }
  682. if ( labelCharCount >= DNS_MAX_NAME_LENGTH )
  683. {
  684. DNSDBG( READ, ( "ERROR: Name failed final length check\n" ));
  685. goto InvalidName;
  686. }
  687. DoneRoot:
  688. //
  689. // return flags
  690. //
  691. // JENHANCE: all returns from validateNamePrivate() could come
  692. // as result flag; then charset issues could be separated
  693. // out by higher level routine
  694. //
  695. *pLabelCount = labelCount;
  696. flag = 0;
  697. if ( fqdn )
  698. {
  699. flag |= DNS_BIT_NAME_FQDN;
  700. }
  701. if ( ! fnameNonNumeric )
  702. {
  703. flag |= DNS_BIT_NAME_NUMERIC;
  704. }
  705. if ( ! flabelNonNumeric )
  706. {
  707. flag |= DNS_BIT_NAME_NUMERIC_FIRST_LABEL;
  708. }
  709. *pResultFlag = flag;
  710. //
  711. // return status
  712. // ERROR_SUCCESS -- full RFC name
  713. // DNS_ERROR_NON_RFC_NAME -- MS extended and '_' names
  714. // DNS_ERROR_INVALID_NAME_CHAR -- syntaxtically valid, but bad chars
  715. // ERROR_INVALID_NAME -- syntaxtically invalid name
  716. //
  717. status = ERROR_SUCCESS;
  718. if ( finvalidChar )
  719. {
  720. status = DNS_ERROR_INVALID_NAME_CHAR;
  721. }
  722. else if ( fnonRfc )
  723. {
  724. status = DNS_ERROR_NON_RFC_NAME;
  725. }
  726. DNSDBG( READ, (
  727. "Leave validateNamePrivate(), status = %d\n",
  728. status ));
  729. return( status );
  730. InvalidName:
  731. DNSDBG( READ, (
  732. "Leave validateNamePrivate(), status = ERROR_INVALID_NAME\n" ));
  733. *pLabelCount = 0;
  734. *pResultFlag = 0;
  735. return( ERROR_INVALID_NAME );
  736. }
  737. DNS_STATUS
  738. Dns_ValidateName_UTF8(
  739. IN LPCSTR pszName,
  740. IN DNS_NAME_FORMAT Format
  741. )
  742. /*++
  743. Routine Description:
  744. Verifies name is valid DNS name.
  745. Arguments:
  746. pszName -- DNS name (standard dotted form) to check
  747. Format -- required format of DNS name
  748. Return Value:
  749. ERROR_SUCCESS -- completely RFC compliant name
  750. DNS_ERROR_NON_RFC_NAME -- syntax valid, but not standard RFC name
  751. DNS_ERROR_NUMERIC_NAME -- syntax valid, but numeric label violation
  752. DNS_ERROR_INVALID_NAME_CHAR -- syntax valid, but invalid characters
  753. ERROR_INVALID_NAME -- name completely useless, bogus, toast
  754. --*/
  755. {
  756. DNS_STATUS status;
  757. DWORD labelCount;
  758. BOOL isFqdn;
  759. DWORD flag = 0;
  760. DWORD resultFlag = 0;
  761. DNSDBG( TRACE, (
  762. "Dns_ValidateName_UTF8()\n"
  763. "\tname = %s\n"
  764. "\tformat = %d\n",
  765. pszName,
  766. Format
  767. ));
  768. if ( !pszName )
  769. {
  770. return( ERROR_INVALID_NAME );
  771. }
  772. //
  773. // special casing?
  774. //
  775. // SRV records can have leading underscores
  776. // wildcards can have first label "*"
  777. // backslash ok in classless in-addr domains
  778. //
  779. switch( Format )
  780. {
  781. #if 0
  782. case DnsNameServerZonePrivate:
  783. flag = DNSVAL_ALLOW_BACKSLASH | DNSVAL_ALLOW_LEADING_UNDERSCORE;
  784. #endif
  785. case DnsNameWildcard:
  786. flag = DNSVAL_ALLOW_ASTERISK;
  787. break;
  788. case DnsNameSrvRecord:
  789. flag = DNSVAL_ALLOW_LEADING_UNDERSCORE;
  790. break;
  791. }
  792. //
  793. // do validation
  794. //
  795. // return immediately on invalid name, so type
  796. // specific returns do not overwrite this error
  797. //
  798. status = validateDnsNamePrivate(
  799. pszName,
  800. flag,
  801. & labelCount,
  802. & resultFlag
  803. );
  804. if ( status == ERROR_INVALID_NAME )
  805. {
  806. return( status );
  807. }
  808. //
  809. // do name type specific validation
  810. //
  811. switch( Format )
  812. {
  813. // domain name -- any valid non-numeric DNS name
  814. case DnsNameDomain:
  815. if ( resultFlag & DNS_BIT_NAME_NUMERIC )
  816. {
  817. return( DNS_ERROR_NUMERIC_NAME );
  818. }
  819. return( status );
  820. // domain name label -- any valid single-label DNS name
  821. case DnsNameDomainLabel:
  822. if ( labelCount != 1 || resultFlag & DNS_BIT_NAME_FQDN )
  823. {
  824. return( ERROR_INVALID_NAME );
  825. }
  826. return( status );
  827. // hostname full -- non-numeric hostname label
  828. case DnsNameHostnameFull:
  829. if ( resultFlag & DNS_BIT_NAME_NUMERIC_FIRST_LABEL )
  830. {
  831. return( DNS_ERROR_NUMERIC_NAME );
  832. }
  833. return( status );
  834. // hostname label -- single label and non-numeric
  835. case DnsNameHostnameLabel:
  836. if ( labelCount != 1 || resultFlag & DNS_BIT_NAME_FQDN )
  837. {
  838. return( ERROR_INVALID_NAME );
  839. }
  840. if ( resultFlag & DNS_BIT_NAME_NUMERIC_FIRST_LABEL )
  841. {
  842. return( DNS_ERROR_NUMERIC_NAME );
  843. }
  844. return( status );
  845. //
  846. // wildcard -- single "*" as first label
  847. // if *.???? then must revalidate the rest of the name as
  848. // "*" has probably resulted in validation error
  849. // if "*" then consider this successful
  850. //
  851. case DnsNameWildcard:
  852. if ( *pszName == '*' )
  853. {
  854. return( status );
  855. }
  856. return( ERROR_INVALID_NAME );
  857. //
  858. // SRV label -- validate leading underscore
  859. //
  860. case DnsNameSrvRecord:
  861. if ( *pszName == '_' )
  862. {
  863. return( status );
  864. }
  865. return( ERROR_INVALID_NAME );
  866. //
  867. // unknown format validation
  868. //
  869. default:
  870. return( ERROR_INVALID_PARAMETER );
  871. }
  872. }
  873. DNS_STATUS
  874. Dns_ValidateName_W(
  875. IN LPCWSTR pwszName,
  876. IN DNS_NAME_FORMAT Format
  877. )
  878. /*++
  879. Routine Description:
  880. Verifies name is valid DNS name.
  881. Arguments:
  882. pwszName -- DNS name (standard dotted form) to check
  883. Format -- required format of DNS name
  884. Return Value:
  885. ERROR_SUCCESS -- if completely compliant name
  886. DNS_ERROR_NON_RFC_NAME -- if not standard RFC name
  887. ERROR_INVALID_NAME -- if name completely useless, bogus, toast
  888. --*/
  889. {
  890. DWORD nameLength = MAX_PATH;
  891. CHAR nameBuffer[ MAX_PATH ] = {0}; // init for prefix
  892. //
  893. // convert name to UTF8
  894. // - if can't convert, then can't fit into buffer
  895. // so must be invalid name on length grounds
  896. //
  897. if ( ! Dns_NameCopy(
  898. nameBuffer,
  899. & nameLength, // avail buf length
  900. (PCHAR) pwszName,
  901. 0, // unknown length
  902. DnsCharSetUnicode, // unicode in
  903. DnsCharSetUtf8 // UTF8 out
  904. ) )
  905. {
  906. return( ERROR_INVALID_NAME );
  907. }
  908. //
  909. // validate name in UTF8 format
  910. return Dns_ValidateName_UTF8(
  911. (LPCSTR) nameBuffer,
  912. Format );
  913. }
  914. DNS_STATUS
  915. Dns_ValidateName_A(
  916. IN LPCSTR pszName,
  917. IN DNS_NAME_FORMAT Format
  918. )
  919. /*++
  920. Routine Description:
  921. Verifies name is valid DNS name.
  922. Arguments:
  923. pszName -- DNS name (standard dotted form) to check
  924. Format -- required format of DNS name
  925. Return Value:
  926. ERROR_SUCCESS -- if completely compliant name
  927. DNS_ERROR_NON_RFC_NAME -- if not standard RFC name
  928. ERROR_INVALID_NAME -- if name completely useless, bogus, toast
  929. --*/
  930. {
  931. DWORD nameLength = MAX_PATH;
  932. CHAR nameBuffer[ MAX_PATH ];
  933. //
  934. // convert name to UTF8
  935. // - if can't convert, then can't fit into buffer
  936. // so must be invalid name on length grounds
  937. //
  938. if ( ! Dns_NameCopy(
  939. nameBuffer,
  940. & nameLength, // avail buf length
  941. (PCHAR) pszName,
  942. 0, // unknown length
  943. DnsCharSetAnsi, // unicode in
  944. DnsCharSetUtf8 // UTF8 out
  945. ) )
  946. {
  947. return( ERROR_INVALID_NAME );
  948. }
  949. //
  950. // validate name in UTF8 format
  951. return Dns_ValidateName_UTF8(
  952. (LPCSTR) nameBuffer,
  953. Format );
  954. }
  955. DNS_STATUS
  956. Dns_ValidateDnsString_UTF8(
  957. IN LPCSTR pszString
  958. )
  959. /*++
  960. Routine Description:
  961. Verifies string is valid DNS string.
  962. Arguments:
  963. pszString -- DNS string (standard dotted form) to check
  964. Return Value:
  965. ERROR_SUCCESS -- if completely compliant string
  966. ERROR_INVALID_DATA -- otherwise
  967. --*/
  968. {
  969. PUCHAR pch = (PUCHAR) pszString;
  970. UCHAR ch;
  971. DWORD trailCount = 0;
  972. DNSDBG( TRACE, ( "Dns_ValidateDnsString_UTF8()\n" ));
  973. if ( !pszString )
  974. {
  975. return( ERROR_INVALID_DATA );
  976. }
  977. //
  978. // validations
  979. // - string length (255)
  980. // - UTF8 chars valid
  981. // - no unprintable characters
  982. //
  983. while ( ch = *pch++ )
  984. {
  985. if ( ch & 0x80 )
  986. {
  987. DWORD status;
  988. status = Dns_ValidateUtf8Byte(
  989. ch,
  990. & trailCount );
  991. if ( status != ERROR_SUCCESS )
  992. {
  993. return( status );
  994. }
  995. }
  996. else if ( ch < ' ' )
  997. {
  998. return( ERROR_INVALID_DATA );
  999. }
  1000. }
  1001. // verify string length ok
  1002. if ( pch - pszString > DNS_MAX_NAME_LENGTH )
  1003. {
  1004. return( ERROR_INVALID_DATA );
  1005. }
  1006. return( ERROR_SUCCESS );
  1007. }
  1008. DNS_STATUS
  1009. Dns_ValidateDnsString_W(
  1010. IN LPCWSTR pszString
  1011. )
  1012. /*++
  1013. Routine Description:
  1014. Verifies string is valid DNS string.
  1015. Not sure there's any need to UNICODE string routine.
  1016. Arguments:
  1017. pszString -- DNS string
  1018. Return Value:
  1019. ERROR_SUCCESS -- if completely compliant string
  1020. ERROR_INVALID_DATA -- otherwise
  1021. --*/
  1022. {
  1023. INT count;
  1024. CHAR stringUtf8[ DNS_MAX_NAME_BUFFER_LENGTH ];
  1025. DWORD bufLength = DNS_MAX_NAME_BUFFER_LENGTH;
  1026. DNSDBG( TRACE, ( "Dns_ValidateDnsString_W()\n" ));
  1027. if ( !pszString )
  1028. {
  1029. return( ERROR_INVALID_DATA );
  1030. }
  1031. //
  1032. // need to convert to unicode in order to test UTF8 (wire) length
  1033. // - buffer (twice max length) can hold any valid
  1034. // coversion of unicode name within max length
  1035. //
  1036. count = wcslen( pszString );
  1037. if ( count > DNS_MAX_NAME_LENGTH )
  1038. {
  1039. return( ERROR_INVALID_DATA );
  1040. }
  1041. //
  1042. // convert, then test
  1043. //
  1044. if ( ! Dns_StringCopy(
  1045. stringUtf8,
  1046. & bufLength,
  1047. (LPSTR) pszString,
  1048. (WORD) count,
  1049. DnsCharSetUnicode, // unicode in
  1050. DnsCharSetUtf8 // UTF8 out
  1051. ) )
  1052. {
  1053. return( ERROR_INVALID_DATA );
  1054. }
  1055. return Dns_ValidateDnsString_UTF8( stringUtf8 );
  1056. }
  1057. //
  1058. // Name cannonicalization
  1059. //
  1060. // Currently, clients downcase (when extended) in their locale to go to wire.
  1061. // On server end however all names are cannonicalized.
  1062. //
  1063. // DCR: cannonicalize completely on client end?
  1064. // Ideally client would do complete cannonicallization on its end.
  1065. // The only issue is whether there are locale specific issues where
  1066. // downcasing would be different and yield substaintially different result
  1067. //
  1068. #define MAX_DNS_DOWN_CASE_BUF_LEN 512
  1069. DWORD
  1070. Dns_MakeCanonicalNameW(
  1071. OUT PWSTR pBuffer,
  1072. IN DWORD BufLength,
  1073. IN PWSTR pwsString,
  1074. IN DWORD StringLength
  1075. )
  1076. /*++
  1077. Routine Description:
  1078. Create cannonical unicode DNS name.
  1079. This name is downcased and ambiguities converted to standard
  1080. DNS characters.
  1081. Arguments:
  1082. pBuffer -- buffer to recv canon name
  1083. BufLength -- length of buffer; if 0, buffer MUST have adequate length
  1084. pwsString -- ptr to string to copy
  1085. StringLength -- string length, if known
  1086. Return Value:
  1087. Count of characters converted INCLUDING NULL terminator.
  1088. Zero on error.
  1089. --*/
  1090. {
  1091. DWORD inLength = StringLength;
  1092. //
  1093. // verify adequate buffer length
  1094. //
  1095. // DCR: should allow non-null terminated canonicalizations?
  1096. //
  1097. // note: we allow and convert non-null terminated name
  1098. // the result will not necessarily be NULL terminated
  1099. // if buffer is exactly equal to string length
  1100. //
  1101. if ( inLength == 0 )
  1102. {
  1103. inLength = wcslen( pwsString );
  1104. inLength++;
  1105. }
  1106. if ( BufLength < inLength )
  1107. {
  1108. DNSDBG( ANY, (
  1109. "ERROR: insufficient cannon buffer len = %d\n"
  1110. "\tstring = %S, len = %d\n",
  1111. BufLength,
  1112. pwsString,
  1113. inLength ));
  1114. SetLastError( ERROR_INSUFFICIENT_BUFFER );
  1115. return( 0 );
  1116. }
  1117. //
  1118. // convert name
  1119. // - downcase with canonicalizing rules
  1120. //
  1121. inLength = LCMapStringW(
  1122. DNS_CANONICAL_LOCALE,
  1123. DNS_CANONICALIZING_FLAGS,
  1124. pwsString,
  1125. inLength,
  1126. pBuffer,
  1127. BufLength
  1128. );
  1129. #if DBG
  1130. if ( inLength == 0 )
  1131. {
  1132. DNS_STATUS status = GetLastError();
  1133. DNSDBG( ANY, (
  1134. "Canonicalization failed => %d\n"
  1135. "\tin %S\n",
  1136. status,
  1137. pwsString ));
  1138. SetLastError( status );
  1139. }
  1140. else
  1141. {
  1142. //
  1143. // DCR: warning this print can blow on non-null terminated conversions
  1144. //
  1145. DNSDBG( READ, (
  1146. "Canonicalized name at %p\n"
  1147. "\tin %S\n"
  1148. "\tout %S\n",
  1149. pwsString,
  1150. pwsString,
  1151. (PWSTR) pBuffer ));
  1152. }
  1153. #endif
  1154. return( inLength );
  1155. }
  1156. DWORD
  1157. Dns_MakeCanonicalNameInPlaceW(
  1158. IN PWCHAR pwString,
  1159. IN DWORD StringLength
  1160. )
  1161. /*++
  1162. Routine Description:
  1163. In place cannonicalization of name.
  1164. Arguments:
  1165. pwString -- ptr to string to copy
  1166. StringLength -- length of string
  1167. if zero string assumed to be NULL terminated, in this case
  1168. canonicalization includes NULL terminator
  1169. Return Value:
  1170. Count of characters converted -- including NULL terminator if
  1171. StringLength is unspecified
  1172. Zero on error.
  1173. --*/
  1174. {
  1175. DWORD nameLength = StringLength;
  1176. WCHAR tempBuffer[ DNS_MAX_NAME_BUFFER_LENGTH_UNICODE ] = { 0 }; // init for prefix
  1177. DNSDBG( READ, (
  1178. "Dns_MakeCanonicalNameInPlace()\n"
  1179. "\tpwString = %S\n"
  1180. "\tlength = %d\n",
  1181. pwString,
  1182. StringLength ));
  1183. // if length unknown, must be NULL terminated string
  1184. if ( nameLength == 0 )
  1185. {
  1186. nameLength = (DWORD) wcslen( pwString );
  1187. nameLength++;
  1188. }
  1189. //
  1190. // cannonicalize (downcase and cleanup)
  1191. // - copy string to temp buffer
  1192. // - then cannonicalize into original buffer
  1193. //
  1194. if ( nameLength <= DNS_MAX_NAME_BUFFER_LENGTH_UNICODE )
  1195. {
  1196. wcsncpy( tempBuffer, pwString, nameLength );
  1197. return Dns_MakeCanonicalNameW(
  1198. pwString, // write back to original string
  1199. nameLength, // length of buffer
  1200. tempBuffer, // input is temp copy
  1201. nameLength // input length
  1202. );
  1203. }
  1204. return 0;
  1205. }
  1206. INT
  1207. Dns_DowncaseNameLabel(
  1208. OUT PCHAR pchResult,
  1209. //OUT PDWORD pNameProperty,
  1210. IN PCHAR pchLabel,
  1211. IN DWORD cchLabel,
  1212. IN DWORD dwFlag
  1213. )
  1214. /*++
  1215. Routine Description:
  1216. Create a downcased version of DNS name.
  1217. This is UTF8 only routine for use by DNS server to
  1218. validate and downcase label during node creation.
  1219. Arguments:
  1220. pchResult -- resulting downcased label;
  1221. MUST have MAX_LABEL_BUFFER_LENGTH
  1222. //pNameProperty -- name properties of result
  1223. // ResultLength -- ptr to DWORD to recieve resulting length
  1224. pchLabel -- ptr to label
  1225. cchLabel -- count of bytes in label
  1226. dwFlag -- flag indicating what names are acceptable
  1227. strict RFC => DNS_ALLOW_RFC_NAMES_ONLY
  1228. non RFC names => DNS_ALLOW_NONRFC_NAMES
  1229. UTF8 extended => DNS_ALLOW_MULTIBYTE_NAMES
  1230. anything => DNS_ALLOW_ALL_NAMES
  1231. Return Value:
  1232. If extended name -- length of converted name.
  1233. Zero if success.
  1234. (-1) on error.
  1235. --*/
  1236. {
  1237. UCHAR ch;
  1238. PUCHAR pchout = pchResult;
  1239. PUCHAR pch = pchLabel;
  1240. DWORD count = cchLabel;
  1241. DWORD charProp;
  1242. DWORD trailCount = 0;
  1243. DWORD property = 0;
  1244. DNSDBG( TRACE, (
  1245. "Dns_DowncaseNameLabel( %.*s )\n"
  1246. "\tflag = %08x\n",
  1247. cchLabel,
  1248. pchLabel,
  1249. dwFlag ));
  1250. if ( count == 0 || count > DNS_MAX_LABEL_LENGTH )
  1251. {
  1252. goto InvalidName;
  1253. }
  1254. //
  1255. // copy each character
  1256. // - downcasing upper case chars
  1257. // - detecting invalid chars (unprintable, blank, dot)
  1258. //
  1259. while ( count-- )
  1260. {
  1261. // get next character and properties
  1262. ch = *pch++;
  1263. *pchout++ = ch;
  1264. charProp = DnsCharPropertyTable[ ch ];
  1265. // trail count check
  1266. // check this first to avoid trail count check on all
  1267. // other char types
  1268. //
  1269. // DEVNOTE: note this screens binary labels
  1270. if ( trailCount )
  1271. {
  1272. if ( (charProp & B_UTF8_TRAIL) )
  1273. {
  1274. trailCount--;
  1275. continue;
  1276. }
  1277. DNSDBG( READ, (
  1278. "ERROR: Name failed trail count check at %c\n", ch ));
  1279. property |= DNS_BIT_NAME_BINARY_LABEL;
  1280. }
  1281. // full RFC
  1282. // - map upper case to lower case
  1283. // - continue
  1284. if ( charProp & B_RFC )
  1285. {
  1286. if ( charProp & B_UPPER )
  1287. {
  1288. --pchout;
  1289. *pchout++ = ch + 0x20;
  1290. }
  1291. continue;
  1292. }
  1293. //
  1294. // check for extended chars
  1295. // - trail characters should have been caught above
  1296. // - doing this first so can make single trailCount
  1297. // check for all other ASCII chars
  1298. if ( ch >= 0x80 )
  1299. {
  1300. DWORD tempStatus;
  1301. tempStatus = Dns_ValidateUtf8Byte(
  1302. ch,
  1303. & trailCount );
  1304. if ( tempStatus != ERROR_SUCCESS )
  1305. {
  1306. DNSDBG( READ, (
  1307. "ERROR: Name UTF8 trail count check at %c\n", ch ));
  1308. goto InvalidName;
  1309. }
  1310. property |= DNS_BIT_NAME_MULTIBYTE;
  1311. continue;
  1312. }
  1313. //
  1314. // non-RFC
  1315. // - currently accepting only "_" as allowable as part of
  1316. // microsoft acceptable non-RFC set
  1317. //
  1318. // however DNS server must be able to read *, \, etc
  1319. // as these can be part of valid label
  1320. //
  1321. // note, could tighten this up with special flag, but since
  1322. // this only speeds case with invalid chars, there's not much
  1323. // point; underscore is likely to see significant use
  1324. //
  1325. // underscore
  1326. // - can be valid as leading label as part of SRV domain name
  1327. // - otherwise non-RFC
  1328. if ( ch == '_' )
  1329. {
  1330. if ( count == cchLabel - 1 )
  1331. {
  1332. continue;
  1333. }
  1334. property |= DNS_BIT_NAME_UNDERSCORE;
  1335. continue;
  1336. }
  1337. // backslash
  1338. // - used to denote classless in-addr domains
  1339. // must have leading and following chars
  1340. // - otherwise completely invalid
  1341. else if ( ch == '/' )
  1342. {
  1343. if ( count != 0 && count != cchLabel-1 )
  1344. {
  1345. continue;
  1346. }
  1347. }
  1348. // asterisk
  1349. // - valid only as single-byte first label in wildcard name
  1350. // - otherwise completely invalid
  1351. else if ( ch == '*' )
  1352. {
  1353. if ( count == 0 )
  1354. {
  1355. continue;
  1356. }
  1357. }
  1358. // anything else is complete junk
  1359. // currently only acceptable if allow binary labels
  1360. //
  1361. // JENHANCE: could break out non-RFC (printable\non)
  1362. property |= DNS_BIT_NAME_BINARY_LABEL;
  1363. DNSDBG( READ, ( "ERROR: Name character %c failed check\n", ch ));
  1364. continue;
  1365. }
  1366. //
  1367. // fill out name properties
  1368. //
  1369. // JENHANCE: full property fill out
  1370. //
  1371. // currently only property we're returning is multibyte name issue
  1372. // as that's all the server needs to check
  1373. //
  1374. // if save more properties then test becomes something like this
  1375. // if ( (property & dwFlags) != (property & SUPPORTED_CHECK_FLAGS) )
  1376. //
  1377. #if 0
  1378. //*pNameProperty = property;
  1379. if ( (property & dwFlags) != property )
  1380. {
  1381. goto InvalidName;
  1382. }
  1383. if ( property & DNS_BIT_NAME_MULTIBYTE )
  1384. {
  1385. goto Extended;
  1386. }
  1387. #endif
  1388. // standard RFC name -- skip the detail parsing
  1389. if ( property == 0 )
  1390. {
  1391. goto Done;
  1392. }
  1393. // other chars invalid unless allowing all
  1394. if ( property & DNS_BIT_NAME_BINARY_LABEL )
  1395. {
  1396. if ( dwFlag != DNS_ALLOW_ALL_NAMES )
  1397. {
  1398. goto InvalidName;
  1399. }
  1400. }
  1401. // multibyte
  1402. // - do extended downcase if multibyte
  1403. // - do nothing if binary
  1404. // - for strict this is invalid
  1405. if ( property & DNS_BIT_NAME_MULTIBYTE )
  1406. {
  1407. if ( dwFlag == DNS_ALLOW_MULTIBYTE_NAMES ||
  1408. dwFlag == DNS_ALLOW_ALL_NAMES )
  1409. {
  1410. goto Extended;
  1411. }
  1412. #if 0
  1413. if ( dwFlag != DNS_BINARY_LABELS )
  1414. {
  1415. goto InvalidName;
  1416. }
  1417. #endif
  1418. goto InvalidName;
  1419. }
  1420. // underscore valid unless completely strict
  1421. if ( property & DNS_BIT_NAME_UNDERSCORE )
  1422. {
  1423. if ( dwFlag == DNS_ALLOW_RFC_NAMES_ONLY )
  1424. {
  1425. goto InvalidName;
  1426. }
  1427. }
  1428. Done:
  1429. //
  1430. // NULL terminate, return success.
  1431. //
  1432. *pchout = 0;
  1433. return( 0 );
  1434. Extended:
  1435. //
  1436. // DCR: better approach to extended names
  1437. // 1) cannonicalize upfront
  1438. // - do whole name in one pass
  1439. // - no need to upcase here, similar to validateName() routine
  1440. // 2) cannonicalize here
  1441. // - detect extended
  1442. // - cannonicalize here
  1443. // - single recursion into routine like validateName()
  1444. //
  1445. //
  1446. // extended character encountered
  1447. // - convert to unicode
  1448. // - downcase
  1449. // - convert back to UTF8
  1450. //
  1451. //
  1452. // DCR_PERF: optimize for names where extended already downcased
  1453. //
  1454. // DCR_PERF: should wrap this code into UTF8 cannon routine
  1455. //
  1456. //if ( ! (dwFlags & DNS_ALLOW_ALREADY_EXTENDED_DOWN) )
  1457. {
  1458. DWORD length;
  1459. WCHAR unicodeString[ DNS_MAX_LABEL_BUFFER_LENGTH ];
  1460. DNSDBG( READ, (
  1461. "Extended character encountered downcasing string %*.s\n"
  1462. "\tconverting to unicode for case conversion\n",
  1463. cchLabel,
  1464. pchLabel ));
  1465. length = Dns_Utf8ToUnicode(
  1466. pchResult,
  1467. cchLabel,
  1468. unicodeString,
  1469. DNS_MAX_LABEL_BUFFER_LENGTH
  1470. );
  1471. if ( length == 0 )
  1472. {
  1473. DNSDBG( READ, (
  1474. "Rejecting invalid UTF8 string %.*s\n"
  1475. "\tFailed conversion to unicode OR conversion created\n"
  1476. "\tinvalid unicode string\n",
  1477. cchLabel,
  1478. pchResult ));
  1479. goto InvalidName;
  1480. }
  1481. DNSDBG( READ, (
  1482. "Unicode converted string %.*S\n"
  1483. "\tlength = %d\n"
  1484. "\tlast error = %d\n",
  1485. length,
  1486. unicodeString,
  1487. length,
  1488. GetLastError() ));
  1489. // no possible conversion of valid length UTF8, can
  1490. // overflow unicode buffer
  1491. ASSERT( length <= DNS_MAX_LABEL_LENGTH );
  1492. Dns_MakeCanonicalNameInPlaceW( unicodeString, length );
  1493. #if 1
  1494. DNSDBG( READ, (
  1495. "Canonical unicode name %.*S\n"
  1496. "\tlength = %d\n",
  1497. length,
  1498. unicodeString,
  1499. length ));
  1500. #endif
  1501. //
  1502. // reconvert to UTF8
  1503. // - mapping to UTF8 is just math, so only error
  1504. // is possibly overflowing UTF8 max label buffer
  1505. // - catch this error is character count changes
  1506. // note, that also must catch case where write fills
  1507. // 64 byte buffer eliminating NULL terminator
  1508. //
  1509. length = Dns_UnicodeToUtf8(
  1510. unicodeString,
  1511. length,
  1512. pchResult,
  1513. DNS_MAX_LABEL_BUFFER_LENGTH
  1514. );
  1515. DNSDBG( READ, (
  1516. "UTF8 downcased string %.*s\n"
  1517. "\tlength = %d\n",
  1518. length,
  1519. pchResult,
  1520. length ));
  1521. if ( length != cchLabel )
  1522. {
  1523. DNSDBG( ANY, (
  1524. "Downcasing UTF8 label %.*s, changed character count!\n"
  1525. "\tfrom %d to %d\n"
  1526. "\tResult name %.*s\n"
  1527. "\tlast error = %d\n",
  1528. cchLabel,
  1529. pchLabel,
  1530. cchLabel,
  1531. length,
  1532. length,
  1533. pchResult,
  1534. GetLastError() ));
  1535. if ( length == 0 || length > DNS_MAX_LABEL_LENGTH )
  1536. {
  1537. DNSDBG( ANY, (
  1538. "Failed conversion of downcased unicode string %S\n"
  1539. "\tback into UTF8.\n",
  1540. unicodeString ));
  1541. goto InvalidName;
  1542. }
  1543. }
  1544. //
  1545. // NULL terminate, return length to indicate extended name
  1546. //
  1547. pchResult[ length ] = 0;
  1548. return( (INT)length );
  1549. }
  1550. // no UTF8 multi-byte allowed -- drop through to invalid name return
  1551. InvalidName:
  1552. // return (-1) for error
  1553. DNSDBG( READ, (
  1554. "Dns_DowncaseNameLabel() found label to be invalid.\n"
  1555. "\tlabel = %.*s\n"
  1556. "\tcount = %d\n"
  1557. "\tproperty = %08x\n",
  1558. cchLabel,
  1559. pchLabel,
  1560. count,
  1561. property ));
  1562. return( -1 );
  1563. }
  1564. LPSTR
  1565. Dns_CreateStandardDnsNameCopy(
  1566. IN PCHAR pchName,
  1567. IN DWORD cchName,
  1568. IN DWORD dwFlag
  1569. )
  1570. /*++
  1571. Routine Description:
  1572. Makes copy of DNS name in valid "standard form"
  1573. - downcased
  1574. - no trailing dot (to avoid confusing DS)
  1575. Arguments:
  1576. pchName -- ptr DNS name in UTF8
  1577. cchName -- count of chars in name; may be NULL
  1578. dwFlag -- strict checking flags; currently ignored
  1579. Return Value:
  1580. Ptr to copy of DNS name.
  1581. NULL on invalid name.
  1582. --*/
  1583. {
  1584. PCHAR pszcopy = NULL;
  1585. DNS_STATUS status;
  1586. DWORD length;
  1587. DNSDBG( TRACE, ( "Dns_CreateStandardDnsName()\n" ));
  1588. DNSDBG( READ, (
  1589. "Dns_CreateStandardDnsName()\n"
  1590. "\tpchName = %.*s\n"
  1591. "\tcchName = %d\n",
  1592. cchName,
  1593. pchName,
  1594. cchName ));
  1595. if ( !pchName )
  1596. {
  1597. status = ERROR_INVALID_NAME;
  1598. goto Failed;
  1599. }
  1600. //
  1601. // ASCII string?
  1602. //
  1603. if ( Dns_IsStringAsciiEx( pchName, cchName ) )
  1604. {
  1605. //
  1606. // make copy
  1607. //
  1608. pszcopy = Dns_CreateStringCopy( pchName, cchName );
  1609. if ( !pszcopy )
  1610. {
  1611. status = DNS_ERROR_NO_MEMORY;
  1612. goto Failed;
  1613. }
  1614. //
  1615. // validate, check against strict criteria
  1616. //
  1617. // no validation until relax criteria
  1618. //
  1619. // DCR: name validation within Dns_CreateStandardNameCopy()
  1620. // accept anything except INVALID_NAME
  1621. // flags return FQDN info
  1622. //
  1623. #if 0
  1624. status = Dns_ValidateName_UTF8( pszcopy, DnsNameDomain );
  1625. if ( status == ERROR_INVALID_NAME )
  1626. {
  1627. goto Failed;
  1628. }
  1629. #endif
  1630. //
  1631. // downcase
  1632. // remove any trailing dot, except for root name
  1633. //
  1634. _strlwr( pszcopy );
  1635. length = strlen( pszcopy );
  1636. if ( length == 0 )
  1637. {
  1638. status = ERROR_INVALID_NAME;
  1639. goto Failed;
  1640. }
  1641. length--;
  1642. if ( length > 0 && pszcopy[length] == '.' )
  1643. {
  1644. pszcopy[length] = 0;
  1645. }
  1646. DNSDBG( READ, (
  1647. "Standard DNS name copy of %.*s is %s\n",
  1648. cchName,
  1649. pchName,
  1650. pszcopy ));
  1651. return( pszcopy );
  1652. }
  1653. //
  1654. // unicode name
  1655. //
  1656. else
  1657. {
  1658. WCHAR unicodeName[ DNS_MAX_NAME_BUFFER_LENGTH ];
  1659. WCHAR cannonicalName[ DNS_MAX_NAME_BUFFER_LENGTH ];
  1660. DWORD unicodeBufferLength;
  1661. //
  1662. // convert to unicode
  1663. // - buf length is in bytes
  1664. //
  1665. unicodeBufferLength = DNS_MAX_NAME_BUFFER_LENGTH * 2;
  1666. length = Dns_NameCopy(
  1667. (PSTR) unicodeName,
  1668. & unicodeBufferLength,
  1669. pchName,
  1670. cchName,
  1671. DnsCharSetUtf8,
  1672. DnsCharSetUnicode
  1673. );
  1674. if ( length == 0 )
  1675. {
  1676. DNSDBG( ANY, (
  1677. "ERROR conversion of name %.*s to unicode failed!\n",
  1678. cchName,
  1679. pchName ));
  1680. status = ERROR_INVALID_NAME;
  1681. goto Failed;
  1682. }
  1683. //
  1684. // make cannonical name
  1685. // - buf length is in unicode characters
  1686. // - output length is in unicode chars
  1687. length = Dns_MakeCanonicalNameW(
  1688. cannonicalName,
  1689. length / 2,
  1690. unicodeName,
  1691. dwFlag );
  1692. ASSERT( length != 0 );
  1693. if ( length == 0 )
  1694. {
  1695. status = ERROR_INVALID_NAME;
  1696. goto Failed;
  1697. }
  1698. //
  1699. // allocate UTF8 converted copy
  1700. // - this conversion should never fail
  1701. // - string length is unicode chars
  1702. //
  1703. pszcopy = Dns_StringCopyAllocate(
  1704. (PSTR) cannonicalName,
  1705. length,
  1706. DnsCharSetUnicode, // unicode in
  1707. DnsCharSetUtf8 // UTF8 out
  1708. );
  1709. if ( !pszcopy )
  1710. {
  1711. status = DNS_ERROR_NO_MEMORY;
  1712. goto Failed;
  1713. }
  1714. //
  1715. // validate, check against strict criteria
  1716. //
  1717. // no validation until relax criteria
  1718. //
  1719. // DCR: name validation within Dns_CreateStandardNameCopy()
  1720. // accept anything except INVALID_NAME
  1721. // flags return FQDN info
  1722. //
  1723. #if 0
  1724. status = Dns_ValidateName_UTF8( pszcopy, DnsNameDomain );
  1725. if ( status == ERROR_INVALID_NAME )
  1726. {
  1727. goto Failed;
  1728. }
  1729. #endif
  1730. return( pszcopy );
  1731. }
  1732. Failed:
  1733. FREE_HEAP( pszcopy );
  1734. SetLastError( status );
  1735. return( NULL );
  1736. }
  1737. //
  1738. // Public compare functions
  1739. //
  1740. BOOL
  1741. Dns_NameCompare_A(
  1742. IN PCSTR pName1,
  1743. IN PCSTR pName2
  1744. )
  1745. /*++
  1746. Routine Description:
  1747. Compare two DNS names.
  1748. Can not use stricmp() because of the possiblity of names with
  1749. trailing dots.
  1750. Arguments:
  1751. pName1 - ptr to first DNS name string (dotted format)
  1752. pName2 - ptr to second DNS name string (dotted format)
  1753. Return Value:
  1754. TRUE if names equal.
  1755. FALSE otherwise.
  1756. --*/
  1757. {
  1758. INT len1;
  1759. INT len2;
  1760. INT result;
  1761. //
  1762. // flat out match
  1763. // - this is possible with owner names and possibly other fields
  1764. //
  1765. if ( pName1 == pName2 )
  1766. {
  1767. return( TRUE );
  1768. }
  1769. if ( !pName1 || !pName2 )
  1770. {
  1771. return( FALSE );
  1772. }
  1773. //
  1774. // if lengths NOT equal, then
  1775. // they must be within one and longer string must have trailing dot
  1776. // - in this case save
  1777. //
  1778. len1 = strlen( pName1 );
  1779. len2 = strlen( pName2 );
  1780. if ( len2 != len1 )
  1781. {
  1782. if ( len2 == len1+1 )
  1783. {
  1784. if ( pName2[len1] != '.' )
  1785. {
  1786. return( FALSE );
  1787. }
  1788. // len1 is comparable length
  1789. }
  1790. else if ( len2+1 == len1 )
  1791. {
  1792. if ( pName1[len2] != '.' )
  1793. {
  1794. return( FALSE );
  1795. }
  1796. // len1 is set to comparable length
  1797. len1 = len2;
  1798. }
  1799. else
  1800. {
  1801. return( FALSE );
  1802. }
  1803. }
  1804. //
  1805. // compare only comparable length of string
  1806. //
  1807. result = CompareStringA(
  1808. //LOCALE_SYSTEM_DEFAULT,
  1809. DNS_CANONICAL_LOCALE,
  1810. DNS_CANONICAL_COMPARE_FLAGS,
  1811. pName1,
  1812. len1,
  1813. pName2,
  1814. len1 );
  1815. if ( result == CSTR_EQUAL )
  1816. {
  1817. return( TRUE );
  1818. }
  1819. // not equal or error
  1820. return( FALSE );
  1821. }
  1822. BOOL
  1823. Dns_NameCompare_W(
  1824. IN PCWSTR pName1,
  1825. IN PCWSTR pName2
  1826. )
  1827. /*++
  1828. Routine Description:
  1829. Compare two (Wide) DNS names.
  1830. Note: this is unicode aware, it assumes names in WCHAR string
  1831. format. Can not use stricmp() because of the possiblity of names
  1832. with trailing dots.
  1833. Arguments:
  1834. pName1 - ptr to first DNS name string (dotted format)
  1835. pName2 - ptr to second DNS name string (dotted format)
  1836. Return Value:
  1837. TRUE if names equal.
  1838. FALSE otherwise.
  1839. --*/
  1840. {
  1841. INT len1;
  1842. INT len2;
  1843. INT result;
  1844. //
  1845. // flat out match
  1846. // - this is possible with owner names and possibly other fields
  1847. //
  1848. if ( pName1 == pName2 )
  1849. {
  1850. return( TRUE );
  1851. }
  1852. if ( !pName1 || !pName2 )
  1853. {
  1854. return( FALSE );
  1855. }
  1856. //
  1857. // if lengths NOT equal, then
  1858. // they must be within one and longer string must have trailing dot
  1859. // - in this case save
  1860. //
  1861. len1 = wcslen( pName1 );
  1862. len2 = wcslen( pName2 );
  1863. if ( len2 != len1 )
  1864. {
  1865. if ( len2 == len1+1 )
  1866. {
  1867. if ( pName2[len1] != L'.' )
  1868. {
  1869. return( FALSE );
  1870. }
  1871. // len1 is comparable length
  1872. }
  1873. else if ( len2+1 == len1 )
  1874. {
  1875. if ( pName1[len2] != L'.' )
  1876. {
  1877. return( FALSE );
  1878. }
  1879. // len1 is set to comparable length
  1880. len1 = len2;
  1881. }
  1882. else
  1883. {
  1884. return( FALSE );
  1885. }
  1886. }
  1887. //
  1888. // compare only comparable length of string
  1889. //
  1890. #if DNSWIN95
  1891. //
  1892. // Win9x does not currently support CompareStringW()
  1893. //
  1894. if ( Dns_IsWin9x() )
  1895. {
  1896. return( !_wcsnicmp( pName1, pName2, len1 ) );
  1897. }
  1898. #endif
  1899. result = CompareStringW(
  1900. //LOCALE_SYSTEM_DEFAULT,
  1901. DNS_CANONICAL_LOCALE,
  1902. DNS_CANONICAL_COMPARE_FLAGS,
  1903. pName1,
  1904. len1,
  1905. pName2,
  1906. len1 );
  1907. if ( result == CSTR_EQUAL )
  1908. {
  1909. return( TRUE );
  1910. }
  1911. // not equal or error
  1912. return( FALSE );
  1913. }
  1914. BOOL
  1915. Dns_NameCompare_UTF8(
  1916. IN PCSTR pName1,
  1917. IN PCSTR pName2
  1918. )
  1919. /*++
  1920. Routine Description:
  1921. Compare two DNS names.
  1922. Arguments:
  1923. pName1 - ptr to first DNS name string (dotted format)
  1924. pName2 - ptr to second DNS name string (dotted format)
  1925. Return Value:
  1926. TRUE if names equal.
  1927. FALSE otherwise.
  1928. --*/
  1929. {
  1930. WCHAR nameBuffer1[ DNS_MAX_NAME_BUFFER_LENGTH ];
  1931. WCHAR nameBuffer2[ DNS_MAX_NAME_BUFFER_LENGTH ];
  1932. DWORD bufLen;
  1933. //
  1934. // flat out match
  1935. // - this is possible with owner names and possibly other fields
  1936. //
  1937. if ( pName1 == pName2 )
  1938. {
  1939. return( TRUE );
  1940. }
  1941. if ( !pName1 || !pName2 )
  1942. {
  1943. return( FALSE );
  1944. }
  1945. //
  1946. // if strings pure ASCII, then use ANSI version
  1947. //
  1948. if ( Dns_IsStringAscii( (PCHAR)pName1 ) &&
  1949. Dns_IsStringAscii( (PCHAR)pName2 ) )
  1950. {
  1951. return Dns_NameCompare_A( pName1, pName2 );
  1952. }
  1953. //
  1954. // otherwise must take names back to unicode to compare
  1955. //
  1956. bufLen = DNS_MAX_NAME_LENGTH;
  1957. if ( ! Dns_NameCopy(
  1958. (PCHAR) nameBuffer1,
  1959. & bufLen,
  1960. (PCHAR) pName1,
  1961. 0, // length unknown
  1962. DnsCharSetUtf8,
  1963. DnsCharSetUnicode
  1964. ) )
  1965. {
  1966. return( FALSE );
  1967. }
  1968. bufLen = DNS_MAX_NAME_LENGTH;
  1969. if ( ! Dns_NameCopy(
  1970. (PCHAR) nameBuffer2,
  1971. & bufLen,
  1972. (PCHAR) pName2,
  1973. 0, // length unknown
  1974. DnsCharSetUtf8,
  1975. DnsCharSetUnicode
  1976. ) )
  1977. {
  1978. return( FALSE );
  1979. }
  1980. return Dns_NameCompare_W( nameBuffer1, nameBuffer2 );
  1981. }
  1982. BOOL
  1983. Dns_NameComparePrivate(
  1984. IN PCSTR pName1,
  1985. IN PCSTR pName2,
  1986. IN DNS_CHARSET CharSet
  1987. )
  1988. /*++
  1989. Routine Description:
  1990. Compare two DNS names.
  1991. This is simply helpful utility to avoid coding the wide\narrow
  1992. test in the code in a hundred different places.
  1993. Arguments:
  1994. pName1 - ptr to first DNS name string (dotted format)
  1995. pName2 - ptr to second DNS name string (dotted format)
  1996. Return Value:
  1997. TRUE if names equal.
  1998. FALSE otherwise.
  1999. --*/
  2000. {
  2001. if ( CharSet == DnsCharSetUnicode )
  2002. {
  2003. return Dns_NameCompare_W(
  2004. (PCWSTR) pName1,
  2005. (PCWSTR) pName2 );
  2006. }
  2007. else if ( CharSet == DnsCharSetAnsi )
  2008. {
  2009. return Dns_NameCompare_A(
  2010. pName1,
  2011. pName2 );
  2012. }
  2013. else
  2014. {
  2015. return Dns_NameCompare_UTF8(
  2016. pName1,
  2017. pName2 );
  2018. }
  2019. }
  2020. //
  2021. // Advanced name comparison
  2022. // Includes hierarchial name relationship.
  2023. //
  2024. DNS_NAME_COMPARE_STATUS
  2025. Dns_NameCompareEx(
  2026. IN PCSTR pszNameLeft,
  2027. IN PCSTR pszNameRight,
  2028. IN DWORD dwReserved,
  2029. IN DNS_CHARSET CharSet
  2030. )
  2031. /*++
  2032. Routine Description:
  2033. Advanced compare of DNS names, including hierarchial relationship.
  2034. Arguments:
  2035. pszNameLeft -- left name
  2036. pszNameRight -- right name
  2037. dwReserved -- reserved for future use (type of comparison)
  2038. CharSet -- char set of names
  2039. Return Value:
  2040. DnsNameCompareInvalid -- one of the names was invalid
  2041. DnsNameCompareEqual -- names are equal
  2042. DnsNameCompareLeftParent -- left is ancestor of right name
  2043. DnsNameCompareRightParent -- right is ancestor of left name
  2044. DnsNameCompareNotEqual -- name not equal, no hierarchial relationship
  2045. --*/
  2046. {
  2047. DNS_NAME_COMPARE_STATUS result;
  2048. DNS_STATUS status;
  2049. INT lengthLeft;
  2050. INT lengthRight;
  2051. INT lengthDiff;
  2052. INT compareResult;
  2053. DWORD bufLength;
  2054. WCHAR nameLeft[ DNS_MAX_NAME_BUFFER_LENGTH ];
  2055. WCHAR nameRight[ DNS_MAX_NAME_BUFFER_LENGTH ];
  2056. DNSDBG( TRACE, (
  2057. "Dns_NameCompareEx( %s%S, %s%S )\n",
  2058. (CharSet==DnsCharSetUnicode) ? "" : pszNameLeft,
  2059. (CharSet==DnsCharSetUnicode) ? pszNameLeft : "",
  2060. (CharSet==DnsCharSetUnicode) ? "" : pszNameRight,
  2061. (CharSet==DnsCharSetUnicode) ? pszNameRight : ""
  2062. ));
  2063. //
  2064. // implementation note
  2065. // there's a lot of inefficiency here, because there are
  2066. // two different character sets required for
  2067. // validation -- UTF8 to check packet limits
  2068. // downcasing\comparison -- unicode for case insensitivity
  2069. //
  2070. // obviously there are much more efficient paths through this
  2071. // morass for particular special cases (ASCII names: downcase
  2072. // in ANSI, validate, compare); but since this is not perf
  2073. // path code we'll take the approach
  2074. // - convert to unicode
  2075. // - validate (which will convert at copy to UTF8)
  2076. // - downcase unicode
  2077. // - compare unicode
  2078. //
  2079. //
  2080. // validate args
  2081. //
  2082. if ( ! pszNameLeft || ! pszNameRight )
  2083. {
  2084. goto Invalid;
  2085. }
  2086. //
  2087. // copy convert to unicode
  2088. // - downcasing and compare will be done in unicode
  2089. // - return lengths are bytes converted, convert to string lengths
  2090. // - Dns_NameCopy() returns zero for invalid convert
  2091. //
  2092. bufLength = DNS_MAX_NAME_BUFFER_LENGTH * 2;
  2093. lengthLeft = (INT) Dns_NameCopy(
  2094. (PCHAR) nameLeft,
  2095. & bufLength,
  2096. (LPSTR) pszNameLeft,
  2097. 0, // string NULL terminated
  2098. CharSet, // char set in
  2099. DnsCharSetUnicode // unicode out
  2100. );
  2101. if ( lengthLeft == 0 )
  2102. {
  2103. goto Invalid;
  2104. }
  2105. lengthLeft = (lengthLeft/2) - 1;
  2106. ASSERT( lengthLeft >= 0 );
  2107. bufLength = DNS_MAX_NAME_BUFFER_LENGTH * 2;
  2108. lengthRight = (INT) Dns_NameCopy(
  2109. (PCHAR) nameRight,
  2110. & bufLength,
  2111. (LPSTR) pszNameRight,
  2112. 0, // string NULL terminated
  2113. CharSet, // char set in
  2114. DnsCharSetUnicode // unicode out
  2115. );
  2116. if ( lengthRight == 0 )
  2117. {
  2118. goto Invalid;
  2119. }
  2120. lengthRight = (lengthRight/2) - 1;
  2121. ASSERT( lengthRight >= 0 );
  2122. //
  2123. // cannonicalize names
  2124. //
  2125. Dns_MakeCanonicalNameInPlaceW( nameLeft, lengthLeft );
  2126. Dns_MakeCanonicalNameInPlaceW( nameRight, lengthRight );
  2127. //
  2128. // validate names
  2129. // - must screen empty string or we can fault below
  2130. //
  2131. status = Dns_ValidateName_W( nameLeft, DnsNameDomain );
  2132. if ( ERROR_SUCCESS != status &&
  2133. DNS_ERROR_NON_RFC_NAME != status )
  2134. {
  2135. goto Invalid;
  2136. }
  2137. status = Dns_ValidateName_W( nameRight, DnsNameDomain );
  2138. if ( ERROR_SUCCESS != status &&
  2139. DNS_ERROR_NON_RFC_NAME != status )
  2140. {
  2141. goto Invalid;
  2142. }
  2143. //
  2144. // add trailing dots
  2145. //
  2146. // we need to either add or remove trailing dots to make comparisons
  2147. // the advantage of adding them is that then, the root name does
  2148. // not require any special casing -- the root is the ancestor of
  2149. // every name
  2150. //
  2151. if ( nameLeft[ lengthLeft-1 ] != (WORD)'.')
  2152. {
  2153. nameLeft[ lengthLeft++ ] = (WORD) '.';
  2154. nameLeft[ lengthLeft ] = (WORD) 0;
  2155. }
  2156. if ( nameRight[ lengthRight-1 ] != (WORD)'.')
  2157. {
  2158. nameRight[ lengthRight++ ] = (WORD) '.';
  2159. nameRight[ lengthRight ] = (WORD) 0;
  2160. }
  2161. //
  2162. // compare equal length strings
  2163. //
  2164. result = DnsNameCompareNotEqual;
  2165. lengthDiff = (INT)lengthLeft - (INT)lengthRight;
  2166. if ( lengthLeft == lengthRight )
  2167. {
  2168. compareResult = wcscmp( nameLeft, nameRight );
  2169. if ( compareResult == 0 )
  2170. {
  2171. result = DnsNameCompareEqual;
  2172. }
  2173. goto Done;
  2174. }
  2175. //
  2176. // strings not equal
  2177. // - compare smaller string of length X
  2178. // to last X characters of larger string
  2179. // - also must make sure starting at label boundary
  2180. //
  2181. // note: strstr() is NOT useful for this work, because it
  2182. // compares useless for this work because it is finding the
  2183. // first match -- a little thought would indicate that this
  2184. // will fail in several obvious cases
  2185. //
  2186. // right string longer
  2187. // - need to sign change diff to make it offset in right string
  2188. else if ( lengthDiff < 0 )
  2189. {
  2190. lengthDiff = -lengthDiff;
  2191. if ( nameRight[ lengthDiff-1 ] != L'.' )
  2192. {
  2193. goto Done;
  2194. }
  2195. compareResult = wcscmp( nameLeft, nameRight+lengthDiff );
  2196. if ( compareResult == 0 )
  2197. {
  2198. result = DnsNameCompareLeftParent;
  2199. }
  2200. goto Done;
  2201. }
  2202. // left string longer
  2203. // - lengthDiff is offset into left string to start compare
  2204. else
  2205. {
  2206. if ( nameLeft[ lengthDiff-1 ] != L'.' )
  2207. {
  2208. goto Done;
  2209. }
  2210. compareResult = wcscmp( nameLeft+lengthDiff, nameRight );
  2211. if ( compareResult == 0 )
  2212. {
  2213. result = DnsNameCompareRightParent;
  2214. }
  2215. goto Done;
  2216. }
  2217. Done:
  2218. DNSDBG( TRACE, (
  2219. "Leave DnsNameCompareEx() result = %d\n",
  2220. result ));
  2221. return( result );
  2222. Invalid:
  2223. DNSDBG( ANY, (
  2224. "ERROR: Invalid name to Dns_NameCompareEx()\n" ));
  2225. return( DnsNameCompareInvalid );
  2226. }
  2227. //
  2228. // Random name utilities
  2229. //
  2230. PCHAR
  2231. _fastcall
  2232. Dns_GetDomainNameA(
  2233. IN PCSTR pszName
  2234. )
  2235. /*++
  2236. Routine Description:
  2237. Get domain name of DNS name.
  2238. Note, this assumes name already in UTF-8
  2239. Arguments:
  2240. pszName - standard dotted DNS name
  2241. Return Value:
  2242. Ptr to domain name of pszName.
  2243. NULL if pszName is in root domain.
  2244. --*/
  2245. {
  2246. CHAR ch;
  2247. //
  2248. // find next "." in name, then return ptr to next character
  2249. //
  2250. while( ch = *pszName++ )
  2251. {
  2252. if ( ch == '.' )
  2253. {
  2254. if ( *pszName )
  2255. {
  2256. return( (PCHAR)pszName );
  2257. }
  2258. return( NULL );
  2259. }
  2260. }
  2261. return( NULL );
  2262. }
  2263. PWSTR
  2264. _fastcall
  2265. Dns_GetDomainNameW(
  2266. IN PCWSTR pwsName
  2267. )
  2268. {
  2269. PWSTR pdomain;
  2270. //
  2271. // find next "." in name, then return ptr to next character
  2272. //
  2273. pdomain = wcschr( pwsName, L'.' );
  2274. if ( pdomain && *(++pdomain) )
  2275. {
  2276. return( pdomain );
  2277. }
  2278. return NULL;
  2279. }
  2280. PSTR
  2281. _fastcall
  2282. Dns_GetTldForNameA(
  2283. IN PCSTR pszName
  2284. )
  2285. /*++
  2286. Routine Description:
  2287. Get domain name of DNS name.
  2288. Arguments:
  2289. pszName - standard dotted DNS name
  2290. Return Value:
  2291. Ptr to domain name of pszName.
  2292. NULL if pszName is in root domain.
  2293. --*/
  2294. {
  2295. PSTR pdomain = (PSTR) pszName;
  2296. PSTR ptld = NULL;
  2297. //
  2298. // find last domain name in name
  2299. //
  2300. while ( pdomain = Dns_GetDomainNameA(pdomain) )
  2301. {
  2302. ptld = (PSTR) pdomain;
  2303. }
  2304. return ptld;
  2305. }
  2306. PWSTR
  2307. _fastcall
  2308. Dns_GetTldForNameW(
  2309. IN PCWSTR pszName
  2310. )
  2311. {
  2312. PWSTR pdomain = (PWSTR) pszName;
  2313. PWSTR ptld = NULL;
  2314. //
  2315. // find last domain name in name
  2316. //
  2317. while ( pdomain = Dns_GetDomainNameW(pdomain) )
  2318. {
  2319. ptld = pdomain;
  2320. }
  2321. return ptld;
  2322. }
  2323. BOOL
  2324. _fastcall
  2325. Dns_IsNameShortA(
  2326. IN PCSTR pszName
  2327. )
  2328. /*++
  2329. Routine Description:
  2330. Determine if a name is a multi-label DNS name.
  2331. This is a test of whether name at least one non-terminal dot.
  2332. Arguments:
  2333. pszName - standard dotted DNS name
  2334. Return Value:
  2335. TRUE if multiple labels.
  2336. FALSE otherwise.
  2337. --*/
  2338. {
  2339. INT nameLen;
  2340. // trailing domain? -- done
  2341. if ( Dns_GetDomainNameA( pszName ) )
  2342. {
  2343. return FALSE;
  2344. }
  2345. // otherwise test for valid label
  2346. nameLen = strlen( pszName );
  2347. if ( nameLen <= DNS_MAX_LABEL_LENGTH )
  2348. {
  2349. return TRUE;
  2350. }
  2351. nameLen--;
  2352. if ( nameLen == DNS_MAX_LABEL_LENGTH &&
  2353. pszName[nameLen] == '.')
  2354. {
  2355. return TRUE;
  2356. }
  2357. return FALSE;
  2358. }
  2359. BOOL
  2360. _fastcall
  2361. Dns_IsNameShortW(
  2362. IN PCWSTR pszName
  2363. )
  2364. {
  2365. INT nameLen;
  2366. // trailing domain? -- done
  2367. if ( Dns_GetDomainNameW( pszName ) )
  2368. {
  2369. return FALSE;
  2370. }
  2371. // otherwise test for valid label
  2372. nameLen = wcslen( pszName );
  2373. if ( nameLen <= DNS_MAX_LABEL_LENGTH )
  2374. {
  2375. return TRUE;
  2376. }
  2377. nameLen--;
  2378. if ( nameLen == DNS_MAX_LABEL_LENGTH &&
  2379. pszName[nameLen] == L'.')
  2380. {
  2381. return TRUE;
  2382. }
  2383. return FALSE;
  2384. }
  2385. BOOL
  2386. _fastcall
  2387. Dns_IsNameNumericA(
  2388. IN PCSTR pszName
  2389. )
  2390. /*++
  2391. Routine Description:
  2392. Determine if numeric name.
  2393. Note, this assumes name already in UTF-8
  2394. Arguments:
  2395. pszName - standard dotted DNS name
  2396. Return Value:
  2397. TRUE if all numeric.
  2398. FALSE otherwise.
  2399. --*/
  2400. {
  2401. CHAR ch;
  2402. BOOL fnumeric = FALSE;
  2403. //
  2404. // check if everything in name is numeric
  2405. // - dotted names can be numeric
  2406. // - "." not numeric
  2407. //
  2408. while( ch = *pszName++ )
  2409. {
  2410. if ( ch >= '0' && ch <= '9' )
  2411. {
  2412. fnumeric = TRUE;
  2413. continue;
  2414. }
  2415. else if ( ch == '.' )
  2416. {
  2417. continue;
  2418. }
  2419. return FALSE;
  2420. }
  2421. return fnumeric;
  2422. }
  2423. BOOL
  2424. _fastcall
  2425. Dns_IsNameNumericW(
  2426. IN PCWSTR pszName
  2427. )
  2428. {
  2429. WCHAR ch;
  2430. BOOL fnumeric = FALSE;
  2431. //
  2432. // check if everything in name is numeric
  2433. // - dotted names can be numeric
  2434. // - "." not numeric
  2435. //
  2436. while( ch = *pszName++ )
  2437. {
  2438. if ( ch >= '0' && ch <= '9' )
  2439. {
  2440. fnumeric = TRUE;
  2441. continue;
  2442. }
  2443. else if ( ch == '.' )
  2444. {
  2445. continue;
  2446. }
  2447. return FALSE;
  2448. }
  2449. return fnumeric;
  2450. }
  2451. BOOL
  2452. _fastcall
  2453. Dns_IsNameFQDN_A(
  2454. IN PCSTR pszName
  2455. )
  2456. /*++
  2457. Routine Description:
  2458. Determine if a name is a fully qualified DNS name (FQDN).
  2459. This is a test of whether name has trailing dot.
  2460. Arguments:
  2461. pszName - standard dotted DNS name
  2462. Return Value:
  2463. TRUE if FQDN.
  2464. FALSE otherwise.
  2465. --*/
  2466. {
  2467. DWORD nameLen = strlen( pszName );
  2468. if ( nameLen == 0 )
  2469. {
  2470. return FALSE;
  2471. }
  2472. if ( pszName[nameLen - 1] == '.' )
  2473. {
  2474. return TRUE;
  2475. }
  2476. else
  2477. {
  2478. return FALSE;
  2479. }
  2480. }
  2481. BOOL
  2482. _fastcall
  2483. Dns_IsNameFQDN_W(
  2484. IN PCWSTR pszName
  2485. )
  2486. {
  2487. DWORD nameLen = wcslen( pszName );
  2488. if ( nameLen == 0 )
  2489. {
  2490. return FALSE;
  2491. }
  2492. if ( pszName[nameLen - 1] == L'.' )
  2493. {
  2494. return TRUE;
  2495. }
  2496. else
  2497. {
  2498. return FALSE;
  2499. }
  2500. }
  2501. DWORD
  2502. _fastcall
  2503. Dns_GetNameAttributesA(
  2504. IN PCSTR pszName
  2505. )
  2506. /*++
  2507. Routine Description:
  2508. Determine the attributes that a name has.
  2509. Note, this assumes name already in UTF-8
  2510. Arguments:
  2511. pszName - standard dotted DNS name
  2512. Return Value:
  2513. DWORD with possible flags:
  2514. DNS_NAME_IS_FQDN
  2515. DNS_NAME_SINGLE_LABEL
  2516. DNS_NAME_MULTI_LABEL
  2517. --*/
  2518. {
  2519. DWORD attributes = DNS_NAME_UNKNOWN;
  2520. if ( Dns_IsNameFQDN_A( pszName ) )
  2521. {
  2522. attributes = DNS_NAME_IS_FQDN;
  2523. }
  2524. if ( Dns_IsNameShortA( pszName ) )
  2525. {
  2526. attributes |= DNS_NAME_SINGLE_LABEL;
  2527. }
  2528. else
  2529. {
  2530. attributes |= DNS_NAME_MULTI_LABEL;
  2531. }
  2532. return attributes;
  2533. }
  2534. DWORD
  2535. _fastcall
  2536. Dns_GetNameAttributesW(
  2537. IN PCWSTR pszName
  2538. )
  2539. {
  2540. DWORD attributes = DNS_NAME_UNKNOWN;
  2541. if ( Dns_IsNameFQDN_W( pszName ) )
  2542. {
  2543. attributes = DNS_NAME_IS_FQDN;
  2544. }
  2545. if ( Dns_IsNameShortW( pszName ) )
  2546. {
  2547. attributes |= DNS_NAME_SINGLE_LABEL;
  2548. }
  2549. else
  2550. {
  2551. attributes |= DNS_NAME_MULTI_LABEL;
  2552. }
  2553. return attributes;
  2554. }
  2555. DNS_STATUS
  2556. Dns_ValidateAndCategorizeDnsNameEx(
  2557. IN PCHAR pchName,
  2558. IN DWORD cchNameLength,
  2559. OUT PDWORD pLabelCount
  2560. )
  2561. /*++
  2562. Routine Description:
  2563. Determine type of name.
  2564. Three types:
  2565. 1) FQDN -- dot on end, signifies full DNS name, never appended
  2566. 2) dotted -- dot in name; probably FQDN, but may need to be appended
  2567. (as in file store)
  2568. 3) single part -- single part name (not FQDN), always appended with zone
  2569. or default domain name
  2570. Arguments:
  2571. pchName -- ptr to name
  2572. cchNameLength -- name length
  2573. pLabelCount -- address to receive label count
  2574. Return Value:
  2575. DNS_STATUS_FQDN
  2576. DNS_STATUS_DOTTED_NAME
  2577. DNS_STATUS_SINGLE_PART_NAME
  2578. DNS_ERROR_INVALID_NAME on non-DNS name
  2579. --*/
  2580. {
  2581. register PCHAR pch;
  2582. register CHAR ch;
  2583. PCHAR pchstop;
  2584. BOOL fdotted = FALSE;
  2585. DWORD labelCount = 0;
  2586. DWORD charCount = 0;
  2587. DNS_STATUS status = DNS_STATUS_SINGLE_PART_NAME;
  2588. //
  2589. // name length for string
  2590. //
  2591. if ( cchNameLength == 0 )
  2592. {
  2593. cchNameLength = strlen( pchName );
  2594. }
  2595. if ( cchNameLength > DNS_MAX_NAME_LENGTH ||
  2596. cchNameLength == 0 )
  2597. {
  2598. goto InvalidName;
  2599. }
  2600. //
  2601. // run through name
  2602. //
  2603. pch = pchName;
  2604. pchstop = pch + cchNameLength;
  2605. while ( pch < pchstop )
  2606. {
  2607. ch = *pch++;
  2608. if ( ch == '.' )
  2609. {
  2610. if ( charCount > DNS_MAX_LABEL_LENGTH )
  2611. {
  2612. goto InvalidName;
  2613. }
  2614. if ( charCount > 0 )
  2615. {
  2616. labelCount++;
  2617. charCount = 0;
  2618. status = DNS_STATUS_DOTTED_NAME;
  2619. continue;
  2620. }
  2621. else
  2622. {
  2623. // only valid zero label name is "."
  2624. if ( pch == pchstop &&
  2625. pch-1 == pchName )
  2626. {
  2627. break;
  2628. }
  2629. goto InvalidName;
  2630. }
  2631. }
  2632. else if ( ch == 0 )
  2633. {
  2634. DNS_ASSERT( FALSE );
  2635. break;
  2636. }
  2637. // regular character
  2638. charCount++;
  2639. }
  2640. //
  2641. // handle last label
  2642. // - if count, then boost label count
  2643. // - if zero and previously had dot, then string
  2644. // ended in dot and is FQDN
  2645. //
  2646. if ( charCount > 0 )
  2647. {
  2648. if ( charCount > DNS_MAX_LABEL_LENGTH )
  2649. {
  2650. goto InvalidName;
  2651. }
  2652. labelCount++;
  2653. }
  2654. else if ( status == DNS_STATUS_DOTTED_NAME )
  2655. {
  2656. status = DNS_STATUS_FQDN;
  2657. }
  2658. // return label count
  2659. if ( pLabelCount )
  2660. {
  2661. *pLabelCount = labelCount;
  2662. }
  2663. DNSDBG( TRACE, (
  2664. "Leave Dns_ValidateAndCategorizeNameEx()\n"
  2665. "\tstatus = %d\n"
  2666. "\tlabel count = %d\n",
  2667. status,
  2668. labelCount ));
  2669. return( status );
  2670. InvalidName:
  2671. if ( pLabelCount )
  2672. {
  2673. *pLabelCount = 0;
  2674. }
  2675. DNSDBG( TRACE, (
  2676. "Leave Dns_ValidateAndCategorizeNameEx()\n"
  2677. "\tstatus = ERROR_INVALID_NAME\n" ));
  2678. return( DNS_ERROR_INVALID_NAME );
  2679. }
  2680. DNS_STATUS
  2681. Dns_ValidateAndCategorizeDnsNameA(
  2682. IN PCSTR pszName,
  2683. OUT PDWORD pLabelCount
  2684. )
  2685. /*++
  2686. Routine Description:
  2687. Determine type of name.
  2688. Three types:
  2689. 1) FQDN -- dot on end, signifies full DNS name, never appended
  2690. 2) dotted -- dot in name; probably FQDN, but may need to be appended
  2691. (as in file store)
  2692. 3) single part -- single part name (not FQDN), always appended with zone
  2693. or default domain name
  2694. Arguments:
  2695. pszName -- ptr to name
  2696. pLabelCount -- address to receive label count
  2697. Return Value:
  2698. DNS_STATUS_FQDN
  2699. DNS_STATUS_DOTTED_NAME
  2700. DNS_STATUS_SINGLE_PART_NAME
  2701. DNS_ERROR_INVALID_NAME on non-DNS name
  2702. --*/
  2703. {
  2704. //
  2705. // call real function
  2706. //
  2707. return Dns_ValidateAndCategorizeDnsNameEx(
  2708. (PCHAR) pszName,
  2709. 0, // NULL terminated
  2710. pLabelCount );
  2711. }
  2712. DNS_STATUS
  2713. Dns_ValidateAndCategorizeDnsNameW(
  2714. IN PCWSTR pszName,
  2715. OUT PDWORD pLabelCount
  2716. )
  2717. /*++
  2718. Routine Description:
  2719. Determine type of name.
  2720. Three types:
  2721. 1) FQDN -- dot on end, signifies full DNS name, never appended
  2722. 2) dotted -- dot in name; probably FQDN, but may need to be appended
  2723. (as in file store)
  2724. 3) single part -- single part name (not FQDN), always appended with zone
  2725. or default domain name
  2726. Arguments:
  2727. pszName -- ptr to name
  2728. pLabelCount -- address to receive label count
  2729. Return Value:
  2730. DNS_STATUS_FQDN
  2731. DNS_STATUS_DOTTED_NAME
  2732. DNS_STATUS_SINGLE_PART_NAME
  2733. DNS_ERROR_INVALID_NAME on non-DNS name
  2734. --*/
  2735. {
  2736. register PWCHAR pch;
  2737. register WCHAR ch;
  2738. PWCHAR pchstop;
  2739. BOOL fdotted = FALSE;
  2740. DWORD nameLength;
  2741. DWORD labelCount = 0;
  2742. DWORD charCount = 0;
  2743. DNS_STATUS status = DNS_STATUS_SINGLE_PART_NAME;
  2744. //
  2745. // name length for string
  2746. //
  2747. nameLength = wcslen( pszName );
  2748. if ( nameLength > DNS_MAX_NAME_LENGTH ||
  2749. nameLength == 0 )
  2750. {
  2751. goto InvalidName;
  2752. }
  2753. //
  2754. // run through name
  2755. //
  2756. pch = (PWSTR) pszName;
  2757. pchstop = pch + nameLength;
  2758. while ( pch < pchstop )
  2759. {
  2760. ch = *pch++;
  2761. if ( ch == L'.' )
  2762. {
  2763. if ( charCount > DNS_MAX_LABEL_LENGTH )
  2764. {
  2765. goto InvalidName;
  2766. }
  2767. if ( charCount > 0 )
  2768. {
  2769. labelCount++;
  2770. charCount = 0;
  2771. status = DNS_STATUS_DOTTED_NAME;
  2772. continue;
  2773. }
  2774. else
  2775. {
  2776. // only valid zero label name is "."
  2777. if ( pch == pchstop &&
  2778. pch-1 == pszName )
  2779. {
  2780. break;
  2781. }
  2782. goto InvalidName;
  2783. }
  2784. }
  2785. else if ( ch == 0 )
  2786. {
  2787. DNS_ASSERT( FALSE );
  2788. break;
  2789. }
  2790. // regular character
  2791. charCount++;
  2792. }
  2793. //
  2794. // handle last label
  2795. // - if count, then boost label count
  2796. // - if zero and previously had dot, then string
  2797. // ended in dot and is FQDN
  2798. //
  2799. if ( charCount > 0 )
  2800. {
  2801. if ( charCount > DNS_MAX_LABEL_LENGTH )
  2802. {
  2803. goto InvalidName;
  2804. }
  2805. labelCount++;
  2806. }
  2807. else if ( status == DNS_STATUS_DOTTED_NAME )
  2808. {
  2809. status = DNS_STATUS_FQDN;
  2810. }
  2811. // return label count
  2812. if ( pLabelCount )
  2813. {
  2814. *pLabelCount = labelCount;
  2815. }
  2816. DNSDBG( TRACE, (
  2817. "Leave Dns_ValidateAndCategorizeNameW()\n"
  2818. "\tstatus = %d\n"
  2819. "\tlabel count = %d\n",
  2820. status,
  2821. labelCount ));
  2822. return( status );
  2823. InvalidName:
  2824. if ( pLabelCount )
  2825. {
  2826. *pLabelCount = 0;
  2827. }
  2828. DNSDBG( TRACE, (
  2829. "Leave Dns_ValidateAndCategorizeNameW()\n"
  2830. "\tstatus = ERROR_INVALID_NAME\n" ));
  2831. return( DNS_ERROR_INVALID_NAME );
  2832. }
  2833. DWORD
  2834. Dns_NameLabelCountA(
  2835. IN PCSTR pszName
  2836. )
  2837. /*++
  2838. Routine Description:
  2839. Return name label count.
  2840. Arguments:
  2841. pszName -- ptr to name
  2842. Return Value:
  2843. Label count if valid name.
  2844. Zero on root name or error.
  2845. --*/
  2846. {
  2847. DWORD labelCount = 0;
  2848. DNS_STATUS status;
  2849. //
  2850. // call real routine
  2851. //
  2852. status = Dns_ValidateAndCategorizeDnsNameEx(
  2853. (PCHAR) pszName,
  2854. 0,
  2855. & labelCount );
  2856. if ( status == DNS_ERROR_INVALID_NAME )
  2857. {
  2858. labelCount = 0;
  2859. }
  2860. return( labelCount );
  2861. }
  2862. DWORD
  2863. Dns_NameLabelCountW(
  2864. IN PCWSTR pszName
  2865. )
  2866. /*++
  2867. Routine Description:
  2868. Return name label count.
  2869. Arguments:
  2870. pszName -- ptr to name
  2871. Return Value:
  2872. Label count if valid name.
  2873. Zero on root name or error.
  2874. --*/
  2875. {
  2876. DWORD labelCount = 0;
  2877. DNS_STATUS status;
  2878. //
  2879. // call real routine
  2880. //
  2881. status = Dns_ValidateAndCategorizeDnsNameW(
  2882. pszName,
  2883. & labelCount );
  2884. if ( status == DNS_ERROR_INVALID_NAME )
  2885. {
  2886. labelCount = 0;
  2887. }
  2888. return( labelCount );
  2889. }
  2890. PSTR
  2891. Dns_NameAppend_A(
  2892. OUT PCHAR pNameBuffer,
  2893. IN DWORD BufferLength,
  2894. IN PSTR pszName,
  2895. IN PSTR pszDomain
  2896. )
  2897. /*++
  2898. Routine Description:
  2899. Write appended name to buffer (ANSI or UTF8).
  2900. Arguments:
  2901. pNameBuffer -- name buffer to write to
  2902. BufferLength -- buffer length
  2903. pszName -- name to append domain to
  2904. pszDomain -- domain name
  2905. Return Value:
  2906. Ptr to buffer with appended domain name.
  2907. NULL on invalid (too long) name.
  2908. --*/
  2909. {
  2910. DWORD length1;
  2911. DWORD length2;
  2912. DWORD totalLength;
  2913. DNSDBG( TRACE, ( "Dns_NameAppend_A( %A, %A )\n", pszName, pszDomain ));
  2914. //
  2915. // appending NULL domain?
  2916. //
  2917. if ( !pszDomain )
  2918. {
  2919. totalLength = strlen( pszName );
  2920. if ( totalLength >= BufferLength )
  2921. {
  2922. return( NULL );
  2923. }
  2924. RtlCopyMemory(
  2925. pNameBuffer,
  2926. pszName,
  2927. totalLength );
  2928. pNameBuffer[ totalLength ] = 0;
  2929. return( pNameBuffer );
  2930. }
  2931. //
  2932. // get name lengths -- make sure we fit length
  2933. //
  2934. length1 = strlen( pszName );
  2935. if ( !length1 )
  2936. {
  2937. return NULL;
  2938. }
  2939. if ( pszName[ length1-1] == '.' )
  2940. {
  2941. length1--;
  2942. }
  2943. length2 = strlen( pszDomain );
  2944. totalLength = length1 + length2 + 1;
  2945. if ( totalLength >= BufferLength )
  2946. {
  2947. return NULL ;
  2948. }
  2949. //
  2950. // copy to buffer
  2951. //
  2952. RtlCopyMemory(
  2953. pNameBuffer,
  2954. pszName,
  2955. length1 );
  2956. pNameBuffer[ length1 ] = '.';
  2957. RtlCopyMemory(
  2958. & pNameBuffer[length1+1],
  2959. pszDomain,
  2960. length2 );
  2961. pNameBuffer[ totalLength ] = 0;
  2962. return( pNameBuffer );
  2963. }
  2964. PWSTR
  2965. Dns_NameAppend_W(
  2966. OUT PWCHAR pNameBuffer,
  2967. IN DWORD BufferLength,
  2968. IN PWSTR pwsName,
  2969. IN PWSTR pwsDomain
  2970. )
  2971. /*++
  2972. Routine Description:
  2973. Write appended name to buffer (unicode).
  2974. Arguments:
  2975. pNameBuffer -- name buffer to write to
  2976. BufferLength -- buffer length in WCHAR
  2977. pwsName -- name to append domain to
  2978. pwsDomain -- domain name
  2979. Return Value:
  2980. Ptr to buffer with appended domain name.
  2981. NULL on invalid (too long) name.
  2982. --*/
  2983. {
  2984. DWORD length1;
  2985. DWORD length2;
  2986. DWORD totalLength;
  2987. DNSDBG( TRACE, ( "Dns_NameAppend_W( %S, %S )\n", pwsName, pwsDomain ));
  2988. //
  2989. // appending NULL domain?
  2990. //
  2991. if ( !pwsDomain )
  2992. {
  2993. totalLength = wcslen( pwsName );
  2994. if ( totalLength >= BufferLength )
  2995. {
  2996. return( NULL );
  2997. }
  2998. RtlCopyMemory(
  2999. pNameBuffer,
  3000. pwsName,
  3001. totalLength*sizeof(WCHAR) );
  3002. pNameBuffer[ totalLength ] = 0;
  3003. return( pNameBuffer );
  3004. }
  3005. //
  3006. // get name lengths -- make sure we fit length
  3007. //
  3008. length1 = wcslen( pwsName );
  3009. if ( !length1 )
  3010. {
  3011. return NULL;
  3012. }
  3013. if ( pwsName[ length1-1] == L'.' )
  3014. {
  3015. length1--;
  3016. }
  3017. length2 = wcslen( pwsDomain );
  3018. totalLength = length1 + length2 + 1;
  3019. if ( totalLength >= BufferLength )
  3020. {
  3021. return( NULL );
  3022. }
  3023. //
  3024. // copy to buffer
  3025. //
  3026. RtlCopyMemory(
  3027. pNameBuffer,
  3028. pwsName,
  3029. length1*sizeof(WCHAR) );
  3030. pNameBuffer[ length1 ] = L'.';
  3031. RtlCopyMemory(
  3032. & pNameBuffer[length1+1],
  3033. pwsDomain,
  3034. length2*sizeof(WCHAR) );
  3035. pNameBuffer[ totalLength ] = 0;
  3036. return( pNameBuffer );
  3037. }
  3038. PSTR
  3039. Dns_SplitHostFromDomainNameA(
  3040. IN PSTR pszName
  3041. )
  3042. /*++
  3043. Routine Description:
  3044. Split host name from domain name.
  3045. Combines getting domain name and splitting
  3046. off host name.
  3047. Arguments:
  3048. pszName - standard dotted DNS name
  3049. Return Value:
  3050. Ptr to domain name of pszName.
  3051. NULL if pszName is in root domain.
  3052. --*/
  3053. {
  3054. PSTR pnameDomain;
  3055. //
  3056. // get domain name
  3057. // if exists, NULL terminate host name part
  3058. //
  3059. pnameDomain = Dns_GetDomainNameA( (PCSTR)pszName );
  3060. if ( pnameDomain )
  3061. {
  3062. if ( pnameDomain <= pszName )
  3063. {
  3064. return NULL;
  3065. }
  3066. *(pnameDomain-1) = 0;
  3067. }
  3068. return pnameDomain;
  3069. }
  3070. PWSTR
  3071. Dns_SplitHostFromDomainNameW(
  3072. IN PWSTR pszName
  3073. )
  3074. {
  3075. PWSTR pnameDomain;
  3076. //
  3077. // get domain name
  3078. // if exists, NULL terminate host name part
  3079. //
  3080. pnameDomain = Dns_GetDomainNameW( (PCWSTR)pszName );
  3081. if ( pnameDomain )
  3082. {
  3083. if ( pnameDomain <= pszName )
  3084. {
  3085. return NULL;
  3086. }
  3087. *(pnameDomain-1) = 0;
  3088. }
  3089. return pnameDomain;
  3090. }
  3091. BOOL
  3092. Dns_CopyNameLabelA(
  3093. OUT PCHAR pBuffer,
  3094. IN PCSTR pszName
  3095. )
  3096. /*++
  3097. Routine Description:
  3098. Copy first label in name.
  3099. Arguments:
  3100. pBuffer - buffer to hold label copy;
  3101. must be DNS_MAX_LABEL_BUFFER_LENGTH
  3102. pszName - standard dotted DNS name
  3103. Return Value:
  3104. TRUE on successful copy.
  3105. FALSE on bad name. Copy buffer will still be valid.
  3106. --*/
  3107. {
  3108. CHAR ch;
  3109. PCHAR plabel = pBuffer;
  3110. PCHAR pstop;
  3111. BOOL retval = TRUE;
  3112. if ( !pszName )
  3113. {
  3114. retval = FALSE;
  3115. goto Done;
  3116. }
  3117. pstop = plabel + DNS_MAX_LABEL_LENGTH;
  3118. //
  3119. // find next "." in name, then return ptr to next character
  3120. //
  3121. while( ch = *pszName++ )
  3122. {
  3123. if ( ch == '.' )
  3124. {
  3125. if ( plabel == pBuffer )
  3126. {
  3127. *plabel++ = ch;
  3128. }
  3129. break;
  3130. }
  3131. if ( plabel >= pstop )
  3132. {
  3133. retval = FALSE;
  3134. break;
  3135. }
  3136. *plabel++ = ch;
  3137. }
  3138. Done:
  3139. *plabel = 0;
  3140. return( retval );
  3141. }
  3142. BOOL
  3143. Dns_CopyNameLabelW(
  3144. OUT PWCHAR pBuffer,
  3145. IN PCWSTR pszName
  3146. )
  3147. {
  3148. WCHAR ch;
  3149. PWCHAR plabel = pBuffer;
  3150. PWCHAR pstop;
  3151. BOOL retval = TRUE;
  3152. if ( !pszName )
  3153. {
  3154. retval = FALSE;
  3155. goto Done;
  3156. }
  3157. pstop = plabel + DNS_MAX_LABEL_LENGTH;
  3158. //
  3159. // find next "." in name, then return ptr to next character
  3160. //
  3161. while( ch = *pszName++ )
  3162. {
  3163. if ( ch == '.' )
  3164. {
  3165. if ( plabel == pBuffer )
  3166. {
  3167. *plabel++ = ch;
  3168. }
  3169. break;
  3170. }
  3171. if ( plabel >= pstop )
  3172. {
  3173. retval = FALSE;
  3174. break;
  3175. }
  3176. *plabel++ = ch;
  3177. }
  3178. Done:
  3179. *plabel = 0;
  3180. return( retval );
  3181. }
  3182. //
  3183. // Wrappers for most common name conversions
  3184. //
  3185. DWORD
  3186. Dns_NameCopyWireToUnicode(
  3187. OUT PWCHAR pBufferUnicode,
  3188. IN PCSTR pszNameWire
  3189. )
  3190. /*++
  3191. Routine Description:
  3192. Convert name from wire to unicode.
  3193. Simple wrapper on Dns_NameCopy for common operation:
  3194. - unicode to wire
  3195. - NULL terminated name
  3196. - standard length buffer
  3197. Arguments:
  3198. pBufferUnicode -- unicode result buffer
  3199. pszNameWire - name in wire format
  3200. Return Value:
  3201. Count of bytes copied if successful.
  3202. Zero on error -- name too long or conversion error.
  3203. --*/
  3204. {
  3205. DWORD bufferLength = DNS_MAX_NAME_BUFFER_LENGTH_UNICODE;
  3206. //
  3207. // copy name back to unicode
  3208. //
  3209. return Dns_NameCopy(
  3210. (PCHAR) pBufferUnicode,
  3211. & bufferLength,
  3212. (PCHAR) pszNameWire,
  3213. 0, // null terminated
  3214. DnsCharSetWire,
  3215. DnsCharSetUnicode );
  3216. }
  3217. DWORD
  3218. Dns_NameCopyUnicodeToWire(
  3219. OUT PCHAR pBufferWire,
  3220. IN PCWSTR pwsNameUnicode
  3221. )
  3222. /*++
  3223. Routine Description:
  3224. Convert name from unicode to wire.
  3225. Simple wrapper on Dns_NameCopy for common operation:
  3226. - unicode to wire
  3227. - NULL terminated name
  3228. - standard length buffer
  3229. Arguments:
  3230. pBufferWire -- wire format result buffer
  3231. pwsNameUnicode - name in unicode
  3232. Return Value:
  3233. Count of bytes copied if successful.
  3234. Zero on error -- name too long or conversion error.
  3235. --*/
  3236. {
  3237. DWORD bufferLength = DNS_MAX_NAME_BUFFER_LENGTH;
  3238. //
  3239. // copy name to wire format
  3240. //
  3241. return Dns_NameCopy(
  3242. pBufferWire,
  3243. & bufferLength,
  3244. (PCHAR) pwsNameUnicode,
  3245. 0, // null terminated
  3246. DnsCharSetUnicode,
  3247. DnsCharSetWire );
  3248. }
  3249. DWORD
  3250. Dns_NameCopyStandard_W(
  3251. OUT PWCHAR pBuffer,
  3252. IN PCWSTR pwsNameUnicode
  3253. )
  3254. /*++
  3255. Routine Description:
  3256. Copy unicode name.
  3257. Simple wrapper on Dns_NameCopy for common operation:
  3258. - unicode to unicode
  3259. - NULL terminated name
  3260. - standard length buffer
  3261. Arguments:
  3262. pBuffer -- wire format result buffer
  3263. pwsNameUnicode - name in unicode
  3264. Return Value:
  3265. Count of bytes copied if successful.
  3266. Zero on error -- name too long or conversion error.
  3267. --*/
  3268. {
  3269. DWORD bufferLength = DNS_MAX_NAME_BUFFER_LENGTH_UNICODE;
  3270. //
  3271. // copy name
  3272. //
  3273. return Dns_NameCopy(
  3274. (PCHAR) pBuffer,
  3275. & bufferLength,
  3276. (PCHAR) pwsNameUnicode,
  3277. 0, // null terminated
  3278. DnsCharSetUnicode,
  3279. DnsCharSetUnicode );
  3280. }
  3281. DWORD
  3282. Dns_NameCopyStandard_A(
  3283. OUT PCHAR pBuffer,
  3284. IN PCSTR pszName
  3285. )
  3286. /*++
  3287. Routine Description:
  3288. Convert name from unicode to wire.
  3289. Simple wrapper on Dns_NameCopy for common operation:
  3290. - unicode to wire
  3291. - NULL terminated name
  3292. - standard length buffer
  3293. Arguments:
  3294. pBuffer -- wire format result buffer
  3295. pszName - name in narrow char set
  3296. Return Value:
  3297. Count of bytes copied if successful.
  3298. Zero on error -- name too long or conversion error.
  3299. --*/
  3300. {
  3301. DWORD bufferLength = DNS_MAX_NAME_BUFFER_LENGTH;
  3302. //
  3303. // copy name
  3304. //
  3305. return Dns_NameCopy(
  3306. pBuffer,
  3307. & bufferLength,
  3308. (PCHAR) pszName,
  3309. 0, // null terminated
  3310. DnsCharSetUtf8,
  3311. DnsCharSetUtf8 );
  3312. }
  3313. //
  3314. // Temp fix ups for functions exposed in dnslib.h
  3315. //
  3316. // DCR: delete when clean build with dnslib.h functions removed
  3317. //
  3318. PSTR
  3319. _fastcall
  3320. Dns_GetDomainName(
  3321. IN PCSTR pszName
  3322. )
  3323. {
  3324. return Dns_GetDomainNameA( pszName );
  3325. }
  3326. PWSTR
  3327. _fastcall
  3328. Dns_GetDomainName_W(
  3329. IN PCWSTR pwsName
  3330. )
  3331. {
  3332. return Dns_GetDomainNameW( pwsName );
  3333. }
  3334. PCHAR
  3335. _fastcall
  3336. Dns_GetTldForName(
  3337. IN PCSTR pszName
  3338. )
  3339. {
  3340. return Dns_GetTldForNameA( pszName );
  3341. }
  3342. BOOL
  3343. _fastcall
  3344. Dns_IsNameShort(
  3345. IN PCSTR pszName
  3346. )
  3347. {
  3348. return Dns_IsNameShortA( pszName );
  3349. }
  3350. BOOL
  3351. _fastcall
  3352. Dns_IsNameFQDN(
  3353. IN PCSTR pszName
  3354. )
  3355. {
  3356. return Dns_IsNameFQDN_A( pszName );
  3357. }
  3358. DWORD
  3359. Dns_NameLabelCount(
  3360. IN PCSTR pszName
  3361. )
  3362. {
  3363. return Dns_NameLabelCountA( pszName );
  3364. }
  3365. DWORD
  3366. _fastcall
  3367. Dns_GetNameAttributes(
  3368. IN PCSTR pszName
  3369. )
  3370. {
  3371. return Dns_GetNameAttributesA( pszName );
  3372. }
  3373. //
  3374. // End name.c
  3375. //