windows-nt/Source/XPSP1/NT/net/homenet/bridge/sys/brdgtdi.c

946 lines
27 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright(c) 1999-2002 Microsoft Corporation
Module Name:
brdgtdi.c
Abstract:
Ethernet MAC level bridge.
Tdi registration for address notifications.
Author:
Salahuddin J. Khan (sjkhan)
Environment:
Kernel mode
Revision History:
March 2002 - Original version
--*/
#define NDIS_MINIPORT_DRIVER
#define NDIS50_MINIPORT 1
#define NDIS_WDM 1
#pragma warning( push, 3 )
#include <ndis.h>
#include <tdikrnl.h>
#include <ntstatus.h>
#include <wchar.h>
#pragma warning( pop )
#include "bridge.h"
#include "brdgtdi.h"
#include "brdgsta.h"
#include "brdgmini.h"
#include "brdgprot.h"
#include "brdgbuf.h"
#include "brdgfwd.h"
#include "brdgtbl.h"
#include "brdgctl.h"
#include "brdggpo.h"
// ===========================================================================
//
// GLOBALS
//
// ===========================================================================
BRDG_TDI_GLOBALS g_BrdgTdiGlobals;
// ===========================================================================
//
// CONSTANTS
//
// ===========================================================================
#define MAX_GUID_LEN 39
#define MAX_IP4_STRING_LEN 17
const WCHAR TcpipAdaptersKey[] = {L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Adapters"};
// ===========================================================================
//
// PRIVATE PROTOTYPES
//
// ===========================================================================
NTSTATUS
BrdgTdiPnpPowerHandler(
IN PUNICODE_STRING DeviceName,
IN PNET_PNP_EVENT PowerEvent,
IN PTDI_PNP_CONTEXT Context1,
IN PTDI_PNP_CONTEXT Context2
);
VOID
BrdgTdiBindingHandler(
IN TDI_PNP_OPCODE PnPOpcode,
IN PUNICODE_STRING DeviceName,
IN PWSTR MultiSZBindList
);
VOID
BrdgTdiAddAddressHandler(
IN PTA_ADDRESS Address,
IN PUNICODE_STRING DeviceName,
IN PTDI_PNP_CONTEXT Context
);
VOID
BrdgTdiDelAddressHandler(
IN PTA_ADDRESS Address,
IN PUNICODE_STRING DeviceName,
IN PTDI_PNP_CONTEXT Context
);
VOID
TSPrintTaAddress(PTA_ADDRESS pTaAddress);
// ===========================================================================
//
// INLINE FUNCTIONS
//
// ===========================================================================
__forceinline
BOOLEAN
IsLower(WCHAR c)
{
return (BOOLEAN)((c >= L'a') && (c <= 'z'));
}
__forceinline
BOOLEAN
IsDigit(WCHAR c)
{
return (BOOLEAN)((c >= L'0') && (c <= '9'));
}
__forceinline
BOOLEAN
IsXDigit(WCHAR c)
{
return (BOOLEAN)( ((c >= L'0') && (c <= '9')) || ((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'F')) );
}
// ===========================================================================
//
// BRIDGE TDI IMPLEMENTATION
//
// ===========================================================================
VOID
BrdgTdiInitializeClientInterface(
IN PTDI_CLIENT_INTERFACE_INFO ClientInterfaceInfo,
IN PUNICODE_STRING ClientName
)
{
DBGPRINT(TDI, ("BrdgTdiInitializeClientInterface\r\n"));
ClientInterfaceInfo->MajorTdiVersion = TDI_CURRENT_MAJOR_VERSION;
ClientInterfaceInfo->MinorTdiVersion = TDI_CURRENT_MINOR_VERSION;
ClientInterfaceInfo->ClientName = ClientName;
ClientInterfaceInfo->PnPPowerHandler = BrdgTdiPnpPowerHandler;
ClientInterfaceInfo->BindingHandler = BrdgTdiBindingHandler;
ClientInterfaceInfo->AddAddressHandlerV2 = BrdgTdiAddAddressHandler;
ClientInterfaceInfo->DelAddressHandlerV2 = BrdgTdiDelAddressHandler;
}
NTSTATUS
BrdgTdiDriverInit()
/*++
Routine Description:
Driver load-time initialization
Return Value:
Status of initialization
Locking Constraints:
Top-level function. Assumes no locks are held by caller.
--*/
{
NTSTATUS status;
DBGPRINT(TDI, ("BrdgTdiDriverInit\r\n"));
RtlInitUnicodeString(&g_BrdgTdiGlobals.ClientName, L"Bridge");
RtlZeroMemory(&g_BrdgTdiGlobals.ciiBridge, sizeof(TDI_CLIENT_INTERFACE_INFO));
BrdgTdiInitializeClientInterface(&g_BrdgTdiGlobals.ciiBridge, &g_BrdgTdiGlobals.ClientName);
status = BrdgGpoDriverInit();
if (!NT_SUCCESS(status))
{
BrdgTdiCleanup();
}
else
{
status = TdiRegisterPnPHandlers(&g_BrdgTdiGlobals.ciiBridge,
sizeof(TDI_CLIENT_INTERFACE_INFO),
&g_BrdgTdiGlobals.hBindingHandle);
}
return status;
}
VOID
BrdgTdiCleanup()
/*++
Routine Description:
Driver shutdown cleanup
Return Value:
None
Locking Constraints:
Top-level function. Assumes no locks are held by caller.
--*/
{
NTSTATUS status;
status = TdiDeregisterPnPHandlers(g_BrdgTdiGlobals.hBindingHandle);
SAFEASSERT(NT_SUCCESS(status));
BrdgGpoCleanup();
}
NTSTATUS
BrdgTdiPnpPowerHandler(
IN PUNICODE_STRING DeviceName,
IN PNET_PNP_EVENT PowerEvent,
IN PTDI_PNP_CONTEXT Context1,
IN PTDI_PNP_CONTEXT Context2
)
{
DBGPRINT(TDI, ("BrdgTdiPnpPowerHandler\r\n"));
return STATUS_SUCCESS;
}
VOID
BrdgTdiBindingHandler(
IN TDI_PNP_OPCODE PnPOpcode,
IN PUNICODE_STRING DeviceName,
IN PWSTR MultiSZBindList
)
{
DBGPRINT(TDI, ("BrdgTdiBindingHandler\r\n"));
}
VOID
BrdgTdiAddAddressHandler(
IN PTA_ADDRESS Address,
IN PUNICODE_STRING DeviceName,
IN PTDI_PNP_CONTEXT Context
)
/*++
Routine Description:
Called if a new address is added.
Arguments:
Address - New address that has been added.
DeviceName - The device that this is changing for.
Context - Not something we're interested in for now.
Return Value:
None.
--*/
{
DBGPRINT(TDI, ("BrdgTdiAddAddressHandler\r\n"));
if ((Address->AddressType == TDI_ADDRESS_TYPE_IP))
{
if (NULL != DeviceName->Buffer)
{
//
// Find the start of the GUID
//
PWCHAR DeviceId = wcsrchr(DeviceName->Buffer, L'{');
if (NULL != DeviceId)
{
NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES;
LPWSTR AdapterPath;
AdapterPath = ExAllocatePoolWithTag(PagedPool,
(wcslen(TcpipAdaptersKey) + 1 + wcslen(DeviceId) + 1) * sizeof(WCHAR),
'gdrB');
if (AdapterPath)
{
OBJECT_ATTRIBUTES ObAttr;
UNICODE_STRING Adapter;
HANDLE hKey;
wcscpy(AdapterPath, TcpipAdaptersKey);
wcscat(AdapterPath, L"\\");
wcscat(AdapterPath, DeviceId);
RtlInitUnicodeString(&Adapter, AdapterPath);
InitializeObjectAttributes( &ObAttr,
&Adapter,
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
NULL,
NULL);
status = ZwOpenKey(&hKey,
KEY_READ,
&ObAttr);
if (NT_SUCCESS(status))
{
ZwClose(hKey);
//
// This is a valid adapter on this machine. Otherwise it could be an NdisAdapter etc and
// we don't pay attention to these for group policies.
//
BrdgGpoNewAddressNotification(DeviceId);
}
ExFreePool(AdapterPath);
}
}
}
#if DBG
TSPrintTaAddress(Address);
#endif
}
}
VOID
BrdgTdiDelAddressHandler(
IN PTA_ADDRESS Address,
IN PUNICODE_STRING DeviceName,
IN PTDI_PNP_CONTEXT Context
)
{
DBGPRINT(TDI, ("BrdgTdiDelAddressHandler\r\n"));
//
// We don't delete the current list of networks that we have since we need them to make
// an accurate assessment on whether to follow the GPO. Instead, the AddAddressHandler
// will simply update the existing network address for the ID's and if this results in
// a different network then we'll change the bridge mode.
//
}
VOID
TSPrintTaAddress(PTA_ADDRESS pTaAddress)
{
BOOLEAN fShowAddress = TRUE;
DbgPrint("AddressType = TDI_ADDRESS_TYPE_");
switch (pTaAddress->AddressType)
{
case TDI_ADDRESS_TYPE_UNSPEC:
DbgPrint("UNSPEC\n");
break;
case TDI_ADDRESS_TYPE_UNIX:
DbgPrint("UNIX\n");
break;
case TDI_ADDRESS_TYPE_IP:
DbgPrint("IP\n");
fShowAddress = FALSE;
{
PTDI_ADDRESS_IP pTdiAddressIp = (PTDI_ADDRESS_IP)pTaAddress->Address;
PUCHAR pucTemp = (PUCHAR)&pTdiAddressIp->in_addr;
DbgPrint("sin_port = 0x%04x\n"
"in_addr = %u.%u.%u.%u\n",
pTdiAddressIp->sin_port,
pucTemp[0], pucTemp[1],
pucTemp[2], pucTemp[3]);
}
break;
case TDI_ADDRESS_TYPE_IMPLINK:
DbgPrint("IMPLINK\n");
break;
case TDI_ADDRESS_TYPE_PUP:
DbgPrint("PUP\n");
break;
case TDI_ADDRESS_TYPE_CHAOS:
DbgPrint("CHAOS\n");
break;
case TDI_ADDRESS_TYPE_IPX:
DbgPrint("IPX\n");
fShowAddress = FALSE;
{
PTDI_ADDRESS_IPX pTdiAddressIpx = (PTDI_ADDRESS_IPX)pTaAddress->Address;
DbgPrint("NetworkAddress = 0x%08x\n"
"NodeAddress = %u.%u.%u.%u.%u.%u\n"
"Socket = 0x%04x\n",
pTdiAddressIpx->NetworkAddress,
pTdiAddressIpx->NodeAddress[0],
pTdiAddressIpx->NodeAddress[1],
pTdiAddressIpx->NodeAddress[2],
pTdiAddressIpx->NodeAddress[3],
pTdiAddressIpx->NodeAddress[4],
pTdiAddressIpx->NodeAddress[5],
pTdiAddressIpx->Socket);
}
break;
case TDI_ADDRESS_TYPE_NBS:
DbgPrint("NBS\n");
break;
case TDI_ADDRESS_TYPE_ECMA:
DbgPrint("ECMA\n");
break;
case TDI_ADDRESS_TYPE_DATAKIT:
DbgPrint("DATAKIT\n");
break;
case TDI_ADDRESS_TYPE_CCITT:
DbgPrint("CCITT\n");
break;
case TDI_ADDRESS_TYPE_SNA:
DbgPrint("SNA\n");
break;
case TDI_ADDRESS_TYPE_DECnet:
DbgPrint("DECnet\n");
break;
case TDI_ADDRESS_TYPE_DLI:
DbgPrint("DLI\n");
break;
case TDI_ADDRESS_TYPE_LAT:
DbgPrint("LAT\n");
break;
case TDI_ADDRESS_TYPE_HYLINK:
DbgPrint("HYLINK\n");
break;
case TDI_ADDRESS_TYPE_APPLETALK:
DbgPrint("APPLETALK\n");
fShowAddress = FALSE;
{
PTDI_ADDRESS_APPLETALK pTdiAddressAppleTalk = (PTDI_ADDRESS_APPLETALK)pTaAddress->Address;
DbgPrint("Network = 0x%04x\n"
"Node = 0x%02x\n"
"Socket = 0x%02x\n",
pTdiAddressAppleTalk->Network,
pTdiAddressAppleTalk->Node,
pTdiAddressAppleTalk->Socket);
}
break;
case TDI_ADDRESS_TYPE_NETBIOS:
DbgPrint("NETBIOS\n");
fShowAddress = FALSE;
{
PTDI_ADDRESS_NETBIOS pTdiAddressNetbios = (PTDI_ADDRESS_NETBIOS)pTaAddress->Address;
UCHAR pucName[17];
//
// make sure we have a zero-terminated name to print...
//
RtlCopyMemory(pucName, pTdiAddressNetbios->NetbiosName, 16);
pucName[16] = 0;
DbgPrint("NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_");
switch (pTdiAddressNetbios->NetbiosNameType)
{
case TDI_ADDRESS_NETBIOS_TYPE_UNIQUE:
DbgPrint("UNIQUE\n");
break;
case TDI_ADDRESS_NETBIOS_TYPE_GROUP:
DbgPrint("GROUP\n");
break;
case TDI_ADDRESS_NETBIOS_TYPE_QUICK_UNIQUE:
DbgPrint("QUICK_UNIQUE\n");
break;
case TDI_ADDRESS_NETBIOS_TYPE_QUICK_GROUP:
DbgPrint("QUICK_GROUP\n");
break;
default:
DbgPrint("INVALID [0x%04x]\n",
pTdiAddressNetbios->NetbiosNameType);
break;
}
DbgPrint("NetbiosName = %s\n", pucName);
}
break;
case TDI_ADDRESS_TYPE_8022:
DbgPrint("8022\n");
fShowAddress = FALSE;
{
PTDI_ADDRESS_8022 pTdiAddress8022 = (PTDI_ADDRESS_8022)pTaAddress->Address;
DbgPrint("Address = %02x-%02x-%02x-%02x-%02x-%02x\n",
pTdiAddress8022->MACAddress[0],
pTdiAddress8022->MACAddress[1],
pTdiAddress8022->MACAddress[2],
pTdiAddress8022->MACAddress[3],
pTdiAddress8022->MACAddress[4],
pTdiAddress8022->MACAddress[5]);
}
break;
case TDI_ADDRESS_TYPE_OSI_TSAP:
DbgPrint("OSI_TSAP\n");
fShowAddress = FALSE;
{
PTDI_ADDRESS_OSI_TSAP pTdiAddressOsiTsap = (PTDI_ADDRESS_OSI_TSAP)pTaAddress->Address;
ULONG ulSelectorLength;
ULONG ulAddressLength;
PUCHAR pucTemp = pTdiAddressOsiTsap->tp_addr;
DbgPrint("TpAddrType = ISO_");
switch (pTdiAddressOsiTsap->tp_addr_type)
{
case ISO_HIERARCHICAL:
DbgPrint("HIERARCHICAL\n");
ulSelectorLength = pTdiAddressOsiTsap->tp_tsel_len;
ulAddressLength = pTdiAddressOsiTsap->tp_taddr_len;
break;
case ISO_NON_HIERARCHICAL:
DbgPrint("NON_HIERARCHICAL\n");
ulSelectorLength = 0;
ulAddressLength = pTdiAddressOsiTsap->tp_taddr_len;
break;
default:
DbgPrint("INVALID [0x%04x]\n",
pTdiAddressOsiTsap->tp_addr_type);
ulSelectorLength = 0;
ulAddressLength = 0;
break;
}
if (ulSelectorLength)
{
ULONG ulCount;
DbgPrint("TransportSelector: ");
for (ulCount = 0; ulCount < ulSelectorLength; ulCount++)
{
DbgPrint("%02x ", *pucTemp);
++pucTemp;
}
DbgPrint("\n");
}
if (ulAddressLength)
{
ULONG ulCount;
DbgPrint("TransportAddress: ");
for (ulCount = 0; ulCount < ulAddressLength; ulCount++)
{
DbgPrint("%02x ", *pucTemp);
++pucTemp;
}
DbgPrint("\n");
}
}
break;
case TDI_ADDRESS_TYPE_NETONE:
DbgPrint("NETONE\n");
fShowAddress = FALSE;
{
PTDI_ADDRESS_NETONE pTdiAddressNetone = (PTDI_ADDRESS_NETONE)pTaAddress->Address;
UCHAR pucName[21];
//
// make sure have 0-terminated name
//
RtlCopyMemory(pucName,
pTdiAddressNetone->NetoneName,
20);
pucName[20] = 0;
DbgPrint("NetoneNameType = TDI_ADDRESS_NETONE_TYPE_");
switch (pTdiAddressNetone->NetoneNameType)
{
case TDI_ADDRESS_NETONE_TYPE_UNIQUE:
DbgPrint("UNIQUE\n");
break;
case TDI_ADDRESS_NETONE_TYPE_ROTORED:
DbgPrint("ROTORED\n");
break;
default:
DbgPrint("INVALID [0x%04x]\n",
pTdiAddressNetone->NetoneNameType);
break;
}
DbgPrint("NetoneName = %s\n",
pucName);
}
break;
case TDI_ADDRESS_TYPE_VNS:
DbgPrint("VNS\n");
fShowAddress = FALSE;
{
PTDI_ADDRESS_VNS pTdiAddressVns = (PTDI_ADDRESS_VNS)pTaAddress->Address;
DbgPrint("NetAddress: %02x-%02x-%02x-%02x\n",
pTdiAddressVns->net_address[0],
pTdiAddressVns->net_address[1],
pTdiAddressVns->net_address[2],
pTdiAddressVns->net_address[3]);
DbgPrint("SubnetAddr: %02x-%02x\n"
"Port: %02x-%02x\n"
"Hops: %u\n",
pTdiAddressVns->subnet_addr[0],
pTdiAddressVns->subnet_addr[1],
pTdiAddressVns->port[0],
pTdiAddressVns->port[1],
pTdiAddressVns->hops);
}
break;
case TDI_ADDRESS_TYPE_NETBIOS_EX:
DbgPrint("NETBIOS_EX\n");
fShowAddress = FALSE;
{
PTDI_ADDRESS_NETBIOS_EX pTdiAddressNetbiosEx = (PTDI_ADDRESS_NETBIOS_EX)pTaAddress->Address;
UCHAR pucEndpointName[17];
UCHAR pucNetbiosName[17];
//
// make sure we have zero-terminated names to print...
//
RtlCopyMemory(pucEndpointName,
pTdiAddressNetbiosEx->EndpointName,
16);
pucEndpointName[16] = 0;
RtlCopyMemory(pucNetbiosName,
pTdiAddressNetbiosEx->NetbiosAddress.NetbiosName,
16);
pucNetbiosName[16] = 0;
DbgPrint("EndpointName = %s\n"
"NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_",
pucEndpointName);
switch (pTdiAddressNetbiosEx->NetbiosAddress.NetbiosNameType)
{
case TDI_ADDRESS_NETBIOS_TYPE_UNIQUE:
DbgPrint("UNIQUE\n");
break;
case TDI_ADDRESS_NETBIOS_TYPE_GROUP:
DbgPrint("GROUP\n");
break;
case TDI_ADDRESS_NETBIOS_TYPE_QUICK_UNIQUE:
DbgPrint("QUICK_UNIQUE\n");
break;
case TDI_ADDRESS_NETBIOS_TYPE_QUICK_GROUP:
DbgPrint("QUICK_GROUP\n");
break;
default:
DbgPrint("INVALID [0x%04x]\n",
pTdiAddressNetbiosEx->NetbiosAddress.NetbiosNameType);
break;
}
DbgPrint("NetbiosName = %s\n", pucNetbiosName);
}
break;
case TDI_ADDRESS_TYPE_IP6:
DbgPrint("IPv6\n");
fShowAddress = FALSE;
{
PTDI_ADDRESS_IP6 pTdiAddressIp6 = (PTDI_ADDRESS_IP6)pTaAddress->Address;
PUCHAR pucTemp = (PUCHAR)&pTdiAddressIp6->sin6_addr;
DbgPrint("SinPort6 = 0x%04x\n"
"FlowInfo = 0x%08x\n"
"ScopeId = 0x%08x\n",
pTdiAddressIp6->sin6_port,
pTdiAddressIp6->sin6_flowinfo,
pTdiAddressIp6->sin6_scope_id);
DbgPrint("In6_addr = %x%02x:%x%02x:%x%02x:%x%02x:",
pucTemp[0], pucTemp[1],
pucTemp[2], pucTemp[3],
pucTemp[4], pucTemp[5],
pucTemp[6], pucTemp[7]);
DbgPrint("%x%02x:%x%02x:%x%02x:%x%02x\n",
pucTemp[8], pucTemp[9],
pucTemp[10], pucTemp[11],
pucTemp[12], pucTemp[13],
pucTemp[14], pucTemp[15]);
}
break;
default:
DbgPrint("UNKNOWN [0x%08x]\n", pTaAddress->AddressType);
break;
}
if (fShowAddress)
{
PUCHAR pucTemp = pTaAddress->Address;
ULONG ulCount;
DbgPrint("AddressLength = %d\n"
"Address = ",
pTaAddress->AddressLength);
for (ulCount = 0; ulCount < pTaAddress->AddressLength; ulCount++)
{
DbgPrint("%02x ", *pucTemp);
pucTemp++;
}
DbgPrint("\n");
}
}
NTSTATUS
BrdgTdiIpv4StringToAddress(
IN LPWSTR String,
IN BOOLEAN Strict,
OUT LPWSTR *Terminator,
OUT in_addr *Addr)
/*++
Routine Description:
This function interprets the character string specified by the cp
parameter. This string represents a numeric Internet address
expressed in the Internet standard ".'' notation. The value
returned is a number suitable for use as an Internet address. All
Internet addresses are returned in network order (bytes ordered from
left to right).
Internet Addresses
Values specified using the "." notation take one of the following
forms:
a.b.c.d a.b.c a.b a
When four parts are specified, each is interpreted as a byte of data
and assigned, from left to right, to the four bytes of an Internet
address. Note that when an Internet address is viewed as a 32-bit
integer quantity on the Intel architecture, the bytes referred to
above appear as "d.c.b.a''. That is, the bytes on an Intel
processor are ordered from right to left.
Note: The following notations are only used by Berkeley, and nowhere
else on the Internet. In the interests of compatibility with their
software, they are supported as specified.
When a three part address is specified, the last part is interpreted
as a 16-bit quantity and placed in the right most two bytes of the
network address. This makes the three part address format
convenient for specifying Class B network addresses as
"128.net.host''.
When a two part address is specified, the last part is interpreted
as a 24-bit quantity and placed in the right most three bytes of the
network address. This makes the two part address format convenient
for specifying Class A network addresses as "net.host''.
When only one part is given, the value is stored directly in the
network address without any byte rearrangement.
Arguments:
String - A character string representing a number expressed in the
Internet standard "." notation.
Terminator - Receives a pointer to the character that terminated
the conversion.
Addr - Receives a pointer to the structure to fill in with
a suitable binary representation of the Internet address given.
Return Value:
TRUE if parsing was successful. FALSE otherwise.
--*/
{
ULONG val, n;
LONG base;
WCHAR c;
ULONG parts[4], *pp = parts;
BOOLEAN sawDigit;
again:
//
// We must see at least one digit for address to be valid.
//
sawDigit = FALSE;
//
// Collect number up to ``.''.
// Values are specified as for C:
// 0x=hex, 0=octal, other=decimal.
//
val = 0; base = 10;
if (*String == L'0')
{
String++;
if (IsDigit(*String))
{
base = 8;
} else if (*String == L'x' || *String == L'X')
{
base = 16;
String++;
} else
{
//
// It is still decimal but we saw the digit
// and it was 0.
//
sawDigit = TRUE;
}
}
if (Strict && (base != 10))
{
*Terminator = String;
return STATUS_INVALID_PARAMETER;
}
do
{
ULONG newVal;
c = *String;
if (IsDigit(c) && ((c - L'0') < base)) {
newVal = (val * base) + (c - L'0');
} else if ((base == 16) && IsXDigit(c)) {
newVal = (val << 4) + (c + 10 - (IsLower(c) ? L'a' : L'A'));
} else {
break;
}
//
// Protect from overflow
//
if (newVal < val) {
*Terminator = String;
return STATUS_INVALID_PARAMETER;
}
String++;
sawDigit = TRUE;
val = newVal;
} while (c != L'\0');
if (*String == L'.')
{
//
// Internet format:
// a.b.c.d
// a.b.c (with c treated as 16-bits)
// a.b (with b treated as 24 bits)
//
if (pp >= parts + 3)
{
*Terminator = String;
return STATUS_INVALID_PARAMETER;
}
*pp++ = val, String++;
//
// Check if we saw at least one digit.
//
if (!sawDigit) {
*Terminator = String;
return STATUS_INVALID_PARAMETER;
}
goto again;
} while (c != L'\0');
//
// Check if we saw at least one digit.
//
if (!sawDigit) {
*Terminator = String;
return STATUS_INVALID_PARAMETER;
}
*pp++ = val;
//
// Concoct the address according to
// the number of parts specified.
//
n = (ULONG)(pp - parts);
if (Strict && (n != 4)) {
*Terminator = String;
return STATUS_INVALID_PARAMETER;
}
switch ((int) n) {
case 1: /* a -- 32 bits */
val = parts[0];
break;
case 2: /* a.b -- 8.24 bits */
if ((parts[0] > 0xff) || (parts[1] > 0xffffff)) {
*Terminator = String;
return STATUS_INVALID_PARAMETER;
}
val = (parts[0] << 24) | (parts[1] & 0xffffff);
break;
case 3: /* a.b.c -- 8.8.16 bits */
if ((parts[0] > 0xff) || (parts[1] > 0xff) ||
(parts[2] > 0xffff)) {
*Terminator = String;
return STATUS_INVALID_PARAMETER;
}
val = (parts[0] << 24) | ((parts[1] & 0xff) << 16) |
(parts[2] & 0xffff);
break;
case 4: /* a.b.c.d -- 8.8.8.8 bits */
if ((parts[0] > 0xff) || (parts[1] > 0xff) ||
(parts[2] > 0xff) || (parts[3] > 0xff)) {
*Terminator = String;
return STATUS_INVALID_PARAMETER;
}
val = (parts[0] << 24) | ((parts[1] & 0xff) << 16) |
((parts[2] & 0xff) << 8) | (parts[3] & 0xff);
break;
default:
*Terminator = String;
return STATUS_INVALID_PARAMETER;
}
val = RtlUlongByteSwap(val);
*Terminator = String;
Addr->s_addr = val;
return STATUS_SUCCESS;
}