1057 lines
22 KiB
C
1057 lines
22 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1996-2000 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
socket.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Domain Name System (DNS) API
|
|||
|
|
|||
|
Socket setup.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Jim Gilroy (jamesg) October, 1996
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
|
|||
|
#include "local.h"
|
|||
|
|
|||
|
//
|
|||
|
// Winsock startup
|
|||
|
//
|
|||
|
|
|||
|
LONG WinsockStartCount = 0;
|
|||
|
WSADATA WsaData;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Async i/o
|
|||
|
//
|
|||
|
// If want async socket i/o then can create single async socket, with
|
|||
|
// corresponding event and always use it. Requires winsock 2.2
|
|||
|
//
|
|||
|
|
|||
|
SOCKET DnsSocket = 0;
|
|||
|
|
|||
|
OVERLAPPED DnsSocketOverlapped;
|
|||
|
HANDLE hDnsSocketEvent = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// App shutdown flag
|
|||
|
//
|
|||
|
|
|||
|
BOOLEAN fApplicationShutdown = FALSE;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DNS_STATUS
|
|||
|
Dns_InitializeWinsock(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Initialize winsock for this process.
|
|||
|
|
|||
|
Currently, assuming process must do WSAStartup() before
|
|||
|
calling any Dns_Api entry point.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS if successful.
|
|||
|
ErrorCode on failure.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DNSDBG( SOCKET, ( "Dns_InitializeWinsock()\n" ));
|
|||
|
|
|||
|
//
|
|||
|
// start winsock, if not already started
|
|||
|
//
|
|||
|
|
|||
|
if ( WinsockStartCount == 0 )
|
|||
|
{
|
|||
|
DNS_STATUS status;
|
|||
|
|
|||
|
DNSDBG( TRACE, (
|
|||
|
"Dns_InitializeWinsock() version %x\n",
|
|||
|
DNS_WINSOCK_VERSION ));
|
|||
|
|
|||
|
status = WSAStartup( DNS_WINSOCK_VERSION, &WsaData );
|
|||
|
if ( status != ERROR_SUCCESS )
|
|||
|
{
|
|||
|
DNS_PRINT(( "ERROR: WSAStartup failure %d.\n", status ));
|
|||
|
return( status );
|
|||
|
}
|
|||
|
|
|||
|
DNSDBG( TRACE, (
|
|||
|
"Winsock initialized => wHighVersion=0x%x, wVersion=0x%x\n",
|
|||
|
WsaData.wHighVersion,
|
|||
|
WsaData.wVersion ));
|
|||
|
|
|||
|
InterlockedIncrement( &WinsockStartCount );
|
|||
|
}
|
|||
|
return( ERROR_SUCCESS );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DNS_STATUS
|
|||
|
Dns_InitializeWinsockEx(
|
|||
|
IN BOOL fForce
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Dummy -- just in case called somewhere
|
|||
|
|
|||
|
DCR: eliminate Dns_InitializeWinsockEx()
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
return Dns_InitializeWinsock();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
Dns_CleanupWinsock(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Cleanup winsock if it was initialized by dnsapi.dll
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DNSDBG( SOCKET, ( "Dns_CleanupWinsock()\n" ));
|
|||
|
|
|||
|
//
|
|||
|
// WSACleanup() for value of ref count
|
|||
|
// - ref count pushed down to one below real value, but
|
|||
|
// fixed up at end
|
|||
|
// - note: the GUI_MODE_SETUP_WS_CLEANUP deal means that
|
|||
|
// we can be called other than process detach, making
|
|||
|
// interlock necessary
|
|||
|
//
|
|||
|
|
|||
|
while ( InterlockedDecrement( &WinsockStartCount ) >= 0 )
|
|||
|
{
|
|||
|
WSACleanup();
|
|||
|
}
|
|||
|
|
|||
|
InterlockedIncrement( &WinsockStartCount );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
SOCKET
|
|||
|
Dns_CreateSocketEx(
|
|||
|
IN INT Family,
|
|||
|
IN INT SockType,
|
|||
|
IN IP_ADDRESS IpAddress,
|
|||
|
IN USHORT Port,
|
|||
|
IN DWORD dwFlags
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Create socket.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Family -- socket family AF_INET or AF_INET6
|
|||
|
|
|||
|
SockType -- SOCK_DGRAM or SOCK_STREAM
|
|||
|
|
|||
|
IpAddress -- IP address to listen on (net byte order)
|
|||
|
|
|||
|
DCR: will need to change to IP6_ADDRESS then use MAPPED to extract
|
|||
|
|
|||
|
Port -- desired port in net order
|
|||
|
- NET_ORDER_DNS_PORT for DNS listen sockets
|
|||
|
- 0 for any port
|
|||
|
|
|||
|
dwFlags -- specifiy the attributes of the sockets
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Socket if successful.
|
|||
|
Otherwise INVALID_SOCKET.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
SOCKET s;
|
|||
|
INT err;
|
|||
|
INT val;
|
|||
|
DNS_STATUS status;
|
|||
|
BOOL fretry = FALSE;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// create socket
|
|||
|
// - try again if winsock not initialized
|
|||
|
|
|||
|
while( 1 )
|
|||
|
{
|
|||
|
s = WSASocket(
|
|||
|
Family,
|
|||
|
SockType,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
dwFlags );
|
|||
|
|
|||
|
if ( s != INVALID_SOCKET )
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
status = GetLastError();
|
|||
|
|
|||
|
DNSDBG( SOCKET, (
|
|||
|
"ERROR: Failed to open socket of type %d.\n"
|
|||
|
"\terror = %d.\n",
|
|||
|
SockType,
|
|||
|
status ));
|
|||
|
|
|||
|
if ( status != WSANOTINITIALISED || fretry )
|
|||
|
{
|
|||
|
SetLastError( DNS_ERROR_NO_TCPIP );
|
|||
|
return INVALID_SOCKET;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// initialize Winsock if not already started
|
|||
|
//
|
|||
|
// note: do NOT automatically initialize winsock
|
|||
|
// init jacks ref count and will break applications
|
|||
|
// which use WSACleanup to close outstanding sockets;
|
|||
|
// we'll init only when the choice is that or no service;
|
|||
|
// apps can still cleanup with WSACleanup() called
|
|||
|
// in loop until WSANOTINITIALISED failure
|
|||
|
//
|
|||
|
|
|||
|
fretry = TRUE;
|
|||
|
status = Dns_InitializeWinsock();
|
|||
|
if ( status != NO_ERROR )
|
|||
|
{
|
|||
|
SetLastError( DNS_ERROR_NO_TCPIP );
|
|||
|
return INVALID_SOCKET;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// bind socket
|
|||
|
// - only if specific port given, this keeps remote winsock
|
|||
|
// from grabbing it if we are on the local net
|
|||
|
//
|
|||
|
|
|||
|
if ( IpAddress || Port )
|
|||
|
{
|
|||
|
SOCKADDR_IN6 sockaddr;
|
|||
|
INT sockaddrLength;
|
|||
|
|
|||
|
if ( Family == AF_INET )
|
|||
|
{
|
|||
|
PSOCKADDR_IN psin = (PSOCKADDR_IN) &sockaddr;
|
|||
|
|
|||
|
sockaddrLength = sizeof(*psin);
|
|||
|
RtlZeroMemory( psin, sockaddrLength );
|
|||
|
|
|||
|
psin->sin_family = AF_INET;
|
|||
|
psin->sin_port = Port;
|
|||
|
psin->sin_addr.s_addr = IpAddress;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
PSOCKADDR_IN6 psin = (PSOCKADDR_IN6) &sockaddr;
|
|||
|
|
|||
|
DNS_ASSERT( Family == AF_INET6 );
|
|||
|
|
|||
|
sockaddrLength = sizeof(*psin);
|
|||
|
RtlZeroMemory( psin, sockaddrLength );
|
|||
|
|
|||
|
psin->sin6_family = AF_INET6;
|
|||
|
psin->sin6_port = Port;
|
|||
|
//psin->sin6_addr = IpAddress;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if ( Port > 0 )
|
|||
|
{
|
|||
|
// allow us to bind to a port that someone else is already listening on
|
|||
|
val = 1;
|
|||
|
setsockopt(
|
|||
|
s,
|
|||
|
SOL_SOCKET,
|
|||
|
SO_REUSEADDR,
|
|||
|
(const char *)&val,
|
|||
|
sizeof(val) );
|
|||
|
}
|
|||
|
|
|||
|
err = bind(
|
|||
|
s,
|
|||
|
(PSOCKADDR) &sockaddr,
|
|||
|
sockaddrLength );
|
|||
|
|
|||
|
if ( err == SOCKET_ERROR )
|
|||
|
{
|
|||
|
DNSDBG( SOCKET, (
|
|||
|
"Failed to bind() socket %d, to port %d, address %s.\n"
|
|||
|
"\terror = %d.\n",
|
|||
|
s,
|
|||
|
ntohs(Port),
|
|||
|
IP_STRING( IpAddress ),
|
|||
|
GetLastError() ));
|
|||
|
|
|||
|
closesocket( s );
|
|||
|
SetLastError( DNS_ERROR_NO_TCPIP );
|
|||
|
return INVALID_SOCKET;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
DNSDBG( SOCKET, (
|
|||
|
"Created socket %d, of type %d, for address %s, port %d.\n",
|
|||
|
s,
|
|||
|
SockType,
|
|||
|
inet_ntoa( *(struct in_addr *) &IpAddress ),
|
|||
|
ntohs(Port) ));
|
|||
|
|
|||
|
return s;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
SOCKET
|
|||
|
Dns_CreateSocket(
|
|||
|
IN INT SockType,
|
|||
|
IN IP_ADDRESS IpAddress,
|
|||
|
IN USHORT Port
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Wrapper function for CreateSocketAux. Passes in 0 for dwFlags (as opposed
|
|||
|
to Dns_CreateMulticastSocket, which passes in flags to specify that
|
|||
|
the socket is to be used for multicasting).
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
SockType -- SOCK_DGRAM or SOCK_STREAM
|
|||
|
|
|||
|
IpAddress -- IP address to listen on (net byte order)
|
|||
|
|
|||
|
Port -- desired port in net order
|
|||
|
- NET_ORDER_DNS_PORT for DNS listen sockets
|
|||
|
- 0 for any port
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
socket if successful.
|
|||
|
Otherwise INVALID_SOCKET.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
return Dns_CreateSocketEx(
|
|||
|
AF_INET,
|
|||
|
SockType,
|
|||
|
IpAddress,
|
|||
|
Port,
|
|||
|
0 // no flags
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
SOCKET
|
|||
|
Dns_CreateMulticastSocket(
|
|||
|
IN INT SockType,
|
|||
|
IN IP_ADDRESS IpAddress,
|
|||
|
IN USHORT Port,
|
|||
|
IN BOOL fSend,
|
|||
|
IN BOOL fReceive
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Create socket and join it to the multicast DNS address.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
SockType -- SOCK_DGRAM or SOCK_STREAM
|
|||
|
|
|||
|
IpAddress -- IP address to listen on (net byte order)
|
|||
|
|
|||
|
Port -- desired port in net order
|
|||
|
- NET_ORDER_DNS_PORT for DNS listen sockets
|
|||
|
- 0 for any port
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
socket if successful.
|
|||
|
Otherwise INVALID_SOCKET.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
SOCKADDR_IN multicastAddress;
|
|||
|
DWORD byteCount;
|
|||
|
BOOL bflag;
|
|||
|
SOCKET s;
|
|||
|
INT err;
|
|||
|
|
|||
|
s = Dns_CreateSocketEx(
|
|||
|
AF_INET,
|
|||
|
SockType,
|
|||
|
IpAddress,
|
|||
|
Port,
|
|||
|
WSA_FLAG_MULTIPOINT_C_LEAF |
|
|||
|
WSA_FLAG_MULTIPOINT_D_LEAF |
|
|||
|
WSA_FLAG_OVERLAPPED );
|
|||
|
|
|||
|
if ( s != INVALID_SOCKET )
|
|||
|
{
|
|||
|
multicastAddress.sin_family = PF_INET;
|
|||
|
multicastAddress.sin_addr.s_addr = MULTICAST_DNS_RADDR;
|
|||
|
multicastAddress.sin_port = Port;
|
|||
|
|
|||
|
// set loopback
|
|||
|
|
|||
|
bflag = TRUE;
|
|||
|
|
|||
|
err = WSAIoctl(
|
|||
|
s,
|
|||
|
SIO_MULTIPOINT_LOOPBACK, // loopback iotcl
|
|||
|
& bflag, // turn on
|
|||
|
sizeof(bflag),
|
|||
|
NULL, // no output
|
|||
|
0, // no output size
|
|||
|
&byteCount, // bytes returned
|
|||
|
NULL, // no overlapped
|
|||
|
NULL // no completion routine
|
|||
|
);
|
|||
|
|
|||
|
if ( err == SOCKET_ERROR )
|
|||
|
{
|
|||
|
DNSDBG( SOCKET, (
|
|||
|
"Unable to turn multicast loopback on for socket %d; error = %d.\n",
|
|||
|
s,
|
|||
|
GetLastError()
|
|||
|
));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// join socket to multicast group
|
|||
|
//
|
|||
|
|
|||
|
s = WSAJoinLeaf(
|
|||
|
s,
|
|||
|
(PSOCKADDR)&multicastAddress,
|
|||
|
sizeof (multicastAddress),
|
|||
|
NULL, // caller data buffer
|
|||
|
NULL, // callee data buffer
|
|||
|
NULL, // socket QOS setting
|
|||
|
NULL, // socket group QOS
|
|||
|
((fSend && fReceive) ? JL_BOTH : // send and/or receive
|
|||
|
(fSend ? JL_SENDER_ONLY : JL_RECEIVER_ONLY))
|
|||
|
);
|
|||
|
|
|||
|
if ( s == INVALID_SOCKET )
|
|||
|
{
|
|||
|
DNSDBG( SOCKET, (
|
|||
|
"Unable to join socket %d to multicast address, error = %d.\n",
|
|||
|
s,
|
|||
|
GetLastError() ));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return s;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
Dns_CloseSocket(
|
|||
|
IN SOCKET Socket
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Close DNS socket.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Socket -- socket to close
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
if ( Socket == 0 || Socket == INVALID_SOCKET )
|
|||
|
{
|
|||
|
DNS_PRINT(( "WARNING: Dns_CloseSocket() called on invalid socket %d.\n", Socket ));
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
DNSDBG( SOCKET, ( "closesocket( %d )\n", Socket ));
|
|||
|
|
|||
|
closesocket( Socket );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
Dns_CloseConnection(
|
|||
|
IN SOCKET Socket
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Close connection on socket.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Socket -- socket to close
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
if ( Socket == 0 || Socket == INVALID_SOCKET )
|
|||
|
{
|
|||
|
DNS_PRINT((
|
|||
|
"WARNING: Dns_CloseTcpConnection() called on invalid socket.\n" ));
|
|||
|
//DNS_ASSERT( FALSE );
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// shutdown connection, then close
|
|||
|
|
|||
|
DNSDBG( SOCKET, ( "shutdown and closesocket( %d )\n", Socket ));
|
|||
|
|
|||
|
shutdown( Socket, 2 );
|
|||
|
closesocket( Socket );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
#if 0
|
|||
|
//
|
|||
|
// Global async socket routines
|
|||
|
//
|
|||
|
|
|||
|
DNS_STATUS
|
|||
|
Dns_SetupGlobalAsyncSocket(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Create global async UDP socket.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
SockType -- SOCK_DGRAM or SOCK_STREAM
|
|||
|
|
|||
|
IpAddress -- IP address to listen on (net byte order)
|
|||
|
|
|||
|
Port -- desired port in net order
|
|||
|
- NET_ORDER_DNS_PORT for DNS listen sockets
|
|||
|
- 0 for any port
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
socket if successful.
|
|||
|
Otherwise INVALID_SOCKET.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DNS_STATUS status;
|
|||
|
INT err;
|
|||
|
SOCKADDR_IN sockaddrIn;
|
|||
|
|
|||
|
//
|
|||
|
// start winsock, need winsock 2 for async
|
|||
|
//
|
|||
|
|
|||
|
if ( ! fWinsockStarted )
|
|||
|
{
|
|||
|
status = WSAStartup( DNS_WINSOCK_VERSION, &WsaData );
|
|||
|
if ( status != ERROR_SUCCESS )
|
|||
|
{
|
|||
|
DNS_PRINT(( "ERROR: WSAStartup failure %d.\n", status ));
|
|||
|
return( status );
|
|||
|
}
|
|||
|
if ( WsaData.wVersion != DNS_WINSOCK2_VERSION )
|
|||
|
{
|
|||
|
WSACleanup();
|
|||
|
return( WSAVERNOTSUPPORTED );
|
|||
|
}
|
|||
|
fWinsockStarted = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// setup socket
|
|||
|
// - overlapped i\o with event so can run asynchronously in
|
|||
|
// this thread and wait with queuing event
|
|||
|
//
|
|||
|
|
|||
|
DnsSocket = WSASocket(
|
|||
|
AF_INET,
|
|||
|
SOCK_DGRAM,
|
|||
|
0,
|
|||
|
NULL,
|
|||
|
0,
|
|||
|
WSA_FLAG_OVERLAPPED );
|
|||
|
if ( DnsSocket == INVALID_SOCKET )
|
|||
|
{
|
|||
|
status = GetLastError();
|
|||
|
DNS_PRINT(( "\nERROR: Async socket create failed.\n" ));
|
|||
|
goto Error;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// bind socket
|
|||
|
//
|
|||
|
|
|||
|
RtlZeroMemory( &sockaddrIn, sizeof(sockaddrIn) );
|
|||
|
sockaddrIn.sin_family = AF_INET;
|
|||
|
sockaddrIn.sin_port = 0;
|
|||
|
sockaddrIn.sin_addr.s_addr = INADDR_ANY;
|
|||
|
|
|||
|
err = bind( DnsSocket, (PSOCKADDR)&sockaddrIn, sizeof(sockaddrIn) );
|
|||
|
if ( err == SOCKET_ERROR )
|
|||
|
{
|
|||
|
status = GetLastError();
|
|||
|
DNSDBG( SOCKET, (
|
|||
|
"Failed to bind() DnsSocket %d.\n"
|
|||
|
"\terror = %d.\n",
|
|||
|
DnsSocket,
|
|||
|
status ));
|
|||
|
goto Error;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// create event to signal on async i/o completion
|
|||
|
//
|
|||
|
|
|||
|
hDnsSocketEvent = CreateEvent(
|
|||
|
NULL, // Security Attributes
|
|||
|
TRUE, // create Manual-Reset event
|
|||
|
FALSE, // start unsignalled -- paused
|
|||
|
NULL // event name
|
|||
|
);
|
|||
|
if ( !hDnsSocketEvent )
|
|||
|
{
|
|||
|
status = GetLastError();
|
|||
|
DNS_PRINT(( "Failed event creation\n" ));
|
|||
|
goto Error;
|
|||
|
}
|
|||
|
DnsSocketOverlapped.hEvent = hDnsSocketEvent;
|
|||
|
|
|||
|
DNSDBG( SOCKET, (
|
|||
|
"Created global async UDP socket %d.\n"
|
|||
|
"\toverlapped at %p\n"
|
|||
|
"\tevent handle %p\n",
|
|||
|
DnsSocket,
|
|||
|
DnsSocketOverlapped,
|
|||
|
hDnsSocketEvent ));
|
|||
|
|
|||
|
return ERROR_SUCCESS;
|
|||
|
|
|||
|
Error:
|
|||
|
|
|||
|
DNS_PRINT((
|
|||
|
"ERROR: Failed async socket creation, status = %d\n",
|
|||
|
status ));
|
|||
|
closesocket( DnsSocket );
|
|||
|
DnsSocket = INVALID_SOCKET;
|
|||
|
WSACleanup();
|
|||
|
return( status );
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Socket caching
|
|||
|
//
|
|||
|
// Doing limited caching of UDP unbound sockets used for standard
|
|||
|
// DNS lookups in resolver. This allows us to prevent denial of
|
|||
|
// service attack by using up all ports on the machine.
|
|||
|
// Resolver is the main customer for this, but we'll code it to
|
|||
|
// be useable by any process.
|
|||
|
//
|
|||
|
// Implementation notes:
|
|||
|
//
|
|||
|
// There are a couple specific goals to this implementation:
|
|||
|
// - Minimal code impact; Try NOT to change the resolver
|
|||
|
// code.
|
|||
|
// - Usage driven caching; Don't want to create on startup
|
|||
|
// "cache sockets" that we don't use; Instead have actual usage
|
|||
|
// drive up the cached socket count.
|
|||
|
//
|
|||
|
// There are several approaches here.
|
|||
|
//
|
|||
|
// 1) explicit resolver cache -- passed down sockets
|
|||
|
//
|
|||
|
// 2) add caching seamlessly into socket open and close
|
|||
|
// this was my first choice, but the problem here is that on
|
|||
|
// close we must either do additional calls to winsock to determine
|
|||
|
// whether cachable (UDP-unbound) socket OR cache must include some
|
|||
|
// sort of "in-use" tag and we trust that socket is never closed
|
|||
|
// outside of path (otherwise handle reuse could mess us up)
|
|||
|
//
|
|||
|
// 3) new UDP-unbound open\close function
|
|||
|
// this essentially puts the "i-know-i'm-using-UDP-unbound-sockets"
|
|||
|
// burden on the caller who must switch to this new API;
|
|||
|
// fortunately this meshes well with our "SendAndRecvUdp()" function;
|
|||
|
// this approach still allows a caller driven ramp up we desire,
|
|||
|
// so i'm using this approach
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Keep array of sockets
|
|||
|
//
|
|||
|
// Dev note: the Array and MaxCount MUST be kept in sync, no
|
|||
|
// independent check of array is done, it is assumed to exist when
|
|||
|
// MaxCount is non-zero, so they MUST be in sync when lock released
|
|||
|
//
|
|||
|
|
|||
|
SOCKET * g_pCacheSocketArray = NULL;
|
|||
|
|
|||
|
DWORD g_CacheSocketMaxCount = 0;
|
|||
|
DWORD g_CacheSocketCount = 0;
|
|||
|
|
|||
|
|
|||
|
// Hard limit on what we'll allow folks to keep awake
|
|||
|
|
|||
|
#define MAX_SOCKET_CACHE_LIMIT (100)
|
|||
|
|
|||
|
|
|||
|
// Lock access with generic lock
|
|||
|
// This is very short\fast CS, contention will be minimal
|
|||
|
|
|||
|
#define LOCK_SOCKET_CACHE() LOCK_GENERAL()
|
|||
|
#define UNLOCK_SOCKET_CACHE() UNLOCK_GENERAL()
|
|||
|
|
|||
|
|
|||
|
|
|||
|
DNS_STATUS
|
|||
|
Dns_CacheSocketInit(
|
|||
|
IN DWORD MaxSocketCount
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Initialize socket caching.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
MaxSocketCount -- max count of sockets to cache
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS if successful.
|
|||
|
DNS_ERROR_NO_MEMORY on alloc failure.
|
|||
|
ERROR_INVALID_PARAMETER if already initialized or bogus count.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
SOCKET * parray;
|
|||
|
DNS_STATUS status = NO_ERROR;
|
|||
|
|
|||
|
DNSDBG( SOCKET, ( "Dns_CacheSocketInit()\n" ));
|
|||
|
|
|||
|
//
|
|||
|
// validity check
|
|||
|
// - note, one byte of the apple, we don't let you raise
|
|||
|
// count, though we later could; i see this as at most a
|
|||
|
// "configure for machine use" kind of deal
|
|||
|
//
|
|||
|
|
|||
|
LOCK_SOCKET_CACHE();
|
|||
|
|
|||
|
if ( MaxSocketCount == 0 || g_CacheSocketMaxCount != 0 )
|
|||
|
{
|
|||
|
status = ERROR_INVALID_PARAMETER;
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// allocate
|
|||
|
//
|
|||
|
|
|||
|
if ( MaxSocketCount > MAX_SOCKET_CACHE_LIMIT )
|
|||
|
{
|
|||
|
MaxSocketCount = MAX_SOCKET_CACHE_LIMIT;
|
|||
|
}
|
|||
|
|
|||
|
parray = (SOCKET *) ALLOCATE_HEAP_ZERO( sizeof(SOCKET) * MaxSocketCount );
|
|||
|
if ( !parray )
|
|||
|
{
|
|||
|
status = DNS_ERROR_NO_MEMORY;
|
|||
|
goto Done;
|
|||
|
}
|
|||
|
|
|||
|
// set globals
|
|||
|
|
|||
|
g_pCacheSocketArray = parray;
|
|||
|
g_CacheSocketMaxCount = MaxSocketCount;
|
|||
|
g_CacheSocketCount = 0;
|
|||
|
|
|||
|
Done:
|
|||
|
|
|||
|
UNLOCK_SOCKET_CACHE();
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
Dns_CacheSocketCleanup(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Cleanup socket caching.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
DWORD i;
|
|||
|
SOCKET sock;
|
|||
|
|
|||
|
DNSDBG( SOCKET, ( "Dns_CacheSocketCleanup()\n" ));
|
|||
|
|
|||
|
//
|
|||
|
// close cached sockets
|
|||
|
//
|
|||
|
|
|||
|
LOCK_SOCKET_CACHE();
|
|||
|
|
|||
|
for ( i=0; i<g_CacheSocketMaxCount; i++ )
|
|||
|
{
|
|||
|
sock = g_pCacheSocketArray[i];
|
|||
|
if ( sock )
|
|||
|
{
|
|||
|
Dns_CloseSocket( sock );
|
|||
|
g_CacheSocketCount--;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
DNS_ASSERT( g_CacheSocketCount == 0 );
|
|||
|
|
|||
|
// dump socket array memory
|
|||
|
|
|||
|
FREE_HEAP( g_pCacheSocketArray );
|
|||
|
|
|||
|
// clear globals
|
|||
|
|
|||
|
g_pCacheSocketArray = NULL;
|
|||
|
g_CacheSocketMaxCount = 0;
|
|||
|
g_CacheSocketCount = 0;
|
|||
|
|
|||
|
UNLOCK_SOCKET_CACHE();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
SOCKET
|
|||
|
Dns_GetUdpSocket(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Get a cached socket.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Socket handle if successful.
|
|||
|
Zero if no cached socket available.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
SOCKET sock;
|
|||
|
DWORD i;
|
|||
|
|
|||
|
//
|
|||
|
// quick return if nothing available
|
|||
|
// - do outside lock so function can be called cheaply
|
|||
|
// without other checks
|
|||
|
//
|
|||
|
|
|||
|
if ( g_CacheSocketCount == 0 )
|
|||
|
{
|
|||
|
goto Open;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// get a cached socket
|
|||
|
//
|
|||
|
|
|||
|
LOCK_SOCKET_CACHE();
|
|||
|
|
|||
|
for ( i=0; i<g_CacheSocketMaxCount; i++ )
|
|||
|
{
|
|||
|
sock = g_pCacheSocketArray[i];
|
|||
|
if ( sock != 0 )
|
|||
|
{
|
|||
|
g_pCacheSocketArray[i] = 0;
|
|||
|
g_CacheSocketCount--;
|
|||
|
UNLOCK_SOCKET_CACHE();
|
|||
|
|
|||
|
//
|
|||
|
// DCR: clean out any data on cached socket
|
|||
|
// it would be cool to cheaply dump useless data
|
|||
|
//
|
|||
|
// right now we just let XID match, then question match
|
|||
|
// dump data on recv
|
|||
|
//
|
|||
|
|
|||
|
DNSDBG( SOCKET, (
|
|||
|
"Returning cached socket %d.\n",
|
|||
|
sock ));
|
|||
|
return sock;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
UNLOCK_SOCKET_CACHE();
|
|||
|
|
|||
|
Open:
|
|||
|
|
|||
|
sock = Dns_CreateSocket(
|
|||
|
SOCK_DGRAM,
|
|||
|
INADDR_ANY,
|
|||
|
0 );
|
|||
|
return sock;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
Dns_ReturnUdpSocket(
|
|||
|
IN SOCKET Socket
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Return UDP socket for possible caching.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Socket -- socket handle
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
SOCKET sock;
|
|||
|
DWORD i;
|
|||
|
|
|||
|
//
|
|||
|
// quick return if not caching
|
|||
|
// - do outside lock so function can be called cheaply
|
|||
|
// without other checks
|
|||
|
//
|
|||
|
|
|||
|
if ( g_CacheSocketMaxCount == 0 ||
|
|||
|
g_CacheSocketMaxCount == g_CacheSocketCount )
|
|||
|
{
|
|||
|
goto Close;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// return cached socket
|
|||
|
//
|
|||
|
|
|||
|
LOCK_SOCKET_CACHE();
|
|||
|
|
|||
|
for ( i=0; i<g_CacheSocketMaxCount; i++ )
|
|||
|
{
|
|||
|
if ( g_pCacheSocketArray[i] )
|
|||
|
{
|
|||
|
continue;
|
|||
|
}
|
|||
|
g_pCacheSocketArray[i] = Socket;
|
|||
|
g_CacheSocketCount++;
|
|||
|
UNLOCK_SOCKET_CACHE();
|
|||
|
|
|||
|
DNSDBG( SOCKET, (
|
|||
|
"Returned socket %d to cache.\n",
|
|||
|
Socket ));
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
UNLOCK_SOCKET_CACHE();
|
|||
|
|
|||
|
DNSDBG( SOCKET, (
|
|||
|
"Socket cache full, closing socket %d.\n"
|
|||
|
"WARNING: should only see this message on race!\n",
|
|||
|
Socket ));
|
|||
|
|
|||
|
Close:
|
|||
|
|
|||
|
Dns_CloseSocket( Socket );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// End socket.c
|
|||
|
//
|