📄 dns.c
字号:
return( addAddrInfo( NULL, res, &address, sizeof( in_addr_t ),
port ) );
}
/* It's a host name, convert it to the in_addr form */
gethostbyname_threadsafe( nodename, pHostent, hostErrno );
if( pHostent == NULL )
return( -1 );
ENSURES( pHostent->h_length == IP_ADDR_SIZE );
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 ],
pHostent->h_length, port );
currentAddrInfoPtr = *res;
}
else
status = addAddrInfo( currentAddrInfoPtr, ¤tAddrInfoPtr,
pHostent->h_addr_list[ i ],
pHostent->h_length, port );
if( status != 0 )
{
freeaddrinfo( *res );
return( status );
}
}
return( 0 );
}
STDC_NONNULL_ARG( ( 1 ) ) \
static void SOCKET_API my_freeaddrinfo( INOUT struct addrinfo *ai )
{
int i;
assert( isWritePtr( ai, sizeof( struct addrinfo ) ) );
/* Perform basic error checking. Since this is supposed to be an
emulation of a (normally) built-in function we don't perform any
REQUIRES()-style checking but only apply the basic checks that the
normal built-in form does */
if( ai == NULL )
return;
for( i = 0; ai != NULL && i < IP_ADDR_COUNT; i++ )
{
struct addrinfo *addrInfoCursor = ai;
ai = ai->ai_next;
if( addrInfoCursor->ai_addr != NULL )
clFree( "my_freeaddrinfo", addrInfoCursor->ai_addr );
clFree( "my_freeaddrinfo", addrInfoCursor );
}
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3, 5 ) ) \
static int SOCKET_API my_getnameinfo( IN_BUFFER( salen ) const struct sockaddr *sa,
IN SIZE_TYPE salen,
OUT_BUFFER_FIXED( nodelen ) char *node,
IN_LENGTH_SHORT SIZE_TYPE nodelen,
OUT_BUFFER_FIXED( servicelen ) char *service,
IN_LENGTH_SHORT SIZE_TYPE servicelen,
IN int flags )
{
const struct sockaddr_in *sockAddr = ( struct sockaddr_in * ) sa;
const char *ipAddress;
assert( isReadPtr( sa, salen ) && salen >= sizeof( struct sockaddr ) );
assert( isReadPtr( node, nodelen ) && nodelen >= 10 );
assert( isReadPtr( service, servicelen ) && servicelen >= 8 );
/* Perform basic error checking. Since this is supposed to be an
emulation of a (normally) built-in function we don't perform any
REQUIRES()-style checking but only apply the basic checks that the
normal built-in form does */
if( sa == NULL || \
salen < sizeof( struct sockaddr ) || salen > MAX_INTLENGTH_SHORT || \
node == NULL || \
nodelen < 10 || nodelen > MAX_INTLENGTH_SHORT || \
service == NULL || \
servicelen < 8 || servicelen > MAX_INTLENGTH_SHORT )
return( -1 );
/* Clear return values */
strlcpy_s( node, nodelen, "<Unknown>" );
strlcpy_s( service, servicelen, "0" );
/* Get the remote system's address and port number */
if( ( ipAddress = inet_ntoa( sockAddr->sin_addr ) ) == NULL )
return( -1 );
memcpy( node, ipAddress, nodelen );
node[ nodelen - 1 ] = '\0';
if( sprintf_s( service, servicelen, "%d",
ntohs( sockAddr->sin_port ) ) < 0 )
return( -1 );
return( 0 );
}
#endif /* !IPv6 || __WINDOWS__ */
/****************************************************************************
* *
* DNS Interface *
* *
****************************************************************************/
/* Get a host's IP address */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
int getAddressInfo( INOUT NET_STREAM_INFO *netStream,
OUT_PTR struct addrinfo **addrInfoPtrPtr,
IN_BUFFER( nameLen ) const char *name,
IN_LENGTH_DNS const int nameLen,
IN_PORT const int port, const BOOLEAN isServer )
{
struct addrinfo hints;
char nameBuffer[ MAX_DNS_SIZE + 8 ], portBuffer[ 16 + 8 ];
assert( isWritePtr( netStream, sizeof( NET_STREAM_INFO ) ) );
assert( isWritePtr( addrInfoPtrPtr, sizeof( struct addrinfo * ) ) );
assert( isServer || name != NULL );
REQUIRES( port >= 22 && port < 65536L );
REQUIRES( isServer || \
( !isServer && name != NULL ) );
REQUIRES( ( name == NULL && nameLen == 0 ) || \
( nameLen > 0 && nameLen < MAX_DNS_SIZE ) );
/* Convert the name and port into the null-terminated text-string format
required by getaddrinfo(). The reason why the port is given as a
string rather than a port number is that we can also optionally
specify the port to connect to via a service name. Of course it's
more or less pot luck whether the service you want is a recognised
one so everyone specifies the port anyway, however the reason why
this unnecessary flexibility is there is because getaddrinfo() was
seen as a universal replacement for a pile of other functions,
including (for this case) getservbyname() */
if( name != NULL )
{
memcpy( nameBuffer, name, nameLen );
nameBuffer[ nameLen ] = '\0';
name = nameBuffer;
}
sprintf_s( portBuffer, 8, "%d", port );
/* If we're a client and using auto-detection of a PKI service, try and
locate it via DNS SRV */
if( !isServer && name != NULL && nameLen == 12 && \
( !memcmp( name, "[Autodetect]", 12 ) || *name == '_' ) )
{
int localPort, status;
status = findHostInfo( netStream, nameBuffer, MAX_DNS_SIZE,
&localPort, name, nameLen );
if( cryptStatusError( status ) )
return( status );
name = nameBuffer;
sprintf_s( portBuffer, 8, "%d", localPort );
}
/* Convert the address information to the system character set if
required */
#ifdef EBCDIC_CHARS
if( name != NULL )
bufferToEbcdic( nameBuffer, nameBuffer );
bufferToEbcdic( portBuffer, portBuffer );
#endif /* EBCDIC_CHARS */
/* Set up the port information and hint information needed by
getaddrinfo(). The use of PF_UNSPEC is a bit problematic because RFC
2553 is usually interpreted to mean "look for all addresses" rather
than the more sensible "look for any address". The reason why this
is a problem is because getaddrinfo() ends up looking for unnecessary
IPv6 addresses, either by returning IPv6 addresses when the system
doesn't do IPv6 or spending a lot of time groping around for IPv6
stuff and/or further unnecessary addresses when it's already got what
it needs. This is made worse by confusion over implementation
details, for example early implementations of getaddrinfo() in glibc
would always try an AAAA lookup even on an IPv4-only system/network,
resulting in long delays as the resolver timed out and fell back to a
straight A lookup. There was some disagreement over whether this was
right or wrong, and how to fix it (IPv6 purists who never noticed the
problem seemed to think that it was right, everyone else thought that
it was wrong). Variations of this problem exist, e.g. if an IPv4
address is in /etc/hosts and DNS is down, the resolver will still
spend ages (several minutes in some cases) groping around for an IPv6
address before it finally gives up and falls back to what it already
knows from /etc/hosts. Switching the hint from AF_UNSPEC to AF_INET
bypasses this problem, but has the downside of disabling IPv6 use.
This problem was partially fixed post-RFC 2553 by adding the
AI_ADDRCONFIG flag, which tells getaddrinfo() to only do AAAA queries
if the system has at least one IPv6 source address configured, and
the same for A and IPv4 (in other words it applies some common sense,
which is how it should have behaved in the first place).
Unfortunately this flag isn't very widely supported yet, so it usually
ends up being no-op'd out by the auto-config.
Bounds Checker 6.x may crash in the getaddrinfo() call if maximum
checking is enabled. To fix this, set the checking level to normal
rather than maximum */
memset( &hints, 0, sizeof( struct addrinfo ) );
hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG;
if( isServer )
{
/* If it's a server, set the AI_PASSIVE flag so that if the
interface that we're binding to isn't explicitly specified we get
any interface */
hints.ai_flags |= AI_PASSIVE;
}
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if( getaddrinfo( name, portBuffer, &hints, addrInfoPtrPtr ) )
return( getHostError( netStream, CRYPT_ERROR_OPEN ) );
return( CRYPT_OK );
}
STDC_NONNULL_ARG( ( 1 ) ) \
void freeAddressInfo( struct addrinfo *addrInfoPtr )
{
assert( addrInfoPtr != NULL );
freeaddrinfo( addrInfoPtr );
}
STDC_NONNULL_ARG( ( 1, 2, 4, 5 ) ) \
void getNameInfo( const struct sockaddr *sockAddr,
OUT_BUFFER( addressMaxLen, *addressLen ) char *address,
IN_LENGTH_DNS const int addressMaxLen,
OUT_LENGTH_DNS_Z int *addressLen,
OUT_PORT_Z int *port )
{
char nameBuffer[ MAX_DNS_SIZE + 8 ];
char portBuffer[ 32 + 8 ];
int nameLength, portLength, localPort, status;
assert( isReadPtr( sockAddr, sizeof( struct sockaddr ) ) );
assert( isWritePtr( address, addressMaxLen ) );
assert( isWritePtr( port, sizeof( int ) ) );
REQUIRES_V( addressMaxLen >= CRYPT_MAX_TEXTSIZE / 2 && \
addressMaxLen <= MAX_DNS_SIZE );
/* Clear return values */
memcpy( address, "<Unknown>", 9 );
*addressLen = 9;
*port = 0;
/* Some Windows implementations of getnameinfo() call down to
getservbyport() assuming that it will always succeed and therefore
leave the port/service arg unchanged when it doesn't, so the following
call must be made with the NI_NUMERICSERV flag specified (which it
would be anyway, cryptlib always treats the port as a numeric arg).
Oddly enough the macro version of this function in wspiapi.h used for
IPv4-only situations does get it correct */
if( getnameinfo( sockAddr, sizeof( struct sockaddr ), nameBuffer,
MAX_DNS_SIZE, portBuffer, 32,
NI_NUMERICHOST | NI_NUMERICSERV ) != 0 )
{
assert( DEBUG_WARN );
return;
}
nameLength = strlen( nameBuffer );
portLength = strlen( portBuffer );
if( nameLength <= 0 || nameLength > addressMaxLen || \
portLength <= 0 || portLength > 8 )
{
assert( DEBUG_WARN );
return;
}
#ifdef EBCDIC_CHARS
ebcdicToAscii( nameBuffer, nameBuffer, nameLength );
ebcdicToAscii( portBuffer, portBuffer, portLength );
#endif /* EBCDIC_CHARS */
memcpy( address, nameBuffer, nameLength );
*addressLen = nameLength;
status = strGetNumeric( portBuffer, portLength, &localPort, 1, 65536 );
if( cryptStatusError( status ) )
{
assert( DEBUG_WARN );
return;
}
*port = localPort;
}
#endif /* USE_TCP */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -