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.

1212 lines
28 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name :
  4. addr821.cxx
  5. Abstract:
  6. Set of functions to parse RFC 821 addresses.
  7. Author:
  8. Keith Lau (KeithLau) 2/17/98
  9. Project:
  10. SMTP Server DLL
  11. Functions Exported:
  12. Revision History:
  13. --*/
  14. /************************************************************
  15. * Include Headers
  16. ************************************************************/
  17. #include <windows.h>
  18. #include <dbgtrace.h>
  19. #include <addr821.hxx>
  20. #define MAX_EMAIL_NAME 64
  21. #define MAX_DOMAIN_NAME 250
  22. #define MAX_INTERNET_NAME (MAX_EMAIL_NAME + MAX_DOMAIN_NAME + 2)
  23. // Quick and dirty string validation
  24. static BOOL pValidateStringPtr(LPSTR lpwszString, DWORD dwMaxLength)
  25. {
  26. if (IsBadStringPtr((LPCTSTR)lpwszString, dwMaxLength))
  27. return(FALSE);
  28. while (dwMaxLength--)
  29. if (*lpwszString++ == 0)
  30. return(TRUE);
  31. return(FALSE);
  32. }
  33. // ========================================================================
  34. //
  35. // Validation Parser stuff created by KeithLau on 2/17/98
  36. //
  37. static char acOpen[] = "\"[<(";
  38. static char acClose[] = "\"]>)";
  39. //
  40. // NOTE: RFC 821 and RFC 822 versions of this function are different!!
  41. //
  42. // This function finds braces pairs in a given string, and returns
  43. // pointers to the start and end of the first occurence of a
  44. // [nested] pair of braces. The starting and ending character
  45. // may be specified by the caller (starting and ending chars
  46. // must be unique).
  47. #define MAX_STATE_STACK_DEPTH 64
  48. #define OPEN_DELIMITER 0x1
  49. #define CLOSE_DELIMITER 0x2
  50. #define OPEN_AND_CLOSE_DELIMITER (OPEN_DELIMITER | CLOSE_DELIMITER)
  51. typedef struct _BYTE_BUCKET
  52. {
  53. char cClosingDelimiter; // If this is an open delimiter,
  54. // this stores the correesponding closing
  55. // delimiter. Not used otherwise
  56. BYTE fFlags; // Flags, whether it is a delimiter
  57. } BYTE_BUCKET;
  58. static char *pFindNextUnquotedOccurrence(char *lpszString,
  59. DWORD dwStringLength,
  60. char cSearch,
  61. char *lpszOpenDelimiters,
  62. char *lpszCloseDelimiters,
  63. LPBOOL lpfNotFound)
  64. {
  65. char rgcState[MAX_STATE_STACK_DEPTH];
  66. DWORD_PTR dwState = 0;
  67. DWORD dwDelimiters = 0;
  68. DWORD i;
  69. char ch;
  70. char *lpStart = lpszString;
  71. BOOL fFallThru;
  72. BYTE_BUCKET rgbBucket[128];
  73. TraceFunctEnter("pFindNextUnquotedOccurrence");
  74. if (cSearch > 127)
  75. return(NULL);
  76. *lpfNotFound = FALSE;
  77. dwDelimiters = lstrlen(lpszOpenDelimiters);
  78. if (dwDelimiters != (DWORD)lstrlen(lpszCloseDelimiters))
  79. return(NULL);
  80. // Populate the bit bucket
  81. ZeroMemory(rgbBucket, 128 * sizeof(BYTE_BUCKET));
  82. for (i = 0; i < dwDelimiters; i++)
  83. {
  84. rgbBucket[lpszOpenDelimiters[i]].cClosingDelimiter = lpszCloseDelimiters[i];
  85. rgbBucket[lpszOpenDelimiters[i]].fFlags |= OPEN_DELIMITER;
  86. rgbBucket[lpszCloseDelimiters[i]].fFlags |= CLOSE_DELIMITER;
  87. }
  88. // dwState is the stack of unmatched open delimiters
  89. while (ch = *lpStart)
  90. {
  91. if (!dwStringLength)
  92. break;
  93. // Track the length
  94. dwStringLength--;
  95. // See if valid ASCII
  96. if (ch > 127)
  97. return(NULL);
  98. // If we are not in any quotes, and the char is found,
  99. // then we are done!
  100. if (!dwState && (ch == cSearch))
  101. {
  102. DebugTrace((LPARAM)0, "Found %c at %p", ch, lpStart);
  103. return(lpStart);
  104. }
  105. // If it is a quoted char, we can skip it and the following
  106. // char right away ... If the char following a quote '\' is
  107. // the terminating NULL, we have an error.
  108. if (ch == '\\')
  109. {
  110. lpStart++;
  111. if (!*lpStart)
  112. return(NULL);
  113. dwStringLength--;
  114. }
  115. else
  116. {
  117. // Check the close case, too
  118. fFallThru = TRUE;
  119. // See if we have an opening quote of any sort
  120. if (rgbBucket[ch].fFlags & OPEN_DELIMITER)
  121. {
  122. // This is used to take care of the case when the
  123. // open and close delimiters are the same. If it is
  124. // an open delimiter, we do not check the close
  125. // case unless the close delimiter is the same.
  126. fFallThru = FALSE;
  127. // Special case for open = close
  128. if (dwState &&
  129. rgcState[dwState-1] == ch &&
  130. (rgbBucket[ch].fFlags & OPEN_AND_CLOSE_DELIMITER) == OPEN_AND_CLOSE_DELIMITER)
  131. {
  132. // Stack is not empty, top of stack contains the same
  133. // quote, and open quote == close, this is actually a
  134. // close quote in disguise.
  135. fFallThru = TRUE;
  136. }
  137. else
  138. {
  139. // Push the new open quote in the stack
  140. if (dwState == MAX_STATE_STACK_DEPTH)
  141. return(FALSE);
  142. DebugTrace((LPARAM)0, "Push[%u]: %c, looking for %c",
  143. dwState, ch, rgbBucket[ch].cClosingDelimiter);
  144. rgcState[dwState++] = rgbBucket[ch].cClosingDelimiter;
  145. }
  146. }
  147. // See if we have a closing quote of any sort
  148. if (fFallThru && (rgbBucket[ch].fFlags & CLOSE_DELIMITER))
  149. {
  150. if (dwState)
  151. {
  152. // If we are closing the correct kind of quote,
  153. // pop the stack
  154. if (rgcState[dwState-1] == ch)
  155. {
  156. dwState--;
  157. DebugTrace((LPARAM)0, "Pop[%u] %c", dwState, ch);
  158. // Do a second check, in case we are looking
  159. // for a close quote
  160. if (!dwState && ch == cSearch)
  161. {
  162. DebugTrace((LPARAM)0, "Found %c at %p", ch, lpStart);
  163. return(lpStart);
  164. }
  165. }
  166. else
  167. {
  168. // Completely wrong closing brace.
  169. return(FALSE);
  170. }
  171. }
  172. else
  173. {
  174. // We are not in any quotes but we still see a
  175. // closing quote, so we have reached the end of our
  176. // current search scope!
  177. // Note that this is considered as not found
  178. // instead of an error
  179. *lpfNotFound = TRUE;
  180. return(NULL);
  181. }
  182. }
  183. }
  184. lpStart++;
  185. }
  186. *lpfNotFound = TRUE;
  187. TraceFunctLeave();
  188. return(NULL);
  189. }
  190. static inline BOOL IsCrOrLf(char ch)
  191. {
  192. return(ch == '\r' || ch == '\n');
  193. }
  194. static inline BOOL IsControl(char ch)
  195. {
  196. return( ((ch >= 0) && (ch <= 31)) || (ch == 127) );
  197. }
  198. //
  199. // <special> ::= "<" | ">" | "(" | ")" | "[" | "]" | "\" | "."
  200. // | "," | ";" | ":" | "@" """ | the control
  201. // characters (ASCII codes 0 through 31 inclusive and
  202. // 127)
  203. //
  204. static BOOL IsSpecial(char ch)
  205. {
  206. switch (ch)
  207. {
  208. case '(':
  209. case ')':
  210. case '<':
  211. case '>':
  212. case '@':
  213. case ',':
  214. case ':':
  215. case ';':
  216. case '\\':
  217. case '\"':
  218. case '.':
  219. case '[':
  220. case ']':
  221. return(TRUE);
  222. default:
  223. return(IsControl(ch));
  224. }
  225. }
  226. static BOOL pIsSpecialOrSpace(char ch)
  227. {
  228. return((ch == ' ') || (ch == '\t') || (ch == '\0') || IsSpecial(ch));
  229. }
  230. //
  231. // <x> ::= any one of the 128 ASCII characters (no exceptions)
  232. //
  233. static inline BOOL pIsX(char ch)
  234. {
  235. return(TRUE);
  236. }
  237. //
  238. // <a> ::= any one of the 52 alphabetic characters A through Z
  239. // in upper case and a through z in lower case
  240. //
  241. static inline BOOL pIsA(char ch)
  242. {
  243. return(((ch < 'A' || ch > 'z') || (ch > 'Z' && ch < 'a'))?FALSE:TRUE);
  244. }
  245. //
  246. // <d> ::= any one of the ten digits 0 through 9
  247. //
  248. static inline BOOL pIsD(char ch)
  249. {
  250. return((ch < '0' || ch > '9')?FALSE:TRUE);
  251. }
  252. //
  253. // <c> ::= any one of the 128 ASCII characters, but not any
  254. // <special> or <SP>
  255. //
  256. static inline BOOL pIsC(char ch)
  257. {
  258. return((ch == ' ' || IsSpecial(ch))?FALSE:TRUE);
  259. }
  260. //
  261. // <q> ::= any one of the 128 ASCII characters except <CR>,
  262. // <LF>, quote ("), or backslash (\)
  263. //
  264. static inline BOOL pIsQ(char ch)
  265. {
  266. return((ch == '\"' || ch == '\\' || IsCrOrLf(ch))?FALSE:TRUE);
  267. }
  268. //
  269. // <number> ::= <d> | <d> <number>
  270. //
  271. static BOOL pValidateNumber(char *lpszStart, DWORD dwLength)
  272. {
  273. if (!dwLength)
  274. return(FALSE);
  275. while (dwLength--)
  276. {
  277. if (!pIsD(*lpszStart++))
  278. return(FALSE);
  279. }
  280. return(TRUE);
  281. }
  282. //
  283. // <dotnum> ::= <snum> "." <snum> "." <snum> "." <snum>
  284. // <snum> ::= one, two, or three digits representing a decimal
  285. // integer value in the range 0 through 255
  286. //
  287. static BOOL pValidateDotnum(char *lpszStart, DWORD dwLength)
  288. {
  289. char ch;
  290. DWORD dwSnums = 0;
  291. DWORD dwNumLength = 0;
  292. DWORD dwValue = 0;
  293. if (!dwLength || dwLength > 15)
  294. return(FALSE);
  295. while (dwLength--)
  296. {
  297. ch = *lpszStart++;
  298. if (pIsD(ch))
  299. {
  300. // Do each digit and calculate running total
  301. dwValue *= 10;
  302. dwValue += (ch - '0');
  303. dwNumLength++;
  304. }
  305. else if (ch == '.')
  306. {
  307. // There must be a number before each dot and
  308. // the running total must be between 0 and 255
  309. if (!dwNumLength)
  310. return(FALSE);
  311. if (dwValue > 255)
  312. return(FALSE);
  313. // Reset the counter
  314. dwSnums++;
  315. dwValue = 0;
  316. dwNumLength = 0;
  317. }
  318. else
  319. return(FALSE);
  320. }
  321. // Do the last snum
  322. if (!dwNumLength)
  323. return(FALSE);
  324. if (dwValue > 255)
  325. return(FALSE);
  326. dwSnums++;
  327. // Each IP address must have 4 snums
  328. if (dwSnums != 4)
  329. return(FALSE);
  330. return(TRUE);
  331. }
  332. //
  333. // <quoted-string> ::= """ <qtext> """
  334. // <qtext> ::= "\" <x> | "\" <x> <qtext> | <q> | <q> <qtext>
  335. //
  336. static BOOL pValidateQuotedString(char *lpszStart, DWORD dwLength)
  337. {
  338. char ch;
  339. // At least 3 chars
  340. if (dwLength < 3)
  341. return(FALSE);
  342. // Must begin and end with double quotes
  343. if (lpszStart[0] != '\"' || lpszStart[dwLength-1] != '\"')
  344. return(FALSE);
  345. // Factor out the quotes
  346. dwLength -= 2;
  347. lpszStart++;
  348. // The inside must be <qtext>
  349. while (dwLength--)
  350. {
  351. ch = *lpszStart++;
  352. // Each character must be either an escape pair or <q>
  353. if (ch == '\\')
  354. {
  355. if (!dwLength)
  356. return(FALSE);
  357. dwLength--;
  358. lpszStart++;
  359. }
  360. else if (!pIsQ(ch))
  361. return(FALSE);
  362. }
  363. return(TRUE);
  364. }
  365. //
  366. // <dot-string> ::= <string> | <string> "." <dot-string>
  367. // <string> ::= <char> | <char> <string>
  368. // <char> ::= <c> | "\" <x>
  369. //
  370. static BOOL pValidateDotString(char *lpszStart, DWORD dwLength)
  371. {
  372. char ch;
  373. BOOL fChar = FALSE;
  374. if (!dwLength)
  375. return(FALSE);
  376. while (dwLength--)
  377. {
  378. ch = *lpszStart++;
  379. if (ch == '\\')
  380. {
  381. // Escape pair
  382. if (!dwLength)
  383. return(FALSE);
  384. dwLength--;
  385. lpszStart++;
  386. fChar = TRUE;
  387. }
  388. else if (ch == '.')
  389. {
  390. // 1) Must not start with a dot,
  391. // 2) Consecutive dots are not allowed
  392. if (!fChar)
  393. return(FALSE);
  394. // Reset the flag
  395. fChar = FALSE;
  396. }
  397. else if (pIsC(ch))
  398. fChar = TRUE;
  399. else
  400. return(FALSE);
  401. }
  402. // Cannot end with a dot
  403. if (ch == '.')
  404. return(FALSE);
  405. return(TRUE);
  406. }
  407. //
  408. // Note: Original RFC 821:
  409. // <name> ::= <a> <ldh-str> <let-dig>
  410. // <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
  411. // <let-dig> ::= <a> | <d>
  412. // <let-dig-hyp> ::= <a> | <d> | "-"
  413. //
  414. // Our implementation:
  415. // <name> ::= <let-dig-hyp-und> | <let-dig-hyp-und> <name>
  416. // <let-dig-hyp-und> ::= <a> | <d> | "-" | "_"
  417. //
  418. // Reasons:
  419. // 1) 3COM start their domains with a digit
  420. // 2) Some customers start their domain names with underscores,
  421. // and some comtain underscores.
  422. //
  423. static BOOL pValidateName(char *lpszStart, DWORD dwLength)
  424. {
  425. char ch;
  426. if (!dwLength)
  427. return(FALSE);
  428. while (dwLength--)
  429. {
  430. ch = *lpszStart++;
  431. if (pIsA(ch) || pIsD(ch) || ch == '-' || ch == '_')
  432. ;
  433. else
  434. return(FALSE);
  435. }
  436. return(TRUE);
  437. }
  438. //
  439. // <local-part> ::= <dot-string> | <quoted-string>
  440. //
  441. static BOOL pValidateLocalPart(char *lpszStart, DWORD dwLength)
  442. {
  443. if (!dwLength)
  444. return(FALSE);
  445. return(pValidateDotString(lpszStart, dwLength) ||
  446. pValidateQuotedString(lpszStart, dwLength));
  447. }
  448. //
  449. // <element> ::= <name> | "#" <number> | "[" <dotnum> "]"
  450. //
  451. static BOOL pValidateElement(char *lpszStart, DWORD dwLength)
  452. {
  453. char ch;
  454. if (!dwLength)
  455. return(FALSE);
  456. ch = *lpszStart;
  457. if (ch == '#')
  458. // This is the # <number> form
  459. return(pValidateNumber(lpszStart+1, dwLength-1));
  460. else if (ch == '[')
  461. {
  462. if (lpszStart[dwLength-1] != ']')
  463. return(FALSE);
  464. // This is a domain literal
  465. return(pValidateDotnum(lpszStart+1, dwLength-2));
  466. }
  467. // Validate as a name
  468. return(pValidateName(lpszStart, dwLength));
  469. }
  470. //
  471. // sub-domain ::= let-dig *(ldh-str)
  472. // ldh-str = *( Alpha / Digit / "-" ) let-dig
  473. // let-dig = Alpha / Digit
  474. //
  475. static BOOL pValidateDRUMSSubDomain(char *lpszStart, DWORD dwLength)
  476. {
  477. unsigned char ch;
  478. DWORD ec;
  479. if (!dwLength)
  480. return(FALSE);
  481. // validate all of the characters in the name
  482. while (dwLength--)
  483. {
  484. ch = (unsigned char) *lpszStart++;
  485. // this list of characters comes from NT, dnsvldnm.doc. we
  486. // also allow #, [, and ]
  487. if ((ch >= 1 && ch <= 34) ||
  488. (ch >= 36 && ch <= 41) ||
  489. (ch == 43) ||
  490. (ch == 44) ||
  491. (ch == 47) ||
  492. (ch >= 58 && ch <= 64) ||
  493. (ch == 92) ||
  494. (ch == 94) ||
  495. (ch == 96) ||
  496. (ch >= 123))
  497. {
  498. return FALSE;
  499. }
  500. } //while
  501. //We have a valid subdomain
  502. return (TRUE);
  503. }
  504. //
  505. // ======================================================
  506. //
  507. BOOL FindNextUnquotedOccurrence(char *lpszString,
  508. DWORD dwStringLength,
  509. char cSearch,
  510. char **ppszLocation)
  511. {
  512. BOOL fNotFound = FALSE;
  513. *ppszLocation = pFindNextUnquotedOccurrence(lpszString,
  514. dwStringLength,cSearch, acOpen,acClose, &fNotFound);
  515. if (!*ppszLocation)
  516. {
  517. // If failed but not because of not found, then bad line
  518. if (!fNotFound)
  519. {
  520. SetLastError(ERROR_INVALID_DATA);
  521. return FALSE;
  522. }
  523. }
  524. return TRUE;
  525. }
  526. //
  527. // This function extracts an email address from the given command line
  528. // and returns the tail of the line after the address. Any angle braces
  529. // present will be included as part of the 821 address. The returned
  530. // address is not validated at all.
  531. //
  532. BOOL Extract821AddressFromLine( char *lpszLine,
  533. char **ppszAddress,
  534. DWORD *pdwAddressLength,
  535. char **ppszTail)
  536. {
  537. DWORD dwAddressLength = 0;
  538. char *pAddressEnd;
  539. BOOL fNotFound;
  540. TraceFunctEnter("Extract821AddressFromLine");
  541. _ASSERT(lpszLine);
  542. _ASSERT(ppszAddress);
  543. _ASSERT(pdwAddressLength);
  544. _ASSERT(ppszTail);
  545. // Initialize
  546. *ppszAddress = lpszLine;
  547. *pdwAddressLength = 0;
  548. *ppszTail = lpszLine;
  549. // Routine checking
  550. if (!lpszLine ||
  551. // Big enough for MAX_INTERNET_NAME + any options on mail from/rcpt to
  552. !pValidateStringPtr(lpszLine, MAX_INTERNET_NAME + 2000) ||
  553. !ppszAddress ||
  554. IsBadWritePtr(ppszAddress, sizeof(char *)) ||
  555. !ppszTail ||
  556. IsBadWritePtr(ppszTail, sizeof(char *)) ||
  557. !pdwAddressLength ||
  558. IsBadWritePtr(pdwAddressLength, sizeof(DWORD)))
  559. {
  560. SetLastError(ERROR_INVALID_DATA);
  561. TraceFunctLeave();
  562. return(FALSE);
  563. }
  564. // Skip all leading spaces
  565. while (*lpszLine == ' ')
  566. lpszLine++;
  567. // The first unquoted space indicates the end of the address
  568. pAddressEnd = pFindNextUnquotedOccurrence(lpszLine,
  569. lstrlen(lpszLine), ' ', acOpen, acClose, &fNotFound);
  570. if (!pAddressEnd)
  571. {
  572. // If failed but not because of not found, then bad line
  573. if (!fNotFound)
  574. return(FALSE);
  575. // Space not found, the entire line is the address
  576. dwAddressLength = lstrlen(lpszLine);
  577. pAddressEnd = lpszLine + dwAddressLength;
  578. *ppszTail = pAddressEnd;
  579. }
  580. else
  581. {
  582. // Calculate the length
  583. dwAddressLength = (DWORD)(pAddressEnd - lpszLine);
  584. // Get the start of the tail, after all the spaces
  585. while (*pAddressEnd == ' ')
  586. pAddressEnd++;
  587. *ppszTail = pAddressEnd;
  588. }
  589. if (dwAddressLength < 1 || dwAddressLength > MAX_INTERNET_NAME)
  590. return(FALSE);
  591. *ppszAddress = lpszLine;
  592. *pdwAddressLength = dwAddressLength;
  593. DebugTrace((LPARAM)0, "Extracted \"%*s\"", dwAddressLength, lpszLine);
  594. TraceFunctLeave();
  595. return(TRUE);
  596. }
  597. //
  598. // This function takes in a RFC 821 address with optional angle braces
  599. // and extracts the canonical form of the address. All at-domain-list
  600. // entries are removed. Angle braces will be matched and removed.
  601. // Mismatched angle braces are considered invalid. The returned address
  602. // will be in the <local-part> "@" <domain> form.
  603. //
  604. // There must be no leading or trailing spaces included.
  605. //
  606. // jstamerj 1999/01/13 14:02:13: Modified to remove a trailing '.' from the <domain> portion of the address
  607. //
  608. BOOL ExtractCanonical821Address( char *lpszAddress,
  609. DWORD dwAddressLength,
  610. char **ppszCanonicalAddress,
  611. DWORD *pdwCanonicalAddressLength)
  612. {
  613. char *pAddressStart;
  614. BOOL fNotFound;
  615. TraceFunctEnter("ExtractCanonical821Address");
  616. _ASSERT(lpszAddress);
  617. _ASSERT(ppszCanonicalAddress);
  618. _ASSERT(pdwCanonicalAddressLength);
  619. // Initialize
  620. *ppszCanonicalAddress = lpszAddress;
  621. *ppszCanonicalAddress = 0;
  622. // Routine checking
  623. if (!lpszAddress ||
  624. // Big enough for MAX_INTERNET_NAME + any options on mail from/rcpt to
  625. !pValidateStringPtr(lpszAddress, MAX_INTERNET_NAME + 2000) ||
  626. !ppszCanonicalAddress ||
  627. IsBadWritePtr(ppszCanonicalAddress, sizeof(char *)) ||
  628. !pdwCanonicalAddressLength ||
  629. IsBadWritePtr(pdwCanonicalAddressLength, sizeof(DWORD)))
  630. {
  631. SetLastError(ERROR_INVALID_DATA);
  632. TraceFunctLeave();
  633. return(FALSE);
  634. }
  635. // See how many layers of nesting we have, and match
  636. // each pair of angle braces
  637. while (*lpszAddress == '<')
  638. {
  639. if (!dwAddressLength--)
  640. return(FALSE);
  641. if (lpszAddress[dwAddressLength] != '>')
  642. return(FALSE);
  643. if (!dwAddressLength--)
  644. return(FALSE);
  645. lpszAddress++;
  646. }
  647. // Next, skip all at-domain-list entries and get to
  648. // the meat of the address
  649. do
  650. {
  651. // Skip all leading spaces
  652. while (*lpszAddress == ' ')
  653. {
  654. lpszAddress++;
  655. if (!dwAddressLength--)
  656. return(FALSE);
  657. }
  658. //skip all the trailing spaces
  659. while (*(lpszAddress + dwAddressLength - 1) == ' ')
  660. {
  661. if (!dwAddressLength--)
  662. return(FALSE);
  663. }
  664. // Initialize lest it falls through right away
  665. pAddressStart = lpszAddress;
  666. if (*lpszAddress == '@')
  667. {
  668. // Yep, there's a domain route there ...
  669. // Skip it ...
  670. pAddressStart = pFindNextUnquotedOccurrence(lpszAddress,
  671. dwAddressLength, ',', acOpen, acClose, &fNotFound);
  672. if (!pAddressStart)
  673. {
  674. if (!fNotFound)
  675. return(FALSE);
  676. // No comma, now see if we get a semicolon
  677. pAddressStart = pFindNextUnquotedOccurrence(lpszAddress,
  678. dwAddressLength, ':', acOpen, acClose, &fNotFound);
  679. if (!pAddressStart)
  680. {
  681. // No semicolon either, this is a bad address
  682. return(FALSE);
  683. }
  684. // This is a semicolon, so we break out
  685. pAddressStart++;
  686. dwAddressLength -= (DWORD)(pAddressStart - lpszAddress);
  687. break;
  688. }
  689. // We have a comma, we let it iterate
  690. pAddressStart++;
  691. dwAddressLength -= (DWORD)(pAddressStart - lpszAddress);
  692. lpszAddress = pAddressStart;
  693. }
  694. else
  695. break;
  696. } while (dwAddressLength);
  697. // Skip all leading spaces
  698. while (*pAddressStart == ' ')
  699. {
  700. pAddressStart++;
  701. if (!dwAddressLength--)
  702. return(FALSE);
  703. }
  704. if((dwAddressLength > 1) && // Must be at least 2 for the address "@."
  705. (pAddressStart[dwAddressLength-1] == '.')) {
  706. //
  707. // jstamerj 1999/01/13 14:05:39:
  708. // If the domain part of the address has a trailing '.', do
  709. // not count it in the canonical length
  710. //
  711. LPSTR pDomain;
  712. BOOL fNotFound;
  713. // Find the domain
  714. pDomain = pFindNextUnquotedOccurrence(
  715. pAddressStart,
  716. dwAddressLength - 1,
  717. '@',
  718. acOpen,
  719. acClose,
  720. &fNotFound);
  721. //
  722. // If we found the '@' and the '.' is after the '@' (it must
  723. // be if we really found it), then shorten the canonical
  724. // address so that it doesn't include '.'
  725. //
  726. if((fNotFound == FALSE) &&
  727. (&(pAddressStart[dwAddressLength]) > pDomain))
  728. dwAddressLength--;
  729. }
  730. if (dwAddressLength < 1 || dwAddressLength > MAX_INTERNET_NAME)
  731. return(FALSE);
  732. // Fill in the output
  733. *ppszCanonicalAddress = pAddressStart;
  734. *pdwCanonicalAddressLength = dwAddressLength;
  735. DebugTrace((LPARAM)0, "Extracted \"%*s\"", dwAddressLength, pAddressStart);
  736. TraceFunctLeave();
  737. return(TRUE);
  738. }
  739. //
  740. // This function takes in a RFC 821 domain in canonical form
  741. // and validates it according to the RFC 821 grammar
  742. // (some modifications for real-life scenarios)
  743. //
  744. // <domain> ::= <element> | <element> "." <domain>
  745. //
  746. BOOL Validate821Domain( char *lpszDomain,
  747. DWORD dwDomainLength)
  748. {
  749. char *pSubdomainOffset;
  750. DWORD dwSubdomainLength;
  751. BOOL fNotFound;
  752. TraceFunctEnter("Validate821Domain");
  753. _ASSERT(lpszDomain);
  754. // Routine checking
  755. if (!lpszDomain ||
  756. !pValidateStringPtr(lpszDomain, MAX_INTERNET_NAME+1))
  757. {
  758. SetLastError(ERROR_INVALID_DATA);
  759. TraceFunctLeave();
  760. return(FALSE);
  761. }
  762. // Find each subdomain
  763. do
  764. {
  765. pSubdomainOffset = pFindNextUnquotedOccurrence(lpszDomain,
  766. dwDomainLength, '.', acOpen, acClose, &fNotFound);
  767. if (!pSubdomainOffset)
  768. {
  769. if (!fNotFound)
  770. {
  771. SetLastError(ERROR_INVALID_DATA);
  772. return(FALSE);
  773. }
  774. // Not found and nothing left, domain ends with a dot, invalid.
  775. if (!dwDomainLength)
  776. {
  777. SetLastError(ERROR_INVALID_DATA);
  778. return(FALSE);
  779. }
  780. // No domain, so email alias is all there is
  781. dwSubdomainLength = dwDomainLength;
  782. }
  783. else
  784. {
  785. // Calculate domain parameters
  786. dwSubdomainLength = (DWORD)(pSubdomainOffset - lpszDomain);
  787. // Adjust for the dot
  788. dwDomainLength--;
  789. }
  790. // Cannot allow leading dot or consecutive dots
  791. if (!dwSubdomainLength)
  792. {
  793. SetLastError(ERROR_INVALID_DATA);
  794. return(FALSE);
  795. }
  796. // Check each subdomain as an element
  797. if (!pValidateElement(lpszDomain, dwSubdomainLength))
  798. {
  799. SetLastError(ERROR_INVALID_DATA);
  800. return(FALSE);
  801. }
  802. // Adjust the length and pointers
  803. dwDomainLength -= dwSubdomainLength;
  804. // Skip past dot and scan again
  805. lpszDomain = pSubdomainOffset + 1;
  806. } while (dwDomainLength);
  807. // Make sure no dot's found, either
  808. if (!fNotFound)
  809. {
  810. // If a dot's found, the domain ends with a dot and it's uncool.
  811. SetLastError(ERROR_INVALID_DATA);
  812. return(FALSE);
  813. }
  814. TraceFunctLeave();
  815. return(TRUE);
  816. }
  817. //
  818. // This function takes in a DRUMS domain in canonical form
  819. // and validates it strictly, according to the DRUMS grammar
  820. //
  821. // Domain ::= sub-domain 1*("." sub-domain) | address-literal
  822. // address-literal ::= "[" IPv4-address-literal |
  823. // IPv6-address-literal | General-address-literal "]"
  824. // IPv4-address-literal ::= snum 3("." snum)
  825. // IPv6-address-literal ::= "IPv6" SP <<what did we finally decide on?>>
  826. // General-address-literal ::= Standardized-tag SP String
  827. // Standardized-tag ::= String (Specified in a standards-track RFC
  828. // and registered with IANA)
  829. // snum = one, two, or three digits representing a decimal
  830. // integer value in the range 0 through 255
  831. BOOL ValidateDRUMSDomain( char *lpszDomain,
  832. DWORD dwDomainLength)
  833. {
  834. char *pSubdomainOffset;
  835. DWORD dwSubdomainLength;
  836. BOOL fNotFound;
  837. char *szEndofString;
  838. TraceFunctEnter("Validate821Domain");
  839. _ASSERT(lpszDomain);
  840. // Routine checking
  841. if (!dwDomainLength || dwDomainLength > MAX_INTERNET_NAME)
  842. return(FALSE);
  843. if (!lpszDomain ||
  844. !pValidateStringPtr(lpszDomain, MAX_INTERNET_NAME+1))
  845. {
  846. SetLastError(ERROR_INVALID_DATA);
  847. TraceFunctLeave();
  848. return(FALSE);
  849. }
  850. // Skip all leading spaces
  851. while (*lpszDomain == ' ')
  852. lpszDomain++;
  853. //It has to be either in address-literal format or subdomain format
  854. //
  855. if (*lpszDomain == '[')
  856. {
  857. //It is an Address literal
  858. //Skip trailing white space
  859. szEndofString = &lpszDomain[lstrlen(lpszDomain) - 1];
  860. while(*szEndofString == ' ')
  861. szEndofString--;
  862. if (*szEndofString != ']')
  863. return(FALSE);
  864. // This is a domain literal
  865. return(pValidateDotnum(lpszDomain+1, dwDomainLength-2));
  866. }
  867. else
  868. {
  869. //This is in subdomain format
  870. do
  871. {
  872. pSubdomainOffset = pFindNextUnquotedOccurrence(lpszDomain,
  873. dwDomainLength, '.', acOpen, acClose, &fNotFound);
  874. if (!pSubdomainOffset)
  875. {
  876. if (!fNotFound)
  877. {
  878. SetLastError(ERROR_INVALID_DATA);
  879. return(FALSE);
  880. }
  881. // Not found and nothing left, domain ends with a dot, invalid.
  882. if (!dwDomainLength)
  883. {
  884. SetLastError(ERROR_INVALID_DATA);
  885. return(FALSE);
  886. }
  887. // No domain, so email alias is all there is
  888. dwSubdomainLength = dwDomainLength;
  889. }
  890. else
  891. {
  892. // Calculate domain parameters
  893. dwSubdomainLength = (DWORD)(pSubdomainOffset - lpszDomain);
  894. // Adjust for the dot
  895. //NimishK : **Check with Keith if this should be subdomain.
  896. dwDomainLength--;
  897. }
  898. // Cannot allow leading dot or consecutive dots
  899. if (!dwSubdomainLength)
  900. {
  901. SetLastError(ERROR_INVALID_DATA);
  902. return(FALSE);
  903. }
  904. // Check each subdomain
  905. if (!pValidateDRUMSSubDomain(lpszDomain, dwSubdomainLength))
  906. {
  907. SetLastError(ERROR_INVALID_DATA);
  908. return(FALSE);
  909. }
  910. // Adjust the length and pointers
  911. dwDomainLength -= dwSubdomainLength;
  912. // Skip past dot and scan again
  913. lpszDomain = pSubdomainOffset + 1;
  914. } while (dwDomainLength);
  915. // Make sure no dot's found, either
  916. if (!fNotFound)
  917. {
  918. // If a dot's found, the domain ends with a dot and it's uncool.
  919. SetLastError(ERROR_INVALID_DATA);
  920. return(FALSE);
  921. }
  922. TraceFunctLeave();
  923. return(TRUE);
  924. }
  925. TraceFunctLeave();
  926. return(TRUE);
  927. }
  928. //
  929. // This function takes in a RFC 821 address in canonical form
  930. // (<local-part> ["@" <domain>]) and validates it according to the
  931. // RFC 821 grammar (some modifications for real-life scenarios)
  932. //
  933. BOOL Validate821Address( char *lpszAddress,
  934. DWORD dwAddressLength)
  935. {
  936. char *pDomainOffset;
  937. DWORD dwEmailLength;
  938. DWORD dwDomainLength;
  939. BOOL fNotFound;
  940. TraceFunctEnter("Validate821Address");
  941. _ASSERT(lpszAddress);
  942. // Routine checking
  943. if (!lpszAddress ||
  944. !pValidateStringPtr(lpszAddress, MAX_INTERNET_NAME+1))
  945. {
  946. SetLastError(ERROR_INVALID_DATA);
  947. TraceFunctLeave();
  948. return(FALSE);
  949. }
  950. // Find the domain
  951. pDomainOffset = pFindNextUnquotedOccurrence(lpszAddress,
  952. dwAddressLength, '@', acOpen, acClose, &fNotFound);
  953. if (!pDomainOffset)
  954. {
  955. if (!fNotFound)
  956. {
  957. SetLastError(ERROR_INVALID_DATA);
  958. return(FALSE);
  959. }
  960. // No domain, so email alias is all there is
  961. dwEmailLength = dwAddressLength;
  962. }
  963. else
  964. {
  965. // Calculate domain parameters
  966. dwEmailLength = (DWORD)(pDomainOffset - lpszAddress);
  967. dwDomainLength = dwAddressLength - dwEmailLength - 1;
  968. pDomainOffset++;
  969. }
  970. // Do the check for email name
  971. if (!pValidateLocalPart(lpszAddress, dwEmailLength))
  972. {
  973. SetLastError(ERROR_INVALID_DATA);
  974. return(FALSE);
  975. }
  976. // Now check domain, if applicable
  977. if (pDomainOffset)
  978. {
  979. return(Validate821Domain(pDomainOffset, dwDomainLength));
  980. }
  981. TraceFunctLeave();
  982. return(TRUE);
  983. }
  984. //
  985. // This function takes in a RFC 821 address in canonical form
  986. // (<local-part> ["@" <domain>]) and extracts the domain part
  987. //
  988. BOOL Get821AddressDomain( char *lpszAddress,
  989. DWORD dwAddressLength,
  990. char **ppszDomain)
  991. {
  992. char *pDomainOffset;
  993. BOOL fNotFound = FALSE;
  994. BOOL fReturn = TRUE;
  995. TraceFunctEnter("Get821AddressDomain");
  996. _ASSERT(lpszAddress);
  997. // Find the domain
  998. pDomainOffset = pFindNextUnquotedOccurrence(lpszAddress,
  999. dwAddressLength, '@', acOpen, acClose, &fNotFound);
  1000. if (!pDomainOffset && !fNotFound)
  1001. {
  1002. SetLastError(ERROR_INVALID_DATA);
  1003. fReturn = FALSE;
  1004. goto Exit;
  1005. }
  1006. if (fNotFound)
  1007. {
  1008. *ppszDomain = NULL;
  1009. goto Exit;
  1010. }
  1011. *ppszDomain = pDomainOffset + 1;
  1012. // Validate that the domain part is <= 255 chars
  1013. if ((dwAddressLength - (*ppszDomain - lpszAddress)) > 255)
  1014. {
  1015. *ppszDomain = NULL;
  1016. SetLastError(ERROR_INVALID_DATA);
  1017. fReturn = FALSE;
  1018. goto Exit;
  1019. }
  1020. Exit:
  1021. TraceFunctLeave();
  1022. return fReturn;
  1023. }