Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

704 lines
17 KiB

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
r_send.c
Abstract:
This module implements routines to transmit DNS resolver queries.
Author:
Mike Massa (mikemas) Sept 20, 1991
Revision History:
Who When What
-------- -------- ----------------------------------------------
mikemas 9-20-91 created
Notes:
Exports:
res_send()
--*/
#ident "@(#)res_send.c 5.3 3/8/91"
/******************************************************************
*
* SpiderTCP BIND
*
* Copyright 1990 Spider Systems Limited
*
* RES_SEND.C
*
******************************************************************/
/*
* /usr/projects/tcp/SCCS.rel3/rel/src/lib/net/0/s.res_send.c
* @(#)res_send.c 5.3
*
* Last delta created 14:11:50 3/4/91
* This file extracted 11:20:33 3/8/91
*
* Modifications:
*
* GSS 24 Jul 90 New File
*/
/*
* Copyright (c) 1985, 1989 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that: (1) source distributions retain this entire copyright
* notice and comment, and (2) distributions including binaries display
* the following acknowledgement: ``This product includes software
* developed by the University of California, Berkeley and its contributors''
* in the documentation or other materials provided with the distribution
* and in all advertising materials mentioning features or use of this
* software. Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)res_send.c 6.25 (Berkeley) 6/1/90";
#endif /* LIBC_SCCS and not lint */
/*
* Send query to name server and wait for reply.
*/
/****************************************************************************/
#include <winsockp.h>
#define PING_DNS_SERVERS 0
#if PING_DNS_SERVERS
#include <ipexport.h>
#include <icmpapi.h>
#endif // PING_DNS_SERVERS
//
// External function prototypes
//
BOOL
res_check_ns (
IN LPSOCKADDR_IN NsAddress
);
int
res_ParallelSendRecv(
SOCKET Sockets[],
BOOL ServersUp[MAXNSLIST][MAXNS],
u_long ServerIndex,
char * Buffer,
int BufferLength,
char * Answer,
int AnswerLength,
LPTIMEVAL Timeout,
BOOL * FoundServer
);
void
_res_close(
SOCKET Sockets[]
);
int
res_send(
IN char *buf,
IN int buflen,
OUT char *answer,
IN int anslen
)
{
u_long listIndex;
u_long serverIndex;
u_long maxListSize;
int retryCount;
int numLists;
int result;
TIMEVAL timeout;
BOOL ns_up[MAXNSLIST][MAXNS];
SOCKET sockets[MAXNSLIST];
BOOL foundServer = FALSE;
IF_DEBUG(RESOLVER) {
WS_PRINT(("res_send entered\n"));
p_query(buf);
}
if (res_init() == -1) {
return(-1);
}
maxListSize = 0;
numLists = 0;
for( listIndex = 0 ; listIndex < MAXNSLIST ; listIndex++ ) {
u_long tmp;
sockets[listIndex] = INVALID_SOCKET;
for( serverIndex = 0 ; serverIndex < MAXNS ; serverIndex++ ) {
ns_up[listIndex][serverIndex] = TRUE;
}
tmp = _res.nslist[listIndex].ServerCount;
if( tmp > 0 ) {
numLists++;
}
if( tmp > maxListSize ) {
maxListSize = tmp;
}
}
//
// Create all of the sockets up front.
//
for( listIndex = 0 ; listIndex < MAXNSLIST ; listIndex++ ) {
if( _res.nslist[listIndex].ServerCount > 0 ) {
sockets[listIndex] = WSASocket(
AF_INET,
SOCK_DGRAM,
0,
NULL,
0,
WSA_FLAG_OVERLAPPED
);
if( sockets[listIndex] == INVALID_SOCKET ) {
_res_close( sockets );
return -1;
}
}
}
//
// Send request, RETRY times, or until successful, or until IO is
// cancelled.
//
for ( retryCount = 0;
retryCount < _res.retry && !SockThreadGetXByYCancelled;
retryCount++ ) {
//
// If this is the second time through this loop, then we failed
// to get a response from any of the servers. Check them all
// to see if any are up.
//
#if 0 // not in use
if ( retryCount == 1 ) {
BOOL foundServer;
BOOL foundList;
SOCKADDR_IN addr;
addr.sin_family = AF_INET;
addr.sin_port = htons( NAMESERVER_PORT );
foundList = FALSE;
for( listIndex = 0 ; listIndex < MAXNSLIST ; listIndex++ ) {
if( sockets[listIndex] != INVALID_SOCKET ) {
foundServer = FALSE;
for( serverIndex = 0 ;
serverIndex < _res.nslist[listIndex].ServerCount &&
!SockThreadGetXByYCancelled ;
serverIndex++ ) {
addr.sin_addr.s_addr =
_res.nslist[listIndex].Servers[serverIndex];
ns_up[listIndex][serverIndex] = res_check_ns( &addr );
if( ns_up[listIndex][serverIndex] ) {
foundServer = TRUE;
foundList = TRUE;
}
}
//
// If we didn't find any available servers in the current
// list, close the associated socket so we'll ignore the
// list.
//
if( !foundServer ) {
closesocket( sockets[listIndex] );
sockets[listIndex] = INVALID_SOCKET;
}
}
}
//
// If we didn't find any available servers on any of the
// lists, bail out now setting the special error code
// indicating that we should stop trying DNS.
//
if( !foundList ) {
_res_close( sockets );
SetLastError( WSAECONNREFUSED );
return 0;
}
}
#endif // if 0
//
// Compute the select() timeout.
//
timeout.tv_sec = (_res.retrans * (retryCount+1));
if (retryCount > 0) {
timeout.tv_sec /= max(1, maxListSize);
}
if (timeout.tv_sec <= 0) {
timeout.tv_sec = 1;
}
timeout.tv_usec = 0;
//
// Loop through the servers in the server list(s), making queries.
//
result = 0;
for( serverIndex = 0 ;
serverIndex < maxListSize && !SockThreadGetXByYCancelled ;
serverIndex++ ) {
result = res_ParallelSendRecv(
sockets,
ns_up,
serverIndex,
buf,
buflen,
answer,
anslen,
&timeout,
&foundServer
);
if( result > 0 ) {
IF_DEBUG(RESOLVER) {
WS_PRINT(("got answer:\r\n"));
p_query(answer);
}
_res_close( sockets );
return result;
}
if( result < 0 ) {
break;
}
}
if( result < 0 ) {
foundServer = TRUE; // something happened
break;
}
}
_res_close( sockets );
if( !foundServer ) {
//
// no nameservers found. At a minimum this means
// that no servers responded within the timeout
// interval. Although this is not a definitive
// indication that no DNS servers are available, it's
// a pretty good estimate of this, and likely valid
// for now and a short time into the future. As long
// as returning this error affects only this one
// lookup request, this conclusion is valid.
//
SetLastError(WSAECONNREFUSED);
} else {
//
// no answer obtained
//
SetLastError(WSAETIMEDOUT);
}
return (-1);
}
/*
* This routine is for closing the socket if a virtual circuit is used and
* the program wants to close it. This provides support for endhostent()
* which expects to close the socket.
*
* This routine is not expected to be user visible.
*/
void
_res_close(
SOCKET Sockets[]
)
{
int i;
for( i = 0 ; i < MAXNSLIST ; i++ ) {
if( Sockets[i] != INVALID_SOCKET ) {
closesocket( Sockets[i] );
Sockets[i] = INVALID_SOCKET;
}
}
} // _res_close
BOOL
res_check_ns (
IN LPSOCKADDR_IN NsAddress
)
{
return TRUE;
#if PING_DNS_SERVERS
HANDLE icmpHandle;
CHAR buffer[4096];
int i;
DWORD status;
//
// Get an ICMP handle for the requests.
//
icmpHandle = IcmpCreateFile( );
if ( icmpHandle == NULL ) {
return FALSE;
}
//
// Make four attempts to ping the server. If any succeed, assume
// that the server is available.
//
for ( i = 0; i < 4; i++ ) {
status = IcmpSendEcho(
icmpHandle,
NsAddress->sin_addr.s_addr,
NULL,
0,
NULL,
buffer,
4096,
1000
);
//
// If we got a response, we assume that the server is up.
//
if ( status != 0 ) {
IcmpCloseHandle( icmpHandle );
return TRUE;
}
}
//
// The server is not available.
//
IcmpCloseHandle( icmpHandle );
return FALSE;
#endif
} // res_check_ns
int
res_ParallelSendRecv(
SOCKET Sockets[],
BOOL ServersUp[MAXNSLIST][MAXNS],
u_long ServerIndex,
char * Buffer,
int BufferLength,
char * Answer,
int AnswerLength,
LPTIMEVAL Timeout,
BOOL * FoundServer
)
{
FD_SET readFds;
FD_SET readFdsCopy;
SOCKADDR_IN addr;
u_long i;
int result;
HEADER * bufferHeader;
HEADER * answerHeader;
DWORD BeginTime, Usedus, totTimeout;
TIMEVAL tv;
//
// Initialize.
//
FD_ZERO( &readFds );
addr.sin_family = AF_INET;
addr.sin_port = htons( NAMESERVER_PORT );
bufferHeader = (HEADER *)Buffer;
answerHeader = (HEADER *)Answer;
//
// Initiate the sendto()s, setup the FD_SET for select().
//
for( i = 0 ; i < MAXNSLIST ; i++ ) {
if( Sockets[i] != INVALID_SOCKET &&
_res.nslist[i].ServerCount > ServerIndex &&
ServersUp[i][ServerIndex] ) {
addr.sin_addr.s_addr = _res.nslist[i].Servers[ServerIndex];
result = sendto(
Sockets[i],
Buffer,
BufferLength,
0,
(SOCKADDR *)&addr,
sizeof(addr)
);
if( result == BufferLength ) {
FD_SET( Sockets[i], &readFds );
}
}
}
//
// Bail if there were no sockets or all of the sendto()s failed.
//
if( readFds.fd_count == 0 ) {
return SOCKET_ERROR;
}
//
// Save a copy of the FD_SET in case we get an old response (with
// a mis-matched query ID) and we need to retry the wait.
//
readFdsCopy = readFds;
//
// Compute timeout values. Note the use of usecs means
// the timout values are limited to around 30 minutes. For
// this use, that is sufficient.
//
BeginTime = GetTickCount(); // remember the present time
tv.tv_sec = 0;
tv.tv_usec = (Timeout->tv_sec * 1000 * 1000) + Timeout->tv_usec;
totTimeout = tv.tv_usec; // save original timeout value
Usedus = 0; // no time has elapsed yet.
do
{
DWORD Now, Delta;
//
// Wait for something to happen.
//
result = select(
1,
&readFds,
NULL,
NULL,
&tv
);
if( result == SOCKET_ERROR ) {
return SOCKET_ERROR;
}
//
// Scan for active sockets.
//
if(result)
{
//
// select() claims there is some data ready
//
for( i = 0 ; i < MAXNSLIST ; i++ )
{
if( Sockets[i] != INVALID_SOCKET &&
FD_ISSET( Sockets[i], &readFds ) ) {
//
// Found one. Read the response.
//
result = recvfrom(
Sockets[i],
Answer,
AnswerLength,
0,
NULL,
NULL
);
if( result == SOCKET_ERROR ) {
return SOCKET_ERROR;
}
//
// A DNS is out there, or appears to be. So
// declare that retries are in order.
//
*FoundServer = TRUE;
//
// Check for valid response & valid query ID.
//
if( result >= sizeof(HEADER) &&
answerHeader->id == bufferHeader->id ) {
//
// We've got data. If the response looks good
// (no error, and with at least one answer record)
// then return it to the caller.
//
if( answerHeader->rcode == 0 &&
ntohs( answerHeader->ancount ) > 0 ) {
return result;
}
//
// Remove the current socket from the list (so we'll
// ignore this column) and wait for another response
// from a different server.
//
FD_CLR( Sockets[i], &readFdsCopy );
closesocket( Sockets[i] );
Sockets[i] = INVALID_SOCKET;
//
// If that was the last one, then give up. We'll
// return the result of the last recvfrom() so that
// the caller can see the packet with the DNS error.
//
if( readFdsCopy.fd_count == 0 ) {
return result;
}
}
//
// If we make it to this point, then we've receive
// a bogus packet (with a possibly stale query ID) OR
// a resolution error. We'll continue with this inner
// loop to see if any additional sockets are readable.
//
}
}
}
else
{
//
// timeout
//
return(0);
}
//
// At this point, we've received at least one packet, but
// all packet were invalid. Restore the FD_SET to its former
// glory and loop around to try again. But honor the timeout
// value.
//
readFds = readFdsCopy;
Now = GetTickCount();
Delta = (Now - BeginTime) * 1000; // elapsed us
Usedus += Delta; // total elapsed time
tv.tv_usec -= Delta; // remaining wait time
BeginTime = Now;
} while(Usedus < totTimeout);
//
// We get here when we get junk data for longer than the timeout
// period. This is indicative of a bug or of a misbehaving DNS
// server -- also a bug, but a remote one.
//
return(0); // treat it like a timeout
} // res_ParallelSendRecv