📄 dns_srv.c
字号:
CRYPT_ERROR_NOTFOUND, TRUE ) );
}
/* If we're doing a full autodetect we construct the SRV query using the
local machine's FQDN. This fails more often than not because of
NATing and the use of private networks, but at least we can try */
if( !strCompareZ( name, "[Autodetect]" ) )
{
const int status = getSrvFQDN( netStream, hostName, hostNameMaxLen );
if( cryptStatusError( status ) )
return( status );
name = hostName;
}
/* Perform a DNS SRV lookup to find the host info. SRV has basic load-
balancing facilities but for now we just use the highest-priority
host that we find (it's rarely-enough used that we'll be lucky to
find SRV info let alone any load-balancing setup) */
dwRet = DnsQuery( ( const LPSTR ) name, DNS_TYPE_SRV, DNS_QUERY_STANDARD,
NULL, &pDns, NULL );
if( dwRet != 0 || pDns == NULL )
return( getSocketError( netStream, CRYPT_ERROR_NOTFOUND ) );
for( pDnsCursor = pDns, i = 0;
pDnsCursor != NULL && i < IP_ADDR_COUNT;
pDnsCursor = pDnsCursor->pNext, i++ )
{
if( pDnsCursor->Data.SRV.wPriority < priority )
{
priority = pDnsCursor->Data.SRV.wPriority;
pDnsInfo = pDnsCursor;
}
}
#ifdef __WINCE__
if( pDnsInfo == NULL || \
wcslen( pDnsInfo->Data.SRV.pNameTarget ) + 1 > hostNameMaxLen )
#else
if( pDnsInfo == NULL || \
strlen( pDnsInfo->Data.SRV.pNameTarget ) + 1 > hostNameMaxLen )
#endif /* Win32 vs. WinCE */
{
DnsFreeFn( pDns, DnsFreeRecordList );
return( setSocketError( netStream, "Invalid DNS SRV entry for host", 30,
CRYPT_ERROR_NOTFOUND, TRUE ) );
}
/* Copy over the host info for this SRV record */
#ifdef __WINCE__
unicodeToAscii( hostName, hostNameMaxLen,
pDnsInfo->Data.SRV.pNameTarget,
wcslen( pDnsInfo->Data.SRV.pNameTarget ) + 1 );
#else
strlcpy_s( hostName, hostNameMaxLen, pDnsInfo->Data.SRV.pNameTarget );
#endif /* Win32 vs. WinCE */
*hostPort = pDnsInfo->Data.SRV.wPort;
/* Clean up */
DnsFreeFn( pDns, DnsFreeRecordList );
return( CRYPT_OK );
}
/****************************************************************************
* *
* Unix DNS SRV Interface *
* *
****************************************************************************/
#elif defined( __UNIX__ ) && \
!( defined( __CYGWIN__) || ( defined( sun ) && OSVERSION <= 5 ) || \
defined( __TANDEM_NSK__ ) || defined( __TANDEM_OSS__ ) || \
defined( __UCLIBC__ ) )
#define SRV_PRIORITY_OFFSET ( NS_RRFIXEDSZ + 0 )
#define SRV_WEIGHT_OFFSET ( NS_RRFIXEDSZ + 2 )
#define SRV_PORT_OFFSET ( NS_RRFIXEDSZ + 4 )
#define SRV_NAME_OFFSET ( NS_RRFIXEDSZ + 6 )
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int getFQDN( INOUT NET_STREAM_INFO *netStream,
OUT_BUFFER_FIXED( fqdnMaxLen ) char *fqdn,
IN_LENGTH_DNS const int fqdnMaxLen )
{
struct hostent *hostInfo;
char *hostNamePtr = NULL;
int i, addressCount;
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 ) );
/* First get the host name and if it's the FQDN, exit. gethostname()
has the idiotic property that if the name doesn't fit into the given
buffer the function will return a (possibly non-null-terminated)
truncated value instead of reporting an error (or at least that's
what the spec says, hopefully no implementation is stupid enough to
actually do this), so to be safe we force null-termination after
we've called the function */
if( gethostname( fqdn, fqdnMaxLen ) == -1 )
return( CRYPT_ERROR_NOTFOUND );
fqdn[ fqdnMaxLen - 1 ] = '\0';
if( strchr( fqdn, '.' ) != NULL )
{
/* If the hostname has a dot in it, it's the FQDN */
return( CRYPT_OK );
}
/* Now get the hostent info and walk through it looking for the FQDN */
if( ( hostInfo = gethostbyname( fqdn ) ) == NULL )
return( CRYPT_ERROR_NOTFOUND );
for( i = 0;
hostInfo->h_addr_list[ i ] != NULL && addressCount < IP_ADDR_COUNT;
i++ )
{
char **aliasPtrPtr;
int j;
/* If the hostname has a dot in it, it's the FQDN. This should be
the same as the gethostname() output, but we check again just in
case */
if( strchr( hostInfo->h_name, '.' ) != NULL )
{
hostNamePtr = hostInfo->h_name;
break;
}
/* Try for the FQDN in the aliases */
if( hostInfo->h_aliases == NULL )
continue;
for( aliasPtrPtr = hostInfo->h_aliases, j = 0;
*aliasPtrPtr != NULL && !strchr( *aliasPtrPtr, '.' ) && \
j < IP_ADDR_COUNT;
aliasPtrPtr++, j++ );
if( *aliasPtrPtr != NULL )
{
hostNamePtr = *aliasPtrPtr;
break;
}
}
if( hostNamePtr == NULL )
return( CRYPT_ERROR_NOTFOUND );
/* We found the FQDN, return it to the caller */
if( strlen( hostNamePtr ) + 1 > fqdnMaxLen )
return( CRYPT_ERROR_OVERFLOW );
strlcpy_s( fqdn, fqdnMaxLen, hostNamePtr );
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 )
{
union {
HEADER header;
BYTE buffer[ NS_PACKETSZ + 8 ];
} dnsQueryInfo;
BYTE *namePtr, *endPtr;
char nameBuffer[ MAX_DNS_SIZE + 8 ];
int resultLen, nameLen, qCount, aCount, minPriority = 32767;
int 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 doing a full autodetect, we construct the SRV query using
the local machine's FQDN. This fails more often than not because of
NATing and the use of private networks, but at least we can try */
if( !strCompareZ( name, "[Autodetect]" ) )
{
const int status = getFQDN( netStream, hostName, hostNameMaxLen );
if( cryptStatusError( status ) )
return( status );
name = hostName;
}
#ifdef EBCDIC_CHARS
else
{
/* We're about to use OS functions, convert the input to EBCDIC. If
we've used autodetection the output from getFQDN will already be
in EBCDIC form */
name = bufferToEbcdic( hostName, name );
}
#endif /* EBCDIC_CHARS */
/* Try and fetch a DNS SRV record (RFC 2782) matching the host info.
Unlike Windows' relatively nice DnsQuery() API, Unix has a horribly
clunky interface that requires manually grovelling through wire-
format data to dig out the bits of interest */
resultLen = res_query( name, C_IN, T_SRV, dnsQueryInfo.buffer,
NS_PACKETSZ );
if( resultLen < NS_HFIXEDSZ || resultLen > NS_PACKETSZ )
return( getSocketError( netStream, CRYPT_ERROR_NOTFOUND ) );
if( dnsQueryInfo.header.rcode != 0 || dnsQueryInfo.header.tc != 0 )
{
/* If we get a non-zero response code (rcode) or the results were
truncated (tc), we can't go any further. In theory a truncated
response is probably OK since many servers return the address
records for the host in the Additional Data section to save the
client having to perform a second lookup and we don't need these
at this point so we can ignore the fact that they've been
truncated, but for now we treat truncation as an error */
return( setSocketError( netStream,
"RR contains non-zero response code or "
"response was truncated", 60,
CRYPT_ERROR_NOTFOUND, FALSE ) );
}
qCount = ntohs( dnsQueryInfo.header.qdcount );
aCount = ntohs( dnsQueryInfo.header.ancount );
if( qCount < 0 || qCount > 100 || aCount <= 0 || aCount > 100 )
{
/* No answer entries (or a suspicious number of entries, which is
less likely), we're done */
return( setSocketError( netStream, "RR contains no answer entries", 29,
CRYPT_ERROR_NOTFOUND, FALSE ) );
}
/* Skip the queries */
namePtr = dnsQueryInfo.buffer + NS_HFIXEDSZ;
endPtr = dnsQueryInfo.buffer + resultLen;
for( i = 0; i < qCount && namePtr < endPtr && i < 100; i++ )
{
nameLen = dn_skipname( namePtr, endPtr );
if( nameLen <= 0 || nameLen > MAX_DNS_SIZE )
{
return( setSocketError( netStream,
"RR contains invalid question", 28,
CRYPT_ERROR_BADDATA, FALSE ) );
}
namePtr += nameLen + NS_QFIXEDSZ;
}
if( namePtr > endPtr )
{
return( setSocketError( netStream, "RR contains invalid data", 24,
CRYPT_ERROR_BADDATA, FALSE ) );
}
/* Process the answers. SRV has basic load-balancing facilities, but
for now we just use the highest-priority host that we find (it's
rarely-enough used that we'll be lucky to find SRV info, let alone
any load-balancing setup) */
for( i = 0; i < aCount && i < FAILSAFE_ITERATIONS_MED; i++ )
{
int priority, port;
nameLen = dn_skipname( namePtr, endPtr );
if( nameLen <= 0 )
{
return( setSocketError( netStream, "RR contains invalid answer", 26,
CRYPT_ERROR_BADDATA, FALSE ) );
}
namePtr += nameLen;
priority = ntohs( *( ( u_short * ) ( namePtr + SRV_PRIORITY_OFFSET ) ) );
port = ntohs( *( ( u_short * ) ( namePtr + SRV_PORT_OFFSET ) ) );
namePtr += NS_SRVFIXEDSZ;
if( priority < minPriority )
{
/* We've got a new higher-priority host, use that */
nameLen = dn_expand( dnsQueryInfo.buffer, endPtr,
namePtr, hostName, hostNameMaxLen );
*hostPort = port;
minPriority = priority;
}
else
{
/* It's a lower-priority host, skip it */
nameLen = dn_skipname( namePtr, endPtr );
}
if( nameLen <= 0 || nameLen > MAX_DNS_SIZE )
{
return( setSocketError( netStream, "RR contains invalid answer", 26,
CRYPT_ERROR_NOTFOUND, FALSE ) );
}
hostName[ nameLen ] = '\0';
namePtr += nameLen;
}
if( namePtr > endPtr )
{
return( setSocketError( netStream, "RR contains invalid data", 24,
CRYPT_ERROR_BADDATA, FALSE ) );
}
#ifdef EBCDIC_CHARS
ebcdicToAscii( hostName, strlen( hostName ) );
#endif /* EBCDIC_CHARS */
return( CRYPT_OK );
}
#endif /* OS-specific host detection */
#endif /* USE_TCP && USE_DNSSRV */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -