Source code of Windows XP (NT5)
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.

3806 lines
84 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. #if DBG
  1461. WCHAR originalName[ DNS_MAX_LABEL_BUFFER_LENGTH ];
  1462. WCHAR downName[ DNS_MAX_LABEL_BUFFER_LENGTH ];
  1463. WCHAR cannonDownName[ DNS_MAX_LABEL_BUFFER_LENGTH ];
  1464. #endif
  1465. DNSDBG( READ, (
  1466. "Extended character encountered downcasing string %*.s\n"
  1467. "\tconverting to unicode for case conversion\n",
  1468. cchLabel,
  1469. pchLabel ));
  1470. length = Dns_Utf8ToUnicode(
  1471. pchResult,
  1472. cchLabel,
  1473. unicodeString,
  1474. DNS_MAX_LABEL_BUFFER_LENGTH
  1475. );
  1476. DNSDBG( READ, (
  1477. "Unicode converted string %.*S\n"
  1478. "\tlength = %d\n"
  1479. "\tlast error = %d\n",
  1480. length,
  1481. unicodeString,
  1482. length,
  1483. GetLastError() ));
  1484. if ( length == 0 )
  1485. {
  1486. DNSDBG( READ, (
  1487. "Rejecting invalid UTF8 string %.*s\n"
  1488. "\tFailed conversion to unicode OR conversion created\n"
  1489. "\tinvalid unicode string\n",
  1490. cchLabel,
  1491. pchResult ));
  1492. goto InvalidName;
  1493. }
  1494. // no possible conversion of valid length UTF8, can
  1495. // overflow unicode buffer
  1496. ASSERT( length <= DNS_MAX_LABEL_LENGTH );
  1497. Dns_MakeCanonicalNameInPlaceW( unicodeString, length );
  1498. #if 1
  1499. DNSDBG( READ, (
  1500. "Canonical unicode name %.*S\n"
  1501. "\tlength = %d\n",
  1502. length,
  1503. unicodeString,
  1504. length ));
  1505. #endif
  1506. //
  1507. // reconvert to UTF8
  1508. // - mapping to UTF8 is just math, so only error
  1509. // is possibly overflowing UTF8 max label buffer
  1510. // - catch this error is character count changes
  1511. // note, that also must catch case where write fills
  1512. // 64 byte buffer eliminating NULL terminator
  1513. //
  1514. length = Dns_UnicodeToUtf8(
  1515. unicodeString,
  1516. length,
  1517. pchResult,
  1518. DNS_MAX_LABEL_BUFFER_LENGTH
  1519. );
  1520. DNSDBG( READ, (
  1521. "UTF8 downcased string %.*s\n"
  1522. "\tlength = %d\n",
  1523. length,
  1524. pchResult,
  1525. length ));
  1526. if ( length != cchLabel )
  1527. {
  1528. DNSDBG( ANY, (
  1529. "Downcasing UTF8 label %.*s, changed character count!\n"
  1530. "\tfrom %d to %d\n"
  1531. "\tResult name %.*s\n"
  1532. "\tlast error = %d\n",
  1533. cchLabel,
  1534. pchLabel,
  1535. cchLabel,
  1536. length,
  1537. length,
  1538. pchResult,
  1539. GetLastError() ));
  1540. if ( length == 0 || length > DNS_MAX_LABEL_LENGTH )
  1541. {
  1542. DNSDBG( ANY, (
  1543. "Failed conversion of downcased unicode string %S\n"
  1544. "\tback into UTF8.\n",
  1545. unicodeString ));
  1546. goto InvalidName;
  1547. }
  1548. }
  1549. //
  1550. // NULL terminate, return length to indicate extended name
  1551. //
  1552. pchResult[ length ] = 0;
  1553. return( (INT)length );
  1554. }
  1555. // no UTF8 multi-byte allowed -- drop through to invalid name return
  1556. InvalidName:
  1557. // return (-1) for error
  1558. DNSDBG( READ, (
  1559. "Dns_DowncaseNameLabel() found label to be invalid.\n"
  1560. "\tlabel = %.*s\n"
  1561. "\tcount = %d\n"
  1562. "\tproperty = %08x\n",
  1563. cchLabel,
  1564. pchLabel,
  1565. count,
  1566. property ));
  1567. return( -1 );
  1568. }
  1569. LPSTR
  1570. Dns_CreateStandardDnsNameCopy(
  1571. IN PCHAR pchName,
  1572. IN DWORD cchName,
  1573. IN DWORD dwFlag
  1574. )
  1575. /*++
  1576. Routine Description:
  1577. Makes copy of DNS name in valid "standard form"
  1578. - downcased
  1579. - no trailing dot (to avoid confusing DS)
  1580. Arguments:
  1581. pchName -- ptr DNS name in UTF8
  1582. cchName -- count of chars in name; may be NULL
  1583. dwFlag -- strict checking flags; currently ignored
  1584. Return Value:
  1585. Ptr to copy of DNS name.
  1586. NULL on invalid name.
  1587. --*/
  1588. {
  1589. PCHAR pszcopy = NULL;
  1590. DNS_STATUS status;
  1591. DWORD length;
  1592. DNSDBG( TRACE, ( "Dns_CreateStandardDnsName()\n" ));
  1593. DNSDBG( READ, (
  1594. "Dns_CreateStandardDnsName()\n"
  1595. "\tpchName = %.*s\n"
  1596. "\tcchName = %d\n",
  1597. cchName,
  1598. pchName,
  1599. cchName ));
  1600. if ( !pchName )
  1601. {
  1602. status = ERROR_INVALID_NAME;
  1603. goto Failed;
  1604. }
  1605. //
  1606. // ASCII string?
  1607. //
  1608. if ( Dns_IsStringAsciiEx( pchName, cchName ) )
  1609. {
  1610. //
  1611. // make copy
  1612. //
  1613. pszcopy = Dns_CreateStringCopy( pchName, cchName );
  1614. if ( !pszcopy )
  1615. {
  1616. status = DNS_ERROR_NO_MEMORY;
  1617. goto Failed;
  1618. }
  1619. //
  1620. // validate, check against strict criteria
  1621. //
  1622. // no validation until relax criteria
  1623. //
  1624. // DCR: name validation within Dns_CreateStandardNameCopy()
  1625. // accept anything except INVALID_NAME
  1626. // flags return FQDN info
  1627. //
  1628. #if 0
  1629. status = Dns_ValidateName_UTF8( pszcopy, DnsNameDomain );
  1630. if ( status == ERROR_INVALID_NAME )
  1631. {
  1632. goto Failed;
  1633. }
  1634. #endif
  1635. //
  1636. // downcase
  1637. // remove any trailing dot, except for root name
  1638. //
  1639. _strlwr( pszcopy );
  1640. length = strlen( pszcopy );
  1641. if ( length == 0 )
  1642. {
  1643. status = ERROR_INVALID_NAME;
  1644. goto Failed;
  1645. }
  1646. length--;
  1647. if ( length > 0 && pszcopy[length] == '.' )
  1648. {
  1649. pszcopy[length] = 0;
  1650. }
  1651. DNSDBG( READ, (
  1652. "Standard DNS name copy of %.*s is %s\n",
  1653. cchName,
  1654. pchName,
  1655. pszcopy ));
  1656. return( pszcopy );
  1657. }
  1658. //
  1659. // unicode name
  1660. //
  1661. else
  1662. {
  1663. WCHAR unicodeName[ DNS_MAX_NAME_BUFFER_LENGTH ];
  1664. WCHAR cannonicalName[ DNS_MAX_NAME_BUFFER_LENGTH ];
  1665. DWORD unicodeBufferLength;
  1666. //
  1667. // convert to unicode
  1668. // - buf length is in bytes
  1669. //
  1670. unicodeBufferLength = DNS_MAX_NAME_BUFFER_LENGTH * 2;
  1671. length = Dns_NameCopy(
  1672. (PSTR) unicodeName,
  1673. & unicodeBufferLength,
  1674. pchName,
  1675. cchName,
  1676. DnsCharSetUtf8,
  1677. DnsCharSetUnicode
  1678. );
  1679. if ( length == 0 )
  1680. {
  1681. DNSDBG( ANY, (
  1682. "ERROR conversion of name %.*s to unicode failed!\n",
  1683. cchName,
  1684. pchName ));
  1685. status = ERROR_INVALID_NAME;
  1686. goto Failed;
  1687. }
  1688. //
  1689. // make cannonical name
  1690. // - buf length is in unicode characters
  1691. // - output length is in unicode chars
  1692. length = Dns_MakeCanonicalNameW(
  1693. cannonicalName,
  1694. length / 2,
  1695. unicodeName,
  1696. dwFlag );
  1697. ASSERT( length != 0 );
  1698. if ( length == 0 )
  1699. {
  1700. status = ERROR_INVALID_NAME;
  1701. goto Failed;
  1702. }
  1703. //
  1704. // allocate UTF8 converted copy
  1705. // - this conversion should never fail
  1706. // - string length is unicode chars
  1707. //
  1708. pszcopy = Dns_StringCopyAllocate(
  1709. (PSTR) cannonicalName,
  1710. length,
  1711. DnsCharSetUnicode, // unicode in
  1712. DnsCharSetUtf8 // UTF8 out
  1713. );
  1714. if ( !pszcopy )
  1715. {
  1716. status = DNS_ERROR_NO_MEMORY;
  1717. goto Failed;
  1718. }
  1719. //
  1720. // validate, check against strict criteria
  1721. //
  1722. // no validation until relax criteria
  1723. //
  1724. // DCR: name validation within Dns_CreateStandardNameCopy()
  1725. // accept anything except INVALID_NAME
  1726. // flags return FQDN info
  1727. //
  1728. #if 0
  1729. status = Dns_ValidateName_UTF8( pszcopy, DnsNameDomain );
  1730. if ( status == ERROR_INVALID_NAME )
  1731. {
  1732. goto Failed;
  1733. }
  1734. #endif
  1735. return( pszcopy );
  1736. }
  1737. Failed:
  1738. FREE_HEAP( pszcopy );
  1739. SetLastError( status );
  1740. return( NULL );
  1741. }
  1742. //
  1743. // Public compare functions
  1744. //
  1745. #if DNSWIN95
  1746. //
  1747. // Running 9x - exposing for debug
  1748. //
  1749. #define WIN9X_FLAG_START_VAL (0x1111)
  1750. BOOL g_fWin9x = WIN9X_FLAG_START_VAL;
  1751. BOOL
  1752. Dns_IsWin9x(
  1753. VOID
  1754. )
  1755. /*++
  1756. Routine Description:
  1757. Are we on Win9x
  1758. Arguments:
  1759. None
  1760. Return Value:
  1761. TRUE if on Win9x
  1762. FALSE otherwise
  1763. --*/
  1764. {
  1765. //
  1766. // if not yet queried, query for system version
  1767. // Win9x (and Win3.1) have high bit set, regardless of major version
  1768. //
  1769. // for Win9x set locale to system default, as we'll use the CRT
  1770. // functions to do name comparisons (CompareStringW() is a non-implemented
  1771. // stub on Win9x
  1772. // - LC_ALL sets for all CRT functions
  1773. // - "" sets to system default code page
  1774. //
  1775. if ( g_fWin9x == WIN9X_FLAG_START_VAL )
  1776. {
  1777. if ( GetVersion() & 0x80000000 )
  1778. {
  1779. g_fWin9x = TRUE;
  1780. setlocale( LC_ALL, "" );
  1781. }
  1782. else
  1783. {
  1784. g_fWin9x = FALSE;
  1785. }
  1786. }
  1787. return( g_fWin9x );
  1788. }
  1789. #endif
  1790. BOOL
  1791. Dns_NameCompare_A(
  1792. IN PCSTR pName1,
  1793. IN PCSTR pName2
  1794. )
  1795. /*++
  1796. Routine Description:
  1797. Compare two DNS names.
  1798. Can not use stricmp() because of the possiblity of names with
  1799. trailing dots.
  1800. Arguments:
  1801. pName1 - ptr to first DNS name string (dotted format)
  1802. pName2 - ptr to second DNS name string (dotted format)
  1803. Return Value:
  1804. TRUE if names equal.
  1805. FALSE otherwise.
  1806. --*/
  1807. {
  1808. INT len1;
  1809. INT len2;
  1810. INT result;
  1811. //
  1812. // flat out match
  1813. // - this is possible with owner names and possibly other fields
  1814. //
  1815. if ( pName1 == pName2 )
  1816. {
  1817. return( TRUE );
  1818. }
  1819. if ( !pName1 || !pName2 )
  1820. {
  1821. return( FALSE );
  1822. }
  1823. //
  1824. // if lengths NOT equal, then
  1825. // they must be within one and longer string must have trailing dot
  1826. // - in this case save
  1827. //
  1828. len1 = strlen( pName1 );
  1829. len2 = strlen( pName2 );
  1830. if ( len2 != len1 )
  1831. {
  1832. if ( len2 == len1+1 )
  1833. {
  1834. if ( pName2[len1] != '.' )
  1835. {
  1836. return( FALSE );
  1837. }
  1838. // len1 is comparable length
  1839. }
  1840. else if ( len2+1 == len1 )
  1841. {
  1842. if ( pName1[len2] != '.' )
  1843. {
  1844. return( FALSE );
  1845. }
  1846. // len1 is set to comparable length
  1847. len1 = len2;
  1848. }
  1849. else
  1850. {
  1851. return( FALSE );
  1852. }
  1853. }
  1854. //
  1855. // compare only comparable length of string
  1856. //
  1857. result = CompareStringA(
  1858. //LOCALE_SYSTEM_DEFAULT,
  1859. DNS_CANONICAL_LOCALE,
  1860. DNS_CANONICAL_COMPARE_FLAGS,
  1861. pName1,
  1862. len1,
  1863. pName2,
  1864. len1 );
  1865. if ( result == CSTR_EQUAL )
  1866. {
  1867. return( TRUE );
  1868. }
  1869. // not equal or error
  1870. return( FALSE );
  1871. }
  1872. BOOL
  1873. Dns_NameCompare_W(
  1874. IN PCWSTR pName1,
  1875. IN PCWSTR pName2
  1876. )
  1877. /*++
  1878. Routine Description:
  1879. Compare two (Wide) DNS names.
  1880. Note: this is unicode aware, it assumes names in WCHAR string
  1881. format. Can not use stricmp() because of the possiblity of names
  1882. with trailing dots.
  1883. Arguments:
  1884. pName1 - ptr to first DNS name string (dotted format)
  1885. pName2 - ptr to second DNS name string (dotted format)
  1886. Return Value:
  1887. TRUE if names equal.
  1888. FALSE otherwise.
  1889. --*/
  1890. {
  1891. INT len1;
  1892. INT len2;
  1893. INT result;
  1894. //
  1895. // flat out match
  1896. // - this is possible with owner names and possibly other fields
  1897. //
  1898. if ( pName1 == pName2 )
  1899. {
  1900. return( TRUE );
  1901. }
  1902. if ( !pName1 || !pName2 )
  1903. {
  1904. return( FALSE );
  1905. }
  1906. //
  1907. // if lengths NOT equal, then
  1908. // they must be within one and longer string must have trailing dot
  1909. // - in this case save
  1910. //
  1911. len1 = wcslen( pName1 );
  1912. len2 = wcslen( pName2 );
  1913. if ( len2 != len1 )
  1914. {
  1915. if ( len2 == len1+1 )
  1916. {
  1917. if ( pName2[len1] != L'.' )
  1918. {
  1919. return( FALSE );
  1920. }
  1921. // len1 is comparable length
  1922. }
  1923. else if ( len2+1 == len1 )
  1924. {
  1925. if ( pName1[len2] != L'.' )
  1926. {
  1927. return( FALSE );
  1928. }
  1929. // len1 is set to comparable length
  1930. len1 = len2;
  1931. }
  1932. else
  1933. {
  1934. return( FALSE );
  1935. }
  1936. }
  1937. //
  1938. // compare only comparable length of string
  1939. //
  1940. #if DNSWIN95
  1941. //
  1942. // Win9x does not currently support CompareStringW()
  1943. //
  1944. if ( Dns_IsWin9x() )
  1945. {
  1946. return( !_wcsnicmp( pName1, pName2, len1 ) );
  1947. }
  1948. #endif
  1949. result = CompareStringW(
  1950. //LOCALE_SYSTEM_DEFAULT,
  1951. DNS_CANONICAL_LOCALE,
  1952. DNS_CANONICAL_COMPARE_FLAGS,
  1953. pName1,
  1954. len1,
  1955. pName2,
  1956. len1 );
  1957. if ( result == CSTR_EQUAL )
  1958. {
  1959. return( TRUE );
  1960. }
  1961. // not equal or error
  1962. return( FALSE );
  1963. }
  1964. BOOL
  1965. Dns_NameCompare_UTF8(
  1966. IN PCSTR pName1,
  1967. IN PCSTR pName2
  1968. )
  1969. /*++
  1970. Routine Description:
  1971. Compare two DNS names.
  1972. Arguments:
  1973. pName1 - ptr to first DNS name string (dotted format)
  1974. pName2 - ptr to second DNS name string (dotted format)
  1975. Return Value:
  1976. TRUE if names equal.
  1977. FALSE otherwise.
  1978. --*/
  1979. {
  1980. WCHAR nameBuffer1[ DNS_MAX_NAME_BUFFER_LENGTH ];
  1981. WCHAR nameBuffer2[ DNS_MAX_NAME_BUFFER_LENGTH ];
  1982. DWORD bufLen;
  1983. //
  1984. // flat out match
  1985. // - this is possible with owner names and possibly other fields
  1986. //
  1987. if ( pName1 == pName2 )
  1988. {
  1989. return( TRUE );
  1990. }
  1991. if ( !pName1 || !pName2 )
  1992. {
  1993. return( FALSE );
  1994. }
  1995. //
  1996. // if strings pure ASCII, then use ANSI version
  1997. //
  1998. if ( Dns_IsStringAscii( (PCHAR)pName1 ) &&
  1999. Dns_IsStringAscii( (PCHAR)pName2 ) )
  2000. {
  2001. return Dns_NameCompare_A( pName1, pName2 );
  2002. }
  2003. //
  2004. // otherwise must take names back to unicode to compare
  2005. //
  2006. bufLen = DNS_MAX_NAME_LENGTH;
  2007. if ( ! Dns_NameCopy(
  2008. (PCHAR) nameBuffer1,
  2009. & bufLen,
  2010. (PCHAR) pName1,
  2011. 0, // length unknown
  2012. DnsCharSetUtf8,
  2013. DnsCharSetUnicode
  2014. ) )
  2015. {
  2016. return( FALSE );
  2017. }
  2018. bufLen = DNS_MAX_NAME_LENGTH;
  2019. if ( ! Dns_NameCopy(
  2020. (PCHAR) nameBuffer2,
  2021. & bufLen,
  2022. (PCHAR) pName2,
  2023. 0, // length unknown
  2024. DnsCharSetUtf8,
  2025. DnsCharSetUnicode
  2026. ) )
  2027. {
  2028. return( FALSE );
  2029. }
  2030. return Dns_NameCompare_W( nameBuffer1, nameBuffer2 );
  2031. }
  2032. BOOL
  2033. Dns_NameComparePrivate(
  2034. IN PCSTR pName1,
  2035. IN PCSTR pName2,
  2036. IN DNS_CHARSET CharSet
  2037. )
  2038. /*++
  2039. Routine Description:
  2040. Compare two DNS names.
  2041. This is simply helpful utility to avoid coding the wide\narrow
  2042. test in the code in a hundred different places.
  2043. Arguments:
  2044. pName1 - ptr to first DNS name string (dotted format)
  2045. pName2 - ptr to second DNS name string (dotted format)
  2046. Return Value:
  2047. TRUE if names equal.
  2048. FALSE otherwise.
  2049. --*/
  2050. {
  2051. if ( CharSet == DnsCharSetUnicode )
  2052. {
  2053. return Dns_NameCompare_W(
  2054. (PCWSTR) pName1,
  2055. (PCWSTR) pName2 );
  2056. }
  2057. else if ( CharSet == DnsCharSetAnsi )
  2058. {
  2059. return Dns_NameCompare_A(
  2060. pName1,
  2061. pName2 );
  2062. }
  2063. else
  2064. {
  2065. return Dns_NameCompare_UTF8(
  2066. pName1,
  2067. pName2 );
  2068. }
  2069. }
  2070. //
  2071. // Advanced name comparison
  2072. // Includes hierarchial name relationship.
  2073. //
  2074. DNS_NAME_COMPARE_STATUS
  2075. Dns_NameCompareEx(
  2076. IN PCSTR pszNameLeft,
  2077. IN PCSTR pszNameRight,
  2078. IN DWORD dwReserved,
  2079. IN DNS_CHARSET CharSet
  2080. )
  2081. /*++
  2082. Routine Description:
  2083. Advanced compare of DNS names, including hierarchial relationship.
  2084. Arguments:
  2085. pszNameLeft -- left name
  2086. pszNameRight -- right name
  2087. dwReserved -- reserved for future use (type of comparison)
  2088. CharSet -- char set of names
  2089. Return Value:
  2090. DnsNameCompareInvalid -- one of the names was invalid
  2091. DnsNameCompareEqual -- names are equal
  2092. DnsNameCompareLeftParent -- left is ancestor of right name
  2093. DnsNameCompareRightParent -- right is ancestor of left name
  2094. DnsNameCompareNotEqual -- name not equal, no hierarchial relationship
  2095. --*/
  2096. {
  2097. DNS_NAME_COMPARE_STATUS result;
  2098. DNS_STATUS status;
  2099. INT lengthLeft;
  2100. INT lengthRight;
  2101. INT lengthDiff;
  2102. INT compareResult;
  2103. DWORD bufLength;
  2104. WCHAR nameLeft[ DNS_MAX_NAME_BUFFER_LENGTH ];
  2105. WCHAR nameRight[ DNS_MAX_NAME_BUFFER_LENGTH ];
  2106. DNSDBG( TRACE, (
  2107. "Dns_NameCompareEx( %s%S, %s%S )\n",
  2108. (CharSet==DnsCharSetUnicode) ? "" : pszNameLeft,
  2109. (CharSet==DnsCharSetUnicode) ? pszNameLeft : "",
  2110. (CharSet==DnsCharSetUnicode) ? "" : pszNameRight,
  2111. (CharSet==DnsCharSetUnicode) ? pszNameRight : ""
  2112. ));
  2113. //
  2114. // implementation note
  2115. // there's a lot of inefficiency here, because there are
  2116. // two different character sets required for
  2117. // validation -- UTF8 to check packet limits
  2118. // downcasing\comparison -- unicode for case insensitivity
  2119. //
  2120. // obviously there are much more efficient paths through this
  2121. // morass for particular special cases (ASCII names: downcase
  2122. // in ANSI, validate, compare); but since this is not perf
  2123. // path code we'll take the approach
  2124. // - convert to unicode
  2125. // - validate (which will convert at copy to UTF8)
  2126. // - downcase unicode
  2127. // - compare unicode
  2128. //
  2129. //
  2130. // validate args
  2131. //
  2132. if ( ! pszNameLeft || ! pszNameRight )
  2133. {
  2134. goto Invalid;
  2135. }
  2136. //
  2137. // copy convert to unicode
  2138. // - downcasing and compare will be done in unicode
  2139. // - return lengths are bytes converted, convert to string lengths
  2140. // - Dns_NameCopy() returns zero for invalid convert
  2141. //
  2142. bufLength = DNS_MAX_NAME_BUFFER_LENGTH * 2;
  2143. lengthLeft = (INT) Dns_NameCopy(
  2144. (PCHAR) nameLeft,
  2145. & bufLength,
  2146. (LPSTR) pszNameLeft,
  2147. 0, // string NULL terminated
  2148. CharSet, // char set in
  2149. DnsCharSetUnicode // unicode out
  2150. );
  2151. if ( lengthLeft == 0 )
  2152. {
  2153. goto Invalid;
  2154. }
  2155. lengthLeft = (lengthLeft/2) - 1;
  2156. ASSERT( lengthLeft >= 0 );
  2157. bufLength = DNS_MAX_NAME_BUFFER_LENGTH * 2;
  2158. lengthRight = (INT) Dns_NameCopy(
  2159. (PCHAR) nameRight,
  2160. & bufLength,
  2161. (LPSTR) pszNameRight,
  2162. 0, // string NULL terminated
  2163. CharSet, // char set in
  2164. DnsCharSetUnicode // unicode out
  2165. );
  2166. if ( lengthRight == 0 )
  2167. {
  2168. goto Invalid;
  2169. }
  2170. lengthRight = (lengthRight/2) - 1;
  2171. ASSERT( lengthRight >= 0 );
  2172. //
  2173. // cannonicalize names
  2174. //
  2175. Dns_MakeCanonicalNameInPlaceW( nameLeft, lengthLeft );
  2176. Dns_MakeCanonicalNameInPlaceW( nameRight, lengthRight );
  2177. //
  2178. // validate names
  2179. // - must screen empty string or we can fault below
  2180. //
  2181. status = Dns_ValidateName_W( nameLeft, DnsNameDomain );
  2182. if ( ERROR_SUCCESS != status &&
  2183. DNS_ERROR_NON_RFC_NAME != status )
  2184. {
  2185. goto Invalid;
  2186. }
  2187. status = Dns_ValidateName_W( nameRight, DnsNameDomain );
  2188. if ( ERROR_SUCCESS != status &&
  2189. DNS_ERROR_NON_RFC_NAME != status )
  2190. {
  2191. goto Invalid;
  2192. }
  2193. //
  2194. // add trailing dots
  2195. //
  2196. // we need to either add or remove trailing dots to make comparisons
  2197. // the advantage of adding them is that then, the root name does
  2198. // not require any special casing -- the root is the ancestor of
  2199. // every name
  2200. //
  2201. if ( nameLeft[ lengthLeft-1 ] != (WORD)'.')
  2202. {
  2203. nameLeft[ lengthLeft++ ] = (WORD) '.';
  2204. nameLeft[ lengthLeft ] = (WORD) 0;
  2205. }
  2206. if ( nameRight[ lengthRight-1 ] != (WORD)'.')
  2207. {
  2208. nameRight[ lengthRight++ ] = (WORD) '.';
  2209. nameRight[ lengthRight ] = (WORD) 0;
  2210. }
  2211. //
  2212. // compare equal length strings
  2213. //
  2214. result = DnsNameCompareNotEqual;
  2215. lengthDiff = (INT)lengthLeft - (INT)lengthRight;
  2216. if ( lengthLeft == lengthRight )
  2217. {
  2218. compareResult = wcscmp( nameLeft, nameRight );
  2219. if ( compareResult == 0 )
  2220. {
  2221. result = DnsNameCompareEqual;
  2222. }
  2223. goto Done;
  2224. }
  2225. //
  2226. // strings not equal
  2227. // - compare smaller string of length X
  2228. // to last X characters of larger string
  2229. // - also must make sure starting at label boundary
  2230. //
  2231. // note: strstr() is NOT useful for this work, because it
  2232. // compares useless for this work because it is finding the
  2233. // first match -- a little thought would indicate that this
  2234. // will fail in several obvious cases
  2235. //
  2236. // right string longer
  2237. // - need to sign change diff to make it offset in right string
  2238. else if ( lengthDiff < 0 )
  2239. {
  2240. lengthDiff = -lengthDiff;
  2241. if ( nameRight[ lengthDiff-1 ] != L'.' )
  2242. {
  2243. goto Done;
  2244. }
  2245. compareResult = wcscmp( nameLeft, nameRight+lengthDiff );
  2246. if ( compareResult == 0 )
  2247. {
  2248. result = DnsNameCompareLeftParent;
  2249. }
  2250. goto Done;
  2251. }
  2252. // left string longer
  2253. // - lengthDiff is offset into left string to start compare
  2254. else
  2255. {
  2256. if ( nameLeft[ lengthDiff-1 ] != L'.' )
  2257. {
  2258. goto Done;
  2259. }
  2260. compareResult = wcscmp( nameLeft+lengthDiff, nameRight );
  2261. if ( compareResult == 0 )
  2262. {
  2263. result = DnsNameCompareRightParent;
  2264. }
  2265. goto Done;
  2266. }
  2267. Done:
  2268. DNSDBG( TRACE, (
  2269. "Leave DnsNameCompareEx() result = %d\n",
  2270. result ));
  2271. return( result );
  2272. Invalid:
  2273. DNSDBG( ANY, (
  2274. "ERROR: Invalid name to Dns_NameCompareEx()\n" ));
  2275. return( DnsNameCompareInvalid );
  2276. }
  2277. //
  2278. // Random name utilities
  2279. //
  2280. PCHAR
  2281. _fastcall
  2282. Dns_GetDomainName(
  2283. IN PCSTR pszName
  2284. )
  2285. /*++
  2286. Routine Description:
  2287. Get domain name of DNS name.
  2288. Note, this assumes name already in UTF-8
  2289. Arguments:
  2290. pszName - standard dotted DNS name
  2291. Return Value:
  2292. Ptr to domain name of pszName.
  2293. NULL if pszName is in root domain.
  2294. --*/
  2295. {
  2296. CHAR ch;
  2297. //
  2298. // find next "." in name, then return ptr to next character
  2299. //
  2300. while( ch = *pszName++ )
  2301. {
  2302. if ( ch == '.' )
  2303. {
  2304. if ( *pszName )
  2305. {
  2306. return( (PCHAR)pszName );
  2307. }
  2308. return( NULL );
  2309. }
  2310. }
  2311. return( NULL );
  2312. }
  2313. PWSTR
  2314. _fastcall
  2315. Dns_GetDomainName_W(
  2316. IN PCWSTR pwsName
  2317. )
  2318. {
  2319. PWSTR pdomain;
  2320. //
  2321. // find next "." in name, then return ptr to next character
  2322. //
  2323. pdomain = wcschr( pwsName, L'.' );
  2324. if ( pdomain && *(++pdomain) )
  2325. {
  2326. return( pdomain );
  2327. }
  2328. return NULL;
  2329. }
  2330. PCHAR
  2331. _fastcall
  2332. Dns_GetTldForName(
  2333. IN PCSTR pszName
  2334. )
  2335. /*++
  2336. Routine Description:
  2337. Get domain name of DNS name.
  2338. Note, this assumes name already in UTF-8
  2339. Arguments:
  2340. pszName - standard dotted DNS name
  2341. Return Value:
  2342. Ptr to domain name of pszName.
  2343. NULL if pszName is in root domain.
  2344. --*/
  2345. {
  2346. PSTR pdomain = (PSTR) pszName;
  2347. PSTR ptld = NULL;
  2348. //
  2349. // find last domain name in name
  2350. //
  2351. while ( pdomain = Dns_GetDomainName(pdomain) )
  2352. {
  2353. ptld = (PSTR) pdomain;
  2354. }
  2355. return ptld;
  2356. }
  2357. BOOL
  2358. _fastcall
  2359. Dns_IsNameShort(
  2360. IN PCSTR pszName
  2361. )
  2362. /*++
  2363. Routine Description:
  2364. Determine if a name is a multi-label DNS name.
  2365. This is a test of whether name at least one non-terminal dot.
  2366. Arguments:
  2367. pszName - standard dotted DNS name
  2368. Return Value:
  2369. TRUE if multiple labels.
  2370. FALSE otherwise.
  2371. --*/
  2372. {
  2373. INT nameLen;
  2374. // trailing domain? -- done
  2375. if ( Dns_GetDomainName( pszName ) )
  2376. {
  2377. return FALSE;
  2378. }
  2379. // otherwise test for valid label
  2380. nameLen = strlen( pszName );
  2381. if ( nameLen <= DNS_MAX_LABEL_LENGTH )
  2382. {
  2383. return TRUE;
  2384. }
  2385. nameLen--;
  2386. if ( nameLen == DNS_MAX_LABEL_LENGTH &&
  2387. pszName[nameLen] == '.')
  2388. {
  2389. return TRUE;
  2390. }
  2391. return FALSE;
  2392. }
  2393. BOOL
  2394. _fastcall
  2395. Dns_IsNameFQDN(
  2396. IN PCSTR pszName
  2397. )
  2398. /*++
  2399. Routine Description:
  2400. Determine if a name is a fully qualified DNS name (FQDN).
  2401. This is a test of whether name has trailing dot.
  2402. Arguments:
  2403. pszName - standard dotted DNS name
  2404. Return Value:
  2405. TRUE if FQDN.
  2406. FALSE otherwise.
  2407. --*/
  2408. {
  2409. DWORD nameLen = strlen( pszName );
  2410. if ( nameLen == 0 )
  2411. {
  2412. return FALSE;
  2413. }
  2414. if ( pszName[nameLen - 1] == '.' )
  2415. {
  2416. return TRUE;
  2417. }
  2418. else
  2419. {
  2420. return FALSE;
  2421. }
  2422. }
  2423. DWORD
  2424. _fastcall
  2425. Dns_GetNameAttributes(
  2426. IN PCSTR pszName
  2427. )
  2428. /*++
  2429. Routine Description:
  2430. Determine the attributes that a name has.
  2431. Note, this assumes name already in UTF-8
  2432. Arguments:
  2433. pszName - standard dotted DNS name
  2434. Return Value:
  2435. DWORD with possible flags:
  2436. DNS_NAME_IS_FQDN
  2437. DNS_NAME_SINGLE_LABEL
  2438. DNS_NAME_MULTI_LABEL
  2439. --*/
  2440. {
  2441. DWORD dwAttributes = DNS_NAME_UNKNOWN;
  2442. if ( Dns_IsNameFQDN( pszName ) )
  2443. {
  2444. dwAttributes = DNS_NAME_IS_FQDN;
  2445. }
  2446. if ( Dns_IsNameShort( pszName ) )
  2447. {
  2448. dwAttributes |= DNS_NAME_SINGLE_LABEL;
  2449. }
  2450. else
  2451. {
  2452. dwAttributes |= DNS_NAME_MULTI_LABEL;
  2453. }
  2454. return dwAttributes;
  2455. }
  2456. DNS_STATUS
  2457. Dns_ValidateAndCategorizeDnsNameEx(
  2458. IN PCHAR pchName,
  2459. IN DWORD cchNameLength,
  2460. OUT PDWORD pLabelCount
  2461. )
  2462. /*++
  2463. Routine Description:
  2464. Determine type of name.
  2465. Three types:
  2466. 1) FQDN -- dot on end, signifies full DNS name, never appended
  2467. 2) dotted -- dot in name; probably FQDN, but may need to be appended
  2468. (as in file store)
  2469. 3) single part -- single part name (not FQDN), always appended with zone
  2470. or default domain name
  2471. Arguments:
  2472. pchName -- ptr to name
  2473. cchNameLength -- name length
  2474. pLabelCount -- address to receive label count
  2475. Return Value:
  2476. DNS_STATUS_FQDN
  2477. DNS_STATUS_DOTTED_NAME
  2478. DNS_STATUS_SINGLE_PART_NAME
  2479. DNS_ERROR_INVALID_NAME on non-DNS name
  2480. --*/
  2481. {
  2482. register PCHAR pch;
  2483. register CHAR ch;
  2484. PCHAR pchstop;
  2485. BOOL fdotted = FALSE;
  2486. DWORD labelCount = 0;
  2487. DWORD charCount = 0;
  2488. DNS_STATUS status = DNS_STATUS_SINGLE_PART_NAME;
  2489. //
  2490. // name length for string
  2491. //
  2492. if ( cchNameLength == 0 )
  2493. {
  2494. cchNameLength = strlen( pchName );
  2495. }
  2496. if ( cchNameLength > DNS_MAX_NAME_LENGTH ||
  2497. cchNameLength == 0 )
  2498. {
  2499. goto InvalidName;
  2500. }
  2501. //
  2502. // run through name
  2503. //
  2504. pch = pchName;
  2505. pchstop = pch + cchNameLength;
  2506. while ( pch < pchstop )
  2507. {
  2508. ch = *pch++;
  2509. if ( ch == '.' )
  2510. {
  2511. if ( charCount > DNS_MAX_LABEL_LENGTH )
  2512. {
  2513. goto InvalidName;
  2514. }
  2515. if ( charCount > 0 )
  2516. {
  2517. labelCount++;
  2518. charCount = 0;
  2519. status = DNS_STATUS_DOTTED_NAME;
  2520. continue;
  2521. }
  2522. else
  2523. {
  2524. // only valid zero label name is "."
  2525. if ( pch == pchstop &&
  2526. pch-1 == pchName )
  2527. {
  2528. break;
  2529. }
  2530. goto InvalidName;
  2531. }
  2532. }
  2533. else if ( ch == 0 )
  2534. {
  2535. DNS_ASSERT( FALSE );
  2536. break;
  2537. }
  2538. // regular character
  2539. charCount++;
  2540. }
  2541. //
  2542. // handle last label
  2543. // - if count, then boost label count
  2544. // - if zero and previously had dot, then string
  2545. // ended in dot and is FQDN
  2546. //
  2547. if ( charCount > 0 )
  2548. {
  2549. if ( charCount > DNS_MAX_LABEL_LENGTH )
  2550. {
  2551. goto InvalidName;
  2552. }
  2553. labelCount++;
  2554. }
  2555. else if ( status == DNS_STATUS_DOTTED_NAME )
  2556. {
  2557. status = DNS_STATUS_FQDN;
  2558. }
  2559. // return label count
  2560. if ( pLabelCount )
  2561. {
  2562. *pLabelCount = labelCount;
  2563. }
  2564. DNSDBG( TRACE, (
  2565. "Leave Dns_ValidateAndCategorizeNameEx()\n"
  2566. "\tstatus = %d\n"
  2567. "\tlabel count = %d\n",
  2568. status,
  2569. labelCount ));
  2570. return( status );
  2571. InvalidName:
  2572. if ( pLabelCount )
  2573. {
  2574. *pLabelCount = 0;
  2575. }
  2576. DNSDBG( TRACE, (
  2577. "Leave Dns_ValidateAndCategorizeNameEx()\n"
  2578. "\tstatus = ERROR_INVALID_NAME\n" ));
  2579. return( DNS_ERROR_INVALID_NAME );
  2580. }
  2581. DWORD
  2582. Dns_NameLabelCount(
  2583. IN PCSTR pszName
  2584. )
  2585. /*++
  2586. Routine Description:
  2587. Return name label count.
  2588. Arguments:
  2589. pszName -- ptr to name
  2590. Return Value:
  2591. Label count if valid name.
  2592. Zero on root name or error.
  2593. --*/
  2594. {
  2595. DWORD labelCount = 0;
  2596. DNS_STATUS status;
  2597. //
  2598. // call real routine
  2599. //
  2600. status = Dns_ValidateAndCategorizeDnsNameEx(
  2601. (PCHAR) pszName,
  2602. 0,
  2603. & labelCount );
  2604. if ( status == DNS_ERROR_INVALID_NAME )
  2605. {
  2606. labelCount = 0;
  2607. }
  2608. return( labelCount );
  2609. }
  2610. PSTR
  2611. Dns_NameAppend_A(
  2612. OUT PCHAR pNameBuffer,
  2613. IN DWORD BufferLength,
  2614. IN PSTR pszName,
  2615. IN PSTR pszDomain
  2616. )
  2617. /*++
  2618. Routine Description:
  2619. Write appended name to buffer (ANSI or UTF8).
  2620. Arguments:
  2621. pNameBuffer -- name buffer to write to
  2622. BufferLength -- buffer length
  2623. pszName -- name to append domain to
  2624. pszDomain -- domain name
  2625. Return Value:
  2626. Ptr to buffer with appended domain name.
  2627. NULL on invalid (too long) name.
  2628. --*/
  2629. {
  2630. DWORD length1;
  2631. DWORD length2;
  2632. DWORD totalLength;
  2633. DNSDBG( TRACE, ( "Dns_NameAppend_A()\n" ));
  2634. //
  2635. // appending NULL domain?
  2636. //
  2637. if ( !pszDomain )
  2638. {
  2639. totalLength = strlen( pszName );
  2640. if ( totalLength >= BufferLength )
  2641. {
  2642. return( NULL );
  2643. }
  2644. RtlCopyMemory(
  2645. pNameBuffer,
  2646. pszName,
  2647. totalLength );
  2648. pNameBuffer[ totalLength ] = 0;
  2649. return( pNameBuffer );
  2650. }
  2651. //
  2652. // get name lengths -- make sure we fit length
  2653. //
  2654. length1 = strlen( pszName );
  2655. if ( pszName[ length1-1] == '.' )
  2656. {
  2657. length1--;
  2658. }
  2659. length2 = strlen( pszDomain );
  2660. totalLength = length1 + length2 + 1;
  2661. if ( totalLength >= BufferLength )
  2662. {
  2663. return( NULL );
  2664. }
  2665. //
  2666. // copy to buffer
  2667. //
  2668. RtlCopyMemory(
  2669. pNameBuffer,
  2670. pszName,
  2671. length1 );
  2672. pNameBuffer[ length1 ] = '.';
  2673. RtlCopyMemory(
  2674. & pNameBuffer[length1+1],
  2675. pszDomain,
  2676. length2 );
  2677. pNameBuffer[ totalLength ] = 0;
  2678. return( pNameBuffer );
  2679. }
  2680. PWSTR
  2681. Dns_NameAppend_W(
  2682. OUT PWCHAR pNameBuffer,
  2683. IN DWORD BufferLength,
  2684. IN PWSTR pwsName,
  2685. IN PWSTR pwsDomain
  2686. )
  2687. /*++
  2688. Routine Description:
  2689. Write appended name to buffer (unicode).
  2690. Arguments:
  2691. pNameBuffer -- name buffer to write to
  2692. BufferLength -- buffer length in WCHAR
  2693. pwsName -- name to append domain to
  2694. pwsDomain -- domain name
  2695. Return Value:
  2696. Ptr to buffer with appended domain name.
  2697. NULL on invalid (too long) name.
  2698. --*/
  2699. {
  2700. DWORD length1;
  2701. DWORD length2;
  2702. DWORD totalLength;
  2703. DNSDBG( TRACE, ( "Dns_NameAppend_W()\n" ));
  2704. //
  2705. // appending NULL domain?
  2706. //
  2707. if ( !pwsDomain )
  2708. {
  2709. totalLength = wcslen( pwsName );
  2710. if ( totalLength >= BufferLength )
  2711. {
  2712. return( NULL );
  2713. }
  2714. RtlCopyMemory(
  2715. pNameBuffer,
  2716. pwsName,
  2717. totalLength*sizeof(WCHAR) );
  2718. pNameBuffer[ totalLength ] = 0;
  2719. return( pNameBuffer );
  2720. }
  2721. //
  2722. // get name lengths -- make sure we fit length
  2723. //
  2724. length1 = wcslen( pwsName );
  2725. if ( pwsName[ length1-1] == '.' )
  2726. {
  2727. length1--;
  2728. }
  2729. length2 = wcslen( pwsDomain );
  2730. totalLength = length1 + length2 + 1;
  2731. if ( totalLength >= BufferLength )
  2732. {
  2733. return( NULL );
  2734. }
  2735. //
  2736. // copy to buffer
  2737. //
  2738. RtlCopyMemory(
  2739. pNameBuffer,
  2740. pwsName,
  2741. length1*sizeof(WCHAR) );
  2742. pNameBuffer[ length1 ] = '.';
  2743. RtlCopyMemory(
  2744. & pNameBuffer[length1+1],
  2745. pwsDomain,
  2746. length2*sizeof(WCHAR) );
  2747. pNameBuffer[ totalLength ] = 0;
  2748. return( pNameBuffer );
  2749. }
  2750. PSTR
  2751. Dns_SplitHostFromDomainName_A(
  2752. IN PSTR pszName
  2753. )
  2754. /*++
  2755. Routine Description:
  2756. Split host name from domain name.
  2757. Combines getting domain name and splitting
  2758. off host name.
  2759. Arguments:
  2760. pszName - standard dotted DNS name
  2761. Return Value:
  2762. Ptr to domain name of pszName.
  2763. NULL if pszName is in root domain.
  2764. --*/
  2765. {
  2766. PSTR pnameDomain;
  2767. //
  2768. // get domain name
  2769. // if exists, NULL terminate host name part
  2770. //
  2771. pnameDomain = Dns_GetDomainName( (PCSTR)pszName );
  2772. if ( pnameDomain )
  2773. {
  2774. if ( pnameDomain <= pszName )
  2775. {
  2776. return NULL;
  2777. }
  2778. *(pnameDomain-1) = 0;
  2779. }
  2780. return pnameDomain;
  2781. }
  2782. BOOL
  2783. _fastcall
  2784. Dns_IsNameNumeric_A(
  2785. IN PCSTR pszName
  2786. )
  2787. /*++
  2788. Routine Description:
  2789. Determine if numeric name.
  2790. Note, this assumes name already in UTF-8
  2791. Arguments:
  2792. pszName - standard dotted DNS name
  2793. Return Value:
  2794. TRUE if all numeric.
  2795. FALSE otherwise.
  2796. --*/
  2797. {
  2798. CHAR ch;
  2799. BOOL fnumeric = FALSE;
  2800. //
  2801. // check if everything in name is numeric
  2802. // - dotted names can be numeric
  2803. // - "." not numeric
  2804. //
  2805. while( ch = *pszName++ )
  2806. {
  2807. if ( ch >= '0' && ch <= '9' )
  2808. {
  2809. fnumeric = TRUE;
  2810. continue;
  2811. }
  2812. else if ( ch == '.' )
  2813. {
  2814. continue;
  2815. }
  2816. return FALSE;
  2817. }
  2818. return fnumeric;
  2819. }
  2820. //
  2821. // Wrappers for most common name conversions
  2822. //
  2823. DWORD
  2824. Dns_NameCopyWireToUnicode(
  2825. OUT PWCHAR pBufferUnicode,
  2826. IN PCSTR pszNameWire
  2827. )
  2828. /*++
  2829. Routine Description:
  2830. Convert name from wire to unicode.
  2831. Simple wrapper on Dns_NameCopy for common operation:
  2832. - unicode to wire
  2833. - NULL terminated name
  2834. - standard length buffer
  2835. Arguments:
  2836. pBufferUnicode -- unicode result buffer
  2837. pszNameWire - name in wire format
  2838. Return Value:
  2839. Count of bytes copied if successful.
  2840. Zero on error -- name too long or conversion error.
  2841. --*/
  2842. {
  2843. DWORD bufferLength = DNS_MAX_NAME_BUFFER_LENGTH_UNICODE;
  2844. //
  2845. // copy name back to unicode
  2846. //
  2847. return Dns_NameCopy(
  2848. (PCHAR) pBufferUnicode,
  2849. & bufferLength,
  2850. (PCHAR) pszNameWire,
  2851. 0, // null terminated
  2852. DnsCharSetWire,
  2853. DnsCharSetUnicode );
  2854. }
  2855. DWORD
  2856. Dns_NameCopyUnicodeToWire(
  2857. OUT PCHAR pBufferWire,
  2858. IN PCWSTR pwsNameUnicode
  2859. )
  2860. /*++
  2861. Routine Description:
  2862. Convert name from unicode to wire.
  2863. Simple wrapper on Dns_NameCopy for common operation:
  2864. - unicode to wire
  2865. - NULL terminated name
  2866. - standard length buffer
  2867. Arguments:
  2868. pBufferWire -- wire format result buffer
  2869. pwsNameUnicode - name in unicode
  2870. Return Value:
  2871. Count of bytes copied if successful.
  2872. Zero on error -- name too long or conversion error.
  2873. --*/
  2874. {
  2875. DWORD bufferLength = DNS_MAX_NAME_BUFFER_LENGTH;
  2876. //
  2877. // copy name to wire format
  2878. //
  2879. return Dns_NameCopy(
  2880. pBufferWire,
  2881. & bufferLength,
  2882. (PCHAR) pwsNameUnicode,
  2883. 0, // null terminated
  2884. DnsCharSetUnicode,
  2885. DnsCharSetWire );
  2886. }
  2887. DWORD
  2888. Dns_NameCopyStandard_W(
  2889. OUT PWCHAR pBuffer,
  2890. IN PCWSTR pwsNameUnicode
  2891. )
  2892. /*++
  2893. Routine Description:
  2894. Copy unicode name.
  2895. Simple wrapper on Dns_NameCopy for common operation:
  2896. - unicode to unicode
  2897. - NULL terminated name
  2898. - standard length buffer
  2899. Arguments:
  2900. pBuffer -- wire format result buffer
  2901. pwsNameUnicode - name in unicode
  2902. Return Value:
  2903. Count of bytes copied if successful.
  2904. Zero on error -- name too long or conversion error.
  2905. --*/
  2906. {
  2907. DWORD bufferLength = DNS_MAX_NAME_BUFFER_LENGTH_UNICODE;
  2908. //
  2909. // copy name
  2910. //
  2911. return Dns_NameCopy(
  2912. (PCHAR) pBuffer,
  2913. & bufferLength,
  2914. (PCHAR) pwsNameUnicode,
  2915. 0, // null terminated
  2916. DnsCharSetUnicode,
  2917. DnsCharSetUnicode );
  2918. }
  2919. DWORD
  2920. Dns_NameCopyStandard_A(
  2921. OUT PCHAR pBuffer,
  2922. IN PCSTR pszName
  2923. )
  2924. /*++
  2925. Routine Description:
  2926. Convert name from unicode to wire.
  2927. Simple wrapper on Dns_NameCopy for common operation:
  2928. - unicode to wire
  2929. - NULL terminated name
  2930. - standard length buffer
  2931. Arguments:
  2932. pBuffer -- wire format result buffer
  2933. pszName - name in narrow char set
  2934. Return Value:
  2935. Count of bytes copied if successful.
  2936. Zero on error -- name too long or conversion error.
  2937. --*/
  2938. {
  2939. DWORD bufferLength = DNS_MAX_NAME_BUFFER_LENGTH;
  2940. //
  2941. // copy name
  2942. //
  2943. return Dns_NameCopy(
  2944. pBuffer,
  2945. & bufferLength,
  2946. (PCHAR) pszName,
  2947. 0, // null terminated
  2948. DnsCharSetUtf8,
  2949. DnsCharSetUtf8 );
  2950. }
  2951. //
  2952. // End name.c
  2953. //