📄 dns.c
字号:
/****************************************************************************
* *
* cryptlib DNS Interface Routines *
* Copyright Peter Gutmann 1998-2004 *
* *
****************************************************************************/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
#include "crypt.h"
#include "stream.h"
#include "tcp.h"
#elif defined( INC_CHILD )
#include "../crypt.h"
#include "stream.h"
#include "tcp.h"
#else
#include "crypt.h"
#include "io/stream.h"
#include "io/tcp.h"
#endif /* Compiler-specific includes */
#ifdef USE_TCP
/****************************************************************************
* *
* Init/Shutdown Routines *
* *
****************************************************************************/
#ifdef __WINDOWS__
/* Global function pointers. These are necessary because the functions need
to be dynamically linked since not all systems contain the necessary
libraries */
#if ( defined( sun ) && OSVERSION > 4 )
#undef htonl /* Slowaris has defines that conflict with our ones */
#undef htons
#undef ntohl
#undef ntohs
#endif /* Slowaris */
static INSTANCE_HANDLE hDNS;
typedef void ( SOCKET_API *FREEADDRINFO )( struct addrinfo *ai );
typedef int ( SOCKET_API *GETADDRINFO )( const char *nodename,
const char *servname,
const struct addrinfo *hints,
struct addrinfo **res );
typedef struct hostent FAR * ( SOCKET_API *GETHOSTBYNAME )( const char FAR *name );
typedef struct hostent FAR * ( SOCKET_API *GETHOSTNAME )( char FAR * name,
int namelen );
typedef int ( SOCKET_API *GETNAMEINFO )( const struct sockaddr *sa,
SIZE_TYPE salen, char *node,
SIZE_TYPE nodelen, char *service,
SIZE_TYPE servicelen, int flags );
typedef u_long ( SOCKET_API *HTONL )( u_long hostlong );
typedef u_short ( SOCKET_API *HTONS )( u_short hostshort );
typedef unsigned long ( SOCKET_API *INET_ADDR )( const char FAR *cp );
typedef char FAR * ( SOCKET_API *INET_NTOA )( struct in_addr in );
typedef u_long ( SOCKET_API *NTOHL )( u_long netlong );
typedef u_short ( SOCKET_API *NTOHS )( u_short netshort );
typedef DNS_STATUS ( WINAPI *DNSQUERY )( const LPSTR lpstrName,
const WORD wType, const DWORD fOptions,
const PIP4_ARRAY aipServers,
PDNS_RECORD *ppQueryResultsSet,
PVOID *pReserved );
typedef DNS_STATUS ( WINAPI *DNSQUERYCONFIG )( const DNS_CONFIG_TYPE Config,
const DWORD Flag,
const PWSTR pwsAdapterName,
PVOID pReserved, PVOID pBuffer,
PDWORD pBufferLength );
typedef VOID ( WINAPI *DNSRECORDLISTFREE )( PDNS_RECORD pRecordList,
DNS_FREE_TYPE FreeType );
typedef int ( SOCKET_API *WSAGETLASTERROR )( void );
static FREEADDRINFO pfreeaddrinfo = NULL;
static GETADDRINFO pgetaddrinfo = NULL;
static GETHOSTBYNAME pgethostbyname = NULL;
static GETHOSTNAME pgethostname = NULL;
static GETNAMEINFO pgetnameinfo = NULL;
static HTONL phtonl = NULL;
static HTONS phtons = NULL;
static INET_ADDR pinet_addr = NULL;
static INET_NTOA pinet_ntoa = NULL;
static NTOHL pntohl = NULL;
static NTOHS pntohs = NULL;
static DNSQUERY pDnsQuery = NULL;
static DNSQUERYCONFIG pDnsQueryConfig = NULL;
static DNSRECORDLISTFREE pDnsRecordListFree = NULL;
static WSAGETLASTERROR pWSAGetLastError = NULL;
#define freeaddrinfo pfreeaddrinfo
#define getaddrinfo pgetaddrinfo
#define gethostbyname pgethostbyname
#define gethostname pgethostname
#define getnameinfo pgetnameinfo
#define htonl phtonl
#define htons phtons
#define inet_addr pinet_addr
#define inet_ntoa pinet_ntoa
#define ntohl pntohl
#define ntohs pntohs
#define DnsQuery pDnsQuery
#define DnsQueryConfig pDnsQueryConfig
#define DnsRecordListFree pDnsRecordListFree
#ifndef WSAGetLastError
/* In some environments WSAGetLastError() is a macro that maps to
GetLastError() */
#define WSAGetLastError pWSAGetLastError
#define DYNLOAD_WSAGETLASTERROR
#endif /* WSAGetLastError */
static int SOCKET_API my_getaddrinfo( const char *nodename,
const char *servname,
const struct addrinfo *hints,
struct addrinfo **res );
static void SOCKET_API my_freeaddrinfo( struct addrinfo *ai );
static int SOCKET_API my_getnameinfo( const struct sockaddr *sa,
SIZE_TYPE salen, char *node,
SIZE_TYPE nodelen, char *service,
SIZE_TYPE servicelen, int flags );
int initDNS( INSTANCE_HANDLE hTCP, INSTANCE_HANDLE hAddr )
{
/* Get the required TCP/IP functions */
gethostbyname = ( GETHOSTBYNAME ) DynamicBind( hTCP, TEXT( "gethostbyname" ) );
gethostname = ( GETHOSTNAME ) DynamicBind( hTCP, TEXT( "gethostname" ) );
htonl = ( HTONL ) DynamicBind( hTCP, TEXT( "htonl" ) );
htons = ( HTONS ) DynamicBind( hTCP, TEXT( "htons" ) );
inet_addr = ( INET_ADDR ) DynamicBind( hTCP, TEXT( "inet_addr" ) );
inet_ntoa = ( INET_NTOA ) DynamicBind( hTCP, TEXT( "inet_ntoa" ) );
ntohl = ( NTOHL ) DynamicBind( hTCP, TEXT( "ntohl" ) );
ntohs = ( NTOHS ) DynamicBind( hTCP, TEXT( "ntohs" ) );
#ifdef DYNLOAD_WSAGETLASTERROR
WSAGetLastError = ( WSAGETLASTERROR ) DynamicBind( hTCP, TEXT( "WSAGetLastError" ) );
#endif /* DYNLOAD_WSAGETLASTERROR */
if( gethostbyname == NULL || gethostname == NULL || htonl == NULL || \
htons == NULL || inet_addr == NULL || inet_ntoa == NULL || \
ntohl == NULL || ntohs == NULL )
return( CRYPT_ERROR );
/* Set up the IPv6-style name/address functions */
if( hAddr != NULL_INSTANCE )
{
freeaddrinfo = ( FREEADDRINFO ) DynamicBind( hAddr, TEXT( "freeaddrinfo" ) );
getaddrinfo = ( GETADDRINFO ) DynamicBind( hAddr, TEXT( "getaddrinfo" ) );
getnameinfo = ( GETNAMEINFO ) DynamicBind( hAddr, TEXT( "getnameinfo" ) );
if( freeaddrinfo == NULL || getaddrinfo == NULL || \
getnameinfo == NULL )
return( CRYPT_ERROR );
}
else
{
/* If we couldn't dynamically bind the IPv6 name/address functions,
use a local emulation */
getaddrinfo = my_getaddrinfo;
freeaddrinfo = my_freeaddrinfo;
getnameinfo = my_getnameinfo;
}
/* Get the required DNS functions if they're available */
#if defined( __WIN16__ )
hDNS = NULL_INSTANCE;
#elif defined( __WIN32__ )
hDNS = DynamicLoad( "dnsapi.dll" );
#elif defined( __WINCE__ )
hDNS = hTCP;
#endif /* Win16/Win32/WinCE */
if( hDNS != NULL_INSTANCE )
{
DnsQuery = ( DNSQUERY ) DynamicBind( hDNS, TEXT( "DnsQuery_A" ) );
DnsQueryConfig = ( DNSQUERYCONFIG ) DynamicBind( hDNS, TEXT( "DnsQueryConfig" ) );
DnsRecordListFree = ( DNSRECORDLISTFREE ) DynamicBind( hDNS, TEXT( "DnsRecordListFree" ) );
if( ( DnsQuery == NULL || DnsQueryConfig == NULL || \
DnsRecordListFree == NULL ) && hDNS != hTCP )
{
DynamicUnload( hDNS );
hDNS = NULL_INSTANCE;
return( CRYPT_ERROR );
}
}
return( CRYPT_OK );
}
void endDNS( INSTANCE_HANDLE hTCP )
{
if( hDNS != NULL_INSTANCE && hDNS != hTCP )
DynamicUnload( hDNS );
hDNS = NULL_INSTANCE;
}
#endif /* __WINDOWS__ */
/****************************************************************************
* *
* IPv6 Emulation *
* *
****************************************************************************/
/* Emulation of IPv6 networking functions. We include these unconditionally
under Windows because with dynamic binding we can't be sure that they're
needed or not */
#if !defined( IPv6 ) || defined( __WINDOWS__ )
static int addAddrInfo( struct addrinfo *prevAddrInfoPtr,
struct addrinfo **addrInfoPtrPtr,
const void *address, const int port )
{
struct addrinfo *addrInfoPtr;
struct sockaddr_in *sockAddrPtr;
/* Allocate the new element, clear it, and set fixed fields for IPv4 */
if( ( addrInfoPtr = clAlloc( "addAddrInfo", \
sizeof( struct addrinfo ) ) ) == NULL || \
( sockAddrPtr = clAlloc( "addAddrInfo", \
sizeof( struct sockaddr ) ) ) == NULL )
{
if( addrInfoPtr != NULL )
clFree( "addAddrInfo", addrInfoPtr );
return( -1 );
}
memset( addrInfoPtr, 0, sizeof( struct addrinfo ) );
memset( sockAddrPtr, 0, sizeof( struct sockaddr ) );
if( prevAddrInfoPtr != NULL )
prevAddrInfoPtr->ai_next = addrInfoPtr;
addrInfoPtr->ai_family = PF_INET;
addrInfoPtr->ai_socktype = SOCK_STREAM;
addrInfoPtr->ai_protocol = IPPROTO_TCP;
addrInfoPtr->ai_addrlen = sizeof( struct sockaddr_in );
addrInfoPtr->ai_addr = ( struct sockaddr * ) sockAddrPtr;
/* Set the port and address information. In general we'd copy the
address to the sockAddrPtr->sin_addr.s_addr member, however on
Crays, which don't have 32-bit data types, this is a 32-bit
bitfield, so we have to use the encapsulating struct */
sockAddrPtr->sin_family = AF_INET;
sockAddrPtr->sin_port = htons( ( in_port_t ) port );
memcpy( &sockAddrPtr->sin_addr, address, IP_ADDR_SIZE );
*addrInfoPtrPtr = addrInfoPtr;
return( 0 );
}
static int SOCKET_API my_getaddrinfo( const char *nodename,
const char *servname,
const struct addrinfo *hints,
struct addrinfo **res )
{
struct hostent *pHostent;
struct addrinfo *currentAddrInfoPtr = NULL;
const int port = aToI( servname );
int hostErrno, i;
gethostbyname_vars();
assert( nodename != NULL || ( hints->ai_flags & AI_PASSIVE ) );
assert( servname != NULL );
assert( isReadPtr( hints, sizeof( struct addrinfo ) ) );
/* Clear return value */
*res = NULL;
/* Perform basic error checking */
if( ( nodename == NULL && !( hints->ai_flags & AI_PASSIVE ) ) || \
servname == NULL )
return( -1 );
/* If there's no interface specified and we're creating a server-side
socket, prepare to listen on any interface. Note that BeOS can only
bind to one interface at a time, so INADDR_ANY actually binds to the
first interface it finds */
if( nodename == NULL && ( hints->ai_flags & AI_PASSIVE ) )
{
const in_addr_t address = INADDR_ANY;
return( addAddrInfo( NULL, res, &address, port ) );
}
/* If it's a dotted address, there's a single address, convert it to
in_addr form and return it. Note for EBCDIC use that since this is
an emulation of an OS function the string is already in EBCDIC form,
so we don't use the cryptlib-internal functions for this */
if( isdigit( *nodename ) )
{
const in_addr_t address = inet_addr( nodename );
if( isBadAddress( address ) )
return( -1 );
return( addAddrInfo( NULL, res, &address, port ) );
}
/* It's a host name, convert it to the in_addr form */
gethostbyname_threadsafe( nodename, pHostent, hostErrno );
if( pHostent == NULL || pHostent->h_length != IP_ADDR_SIZE )
return( -1 );
for( i = 0; pHostent->h_addr_list[ i ] != NULL && i < IP_ADDR_COUNT; i++ )
{
int status;
if( currentAddrInfoPtr == NULL )
{
status = addAddrInfo( NULL, res, pHostent->h_addr_list[ i ], port );
currentAddrInfoPtr = *res;
}
else
status = addAddrInfo( currentAddrInfoPtr, ¤tAddrInfoPtr,
pHostent->h_addr_list[ i ], port );
if( status != 0 )
{
freeaddrinfo( *res );
return( status );
}
}
return( 0 );
}
static void SOCKET_API my_freeaddrinfo( struct addrinfo *ai )
{
while( ai != NULL )
{
struct addrinfo *addrInfoCursor = ai;
ai = ai->ai_next;
if( addrInfoCursor->ai_addr != NULL )
clFree( "my_freeaddrinfo", addrInfoCursor->ai_addr );
clFree( "my_freeaddrinfo", addrInfoCursor );
}
}
static int SOCKET_API my_getnameinfo( const struct sockaddr *sa,
SIZE_TYPE salen, char *node,
SIZE_TYPE nodelen, char *service,
SIZE_TYPE servicelen, int flags )
{
const struct sockaddr_in *sockAddr = ( struct sockaddr_in * ) sa;
const char *ipAddress;
/* Clear return values */
strcpy( node, "<Unknown>" );
strcpy( service, "0" );
/* Get the remote system's address and port number */
if( ( ipAddress = inet_ntoa( sockAddr->sin_addr ) ) == NULL )
return( -1 );
strncpy( node, ipAddress, nodelen );
node[ nodelen - 1 ] = '\0';
sPrintf( service, "%d", ntohs( sockAddr->sin_port ) );
return( 0 );
}
#endif /* !IPv6 || __WINDOWS__ */
/****************************************************************************
* *
* DNS SRV Interface *
* *
****************************************************************************/
/* Use DNS SRV to auto-detect host information */
#if defined( __WINDOWS__ )
static void convertToSrv( char *srvName, const char *hostName )
{
const int nameLength = strlen( hostName );
int i;
/* Prepend the service info to the start of the host name. This
converts foo.bar.com into _pkiboot._tcp.bar.com in preparation for
the DNS SRV lookup */
for( i = 0; i < nameLength; i++ )
if( hostName[ i ] == '.' )
break;
if( i < nameLength && ( nameLength - i ) < MAX_URL_SIZE - 16 )
{
memcpy( srvName, "_pkiboot._tcp.", 14 );
memcpy( srvName + 14, hostName + i, nameLength - i + 1 );
}
else
strcpy( srvName, "_pkiboot._tcp.localhost" );
}
static int getSrvFQDN( STREAM *stream, char *fqdn )
{
PDNS_RECORD pDns = NULL;
struct hostent *hostInfo;
static char cachedFQDN[ MAX_URL_SIZE + 1 ];
static time_t lastFetchTime = 0;
#ifdef __WINCE__
char fqdnBuffer[ MAX_URL_SIZE + 1 ], *fqdnPtr = fqdnBuffer;
#else
char *fqdnPtr;
#endif /* Win32 vs. WinCE */
/* The uncached FQDN check is quite slow and resource-intensive (it
seems to do a full reload of the DNS subsystem), to lighten the load
we only try a new one once a minute */
if( lastFetchTime >= getTime() - 60 )
{
strcpy( fqdn, cachedFQDN );
return( CRYPT_OK );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -