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

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name :
addr821.cxx
Abstract:
Set of functions to parse RFC 821 addresses.
Author:
Keith Lau (KeithLau) 2/17/98
Project:
SMTP Server DLL
Functions Exported:
Revision History:
--*/
/************************************************************
* Include Headers
************************************************************/
#include <windows.h>
#include <dbgtrace.h>
#include <addr821.hxx>
#define MAX_EMAIL_NAME 64
#define MAX_DOMAIN_NAME 250
#define MAX_INTERNET_NAME (MAX_EMAIL_NAME + MAX_DOMAIN_NAME + 2)
// Quick and dirty string validation
static BOOL pValidateStringPtr(LPSTR lpwszString, DWORD dwMaxLength)
{
if (IsBadStringPtr((LPCTSTR)lpwszString, dwMaxLength))
return(FALSE);
while (dwMaxLength--)
if (*lpwszString++ == 0)
return(TRUE);
return(FALSE);
}
// ========================================================================
//
// Validation Parser stuff created by KeithLau on 2/17/98
//
static char acOpen[] = "\"[<(";
static char acClose[] = "\"]>)";
//
// NOTE: RFC 821 and RFC 822 versions of this function are different!!
//
// This function finds braces pairs in a given string, and returns
// pointers to the start and end of the first occurence of a
// [nested] pair of braces. The starting and ending character
// may be specified by the caller (starting and ending chars
// must be unique).
#define MAX_STATE_STACK_DEPTH 64
#define OPEN_DELIMITER 0x1
#define CLOSE_DELIMITER 0x2
#define OPEN_AND_CLOSE_DELIMITER (OPEN_DELIMITER | CLOSE_DELIMITER)
typedef struct _BYTE_BUCKET
{
char cClosingDelimiter; // If this is an open delimiter,
// this stores the correesponding closing
// delimiter. Not used otherwise
BYTE fFlags; // Flags, whether it is a delimiter
} BYTE_BUCKET;
static char *pFindNextUnquotedOccurrence(char *lpszString,
DWORD dwStringLength,
char cSearch,
char *lpszOpenDelimiters,
char *lpszCloseDelimiters,
LPBOOL lpfNotFound)
{
char rgcState[MAX_STATE_STACK_DEPTH];
DWORD_PTR dwState = 0;
DWORD dwDelimiters = 0;
DWORD i;
char ch;
char *lpStart = lpszString;
BOOL fFallThru;
BYTE_BUCKET rgbBucket[128];
TraceFunctEnter("pFindNextUnquotedOccurrence");
if (cSearch > 127)
return(NULL);
*lpfNotFound = FALSE;
dwDelimiters = lstrlen(lpszOpenDelimiters);
if (dwDelimiters != (DWORD)lstrlen(lpszCloseDelimiters))
return(NULL);
// Populate the bit bucket
ZeroMemory(rgbBucket, 128 * sizeof(BYTE_BUCKET));
for (i = 0; i < dwDelimiters; i++)
{
rgbBucket[lpszOpenDelimiters[i]].cClosingDelimiter = lpszCloseDelimiters[i];
rgbBucket[lpszOpenDelimiters[i]].fFlags |= OPEN_DELIMITER;
rgbBucket[lpszCloseDelimiters[i]].fFlags |= CLOSE_DELIMITER;
}
// dwState is the stack of unmatched open delimiters
while (ch = *lpStart)
{
if (!dwStringLength)
break;
// Track the length
dwStringLength--;
// See if valid ASCII
if (ch > 127)
return(NULL);
// If we are not in any quotes, and the char is found,
// then we are done!
if (!dwState && (ch == cSearch))
{
DebugTrace((LPARAM)0, "Found %c at %p", ch, lpStart);
return(lpStart);
}
// If it is a quoted char, we can skip it and the following
// char right away ... If the char following a quote '\' is
// the terminating NULL, we have an error.
if (ch == '\\')
{
lpStart++;
if (!*lpStart)
return(NULL);
dwStringLength--;
}
else
{
// Check the close case, too
fFallThru = TRUE;
// See if we have an opening quote of any sort
if (rgbBucket[ch].fFlags & OPEN_DELIMITER)
{
// This is used to take care of the case when the
// open and close delimiters are the same. If it is
// an open delimiter, we do not check the close
// case unless the close delimiter is the same.
fFallThru = FALSE;
// Special case for open = close
if (dwState &&
rgcState[dwState-1] == ch &&
(rgbBucket[ch].fFlags & OPEN_AND_CLOSE_DELIMITER) == OPEN_AND_CLOSE_DELIMITER)
{
// Stack is not empty, top of stack contains the same
// quote, and open quote == close, this is actually a
// close quote in disguise.
fFallThru = TRUE;
}
else
{
// Push the new open quote in the stack
if (dwState == MAX_STATE_STACK_DEPTH)
return(FALSE);
DebugTrace((LPARAM)0, "Push[%u]: %c, looking for %c",
dwState, ch, rgbBucket[ch].cClosingDelimiter);
rgcState[dwState++] = rgbBucket[ch].cClosingDelimiter;
}
}
// See if we have a closing quote of any sort
if (fFallThru && (rgbBucket[ch].fFlags & CLOSE_DELIMITER))
{
if (dwState)
{
// If we are closing the correct kind of quote,
// pop the stack
if (rgcState[dwState-1] == ch)
{
dwState--;
DebugTrace((LPARAM)0, "Pop[%u] %c", dwState, ch);
// Do a second check, in case we are looking
// for a close quote
if (!dwState && ch == cSearch)
{
DebugTrace((LPARAM)0, "Found %c at %p", ch, lpStart);
return(lpStart);
}
}
else
{
// Completely wrong closing brace.
return(FALSE);
}
}
else
{
// We are not in any quotes but we still see a
// closing quote, so we have reached the end of our
// current search scope!
// Note that this is considered as not found
// instead of an error
*lpfNotFound = TRUE;
return(NULL);
}
}
}
lpStart++;
}
*lpfNotFound = TRUE;
TraceFunctLeave();
return(NULL);
}
static inline BOOL IsCrOrLf(char ch)
{
return(ch == '\r' || ch == '\n');
}
static inline BOOL IsControl(char ch)
{
return( ((ch >= 0) && (ch <= 31)) || (ch == 127) );
}
//
// <special> ::= "<" | ">" | "(" | ")" | "[" | "]" | "\" | "."
// | "," | ";" | ":" | "@" """ | the control
// characters (ASCII codes 0 through 31 inclusive and
// 127)
//
static BOOL IsSpecial(char ch)
{
switch (ch)
{
case '(':
case ')':
case '<':
case '>':
case '@':
case ',':
case ':':
case ';':
case '\\':
case '\"':
case '.':
case '[':
case ']':
return(TRUE);
default:
return(IsControl(ch));
}
}
static BOOL pIsSpecialOrSpace(char ch)
{
return((ch == ' ') || (ch == '\t') || (ch == '\0') || IsSpecial(ch));
}
//
// <x> ::= any one of the 128 ASCII characters (no exceptions)
//
static inline BOOL pIsX(char ch)
{
return(TRUE);
}
//
// <a> ::= any one of the 52 alphabetic characters A through Z
// in upper case and a through z in lower case
//
static inline BOOL pIsA(char ch)
{
return(((ch < 'A' || ch > 'z') || (ch > 'Z' && ch < 'a'))?FALSE:TRUE);
}
//
// <d> ::= any one of the ten digits 0 through 9
//
static inline BOOL pIsD(char ch)
{
return((ch < '0' || ch > '9')?FALSE:TRUE);
}
//
// <c> ::= any one of the 128 ASCII characters, but not any
// <special> or <SP>
//
static inline BOOL pIsC(char ch)
{
return((ch == ' ' || IsSpecial(ch))?FALSE:TRUE);
}
//
// <q> ::= any one of the 128 ASCII characters except <CR>,
// <LF>, quote ("), or backslash (\)
//
static inline BOOL pIsQ(char ch)
{
return((ch == '\"' || ch == '\\' || IsCrOrLf(ch))?FALSE:TRUE);
}
//
// <number> ::= <d> | <d> <number>
//
static BOOL pValidateNumber(char *lpszStart, DWORD dwLength)
{
if (!dwLength)
return(FALSE);
while (dwLength--)
{
if (!pIsD(*lpszStart++))
return(FALSE);
}
return(TRUE);
}
//
// <dotnum> ::= <snum> "." <snum> "." <snum> "." <snum>
// <snum> ::= one, two, or three digits representing a decimal
// integer value in the range 0 through 255
//
static BOOL pValidateDotnum(char *lpszStart, DWORD dwLength)
{
char ch;
DWORD dwSnums = 0;
DWORD dwNumLength = 0;
DWORD dwValue = 0;
if (!dwLength || dwLength > 15)
return(FALSE);
while (dwLength--)
{
ch = *lpszStart++;
if (pIsD(ch))
{
// Do each digit and calculate running total
dwValue *= 10;
dwValue += (ch - '0');
dwNumLength++;
}
else if (ch == '.')
{
// There must be a number before each dot and
// the running total must be between 0 and 255
if (!dwNumLength)
return(FALSE);
if (dwValue > 255)
return(FALSE);
// Reset the counter
dwSnums++;
dwValue = 0;
dwNumLength = 0;
}
else
return(FALSE);
}
// Do the last snum
if (!dwNumLength)
return(FALSE);
if (dwValue > 255)
return(FALSE);
dwSnums++;
// Each IP address must have 4 snums
if (dwSnums != 4)
return(FALSE);
return(TRUE);
}
//
// <quoted-string> ::= """ <qtext> """
// <qtext> ::= "\" <x> | "\" <x> <qtext> | <q> | <q> <qtext>
//
static BOOL pValidateQuotedString(char *lpszStart, DWORD dwLength)
{
char ch;
// At least 3 chars
if (dwLength < 3)
return(FALSE);
// Must begin and end with double quotes
if (lpszStart[0] != '\"' || lpszStart[dwLength-1] != '\"')
return(FALSE);
// Factor out the quotes
dwLength -= 2;
lpszStart++;
// The inside must be <qtext>
while (dwLength--)
{
ch = *lpszStart++;
// Each character must be either an escape pair or <q>
if (ch == '\\')
{
if (!dwLength)
return(FALSE);
dwLength--;
lpszStart++;
}
else if (!pIsQ(ch))
return(FALSE);
}
return(TRUE);
}
//
// <dot-string> ::= <string> | <string> "." <dot-string>
// <string> ::= <char> | <char> <string>
// <char> ::= <c> | "\" <x>
//
static BOOL pValidateDotString(char *lpszStart, DWORD dwLength)
{
char ch;
BOOL fChar = FALSE;
if (!dwLength)
return(FALSE);
while (dwLength--)
{
ch = *lpszStart++;
if (ch == '\\')
{
// Escape pair
if (!dwLength)
return(FALSE);
dwLength--;
lpszStart++;
fChar = TRUE;
}
else if (ch == '.')
{
// 1) Must not start with a dot,
// 2) Consecutive dots are not allowed
if (!fChar)
return(FALSE);
// Reset the flag
fChar = FALSE;
}
else if (pIsC(ch))
fChar = TRUE;
else
return(FALSE);
}
// Cannot end with a dot
if (ch == '.')
return(FALSE);
return(TRUE);
}
//
// Note: Original RFC 821:
// <name> ::= <a> <ldh-str> <let-dig>
// <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
// <let-dig> ::= <a> | <d>
// <let-dig-hyp> ::= <a> | <d> | "-"
//
// Our implementation:
// <name> ::= <let-dig-hyp-und> | <let-dig-hyp-und> <name>
// <let-dig-hyp-und> ::= <a> | <d> | "-" | "_"
//
// Reasons:
// 1) 3COM start their domains with a digit
// 2) Some customers start their domain names with underscores,
// and some comtain underscores.
//
static BOOL pValidateName(char *lpszStart, DWORD dwLength)
{
char ch;
if (!dwLength)
return(FALSE);
while (dwLength--)
{
ch = *lpszStart++;
if (pIsA(ch) || pIsD(ch) || ch == '-' || ch == '_')
;
else
return(FALSE);
}
return(TRUE);
}
//
// <local-part> ::= <dot-string> | <quoted-string>
//
static BOOL pValidateLocalPart(char *lpszStart, DWORD dwLength)
{
if (!dwLength)
return(FALSE);
return(pValidateDotString(lpszStart, dwLength) ||
pValidateQuotedString(lpszStart, dwLength));
}
//
// <element> ::= <name> | "#" <number> | "[" <dotnum> "]"
//
static BOOL pValidateElement(char *lpszStart, DWORD dwLength)
{
char ch;
if (!dwLength)
return(FALSE);
ch = *lpszStart;
if (ch == '#')
// This is the # <number> form
return(pValidateNumber(lpszStart+1, dwLength-1));
else if (ch == '[')
{
if (lpszStart[dwLength-1] != ']')
return(FALSE);
// This is a domain literal
return(pValidateDotnum(lpszStart+1, dwLength-2));
}
// Validate as a name
return(pValidateName(lpszStart, dwLength));
}
//
// sub-domain ::= let-dig *(ldh-str)
// ldh-str = *( Alpha / Digit / "-" ) let-dig
// let-dig = Alpha / Digit
//
static BOOL pValidateDRUMSSubDomain(char *lpszStart, DWORD dwLength)
{
unsigned char ch;
DWORD ec;
if (!dwLength)
return(FALSE);
// validate all of the characters in the name
while (dwLength--)
{
ch = (unsigned char) *lpszStart++;
// this list of characters comes from NT, dnsvldnm.doc. we
// also allow #, [, and ]
if ((ch >= 1 && ch <= 34) ||
(ch >= 36 && ch <= 41) ||
(ch == 43) ||
(ch == 44) ||
(ch == 47) ||
(ch >= 58 && ch <= 64) ||
(ch == 92) ||
(ch == 94) ||
(ch == 96) ||
(ch >= 123))
{
return FALSE;
}
} //while
//We have a valid subdomain
return (TRUE);
}
//
// ======================================================
//
BOOL FindNextUnquotedOccurrence(char *lpszString,
DWORD dwStringLength,
char cSearch,
char **ppszLocation)
{
BOOL fNotFound = FALSE;
*ppszLocation = pFindNextUnquotedOccurrence(lpszString,
dwStringLength,cSearch, acOpen,acClose, &fNotFound);
if (!*ppszLocation)
{
// If failed but not because of not found, then bad line
if (!fNotFound)
{
SetLastError(ERROR_INVALID_DATA);
return FALSE;
}
}
return TRUE;
}
//
// This function extracts an email address from the given command line
// and returns the tail of the line after the address. Any angle braces
// present will be included as part of the 821 address. The returned
// address is not validated at all.
//
BOOL Extract821AddressFromLine( char *lpszLine,
char **ppszAddress,
DWORD *pdwAddressLength,
char **ppszTail)
{
DWORD dwAddressLength = 0;
char *pAddressEnd;
BOOL fNotFound;
TraceFunctEnter("Extract821AddressFromLine");
_ASSERT(lpszLine);
_ASSERT(ppszAddress);
_ASSERT(pdwAddressLength);
_ASSERT(ppszTail);
// Initialize
*ppszAddress = lpszLine;
*pdwAddressLength = 0;
*ppszTail = lpszLine;
// Routine checking
if (!lpszLine ||
// Big enough for MAX_INTERNET_NAME + any options on mail from/rcpt to
!pValidateStringPtr(lpszLine, MAX_INTERNET_NAME + 2000) ||
!ppszAddress ||
IsBadWritePtr(ppszAddress, sizeof(char *)) ||
!ppszTail ||
IsBadWritePtr(ppszTail, sizeof(char *)) ||
!pdwAddressLength ||
IsBadWritePtr(pdwAddressLength, sizeof(DWORD)))
{
SetLastError(ERROR_INVALID_DATA);
TraceFunctLeave();
return(FALSE);
}
// Skip all leading spaces
while (*lpszLine == ' ')
lpszLine++;
// The first unquoted space indicates the end of the address
pAddressEnd = pFindNextUnquotedOccurrence(lpszLine,
lstrlen(lpszLine), ' ', acOpen, acClose, &fNotFound);
if (!pAddressEnd)
{
// If failed but not because of not found, then bad line
if (!fNotFound)
return(FALSE);
// Space not found, the entire line is the address
dwAddressLength = lstrlen(lpszLine);
pAddressEnd = lpszLine + dwAddressLength;
*ppszTail = pAddressEnd;
}
else
{
// Calculate the length
dwAddressLength = (DWORD)(pAddressEnd - lpszLine);
// Get the start of the tail, after all the spaces
while (*pAddressEnd == ' ')
pAddressEnd++;
*ppszTail = pAddressEnd;
}
if (dwAddressLength < 1 || dwAddressLength > MAX_INTERNET_NAME)
return(FALSE);
*ppszAddress = lpszLine;
*pdwAddressLength = dwAddressLength;
DebugTrace((LPARAM)0, "Extracted \"%*s\"", dwAddressLength, lpszLine);
TraceFunctLeave();
return(TRUE);
}
//
// This function takes in a RFC 821 address with optional angle braces
// and extracts the canonical form of the address. All at-domain-list
// entries are removed. Angle braces will be matched and removed.
// Mismatched angle braces are considered invalid. The returned address
// will be in the <local-part> "@" <domain> form.
//
// There must be no leading or trailing spaces included.
//
// jstamerj 1999/01/13 14:02:13: Modified to remove a trailing '.' from the <domain> portion of the address
//
BOOL ExtractCanonical821Address( char *lpszAddress,
DWORD dwAddressLength,
char **ppszCanonicalAddress,
DWORD *pdwCanonicalAddressLength)
{
char *pAddressStart;
BOOL fNotFound;
TraceFunctEnter("ExtractCanonical821Address");
_ASSERT(lpszAddress);
_ASSERT(ppszCanonicalAddress);
_ASSERT(pdwCanonicalAddressLength);
// Initialize
*ppszCanonicalAddress = lpszAddress;
*ppszCanonicalAddress = 0;
// Routine checking
if (!lpszAddress ||
// Big enough for MAX_INTERNET_NAME + any options on mail from/rcpt to
!pValidateStringPtr(lpszAddress, MAX_INTERNET_NAME + 2000) ||
!ppszCanonicalAddress ||
IsBadWritePtr(ppszCanonicalAddress, sizeof(char *)) ||
!pdwCanonicalAddressLength ||
IsBadWritePtr(pdwCanonicalAddressLength, sizeof(DWORD)))
{
SetLastError(ERROR_INVALID_DATA);
TraceFunctLeave();
return(FALSE);
}
// See how many layers of nesting we have, and match
// each pair of angle braces
while (*lpszAddress == '<')
{
if (!dwAddressLength--)
return(FALSE);
if (lpszAddress[dwAddressLength] != '>')
return(FALSE);
if (!dwAddressLength--)
return(FALSE);
lpszAddress++;
}
// Next, skip all at-domain-list entries and get to
// the meat of the address
do
{
// Skip all leading spaces
while (*lpszAddress == ' ')
{
lpszAddress++;
if (!dwAddressLength--)
return(FALSE);
}
//skip all the trailing spaces
while (*(lpszAddress + dwAddressLength - 1) == ' ')
{
if (!dwAddressLength--)
return(FALSE);
}
// Initialize lest it falls through right away
pAddressStart = lpszAddress;
if (*lpszAddress == '@')
{
// Yep, there's a domain route there ...
// Skip it ...
pAddressStart = pFindNextUnquotedOccurrence(lpszAddress,
dwAddressLength, ',', acOpen, acClose, &fNotFound);
if (!pAddressStart)
{
if (!fNotFound)
return(FALSE);
// No comma, now see if we get a semicolon
pAddressStart = pFindNextUnquotedOccurrence(lpszAddress,
dwAddressLength, ':', acOpen, acClose, &fNotFound);
if (!pAddressStart)
{
// No semicolon either, this is a bad address
return(FALSE);
}
// This is a semicolon, so we break out
pAddressStart++;
dwAddressLength -= (DWORD)(pAddressStart - lpszAddress);
break;
}
// We have a comma, we let it iterate
pAddressStart++;
dwAddressLength -= (DWORD)(pAddressStart - lpszAddress);
lpszAddress = pAddressStart;
}
else
break;
} while (dwAddressLength);
// Skip all leading spaces
while (*pAddressStart == ' ')
{
pAddressStart++;
if (!dwAddressLength--)
return(FALSE);
}
if((dwAddressLength > 1) && // Must be at least 2 for the address "@."
(pAddressStart[dwAddressLength-1] == '.')) {
//
// jstamerj 1999/01/13 14:05:39:
// If the domain part of the address has a trailing '.', do
// not count it in the canonical length
//
LPSTR pDomain;
BOOL fNotFound;
// Find the domain
pDomain = pFindNextUnquotedOccurrence(
pAddressStart,
dwAddressLength - 1,
'@',
acOpen,
acClose,
&fNotFound);
//
// If we found the '@' and the '.' is after the '@' (it must
// be if we really found it), then shorten the canonical
// address so that it doesn't include '.'
//
if((fNotFound == FALSE) &&
(&(pAddressStart[dwAddressLength]) > pDomain))
dwAddressLength--;
}
if (dwAddressLength < 1 || dwAddressLength > MAX_INTERNET_NAME)
return(FALSE);
// Fill in the output
*ppszCanonicalAddress = pAddressStart;
*pdwCanonicalAddressLength = dwAddressLength;
DebugTrace((LPARAM)0, "Extracted \"%*s\"", dwAddressLength, pAddressStart);
TraceFunctLeave();
return(TRUE);
}
//
// This function takes in a RFC 821 domain in canonical form
// and validates it according to the RFC 821 grammar
// (some modifications for real-life scenarios)
//
// <domain> ::= <element> | <element> "." <domain>
//
BOOL Validate821Domain( char *lpszDomain,
DWORD dwDomainLength)
{
char *pSubdomainOffset;
DWORD dwSubdomainLength;
BOOL fNotFound;
TraceFunctEnter("Validate821Domain");
_ASSERT(lpszDomain);
// Routine checking
if (!lpszDomain ||
!pValidateStringPtr(lpszDomain, MAX_INTERNET_NAME+1))
{
SetLastError(ERROR_INVALID_DATA);
TraceFunctLeave();
return(FALSE);
}
// Find each subdomain
do
{
pSubdomainOffset = pFindNextUnquotedOccurrence(lpszDomain,
dwDomainLength, '.', acOpen, acClose, &fNotFound);
if (!pSubdomainOffset)
{
if (!fNotFound)
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
// Not found and nothing left, domain ends with a dot, invalid.
if (!dwDomainLength)
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
// No domain, so email alias is all there is
dwSubdomainLength = dwDomainLength;
}
else
{
// Calculate domain parameters
dwSubdomainLength = (DWORD)(pSubdomainOffset - lpszDomain);
// Adjust for the dot
dwDomainLength--;
}
// Cannot allow leading dot or consecutive dots
if (!dwSubdomainLength)
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
// Check each subdomain as an element
if (!pValidateElement(lpszDomain, dwSubdomainLength))
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
// Adjust the length and pointers
dwDomainLength -= dwSubdomainLength;
// Skip past dot and scan again
lpszDomain = pSubdomainOffset + 1;
} while (dwDomainLength);
// Make sure no dot's found, either
if (!fNotFound)
{
// If a dot's found, the domain ends with a dot and it's uncool.
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
TraceFunctLeave();
return(TRUE);
}
//
// This function takes in a DRUMS domain in canonical form
// and validates it strictly, according to the DRUMS grammar
//
// Domain ::= sub-domain 1*("." sub-domain) | address-literal
// address-literal ::= "[" IPv4-address-literal |
// IPv6-address-literal | General-address-literal "]"
// IPv4-address-literal ::= snum 3("." snum)
// IPv6-address-literal ::= "IPv6" SP <<what did we finally decide on?>>
// General-address-literal ::= Standardized-tag SP String
// Standardized-tag ::= String (Specified in a standards-track RFC
// and registered with IANA)
// snum = one, two, or three digits representing a decimal
// integer value in the range 0 through 255
BOOL ValidateDRUMSDomain( char *lpszDomain,
DWORD dwDomainLength)
{
char *pSubdomainOffset;
DWORD dwSubdomainLength;
BOOL fNotFound;
char *szEndofString;
TraceFunctEnter("Validate821Domain");
_ASSERT(lpszDomain);
// Routine checking
if (!dwDomainLength || dwDomainLength > MAX_INTERNET_NAME)
return(FALSE);
if (!lpszDomain ||
!pValidateStringPtr(lpszDomain, MAX_INTERNET_NAME+1))
{
SetLastError(ERROR_INVALID_DATA);
TraceFunctLeave();
return(FALSE);
}
// Skip all leading spaces
while (*lpszDomain == ' ')
lpszDomain++;
//It has to be either in address-literal format or subdomain format
//
if (*lpszDomain == '[')
{
//It is an Address literal
//Skip trailing white space
szEndofString = &lpszDomain[lstrlen(lpszDomain) - 1];
while(*szEndofString == ' ')
szEndofString--;
if (*szEndofString != ']')
return(FALSE);
// This is a domain literal
return(pValidateDotnum(lpszDomain+1, dwDomainLength-2));
}
else
{
//This is in subdomain format
do
{
pSubdomainOffset = pFindNextUnquotedOccurrence(lpszDomain,
dwDomainLength, '.', acOpen, acClose, &fNotFound);
if (!pSubdomainOffset)
{
if (!fNotFound)
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
// Not found and nothing left, domain ends with a dot, invalid.
if (!dwDomainLength)
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
// No domain, so email alias is all there is
dwSubdomainLength = dwDomainLength;
}
else
{
// Calculate domain parameters
dwSubdomainLength = (DWORD)(pSubdomainOffset - lpszDomain);
// Adjust for the dot
//NimishK : **Check with Keith if this should be subdomain.
dwDomainLength--;
}
// Cannot allow leading dot or consecutive dots
if (!dwSubdomainLength)
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
// Check each subdomain
if (!pValidateDRUMSSubDomain(lpszDomain, dwSubdomainLength))
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
// Adjust the length and pointers
dwDomainLength -= dwSubdomainLength;
// Skip past dot and scan again
lpszDomain = pSubdomainOffset + 1;
} while (dwDomainLength);
// Make sure no dot's found, either
if (!fNotFound)
{
// If a dot's found, the domain ends with a dot and it's uncool.
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
TraceFunctLeave();
return(TRUE);
}
TraceFunctLeave();
return(TRUE);
}
//
// This function takes in a RFC 821 address in canonical form
// (<local-part> ["@" <domain>]) and validates it according to the
// RFC 821 grammar (some modifications for real-life scenarios)
//
BOOL Validate821Address( char *lpszAddress,
DWORD dwAddressLength)
{
char *pDomainOffset;
DWORD dwEmailLength;
DWORD dwDomainLength;
BOOL fNotFound;
TraceFunctEnter("Validate821Address");
_ASSERT(lpszAddress);
// Routine checking
if (!lpszAddress ||
!pValidateStringPtr(lpszAddress, MAX_INTERNET_NAME+1))
{
SetLastError(ERROR_INVALID_DATA);
TraceFunctLeave();
return(FALSE);
}
// Find the domain
pDomainOffset = pFindNextUnquotedOccurrence(lpszAddress,
dwAddressLength, '@', acOpen, acClose, &fNotFound);
if (!pDomainOffset)
{
if (!fNotFound)
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
// No domain, so email alias is all there is
dwEmailLength = dwAddressLength;
}
else
{
// Calculate domain parameters
dwEmailLength = (DWORD)(pDomainOffset - lpszAddress);
dwDomainLength = dwAddressLength - dwEmailLength - 1;
pDomainOffset++;
}
// Do the check for email name
if (!pValidateLocalPart(lpszAddress, dwEmailLength))
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
// Now check domain, if applicable
if (pDomainOffset)
{
return(Validate821Domain(pDomainOffset, dwDomainLength));
}
TraceFunctLeave();
return(TRUE);
}
//
// This function takes in a RFC 821 address in canonical form
// (<local-part> ["@" <domain>]) and extracts the domain part
//
BOOL Get821AddressDomain( char *lpszAddress,
DWORD dwAddressLength,
char **ppszDomain)
{
char *pDomainOffset;
BOOL fNotFound = FALSE;
BOOL fReturn = TRUE;
TraceFunctEnter("Get821AddressDomain");
_ASSERT(lpszAddress);
// Find the domain
pDomainOffset = pFindNextUnquotedOccurrence(lpszAddress,
dwAddressLength, '@', acOpen, acClose, &fNotFound);
if (!pDomainOffset && !fNotFound)
{
SetLastError(ERROR_INVALID_DATA);
fReturn = FALSE;
goto Exit;
}
if (fNotFound)
{
*ppszDomain = NULL;
goto Exit;
}
*ppszDomain = pDomainOffset + 1;
// Validate that the domain part is <= 255 chars
if ((dwAddressLength - (*ppszDomain - lpszAddress)) > 255)
{
*ppszDomain = NULL;
SetLastError(ERROR_INVALID_DATA);
fReturn = FALSE;
goto Exit;
}
Exit:
TraceFunctLeave();
return fReturn;
}