📄 dns_srv.c
字号:
/****************************************************************************
* *
* cryptlib DNS SRV Interface Routines *
* Copyright Peter Gutmann 1998-2007 *
* *
****************************************************************************/
#include <ctype.h>
#if defined( INC_ALL )
#include "crypt.h"
#include "stream_int.h"
#include "tcp.h"
#else
#include "crypt.h"
#include "io/stream_int.h"
#include "io/tcp.h"
#endif /* Compiler-specific includes */
#if defined( USE_TCP ) && defined( USE_DNSSRV )
/****************************************************************************
* *
* Init/Shutdown Routines *
* *
****************************************************************************/
#ifdef __WINDOWS__
#ifndef TEXT
#define TEXT /* Win32 windows.h defines this, but not the Win16 one */
#endif /* TEXT */
#if defined( _MSC_VER ) && defined( _PREFAST_ )
#define OUT_STRING_OPT __out_bcount_z
#else
#define OUT_STRING_OPT( size )
#endif /* Additional markups for Win32 API functions */
#ifdef DnsQuery
#undef DnsQuery /* Newer versions of the platform SDK have conflicting defines */
#endif /* DnsQuery defined as a macro in windns.h */
/* Global function pointers. These are necessary because the functions need
to be dynamically linked since not all systems contain the necessary
libraries */
typedef CHECK_RETVAL struct hostent FAR * ( SOCKET_API *GETHOSTNAME )\
( OUT_STRING_OPT( namelen ) \
char FAR *name, int namelen ) \
STDC_NONNULL_ARG( ( 1 ) );
typedef CHECK_RETVAL struct hostent FAR * ( SOCKET_API *GETHOSTBYNAME )\
( IN_STRING const char FAR *name ) \
STDC_NONNULL_ARG( ( 1 ) );
typedef CHECK_RETVAL char FAR * ( SOCKET_API *INET_NTOA )( struct in_addr in );
#if defined( _MSC_VER ) && ( _MSC_VER > 800 )
typedef CHECK_RETVAL DNS_STATUS ( WINAPI *DNSQUERY )\
( IN_STRING const LPSTR lpstrName, const WORD wType,
const DWORD fOptions, const PIP4_ARRAY aipServers,
OUT_PTR PDNS_RECORD *ppQueryResultsSet,
STDC_UNUSED PVOID *pReserved ) \
STDC_NONNULL_ARG( ( 1, 4, 5 ) );
typedef VOID ( WINAPI *DNSFREEFN )\
( IN PVOID pData, DNS_FREE_TYPE FreeType ) \
STDC_NONNULL_ARG( ( 1 ) );
typedef VOID ( WINAPI *DNSRECORDLISTFREE )\
( IN PDNS_RECORD pRecordList, DNS_FREE_TYPE FreeType ) \
STDC_NONNULL_ARG( ( 1 ) );
/* This is a special case, in Win2K it was DnsRecordListFree(), in WinXP
and newer this changed to DnsFree() with almost the same parameters,
the first one was changed from a PDNS_RECORD to a PVOID. We just
point the function pointer to the appropriate one */
static GETHOSTNAME pgethostname = NULL;
static GETHOSTBYNAME pgethostbyname = NULL;
static INET_NTOA pinet_ntoa = NULL;
static DNSQUERY pDnsQuery = NULL;
static DNSFREEFN pDnsFreeFn = NULL;
#define gethostname pgethostname
#define gethostbyname pgethostbyname
#define inet_ntoa pinet_ntoa
#define DnsQuery pDnsQuery
#define DnsFreeFn pDnsFreeFn
#endif /* 32-bit VC++ */
static INSTANCE_HANDLE hDNS;
CHECK_RETVAL \
int initDNSSRV( const INSTANCE_HANDLE hTCP )
{
/* Get the required TCP/IP functions */
gethostname = ( GETHOSTNAME ) DynamicBind( hTCP, TEXT( "gethostname" ) );
gethostbyname = ( GETHOSTBYNAME ) DynamicBind( hTCP, TEXT( "gethostbyname" ) );
inet_ntoa = ( INET_NTOA ) DynamicBind( hTCP, TEXT( "inet_ntoa" ) );
if( gethostname == NULL || gethostbyname == NULL || inet_ntoa == NULL )
return( CRYPT_ERROR );
/* Get the required DNS functions if they're available */
#if defined( __WIN16__ )
hDNS = NULL_INSTANCE;
#else
#if defined( __WIN32__ )
hDNS = DynamicLoad( "dnsapi.dll" );
#elif defined( __WINCE__ )
hDNS = hTCP;
#endif /* Win32 vs.WinCE */
if( hDNS != NULL_INSTANCE )
{
DnsQuery = ( DNSQUERY ) DynamicBind( hDNS, TEXT( "DnsQuery_A" ) );
DnsFreeFn = ( DNSFREEFN ) DynamicBind( hDNS, TEXT( "DnsFree" ) );
if( DnsFreeFn == NULL )
DnsFreeFn = ( DNSFREEFN ) DynamicBind( hDNS, TEXT( "DnsRecordListFree" ) );
if( ( DnsQuery == NULL || DnsFreeFn == NULL ) && hDNS != hTCP )
{
DynamicUnload( hDNS );
hDNS = NULL_INSTANCE;
return( CRYPT_ERROR );
}
}
#endif /* Win16 vs.Win32/WinCE */
return( CRYPT_OK );
}
void endDNSSRV( const INSTANCE_HANDLE hTCP )
{
if( hDNS != NULL_INSTANCE && hDNS != hTCP )
DynamicUnload( hDNS );
hDNS = NULL_INSTANCE;
}
#endif /* __WINDOWS__ */
/****************************************************************************
* *
* Windows DNS SRV Interface *
* *
****************************************************************************/
/* Use DNS SRV to auto-detect host information. Note that this code is
disabled by default, before enabling it you should make sure that your
system's DNS services can't serve as an attack vector due to the
complexity of DNS packet processing */
#if defined( __WINDOWS__ ) && !defined( __WIN16__ )
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
static int convertToSrv( OUT_BUFFER_FIXED( srvNameMaxLen ) char *srvName,
IN_LENGTH_DNS const int srvNameMaxLen,
IN_STRING const char *hostName )
{
const int hostNameLength = strlen( hostName ) + 1;
int i; /* For trailing '\0' */
assert( isReadPtr( srvName, srvNameMaxLen ) );
assert( hostName != NULL );
REQUIRES( srvNameMaxLen > 0 && srvNameMaxLen <= MAX_DNS_SIZE );
REQUIRES( srvName != hostName );
/* Clear return value */
memset( srvName, 0, min( 16, srvNameMaxLen ) );
/* Make sure that the (worst-case) result will fit into the output
buffer */
if( 16 + hostNameLength > srvNameMaxLen )
return( CRYPT_ERROR_OVERFLOW );
/* 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 < hostNameLength; i++ )
{
if( hostName[ i ] == '.' )
break;
}
memcpy( srvName, "_pkiboot._tcp.", 14 );
if( i < hostNameLength )
memcpy( srvName + 14, hostName + i, hostNameLength - i );
else
memcpy( srvName + 14, hostName, hostNameLength );
return( CRYPT_OK );
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int getSrvFQDN( INOUT NET_STREAM_INFO *netStream,
OUT_BUFFER_FIXED( fqdnMaxLen ) char *fqdn,
IN_LENGTH_DNS const int fqdnMaxLen )
{
PDNS_RECORD pDns = NULL;
struct hostent *hostInfo;
static char cachedFQDN[ MAX_DNS_SIZE + 8 ];
static time_t lastFetchTime = 0;
#ifdef __WINCE__
char fqdnBuffer[ MAX_DNS_SIZE + 8 ], *fqdnPtr = fqdnBuffer;
#else
char *fqdnPtr;
#endif /* Win32 vs. WinCE */
int status;
assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );
assert( isWritePtr( fqdn, fqdnMaxLen ) );
REQUIRES( fqdnMaxLen > 0 && fqdnMaxLen <= MAX_DNS_SIZE );
/* Clear return value */
memset( fqdn, 0, min( 16, fqdnMaxLen ) );
/* 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 )
{
strlcpy_s( fqdn, fqdnMaxLen, cachedFQDN );
return( CRYPT_OK );
}
/* If we're doing a full autodetect we first have to determine the local
host's FQDN. This gets quite tricky because the behavior of
gethostbyaddr() changed with Win2K so we have to use the DNS API, but
this isn't available in older versions of Windows. If we are using
the DNS API, we have to use the barely-documented
DNS_QUERY_BYPASS_CACHE option to get what we want */
if( gethostname( cachedFQDN, MAX_DNS_SIZE ) == 0 && \
( hostInfo = gethostbyname( cachedFQDN ) ) != NULL )
{
int i;
for( i = 0;
hostInfo->h_addr_list[ i ] != NULL && i < IP_ADDR_COUNT;
i++ )
{
struct in_addr address;
/* Reverse the byte order for the in-addr.arpa lookup and
convert the address to dotted-decimal notation */
address.S_un.S_addr = *( ( DWORD * ) hostInfo->h_addr_list[ i ] );
sprintf_s( cachedFQDN, MAX_DNS_SIZE, "%s.in-addr.arpa",
inet_ntoa( address ) );
/* Check for a name */
if( DnsQuery( cachedFQDN, DNS_TYPE_PTR, DNS_QUERY_BYPASS_CACHE,
NULL, &pDns, NULL ) == 0 )
break;
}
}
if( pDns == NULL )
{
return( setSocketError( netStream,
"Couldn't determine FQDN of local machine",
40, CRYPT_ERROR_NOTFOUND, TRUE ) );
}
#ifdef __WINCE__
unicodeToAscii( fqdnBuffer, MAX_DNS_SIZE, pDns->Data.PTR.pNameHost,
wcslen( pDns->Data.PTR.pNameHost ) + 1 );
#else
fqdnPtr = pDns->Data.PTR.pNameHost;
#endif /* Win32 vs. WinCE */
status = convertToSrv( cachedFQDN, MAX_DNS_SIZE, fqdnPtr );
DnsFreeFn( pDns, DnsFreeRecordList );
if( cryptStatusError( status ) )
{
return( setSocketError( netStream,
"Couldn't convert FQDN into SRV query name",
41, CRYPT_ERROR_NOTFOUND, TRUE ) );
}
/* Remember the value that we just found to lighten the load on the
resolver when we perform repeat queries */
strlcpy_s( fqdn, fqdnMaxLen, cachedFQDN );
lastFetchTime = getTime();
return( CRYPT_OK );
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4, 5 ) ) \
int findHostInfo( INOUT NET_STREAM_INFO *netStream,
OUT_BUFFER_FIXED( hostNameMaxLen ) char *hostName,
IN_LENGTH_DNS const int hostNameMaxLen,
OUT_PORT_Z int *hostPort,
IN_BUFFER( nameLen ) const char *name,
IN_LENGTH_DNS const int nameLen )
{
PDNS_RECORD pDns = NULL, pDnsInfo = NULL, pDnsCursor;
DWORD dwRet;
char nameBuffer[ MAX_DNS_SIZE + 8 ];
int priority = 32767, i;
assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );
assert( isWritePtr( hostName, hostNameMaxLen ) );
assert( isWritePtr( hostPort, sizeof( int ) ) );
assert( name != NULL );
REQUIRES( hostNameMaxLen > 0 && hostNameMaxLen <= MAX_DNS_SIZE );
REQUIRES( nameLen > 0 && nameLen < MAX_DNS_SIZE );
REQUIRES( hostName != name );
/* Clear return values */
memset( hostName, 0, min( 16, hostNameMaxLen ) );
*hostPort = 0;
/* Convert the name to a null-terminated string */
memcpy( nameBuffer, name, nameLen );
nameBuffer[ nameLen ] = '\0';
name = nameBuffer;
/* If we're running on anything other than a heavily-SP'd Win2K or
WinXP and newer there's not much that we can do */
if( hDNS == NULL_INSTANCE )
{
return( setSocketError( netStream, "DNS services not available", 26,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -