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.
708 lines
22 KiB
708 lines
22 KiB
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
network.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the network interface for the BINL server.
|
|
|
|
Author:
|
|
|
|
Colin Watson (colinw) 2-May-1997
|
|
|
|
Environment:
|
|
|
|
User Mode - Win32
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "binl.h"
|
|
#pragma hdrstop
|
|
|
|
DWORD
|
|
BinlWaitForMessage(
|
|
BINL_REQUEST_CONTEXT *pRequestContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function waits for a request on the BINL port on any of the
|
|
configured interfaces.
|
|
|
|
Arguments:
|
|
|
|
RequestContext - A pointer to a request context block for
|
|
this request.
|
|
|
|
Return Value:
|
|
|
|
The status of the operation.
|
|
|
|
--*/
|
|
{
|
|
DWORD length;
|
|
DWORD error;
|
|
fd_set readSocketSet;
|
|
DWORD i;
|
|
int readySockets;
|
|
struct timeval timeout = { 0x7FFFFFFF, 0 }; // forever.
|
|
|
|
LPOPTION Option;
|
|
LPBYTE EndOfScan;
|
|
LPBYTE MagicCookie;
|
|
BOOLEAN FoundDesirablePacket;
|
|
|
|
#define CLIENTOPTIONSTRING "PXEClient"
|
|
#define CLIENTOPTIONSIZE (sizeof(CLIENTOPTIONSTRING) - 1)
|
|
|
|
//
|
|
// Loop until we get an extended DHCP request or an error
|
|
//
|
|
|
|
while (1) {
|
|
|
|
//
|
|
// Setup the file descriptor set for select.
|
|
//
|
|
|
|
FD_ZERO( &readSocketSet );
|
|
for ( i = 0; i < BinlGlobalNumberOfNets ; i++ ) {
|
|
if (BinlGlobalEndpointList[i].Socket) {
|
|
FD_SET(
|
|
BinlGlobalEndpointList[i].Socket,
|
|
&readSocketSet
|
|
);
|
|
}
|
|
}
|
|
|
|
readySockets = select( 0, &readSocketSet, NULL, NULL, &timeout );
|
|
|
|
//
|
|
// return to caller when the service is shutting down or select()
|
|
// times out.
|
|
//
|
|
|
|
if( (readySockets == 0) ||
|
|
(WaitForSingleObject( BinlGlobalProcessTerminationEvent, 0 ) == 0) ) {
|
|
|
|
return( ERROR_SEM_TIMEOUT );
|
|
}
|
|
|
|
if( readySockets == SOCKET_ERROR) {
|
|
continue; // Closed the DHCP socket?
|
|
}
|
|
|
|
//
|
|
// Time to play 20 question with winsock. Which socket is ready?
|
|
//
|
|
|
|
pRequestContext->ActiveEndpoint = NULL;
|
|
|
|
for ( i = 0; i < BinlGlobalNumberOfNets ; i++ ) {
|
|
if ( FD_ISSET( BinlGlobalEndpointList[i].Socket, &readSocketSet ) ) {
|
|
pRequestContext->ActiveEndpoint = &BinlGlobalEndpointList[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//BinlAssert(pRequestContext->ActiveEndpoint != NULL );
|
|
if ( pRequestContext->ActiveEndpoint == NULL ) {
|
|
return ERROR_SEM_TIMEOUT;
|
|
}
|
|
|
|
|
|
//
|
|
// Read data from the net. If multiple sockets have data, just
|
|
// process the first available socket.
|
|
//
|
|
|
|
pRequestContext->SourceNameLength = sizeof( struct sockaddr );
|
|
|
|
//
|
|
// clean the receive buffer before receiving data in it. We clear
|
|
// out one more byte than we actually hand to recvfrom, so we can
|
|
// be sure the message has a NULL after it (in case we do a
|
|
// wcslen etc. into the received packet).
|
|
//
|
|
|
|
RtlZeroMemory( pRequestContext->ReceiveBuffer, BINL_MESSAGE_SIZE + 1 );
|
|
pRequestContext->ReceiveMessageSize = BINL_MESSAGE_SIZE;
|
|
|
|
length = recvfrom(
|
|
pRequestContext->ActiveEndpoint->Socket,
|
|
(char *)pRequestContext->ReceiveBuffer,
|
|
pRequestContext->ReceiveMessageSize,
|
|
0,
|
|
&pRequestContext->SourceName,
|
|
(int *)&pRequestContext->SourceNameLength
|
|
);
|
|
|
|
if ( length == SOCKET_ERROR ) {
|
|
error = WSAGetLastError();
|
|
BinlPrintDbg(( DEBUG_ERRORS, "Recv failed, error = %ld\n", error ));
|
|
}
|
|
else if (length == 0) {
|
|
//
|
|
// the connection closed under us.
|
|
// lets hope the connection opens again.
|
|
//
|
|
continue;
|
|
}
|
|
//
|
|
// we received a message!!
|
|
// we are expecting to receive a message whose first byte tells us
|
|
// the purpose of the message. Since we have received a message (it
|
|
// must be of positive length), we can look at the first byte (Operation)
|
|
// to tell us what to do with the message. but we still need to be
|
|
// careful, as the rest of the data may be bad.
|
|
//
|
|
else {
|
|
|
|
//
|
|
// Ignore all messages that do not look like DHCP or doesn't have the
|
|
// option "PXEClient", OR that is not an oschooser message (they
|
|
// all start with 0x81).
|
|
//
|
|
|
|
if ( ((LPDHCP_MESSAGE)pRequestContext->ReceiveBuffer)->Operation == OSC_REQUEST) {
|
|
|
|
//
|
|
// All OSC request packets have a 4-byte signature (first byte
|
|
// is OSC_REQUEST) followed by a DWORD length (that does not
|
|
// include the signature/length). Make sure the length matches
|
|
// what we got from recvfrom (we allow padding at the end). We
|
|
// use SIGNED_PACKET but any of the XXX_PACKET structures in
|
|
// oscpkt.h would work.
|
|
//
|
|
|
|
if (length < FIELD_OFFSET(SIGNED_PACKET, SequenceNumber)) {
|
|
BinlPrintDbg(( DEBUG_OSC_ERROR, "Discarding runt packet %d bytes\n", length ));
|
|
continue;
|
|
}
|
|
|
|
if ((length - FIELD_OFFSET(SIGNED_PACKET, SequenceNumber)) <
|
|
((SIGNED_PACKET UNALIGNED *)pRequestContext->ReceiveBuffer)->Length) {
|
|
BinlPrintDbg(( DEBUG_OSC_ERROR, "Discarding invalid length message %d bytes (header said %d)\n",
|
|
length, ((SIGNED_PACKET UNALIGNED *)pRequestContext->ReceiveBuffer)->Length));
|
|
continue;
|
|
}
|
|
|
|
BinlPrintDbg(( DEBUG_MESSAGE, "Received OSC message\n", 0 ));
|
|
error = ERROR_SUCCESS;
|
|
|
|
} else {
|
|
|
|
//
|
|
// check to see if this is a BOOTP message.
|
|
// do so by checking to make sure it is at least the minimum
|
|
// size for a DHCP message. if so, check to see if it has
|
|
// the appriopriate magic cookie '99' '130' '83' '99'.
|
|
//
|
|
// once we verified that this is a BOOTP message,
|
|
// we will be looking for two options. either an option
|
|
// indicating this is a inform packet or
|
|
// an option indicating this Vendor Class as "PXEClient"
|
|
//
|
|
// ignore all others.
|
|
//
|
|
|
|
if ( length < DHCP_MESSAGE_FIXED_PART_SIZE + 4 ) {
|
|
//
|
|
// Message isn't long enough to include the
|
|
// DHCP message header and the BOOTP magic cookie,
|
|
// ignore it.
|
|
//
|
|
continue;
|
|
}
|
|
|
|
if ( ((LPDHCP_MESSAGE)pRequestContext->ReceiveBuffer)->Operation != BOOT_REQUEST) {
|
|
//
|
|
// Doesn't look like an interesting DHCP frame
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// check the BOOTP magic cookie.
|
|
//
|
|
MagicCookie = (LPBYTE)&((LPDHCP_MESSAGE)pRequestContext->ReceiveBuffer)->Option;
|
|
|
|
if( (*MagicCookie != (BYTE)DHCP_MAGIC_COOKIE_BYTE1) ||
|
|
(*(MagicCookie+1) != (BYTE)DHCP_MAGIC_COOKIE_BYTE2) ||
|
|
(*(MagicCookie+2) != (BYTE)DHCP_MAGIC_COOKIE_BYTE3) ||
|
|
(*(MagicCookie+3) != (BYTE)DHCP_MAGIC_COOKIE_BYTE4))
|
|
{
|
|
//
|
|
// this is a vendor specific magic cookie.
|
|
// ignore the message
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// At this point, we have something that looks like a DHCP/BOOTP
|
|
// packet. we will now carefully look for two particular option
|
|
// types that are interest to us.
|
|
// 1. An inform packet which is indicated by the option type
|
|
// OPTION_MESSAGE_TYPE(53) with the message type of
|
|
// DHCP_INFORM_MESSAGE(8)
|
|
// 2. A vendor class indentifier "PXEClient" indicated by
|
|
// the option type OPTION_CLIENT_CLASS_INFO(60) with the
|
|
// value CLIENTOPTIONSTRING("PXEClient")
|
|
// Stop scanning after we have found either one of these options
|
|
// or we run off the packet. if we do not find either option,
|
|
// continue in the while loop looking for a packet with either
|
|
// of these options
|
|
//
|
|
// EndOfScan indicates the last byte we received in the packet
|
|
//
|
|
EndOfScan = pRequestContext->ReceiveBuffer + length - 1;
|
|
|
|
Option = (LPOPTION) (MagicCookie + 4);
|
|
|
|
FoundDesirablePacket = FALSE;
|
|
|
|
while ( ((LPBYTE)Option <= EndOfScan) &&
|
|
(Option->OptionType != OPTION_END) &&
|
|
(FoundDesirablePacket == FALSE) ) {
|
|
|
|
if ( Option->OptionType == OPTION_PAD ) {
|
|
//
|
|
// found an OPTION_PAD. this is a 1 byte option ('0').
|
|
// just walk past this.
|
|
//
|
|
Option = (LPOPTION)((LPBYTE)(Option) + 1);
|
|
}
|
|
else {
|
|
//
|
|
// OPTION_PAD and OPTION_END are the only two options
|
|
// that do not have a length field and a Value field.
|
|
// we know we do not have either, so we have to make
|
|
// sure we do not step past the EndOfScan by
|
|
// looking at the option length or the option value
|
|
//
|
|
// Note. Option type and Option length take up two bytes
|
|
// but we only add one when seeing if the length brings us
|
|
// past EndOfScan, because when we step past the last
|
|
// option, it will bring us 1 byte past EndOfScan.
|
|
// we want to see if this is an invalid option
|
|
// that will somehow overstep the standard case
|
|
//
|
|
if ( (((LPBYTE)(Option) + 1) > EndOfScan) ||
|
|
(((LPBYTE)(Option) + Option->OptionLength + 1) > EndOfScan) ) {
|
|
//
|
|
// invalid option
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// look for the two option types of interest.
|
|
// OPTION_CLIENT_CLASS_INFO and OPTION_MESSAGE_TYPE
|
|
//
|
|
switch ( Option->OptionType ) {
|
|
case OPTION_MESSAGE_TYPE:
|
|
//
|
|
// check to see if we got an inform packet
|
|
//
|
|
if ( (Option->OptionLength == 1) &&
|
|
(Option->OptionValue[0] == DHCP_INFORM_MESSAGE) ) {
|
|
FoundDesirablePacket = TRUE;
|
|
}
|
|
break;
|
|
case OPTION_CLIENT_CLASS_INFO:
|
|
//
|
|
// check to see if the Client class identifier is "PXEClient"
|
|
//
|
|
if (memcmp(Option->OptionValue,
|
|
CLIENTOPTIONSTRING,
|
|
CLIENTOPTIONSIZE) == 0) {
|
|
FoundDesirablePacket = TRUE;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// walk past this option to check the next one
|
|
//
|
|
Option = (LPOPTION)((LPBYTE)(Option) + Option->OptionLength + 2);
|
|
}
|
|
}
|
|
|
|
if ( FoundDesirablePacket == FALSE ) {
|
|
//
|
|
// Message was not an extended DHCP packet
|
|
// with the desired option ("PXEClient")
|
|
// or an inform packet.
|
|
// ignore the message
|
|
//
|
|
continue;
|
|
}
|
|
|
|
BinlPrintDbg(( DEBUG_MESSAGE, "Received message\n", 0 ));
|
|
error = ERROR_SUCCESS;
|
|
|
|
}
|
|
}
|
|
|
|
pRequestContext->ReceiveMessageSize = length;
|
|
return( error );
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
BinlSendMessage(
|
|
LPBINL_REQUEST_CONTEXT RequestContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function send a response to a BINL client.
|
|
|
|
Arguments:
|
|
|
|
RequestContext - A pointer to the BinlRequestContext block for
|
|
this request.
|
|
|
|
Return Value:
|
|
|
|
The status of the operation.
|
|
|
|
--*/
|
|
{
|
|
DWORD error;
|
|
struct sockaddr_in *source;
|
|
LPDHCP_MESSAGE binlMessage;
|
|
LPDHCP_MESSAGE binlReceivedMessage;
|
|
DWORD MessageLength;
|
|
BOOL ArpCacheUpdated = FALSE;
|
|
|
|
binlMessage = (LPDHCP_MESSAGE) RequestContext->SendBuffer;
|
|
binlReceivedMessage = (LPDHCP_MESSAGE) RequestContext->ReceiveBuffer;
|
|
|
|
//
|
|
// if the request arrived from a relay agent, then send the reply
|
|
// on server port otherwise leave it as the client's source port.
|
|
//
|
|
|
|
source = (struct sockaddr_in *)&RequestContext->SourceName;
|
|
if ( binlReceivedMessage->RelayAgentIpAddress != 0 ) {
|
|
source->sin_port = htons( DHCP_SERVR_PORT );
|
|
}
|
|
|
|
//
|
|
// if this request arrived from relay agent then send the
|
|
// response to the address the relay agent says.
|
|
//
|
|
|
|
if ( binlReceivedMessage->RelayAgentIpAddress != 0 ) {
|
|
source->sin_addr.s_addr = binlReceivedMessage->RelayAgentIpAddress;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// if the client didnt specify broadcast bit and if
|
|
// we know the ipaddress of the client then send unicast.
|
|
//
|
|
|
|
//
|
|
// But if IgnoreBroadcastFlag is set in the registry and
|
|
// if the client requested to broadcast or the server is
|
|
// nacking or If the client doesn't have an address yet,
|
|
// respond via broadcast.
|
|
// Note that IgnoreBroadcastFlag is off by default. But it
|
|
// can be set as a workaround for the clients that are not
|
|
// capable of receiving unicast
|
|
// and they also dont set the broadcast bit.
|
|
//
|
|
|
|
if ( (RequestContext->MessageType == DHCP_INFORM_MESSAGE) &&
|
|
(ntohs(binlMessage->Reserved) & DHCP_BROADCAST) ) {
|
|
|
|
source->sin_addr.s_addr = (DWORD)-1;
|
|
|
|
} else if ( BinlGlobalIgnoreBroadcastFlag ) {
|
|
|
|
if ((ntohs(binlReceivedMessage->Reserved) & DHCP_BROADCAST) ||
|
|
(binlReceivedMessage->ClientIpAddress == 0) ||
|
|
(source->sin_addr.s_addr == 0) ) {
|
|
|
|
source->sin_addr.s_addr = (DWORD)-1;
|
|
|
|
binlMessage->Reserved = 0;
|
|
// this flag should be zero in the local response.
|
|
}
|
|
|
|
} else {
|
|
|
|
if( (ntohs(binlReceivedMessage->Reserved) & DHCP_BROADCAST) ||
|
|
(!source->sin_addr.s_addr ) ){
|
|
|
|
source->sin_addr.s_addr = (DWORD)-1;
|
|
|
|
binlMessage->Reserved = 0;
|
|
// this flag should be zero in the local response.
|
|
} else {
|
|
|
|
//
|
|
// Send back to the same IP address that the request came in on (
|
|
// i.e. source->sin_addr.s_addr)
|
|
//
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
BinlPrint(( DEBUG_STOC, "Sending response to = %s, XID = %lx.\n",
|
|
inet_ntoa(source->sin_addr), binlMessage->TransactionID));
|
|
|
|
|
|
//
|
|
// send minimum DHCP_MIN_SEND_RECV_PK_SIZE (300) bytes, otherwise
|
|
// bootp relay agents don't like the packet.
|
|
//
|
|
|
|
MessageLength = (RequestContext->SendMessageSize >
|
|
DHCP_MIN_SEND_RECV_PK_SIZE) ?
|
|
RequestContext->SendMessageSize :
|
|
DHCP_MIN_SEND_RECV_PK_SIZE;
|
|
error = sendto(
|
|
RequestContext->ActiveEndpoint->Socket,
|
|
(char *)RequestContext->SendBuffer,
|
|
MessageLength,
|
|
0,
|
|
&RequestContext->SourceName,
|
|
RequestContext->SourceNameLength
|
|
);
|
|
|
|
if ( error == SOCKET_ERROR ) {
|
|
error = WSAGetLastError();
|
|
BinlPrintDbg(( DEBUG_ERRORS, "Send failed, error = %ld\n", error ));
|
|
} else {
|
|
error = ERROR_SUCCESS;
|
|
}
|
|
|
|
return( error );
|
|
}
|
|
|
|
NTSTATUS
|
|
GetIpAddressInfo (
|
|
ULONG Delay
|
|
)
|
|
{
|
|
ULONG count;
|
|
DWORD Size;
|
|
PIP_ADAPTER_INFO pAddressInfo = NULL;
|
|
|
|
//
|
|
// We can get out ahead of the dns cached info here... let's delay a bit
|
|
// if the pnp logic told us there was a change.
|
|
//
|
|
|
|
if (Delay) {
|
|
Sleep( Delay );
|
|
}
|
|
|
|
Size = 0;
|
|
if ( (GetAdaptersInfo(pAddressInfo,&Size) == ERROR_BUFFER_OVERFLOW) &&
|
|
(pAddressInfo = BinlAllocateMemory(Size)) &&
|
|
(GetAdaptersInfo(pAddressInfo,&Size) == ERROR_SUCCESS)) {
|
|
PIP_ADAPTER_INFO pNext = pAddressInfo;
|
|
count = 0;
|
|
while (pNext) {
|
|
count += 1;
|
|
pNext = pNext->Next;
|
|
}
|
|
} else {
|
|
count = 0;
|
|
}
|
|
|
|
if (count == 0) {
|
|
|
|
//
|
|
// we don't know what went wrong, we'll fall back to old APIs.
|
|
//
|
|
|
|
DHCP_IP_ADDRESS ipaddr = 0;
|
|
PHOSTENT Host = gethostbyname( NULL );
|
|
|
|
if (Host) {
|
|
|
|
ipaddr = *(PDHCP_IP_ADDRESS)Host->h_addr;
|
|
|
|
if ((Host->h_addr_list[0] != NULL) &&
|
|
(Host->h_addr_list[1] != NULL)) {
|
|
|
|
BinlIsMultihomed = TRUE;
|
|
|
|
} else {
|
|
|
|
BinlIsMultihomed = FALSE;
|
|
}
|
|
|
|
BinlGlobalMyIpAddress = ipaddr;
|
|
|
|
} else {
|
|
|
|
//
|
|
// what's with the ip stack? we can't get any type of address
|
|
// info out of it... for now, we won't answer any if we don't
|
|
// already have the info we need.
|
|
//
|
|
|
|
if (BinlIpAddressInfo == NULL) {
|
|
BinlIsMultihomed = TRUE;
|
|
}
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
EnterCriticalSection(&gcsParameters);
|
|
|
|
if (BinlIpAddressInfo) {
|
|
BinlFreeMemory( BinlIpAddressInfo );
|
|
}
|
|
|
|
BinlIpAddressInfo = pAddressInfo;
|
|
BinlIpAddressInfoCount = count;
|
|
|
|
BinlIsMultihomed = (count != 1);
|
|
|
|
if (!BinlIsMultihomed) {
|
|
BinlGlobalMyIpAddress = inet_addr(pAddressInfo->IpAddressList.IpAddress.String);
|
|
}
|
|
|
|
LeaveCriticalSection(&gcsParameters);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
DHCP_IP_ADDRESS
|
|
BinlGetMyNetworkAddress (
|
|
LPBINL_REQUEST_CONTEXT RequestContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns our (the server's) IP address.
|
|
if multihomed, the function will walk through
|
|
each of the server's ip addresses looking for an
|
|
address with the same subnet mask as the sender.
|
|
|
|
Arguments:
|
|
|
|
RequestContext - The RequestContext from the packet
|
|
sent to us by the client.
|
|
|
|
Return Value:
|
|
|
|
The Ip Address of the server. in the multihome
|
|
situation, the ip address on the same subnet
|
|
as the client. In case of failure to find an
|
|
IP address on the same subnet or if we were
|
|
somehow unable to get the client's address, 0 is
|
|
returned
|
|
|
|
--*/
|
|
{
|
|
ULONG RemoteIp;
|
|
DHCP_IP_ADDRESS ipaddr;
|
|
ULONG i;
|
|
ULONG subnetMask;
|
|
ULONG localAddr;
|
|
PIP_ADAPTER_INFO pNext;
|
|
|
|
BinlAssert( RequestContext != NULL);
|
|
|
|
//
|
|
// If we're not multihomed, then we know the address since there's just one.
|
|
//
|
|
|
|
if (!BinlIsMultihomed) {
|
|
return BinlGlobalMyIpAddress;
|
|
}
|
|
|
|
RemoteIp = ((struct sockaddr_in *)&RequestContext->SourceName)->sin_addr.s_addr;
|
|
|
|
//
|
|
// in attempt to be consistent with the previous case where we only
|
|
// have 1 ip address, we should at least return an IP address.
|
|
// Return the first ip address in the list.
|
|
//
|
|
ipaddr = (BinlIpAddressInfo) ? inet_addr(BinlIpAddressInfo->IpAddressList.IpAddress.String) : 0;
|
|
|
|
if (RemoteIp == 0) {
|
|
|
|
return ipaddr;
|
|
}
|
|
|
|
EnterCriticalSection(&gcsParameters);
|
|
|
|
if (BinlIpAddressInfo == NULL) {
|
|
LeaveCriticalSection(&gcsParameters);
|
|
return (BinlIsMultihomed ? 0 : BinlGlobalMyIpAddress);
|
|
}
|
|
|
|
pNext = BinlIpAddressInfo;
|
|
while (pNext) {
|
|
localAddr = inet_addr(pNext->IpAddressList.IpAddress.String);
|
|
subnetMask = inet_addr(pNext->IpAddressList.IpMask.String);
|
|
pNext = pNext->Next;
|
|
|
|
//
|
|
// check that the remote ip address may have come from this subnet.
|
|
// note that the address could be the address of a dhcp relay agent,
|
|
// which is fine since we're just looking for the address of the
|
|
// local subnet to broadcast the response on.
|
|
//
|
|
|
|
//
|
|
// guard against bad ip address
|
|
//
|
|
if (!localAddr || !subnetMask) {
|
|
continue;
|
|
}
|
|
|
|
if ((RemoteIp & subnetMask) == (localAddr & subnetMask)) {
|
|
|
|
ipaddr = localAddr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&gcsParameters);
|
|
|
|
return ipaddr;
|
|
}
|
|
|
|
|
|
VOID
|
|
FreeIpAddressInfo (
|
|
VOID
|
|
)
|
|
{
|
|
EnterCriticalSection(&gcsParameters);
|
|
|
|
if (BinlIpAddressInfo != NULL) {
|
|
BinlFreeMemory( BinlIpAddressInfo );
|
|
}
|
|
BinlIpAddressInfo = NULL;
|
|
BinlIpAddressInfoCount = 0;
|
|
|
|
LeaveCriticalSection(&gcsParameters);
|
|
|
|
return;
|
|
}
|
|
|