reactos/base/applications/network/nslookup/nslookup.c
2021-06-28 10:20:57 +02:00

854 lines
24 KiB
C

/*
* PROJECT: ReactOS nslookup utility
* LICENSE: GPL - See COPYING in the top level directory
* FILE: base/applications/network/nslookup/nslookup.c
* PURPOSE: Perform DNS lookups
* COPYRIGHT: Copyright 2009 Lucas Suggs <lucas.suggs@gmail.com>
*/
#include "nslookup.h"
#include <winbase.h>
#include <iphlpapi.h>
STATE State;
HANDLE ProcessHeap;
ULONG RequestID;
void PrintState()
{
_tprintf( _T("Default Server: (null)\n\n") );
_tprintf( _T("Set options:\n") );
_tprintf( _T(" ") );
if( !State.debug ) _tprintf( _T("no") );
_tprintf( _T("debug\n") );
_tprintf( _T(" ") );
if( !State.defname ) _tprintf( _T("no") );
_tprintf( _T("defname\n") );
_tprintf( _T(" ") );
if( !State.search ) _tprintf( _T("no") );
_tprintf( _T("search\n") );
_tprintf( _T(" ") );
if( !State.recurse ) _tprintf( _T("no") );
_tprintf( _T("recurse\n") );
_tprintf( _T(" ") );
if( !State.d2 ) _tprintf( _T("no") );
_tprintf( _T("d2\n") );
_tprintf( _T(" ") );
if( !State.vc ) _tprintf( _T("no") );
_tprintf( _T("vc\n") );
_tprintf( _T(" ") );
if( !State.ignoretc ) _tprintf( _T("no") );
_tprintf( _T("ignoretc\n") );
_tprintf( _T(" port=%d\n"), State.port );
_tprintf( _T(" type=%s\n"), State.type );
_tprintf( _T(" class=%s\n"), State.Class );
_tprintf( _T(" timeout=%d\n"), (int)State.timeout );
_tprintf( _T(" retry=%d\n"), (int)State.retry );
_tprintf( _T(" root=%s\n"), State.root );
_tprintf( _T(" domain=%s\n"), State.domain );
_tprintf( _T(" ") );
if( !State.MSxfr ) _tprintf( _T("no") );
_tprintf( _T("MSxfr\n") );
_tprintf( _T(" IXFRversion=%d\n"), (int)State.ixfrver );
_tprintf( _T(" srchlist=%s\n\n"), State.srchlist[0] );
}
void PrintUsage()
{
_tprintf( _T("Usage:\n"
" nslookup [-opt ...] # interactive mode using"
" default server\n nslookup [-opt ...] - server #"
" interactive mode using 'server'\n nslookup [-opt ...]"
" host # just look up 'host' using default server\n"
" nslookup [-opt ...] host server # just look up 'host'"
" using 'server'\n") );
}
BOOL PerformInternalLookup( PCHAR pAddr, PCHAR pResult )
{
/* Needed to issue DNS packets and parse them. */
PCHAR Buffer = NULL, RecBuffer = NULL;
CHAR pResolve[256];
ULONG BufferLength = 0, RecBufferLength = 512;
int i = 0, j = 0, k = 0, d = 0;
BOOL bOk = FALSE;
/* Makes things easier when parsing the response packet. */
USHORT NumQuestions;
USHORT Type;
if( (strlen( pAddr ) + 1) > 255 ) return FALSE;
Type = TYPE_A;
if( IsValidIP( pAddr ) ) Type = TYPE_PTR;
/* If it's a PTR lookup then append the ARPA sig to the end. */
if( Type == TYPE_PTR )
{
ReverseIP( pAddr, pResolve );
strcat( pResolve, ARPA_SIG );
}
else
{
strcpy( pResolve, pAddr );
}
/* Base header length + length of QNAME + length of QTYPE and QCLASS */
BufferLength = 12 + (strlen( pResolve ) + 2) + 4;
/* Allocate memory for the buffer. */
Buffer = HeapAlloc( ProcessHeap, 0, BufferLength );
if( !Buffer )
{
_tprintf( _T("ERROR: Out of memory\n") );
goto cleanup;
}
/* Allocate the receiving buffer. */
RecBuffer = HeapAlloc( ProcessHeap, 0, RecBufferLength );
if( !RecBuffer )
{
_tprintf( _T("ERROR: Out of memory\n") );
goto cleanup;
}
/* Insert the ID field. */
((PSHORT)&Buffer[i])[0] = htons( RequestID );
i += 2;
/* Bits 0-7 of the second 16 are all 0, except for when recursion is
desired. */
Buffer[i] = 0x00;
if( State.recurse) Buffer[i] |= 0x01;
i += 1;
/* Bits 8-15 of the second 16 are 0 for a query. */
Buffer[i] = 0x00;
i += 1;
/* Only 1 question. */
((PSHORT)&Buffer[i])[0] = htons( 1 );
i += 2;
/* We aren't sending a response, so 0 out the rest of the header. */
Buffer[i] = 0x00;
Buffer[i + 1] = 0x00;
Buffer[i + 2] = 0x00;
Buffer[i + 3] = 0x00;
Buffer[i + 4] = 0x00;
Buffer[i + 5] = 0x00;
i += 6;
/* Walk through the query address. Split each section delimited by '.'.
Format of the QNAME section is length|data, etc. Last one is null */
j = i;
i += 1;
for( k = 0; k < strlen( pResolve ); k += 1 )
{
if( pResolve[k] != '.' )
{
Buffer[i] = pResolve[k];
i += 1;
}
else
{
Buffer[j] = (i - j) - 1;
j = i;
i += 1;
}
}
Buffer[j] = (i - j) - 1;
Buffer[i] = 0x00;
i += 1;
/* QTYPE */
((PSHORT)&Buffer[i])[0] = htons( Type );
i += 2;
/* QCLASS */
((PSHORT)&Buffer[i])[0] = htons( CLASS_IN );
/* Ship the request off to the DNS server. */
bOk = SendRequest( Buffer,
BufferLength,
RecBuffer,
&RecBufferLength );
if( !bOk ) goto cleanup;
/* Start parsing the received packet. */
NumQuestions = ntohs( ((PSHORT)&RecBuffer[4])[0] );
k = 12;
/* We don't care about the questions section, blow through it. */
if( NumQuestions )
{
for( i = 0; i < NumQuestions; i += 1 )
{
/* Quick way to skip the domain name section. */
k += ExtractName( RecBuffer, pResult, k, 0 );
k += 4;
}
}
/* Skip the answer name. */
k += ExtractName( RecBuffer, pResult, k, 0 );
Type = ntohs( ((PUSHORT)&RecBuffer[k])[0] );
k += 8;
d = ntohs( ((PUSHORT)&RecBuffer[k])[0] );
k += 2;
if( TYPE_PTR == Type )
{
k += ExtractName( RecBuffer, pResult, k, d );
}
else if( TYPE_A == Type )
{
k += ExtractIP( RecBuffer, pResult, k );
}
cleanup:
/* Free memory. */
if( Buffer ) HeapFree( ProcessHeap, 0, Buffer );
if( RecBuffer ) HeapFree( ProcessHeap, 0, RecBuffer );
RequestID += 1;
return bOk;
}
void PerformLookup( PCHAR pAddr )
{
/* Needed to issue DNS packets and parse them. */
PCHAR Buffer = NULL, RecBuffer = NULL;
CHAR pResolve[256];
CHAR pResult[256];
ULONG BufferLength = 0, RecBufferLength = 512;
int i = 0, j = 0, k = 0, d = 0;
BOOL bOk = FALSE;
/* Makes things easier when parsing the response packet. */
UCHAR Header2;
USHORT NumQuestions;
USHORT NumAnswers;
USHORT NumAuthority;
USHORT Type;
if( (strlen( pAddr ) + 1) > 255 ) return;
_tprintf( _T("Server: %s\n"), State.DefaultServer );
_tprintf( _T("Address: %s\n\n"), State.DefaultServerAddress );
if( !strcmp( TypeA, State.type )
|| !strcmp( TypeAAAA, State.type )
|| !strcmp( TypeBoth, State.type ) )
{
Type = TYPE_A;
if( IsValidIP( pAddr ) ) Type = TYPE_PTR;
}
else
Type = TypeNametoTypeID( State.type );
/* If it's a PTR lookup then append the ARPA sig to the end. */
if( (Type == TYPE_PTR) && IsValidIP( pAddr ) )
{
ReverseIP( pAddr, pResolve );
strcat( pResolve, ARPA_SIG );
}
else
{
strcpy( pResolve, pAddr );
}
/* Base header length + length of QNAME + length of QTYPE and QCLASS */
BufferLength = 12 + (strlen( pResolve ) + 2) + 4;
/* Allocate memory for the buffer. */
Buffer = HeapAlloc( ProcessHeap, 0, BufferLength );
if( !Buffer )
{
_tprintf( _T("ERROR: Out of memory\n") );
goto cleanup;
}
/* Allocate memory for the return buffer. */
RecBuffer = HeapAlloc( ProcessHeap, 0, RecBufferLength );
if( !RecBuffer )
{
_tprintf( _T("ERROR: Out of memory\n") );
goto cleanup;
}
/* Insert the ID field. */
((PSHORT)&Buffer[i])[0] = htons( RequestID );
i += 2;
/* Bits 0-7 of the second 16 are all 0, except for when recursion is
desired. */
Buffer[i] = 0x00;
if( State.recurse) Buffer[i] |= 0x01;
i += 1;
/* Bits 8-15 of the second 16 are 0 for a query. */
Buffer[i] = 0x00;
i += 1;
/* Only 1 question. */
((PSHORT)&Buffer[i])[0] = htons( 1 );
i += 2;
/* We aren't sending a response, so 0 out the rest of the header. */
Buffer[i] = 0x00;
Buffer[i + 1] = 0x00;
Buffer[i + 2] = 0x00;
Buffer[i + 3] = 0x00;
Buffer[i + 4] = 0x00;
Buffer[i + 5] = 0x00;
i += 6;
/* Walk through the query address. Split each section delimited by '.'.
Format of the QNAME section is length|data, etc. Last one is null */
j = i;
i += 1;
for( k = 0; k < strlen( pResolve ); k += 1 )
{
if( pResolve[k] != '.' )
{
Buffer[i] = pResolve[k];
i += 1;
}
else
{
Buffer[j] = (i - j) - 1;
j = i;
i += 1;
}
}
Buffer[j] = (i - j) - 1;
Buffer[i] = 0x00;
i += 1;
/* QTYPE */
((PSHORT)&Buffer[i])[0] = htons( Type );
i += 2;
/* QCLASS */
((PSHORT)&Buffer[i])[0] = htons( ClassNametoClassID( State.Class ) );
/* Ship off the request to the DNS server. */
bOk = SendRequest( Buffer,
BufferLength,
RecBuffer,
&RecBufferLength );
if( !bOk ) goto cleanup;
/* Start parsing the received packet. */
Header2 = RecBuffer[3];
NumQuestions = ntohs( ((PSHORT)&RecBuffer[4])[0] );
NumAnswers = ntohs( ((PSHORT)&RecBuffer[6])[0] );
NumAuthority = ntohs( ((PUSHORT)&RecBuffer[8])[0] );
Type = 0;
/* Check the RCODE for failure. */
d = Header2 & 0x0F;
if( d != RCODE_NOERROR )
{
switch( d )
{
case RCODE_NXDOMAIN:
_tprintf( _T("*** %s can't find %s: Non-existant domain\n"), State.DefaultServer, pAddr );
break;
case RCODE_REFUSED:
_tprintf( _T("*** %s can't find %s: Query refused\n"), State.DefaultServer, pAddr );
break;
default:
_tprintf( _T("*** %s can't find %s: Unknown RCODE\n"), State.DefaultServer, pAddr );
}
goto cleanup;
}
k = 12;
if( NumQuestions )
{
/* Blow through the questions section since we don't care about it. */
for( i = 0; i < NumQuestions; i += 1 )
{
k += ExtractName( RecBuffer, pResult, k, 0 );
k += 4;
}
}
if( NumAnswers )
{
/* Skip the name. */
k += ExtractName( RecBuffer, pResult, k, 0 );
Type = ntohs( ((PUSHORT)&RecBuffer[k])[0] );
k += 8;
d = ntohs( ((PUSHORT)&RecBuffer[k])[0] );
k += 2;
if( TYPE_PTR == Type )
{
k += ExtractName( RecBuffer, pResult, k, d );
}
else if( TYPE_A == Type )
{
k += ExtractIP( RecBuffer, pResult, k );
}
}
/* FIXME: This'll need to support more than PTR and A at some point. */
if( !strcmp( State.type, TypePTR ) )
{
if( TYPE_PTR == Type )
{
_tprintf( _T("%s name = %s\n"), pResolve, pResult );
}
else
{
}
}
else if( !strcmp( State.type, TypeA )
|| !strcmp( State.type, TypeAAAA )
|| !strcmp( State.type, TypeBoth ) )
{
if( (TYPE_A == Type) /*|| (TYPE_AAAA == Type)*/ )
{
if( 0 == NumAuthority )
_tprintf( _T("Non-authoritative answer:\n") );
_tprintf( _T("Name: %s\n"), pAddr );
_tprintf( _T("Address: %s\n\n"), pResult );
}
else
{
_tprintf( _T("Name: %s\n"), pResult );
_tprintf( _T("Address: %s\n\n"), pAddr );
}
}
cleanup:
/* Free memory. */
if( Buffer ) HeapFree( ProcessHeap, 0, Buffer );
if( RecBuffer ) HeapFree( ProcessHeap, 0, RecBuffer );
RequestID += 1;
}
BOOL ParseCommandLine( int argc, char* argv[] )
{
int i;
BOOL NoMoreOptions = FALSE;
BOOL Interactive = FALSE;
CHAR AddrToResolve[256];
CHAR Server[256];
RtlZeroMemory( AddrToResolve, 256 );
RtlZeroMemory( Server, 256 );
if( 2 == argc )
{
/* In the Windows nslookup, usage is only displayed if /? is the only
option specified on the command line. */
if( !strncmp( "/?", argv[1], 2 ) )
{
PrintUsage();
return 0;
}
}
if( argc > 1 )
{
for( i = 1; i < argc; i += 1 )
{
if( NoMoreOptions )
{
strncpy( Server, argv[i], 255 );
/* Determine which one to resolve. This is based on whether the
DNS server provided was an IP or an FQDN. */
if( IsValidIP( Server ) )
{
strncpy( State.DefaultServerAddress, Server, 16 );
PerformInternalLookup( State.DefaultServerAddress,
State.DefaultServer );
}
else
{
strncpy( State.DefaultServer, Server, 255 );
PerformInternalLookup( State.DefaultServer,
State.DefaultServerAddress );
}
if( Interactive ) return 1;
PerformLookup( AddrToResolve );
return 0;
}
else
{
if( !strncmp( "-all", argv[i], 4 ) )
{
PrintState();
}
else if( !strncmp( "-type=", argv[i], 6 ) )
{
if( !strncmp( TypeA, &argv[i][6], strlen( TypeA ) ) )
{
State.type = TypeA;
}
else if( !strncmp( TypeAAAA, &argv[i][6], strlen( TypeAAAA ) ) )
{
State.type = TypeAAAA;
}
else if( !strncmp( TypeBoth, &argv[i][6], strlen( TypeBoth ) ) )
{
State.type = TypeBoth;
}
else if( !strncmp( TypeAny, &argv[i][6], strlen( TypeAny ) ) )
{
State.type = TypeAny;
}
else if( !strncmp( TypeCNAME, &argv[i][6], strlen( TypeCNAME ) ) )
{
State.type = TypeCNAME;
}
else if( !strncmp( TypeMX, &argv[i][6], strlen( TypeMX ) ) )
{
State.type = TypeMX;
}
else if( !strncmp( TypeNS, &argv[i][6], strlen( TypeNS ) ) )
{
State.type = TypeNS;
}
else if( !strncmp( TypePTR, &argv[i][6], strlen( TypePTR ) ) )
{
State.type = TypePTR;
}
else if( !strncmp( TypeSOA, &argv[i][6], strlen( TypeSOA ) ) )
{
State.type = TypeSOA;
}
else if( !strncmp( TypeSRV, &argv[i][6], strlen( TypeSRV ) ) )
{
State.type = TypeSRV;
}
else
{
_tprintf( _T("unknown query type: %s"), &argv[i][6] );
}
}
else if( !strncmp( "-domain=", argv[i], 8 ) )
{
strcpy( State.domain, &argv[i][8] );
}
else if( !strncmp( "-srchlist=", argv[i], 10 ) )
{
}
else if( !strncmp( "-root=", argv[i], 6 ) )
{
strcpy( State.root, &argv[i][6] );
}
else if( !strncmp( "-retry=", argv[i], 7 ) )
{
}
else if( !strncmp( "-timeout=", argv[i], 9 ) )
{
}
else if( !strncmp( "-querytype=", argv[i], 11 ) )
{
if( !strncmp( TypeA, &argv[i][11], strlen( TypeA ) ) )
{
State.type = TypeA;
}
else if( !strncmp( TypeAAAA, &argv[i][11], strlen( TypeAAAA ) ) )
{
State.type = TypeAAAA;
}
else if( !strncmp( TypeBoth, &argv[i][11], strlen( TypeBoth ) ) )
{
State.type = TypeBoth;
}
else if( !strncmp( TypeAny, &argv[i][11], strlen( TypeAny ) ) )
{
State.type = TypeAny;
}
else if( !strncmp( TypeCNAME, &argv[i][11], strlen( TypeCNAME ) ) )
{
State.type = TypeCNAME;
}
else if( !strncmp( TypeMX, &argv[i][11], strlen( TypeMX ) ) )
{
State.type = TypeMX;
}
else if( !strncmp( TypeNS, &argv[i][11], strlen( TypeNS ) ) )
{
State.type = TypeNS;
}
else if( !strncmp( TypePTR, &argv[i][11], strlen( TypePTR ) ) )
{
State.type = TypePTR;
}
else if( !strncmp( TypeSOA, &argv[i][11], strlen( TypeSOA ) ) )
{
State.type = TypeSOA;
}
else if( !strncmp( TypeSRV, &argv[i][11], strlen( TypeSRV ) ) )
{
State.type = TypeSRV;
}
else
{
_tprintf( _T("unknown query type: %s"), &argv[i][6] );
}
}
else if( !strncmp( "-class=", argv[i], 7 ) )
{
if( !strncmp( ClassIN, &argv[i][7], strlen( ClassIN ) ) )
{
State.Class = ClassIN;
}
else if( !strncmp( ClassAny, &argv[i][7], strlen( ClassAny ) ) )
{
State.Class = ClassAny;
}
else
{
_tprintf( _T("unknown query class: %s"), &argv[i][7] );
}
}
else if( !strncmp( "-ixfrver=", argv[i], 9 ) )
{
}
else if( !strncmp( "-debug", argv[i], 6 ) )
{
State.debug = TRUE;
}
else if( !strncmp( "-nodebug", argv[i], 8 ) )
{
State.debug = FALSE;
State.d2 = FALSE;
}
else if( !strncmp( "-d2", argv[i], 3 ) )
{
State.d2 = TRUE;
State.debug = TRUE;
}
else if( !strncmp( "-nod2", argv[i], 5 ) )
{
if( State.debug ) _tprintf( _T("d2 mode disabled; still in debug mode\n") );
State.d2 = FALSE;
}
else if( !strncmp( "-defname", argv[i], 8 ) )
{
State.defname = TRUE;
}
else if( !strncmp( "-noddefname", argv[i], 10 ) )
{
State.defname = FALSE;
}
else if( !strncmp( "-recurse", argv[i], 8 ) )
{
State.recurse = TRUE;
}
else if( !strncmp( "-norecurse", argv[i], 10 ) )
{
State.recurse = FALSE;
}
else if( !strncmp( "-search", argv[i], 7 ) )
{
State.search = TRUE;
}
else if( !strncmp( "-nosearch", argv[i], 9 ) )
{
State.search = FALSE;
}
else if( !strncmp( "-vc", argv[i], 3 ) )
{
State.vc = TRUE;
}
else if( !strncmp( "-novc", argv[i], 5 ) )
{
State.vc = FALSE;
}
else if( !strncmp( "-msxfr", argv[i], 6 ) )
{
State.MSxfr = TRUE;
}
else if( !strncmp( "-nomsxfr", argv[i], 8 ) )
{
State.MSxfr = FALSE;
}
else if( !strncmp( "-", argv[i], 1 ) && (strlen( argv[i] ) == 1) )
{
/* Since we received just the plain - switch, we are going
to be entering interactive mode. We also will not be
parsing any more options. */
NoMoreOptions = TRUE;
Interactive = TRUE;
}
else
{
/* Grab the address to resolve. No more options accepted
past this point. */
strncpy( AddrToResolve, argv[i], 255 );
NoMoreOptions = TRUE;
}
}
}
if( NoMoreOptions && !Interactive )
{
/* Get the FQDN of the DNS server. */
PerformInternalLookup( State.DefaultServerAddress,
State.DefaultServer );
PerformLookup( AddrToResolve );
return 0;
}
}
/* Get the FQDN of the DNS server. */
PerformInternalLookup( State.DefaultServerAddress,
State.DefaultServer );
return 1;
}
void InteractiveMode()
{
_tprintf( _T("Default Server: %s\n"), State.DefaultServer );
_tprintf( _T("Address: %s\n\n"), State.DefaultServerAddress );
/* TODO: Implement interactive mode. */
_tprintf( _T("ERROR: Feature not implemented.\n") );
}
int main( int argc, char* argv[] )
{
int i;
ULONG Status;
PFIXED_INFO pNetInfo = NULL;
ULONG NetBufLen = 0;
WSADATA wsaData;
int ret;
ProcessHeap = GetProcessHeap();
RequestID = 1;
/* Set up the initial state. */
State.debug = FALSE;
State.defname = TRUE;
State.search = TRUE;
State.recurse = TRUE;
State.d2 = FALSE;
State.vc = FALSE;
State.ignoretc = FALSE;
State.port = 53;
State.type = TypeBoth;
State.Class = ClassIN;
State.timeout = 2;
State.retry = 1;
State.MSxfr = TRUE;
State.ixfrver = 1;
RtlZeroMemory( State.root, 256 );
RtlZeroMemory( State.domain, 256 );
for( i = 0; i < 6; i += 1 ) RtlZeroMemory( State.srchlist[i], 256 );
RtlZeroMemory( State.DefaultServer, 256 );
RtlZeroMemory( State.DefaultServerAddress, 16 );
memcpy( State.root, DEFAULT_ROOT, sizeof(DEFAULT_ROOT) );
/* We don't know how long of a buffer it will want to return. So we'll
pass an empty one now and let it fail only once, instead of guessing. */
Status = GetNetworkParams( pNetInfo, &NetBufLen );
if( Status != ERROR_BUFFER_OVERFLOW )
{
_tprintf( _T("Error in GetNetworkParams call\n") );
return -2;
}
pNetInfo = (PFIXED_INFO)HeapAlloc( ProcessHeap, 0, NetBufLen );
if( pNetInfo == NULL )
{
_tprintf( _T("ERROR: Out of memory\n") );
return -1;
}
/* For real this time. */
Status = GetNetworkParams( pNetInfo, &NetBufLen );
if( Status != NO_ERROR )
{
_tprintf( _T("Error in GetNetworkParams call\n") );
HeapFree( ProcessHeap, 0, pNetInfo );
return -2;
}
strncpy( State.domain, pNetInfo->DomainName, 255 );
strncpy( State.srchlist[0], pNetInfo->DomainName, 255 );
strncpy( State.DefaultServerAddress,
pNetInfo->DnsServerList.IpAddress.String,
15 );
HeapFree( ProcessHeap, 0, pNetInfo );
ret = WSAStartup( MAKEWORD(2, 2), &wsaData );
if (ret != 0)
{
_tprintf( _T("Winsock initialization failed: %d\n"), ret );
return ret;
}
switch( ParseCommandLine( argc, argv ) )
{
case 0:
/* This means that it was a /? parameter. */
break;
default:
/* Anything else means we enter interactive mode. The only exception
to this is when the host to resolve was provided on the command
line. */
InteractiveMode();
}
WSACleanup();
return 0;
}