2 * PROJECT: ReactOS nslookup utility
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: applications/network/nslookup/nslookup.c
5 * PURPOSE: Perform DNS lookups
6 * COPYRIGHT: Copyright 2009 Lucas Suggs <lucas.suggs@gmail.com>
17 _tprintf( _T("Default Server: (null)\n\n") );
18 _tprintf( _T("Set options:\n") );
21 if( !State
.debug
) _tprintf( _T("no") );
22 _tprintf( _T("debug\n") );
25 if( !State
.defname
) _tprintf( _T("no") );
26 _tprintf( _T("defname\n") );
29 if( !State
.search
) _tprintf( _T("no") );
30 _tprintf( _T("search\n") );
33 if( !State
.recurse
) _tprintf( _T("no") );
34 _tprintf( _T("recurse\n") );
37 if( !State
.d2
) _tprintf( _T("no") );
38 _tprintf( _T("d2\n") );
41 if( !State
.vc
) _tprintf( _T("no") );
42 _tprintf( _T("vc\n") );
45 if( !State
.ignoretc
) _tprintf( _T("no") );
46 _tprintf( _T("ignoretc\n") );
48 _tprintf( _T(" port=%d\n"), State
.port
);
49 _tprintf( _T(" type=%s\n"), State
.type
);
50 _tprintf( _T(" class=%s\n"), State
.Class
);
51 _tprintf( _T(" timeout=%d\n"), (int)State
.timeout
);
52 _tprintf( _T(" retry=%d\n"), (int)State
.retry
);
53 _tprintf( _T(" root=%s\n"), State
.root
);
54 _tprintf( _T(" domain=%s\n"), State
.domain
);
57 if( !State
.MSxfr
) _tprintf( _T("no") );
58 _tprintf( _T("MSxfr\n") );
60 _tprintf( _T(" IXFRversion=%d\n"), (int)State
.ixfrver
);
62 _tprintf( _T(" srchlist=%s\n\n"), State
.srchlist
[0] );
67 _tprintf( _T("Usage:\n"
68 " nslookup [-opt ...] # interactive mode using"
69 " default server\n nslookup [-opt ...] - server #"
70 " interactive mode using 'server'\n nslookup [-opt ...]"
71 " host # just look up 'host' using default server\n"
72 " nslookup [-opt ...] host server # just look up 'host'"
73 " using 'server'\n") );
76 BOOL
PerformInternalLookup( PCHAR pAddr
, PCHAR pResult
)
78 /* Needed to issue DNS packets and parse them. */
79 PCHAR Buffer
= NULL
, RecBuffer
= NULL
;
81 ULONG BufferLength
= 0, RecBufferLength
= 512;
82 int i
= 0, j
= 0, k
= 0, d
= 0;
85 /* Makes things easier when parsing the response packet. */
89 if( (strlen( pAddr
) + 1) > 255 ) return FALSE
;
92 if( IsValidIP( pAddr
) ) Type
= TYPE_PTR
;
94 /* If it's a PTR lookup then append the ARPA sig to the end. */
95 if( Type
== TYPE_PTR
)
97 ReverseIP( pAddr
, pResolve
);
98 strcat( pResolve
, ARPA_SIG
);
102 strcpy( pResolve
, pAddr
);
105 /* Base header length + length of QNAME + length of QTYPE and QCLASS */
106 BufferLength
= 12 + (strlen( pResolve
) + 2) + 4;
108 /* Allocate memory for the buffer. */
109 Buffer
= HeapAlloc( ProcessHeap
, 0, BufferLength
);
112 _tprintf( _T("ERROR: Out of memory\n") );
116 /* Allocate the receiving buffer. */
117 RecBuffer
= HeapAlloc( ProcessHeap
, 0, RecBufferLength
);
120 _tprintf( _T("ERROR: Out of memory\n") );
124 /* Insert the ID field. */
125 ((PSHORT
)&Buffer
[i
])[0] = htons( RequestID
);
128 /* Bits 0-7 of the second 16 are all 0, except for when recursion is
131 if( State
.recurse
) Buffer
[i
] |= 0x01;
134 /* Bits 8-15 of the second 16 are 0 for a query. */
138 /* Only 1 question. */
139 ((PSHORT
)&Buffer
[i
])[0] = htons( 1 );
142 /* We aren't sending a response, so 0 out the rest of the header. */
144 Buffer
[i
+ 1] = 0x00;
145 Buffer
[i
+ 2] = 0x00;
146 Buffer
[i
+ 3] = 0x00;
147 Buffer
[i
+ 4] = 0x00;
148 Buffer
[i
+ 5] = 0x00;
151 /* Walk through the query address. Split each section delimited by '.'.
152 Format of the QNAME section is length|data, etc. Last one is null */
156 for( k
= 0; k
< strlen( pResolve
); k
+= 1 )
158 if( pResolve
[k
] != '.' )
160 Buffer
[i
] = pResolve
[k
];
165 Buffer
[j
] = (i
- j
) - 1;
171 Buffer
[j
] = (i
- j
) - 1;
176 ((PSHORT
)&Buffer
[i
])[0] = htons( Type
);
180 ((PSHORT
)&Buffer
[i
])[0] = htons( CLASS_IN
);
182 /* Ship the request off to the DNS server. */
183 bOk
= SendRequest( Buffer
,
187 if( !bOk
) goto cleanup
;
189 /* Start parsing the received packet. */
190 NumQuestions
= ntohs( ((PSHORT
)&RecBuffer
[4])[0] );
194 /* We don't care about the questions section, blow through it. */
197 for( i
= 0; i
< NumQuestions
; i
+= 1 )
199 /* Quick way to skip the domain name section. */
200 k
+= ExtractName( RecBuffer
, pResult
, k
, 0 );
205 /* Skip the answer name. */
206 k
+= ExtractName( RecBuffer
, pResult
, k
, 0 );
208 Type
= ntohs( ((PUSHORT
)&RecBuffer
[k
])[0] );
211 d
= ntohs( ((PUSHORT
)&RecBuffer
[k
])[0] );
214 if( TYPE_PTR
== Type
)
216 k
+= ExtractName( RecBuffer
, pResult
, k
, d
);
218 else if( TYPE_A
== Type
)
220 k
+= ExtractIP( RecBuffer
, pResult
, k
);
225 if( Buffer
) HeapFree( ProcessHeap
, 0, Buffer
);
226 if( RecBuffer
) HeapFree( ProcessHeap
, 0, RecBuffer
);
233 void PerformLookup( PCHAR pAddr
)
235 /* Needed to issue DNS packets and parse them. */
236 PCHAR Buffer
= NULL
, RecBuffer
= NULL
;
239 ULONG BufferLength
= 0, RecBufferLength
= 512;
240 int i
= 0, j
= 0, k
= 0, d
= 0;
243 /* Makes things easier when parsing the response packet. */
250 if( (strlen( pAddr
) + 1) > 255 ) return;
252 _tprintf( _T("Server: %s\n"), State
.DefaultServer
);
253 _tprintf( _T("Address: %s\n\n"), State
.DefaultServerAddress
);
255 if( !strcmp( TypeA
, State
.type
)
256 || !strcmp( TypeAAAA
, State
.type
)
257 || !strcmp( TypeBoth
, State
.type
) )
260 if( IsValidIP( pAddr
) ) Type
= TYPE_PTR
;
263 Type
= TypeNametoTypeID( State
.type
);
265 /* If it's a PTR lookup then append the ARPA sig to the end. */
266 if( (Type
== TYPE_PTR
) && IsValidIP( pAddr
) )
268 ReverseIP( pAddr
, pResolve
);
269 strcat( pResolve
, ARPA_SIG
);
273 strcpy( pResolve
, pAddr
);
276 /* Base header length + length of QNAME + length of QTYPE and QCLASS */
277 BufferLength
= 12 + (strlen( pResolve
) + 2) + 4;
279 /* Allocate memory for the buffer. */
280 Buffer
= HeapAlloc( ProcessHeap
, 0, BufferLength
);
283 _tprintf( _T("ERROR: Out of memory\n") );
287 /* Allocate memory for the return buffer. */
288 RecBuffer
= HeapAlloc( ProcessHeap
, 0, RecBufferLength
);
291 _tprintf( _T("ERROR: Out of memory\n") );
295 /* Insert the ID field. */
296 ((PSHORT
)&Buffer
[i
])[0] = htons( RequestID
);
299 /* Bits 0-7 of the second 16 are all 0, except for when recursion is
302 if( State
.recurse
) Buffer
[i
] |= 0x01;
305 /* Bits 8-15 of the second 16 are 0 for a query. */
309 /* Only 1 question. */
310 ((PSHORT
)&Buffer
[i
])[0] = htons( 1 );
313 /* We aren't sending a response, so 0 out the rest of the header. */
315 Buffer
[i
+ 1] = 0x00;
316 Buffer
[i
+ 2] = 0x00;
317 Buffer
[i
+ 3] = 0x00;
318 Buffer
[i
+ 4] = 0x00;
319 Buffer
[i
+ 5] = 0x00;
322 /* Walk through the query address. Split each section delimited by '.'.
323 Format of the QNAME section is length|data, etc. Last one is null */
327 for( k
= 0; k
< strlen( pResolve
); k
+= 1 )
329 if( pResolve
[k
] != '.' )
331 Buffer
[i
] = pResolve
[k
];
336 Buffer
[j
] = (i
- j
) - 1;
342 Buffer
[j
] = (i
- j
) - 1;
347 ((PSHORT
)&Buffer
[i
])[0] = htons( Type
);
351 ((PSHORT
)&Buffer
[i
])[0] = htons( ClassNametoClassID( State
.Class
) );
353 /* Ship off the request to the DNS server. */
354 bOk
= SendRequest( Buffer
,
358 if( !bOk
) goto cleanup
;
360 /* Start parsing the received packet. */
361 Header2
= RecBuffer
[3];
362 NumQuestions
= ntohs( ((PSHORT
)&RecBuffer
[4])[0] );
363 NumAnswers
= ntohs( ((PSHORT
)&RecBuffer
[6])[0] );
364 NumAuthority
= ntohs( ((PUSHORT
)&RecBuffer
[8])[0] );
367 /* Check the RCODE for failure. */
369 if( d
!= RCODE_NOERROR
)
374 _tprintf( _T("*** %s can't find %s: Non-existant domain\n"), State
.DefaultServer
, pAddr
);
378 _tprintf( _T("*** %s can't find %s: Query refused\n"), State
.DefaultServer
, pAddr
);
382 _tprintf( _T("*** %s can't find %s: Unknown RCODE\n"), State
.DefaultServer
, pAddr
);
392 /* Blow through the questions section since we don't care about it. */
393 for( i
= 0; i
< NumQuestions
; i
+= 1 )
395 k
+= ExtractName( RecBuffer
, pResult
, k
, 0 );
403 k
+= ExtractName( RecBuffer
, pResult
, k
, 0 );
405 Type
= ntohs( ((PUSHORT
)&RecBuffer
[k
])[0] );
408 d
= ntohs( ((PUSHORT
)&RecBuffer
[k
])[0] );
411 if( TYPE_PTR
== Type
)
413 k
+= ExtractName( RecBuffer
, pResult
, k
, d
);
415 else if( TYPE_A
== Type
)
417 k
+= ExtractIP( RecBuffer
, pResult
, k
);
421 /* FIXME: This'll need to support more than PTR and A at some point. */
422 if( !strcmp( State
.type
, TypePTR
) )
424 if( TYPE_PTR
== Type
)
426 _tprintf( _T("%s name = %s\n"), pResolve
, pResult
);
432 else if( !strcmp( State
.type
, TypeA
)
433 || !strcmp( State
.type
, TypeAAAA
)
434 || !strcmp( State
.type
, TypeBoth
) )
436 if( (TYPE_A
== Type
) /*|| (TYPE_AAAA == Type)*/ )
438 if( 0 == NumAuthority
)
439 _tprintf( _T("Non-authoritative answer:\n") );
441 _tprintf( _T("Name: %s\n"), pAddr
);
442 _tprintf( _T("Address: %s\n\n"), pResult
);
446 _tprintf( _T("Name: %s\n"), pResult
);
447 _tprintf( _T("Address: %s\n\n"), pAddr
);
453 if( Buffer
) HeapFree( ProcessHeap
, 0, Buffer
);
454 if( RecBuffer
) HeapFree( ProcessHeap
, 0, RecBuffer
);
459 BOOL
ParseCommandLine( int argc
, char* argv
[] )
462 BOOL NoMoreOptions
= FALSE
;
463 BOOL Interactive
= FALSE
;
464 CHAR AddrToResolve
[256];
467 RtlZeroMemory( AddrToResolve
, 256 );
468 RtlZeroMemory( Server
, 256 );
472 /* In the Windows nslookup, usage is only displayed if /? is the only
473 option specified on the command line. */
474 if( !strncmp( "/?", argv
[1], 2 ) )
483 for( i
= 1; i
< argc
; i
+= 1 )
487 strncpy( Server
, argv
[i
], 255 );
489 /* Determine which one to resolve. This is based on whether the
490 DNS server provided was an IP or an FQDN. */
491 if( IsValidIP( Server
) )
493 strncpy( State
.DefaultServerAddress
, Server
, 16 );
495 PerformInternalLookup( State
.DefaultServerAddress
,
496 State
.DefaultServer
);
500 strncpy( State
.DefaultServer
, Server
, 255 );
502 PerformInternalLookup( State
.DefaultServer
,
503 State
.DefaultServerAddress
);
506 if( Interactive
) return 1;
508 PerformLookup( AddrToResolve
);
514 if( !strncmp( "-all", argv
[i
], 4 ) )
518 else if( !strncmp( "-type=", argv
[i
], 6 ) )
520 if( !strncmp( TypeA
, &argv
[i
][6], strlen( TypeA
) ) )
524 else if( !strncmp( TypeAAAA
, &argv
[i
][6], strlen( TypeAAAA
) ) )
526 State
.type
= TypeAAAA
;
528 else if( !strncmp( TypeBoth
, &argv
[i
][6], strlen( TypeBoth
) ) )
530 State
.type
= TypeBoth
;
532 else if( !strncmp( TypeAny
, &argv
[i
][6], strlen( TypeAny
) ) )
534 State
.type
= TypeAny
;
536 else if( !strncmp( TypeCNAME
, &argv
[i
][6], strlen( TypeCNAME
) ) )
538 State
.type
= TypeCNAME
;
540 else if( !strncmp( TypeMX
, &argv
[i
][6], strlen( TypeMX
) ) )
544 else if( !strncmp( TypeNS
, &argv
[i
][6], strlen( TypeNS
) ) )
548 else if( !strncmp( TypePTR
, &argv
[i
][6], strlen( TypePTR
) ) )
550 State
.type
= TypePTR
;
552 else if( !strncmp( TypeSOA
, &argv
[i
][6], strlen( TypeSOA
) ) )
554 State
.type
= TypeSOA
;
556 else if( !strncmp( TypeSRV
, &argv
[i
][6], strlen( TypeSRV
) ) )
558 State
.type
= TypeSRV
;
562 _tprintf( _T("unknown query type: %s"), &argv
[i
][6] );
565 else if( !strncmp( "-domain=", argv
[i
], 8 ) )
567 strcpy( State
.domain
, &argv
[i
][8] );
569 else if( !strncmp( "-srchlist=", argv
[i
], 10 ) )
572 else if( !strncmp( "-root=", argv
[i
], 6 ) )
574 strcpy( State
.root
, &argv
[i
][6] );
576 else if( !strncmp( "-retry=", argv
[i
], 7 ) )
579 else if( !strncmp( "-timeout=", argv
[i
], 9 ) )
582 else if( !strncmp( "-querytype=", argv
[i
], 11 ) )
584 if( !strncmp( TypeA
, &argv
[i
][11], strlen( TypeA
) ) )
588 else if( !strncmp( TypeAAAA
, &argv
[i
][11], strlen( TypeAAAA
) ) )
590 State
.type
= TypeAAAA
;
592 else if( !strncmp( TypeBoth
, &argv
[i
][11], strlen( TypeBoth
) ) )
594 State
.type
= TypeBoth
;
596 else if( !strncmp( TypeAny
, &argv
[i
][11], strlen( TypeAny
) ) )
598 State
.type
= TypeAny
;
600 else if( !strncmp( TypeCNAME
, &argv
[i
][11], strlen( TypeCNAME
) ) )
602 State
.type
= TypeCNAME
;
604 else if( !strncmp( TypeMX
, &argv
[i
][11], strlen( TypeMX
) ) )
608 else if( !strncmp( TypeNS
, &argv
[i
][11], strlen( TypeNS
) ) )
612 else if( !strncmp( TypePTR
, &argv
[i
][11], strlen( TypePTR
) ) )
614 State
.type
= TypePTR
;
616 else if( !strncmp( TypeSOA
, &argv
[i
][11], strlen( TypeSOA
) ) )
618 State
.type
= TypeSOA
;
620 else if( !strncmp( TypeSRV
, &argv
[i
][11], strlen( TypeSRV
) ) )
622 State
.type
= TypeSRV
;
626 _tprintf( _T("unknown query type: %s"), &argv
[i
][6] );
629 else if( !strncmp( "-class=", argv
[i
], 7 ) )
631 if( !strncmp( ClassIN
, &argv
[i
][7], strlen( ClassIN
) ) )
633 State
.Class
= ClassIN
;
635 else if( !strncmp( ClassAny
, &argv
[i
][7], strlen( ClassAny
) ) )
637 State
.Class
= ClassAny
;
641 _tprintf( _T("unknown query class: %s"), &argv
[i
][7] );
644 else if( !strncmp( "-ixfrver=", argv
[i
], 9 ) )
647 else if( !strncmp( "-debug", argv
[i
], 6 ) )
651 else if( !strncmp( "-nodebug", argv
[i
], 8 ) )
656 else if( !strncmp( "-d2", argv
[i
], 3 ) )
661 else if( !strncmp( "-nod2", argv
[i
], 5 ) )
663 if( State
.debug
) _tprintf( _T("d2 mode disabled; still in debug mode\n") );
667 else if( !strncmp( "-defname", argv
[i
], 8 ) )
669 State
.defname
= TRUE
;
671 else if( !strncmp( "-noddefname", argv
[i
], 10 ) )
673 State
.defname
= FALSE
;
675 else if( !strncmp( "-recurse", argv
[i
], 8 ) )
677 State
.recurse
= TRUE
;
679 else if( !strncmp( "-norecurse", argv
[i
], 10 ) )
681 State
.recurse
= FALSE
;
683 else if( !strncmp( "-search", argv
[i
], 7 ) )
687 else if( !strncmp( "-nosearch", argv
[i
], 9 ) )
689 State
.search
= FALSE
;
691 else if( !strncmp( "-vc", argv
[i
], 3 ) )
695 else if( !strncmp( "-novc", argv
[i
], 5 ) )
699 else if( !strncmp( "-msxfr", argv
[i
], 6 ) )
703 else if( !strncmp( "-nomsxfr", argv
[i
], 8 ) )
707 else if( !strncmp( "-", argv
[i
], 1 ) && (strlen( argv
[i
] ) == 1) )
709 /* Since we received just the plain - switch, we are going
710 to be entering interactive mode. We also will not be
711 parsing any more options. */
712 NoMoreOptions
= TRUE
;
717 /* Grab the address to resolve. No more options accepted
719 strncpy( AddrToResolve
, argv
[i
], 255 );
720 NoMoreOptions
= TRUE
;
725 if( NoMoreOptions
&& !Interactive
)
727 /* Get the FQDN of the DNS server. */
728 PerformInternalLookup( State
.DefaultServerAddress
,
729 State
.DefaultServer
);
731 PerformLookup( AddrToResolve
);
737 /* Get the FQDN of the DNS server. */
738 PerformInternalLookup( State
.DefaultServerAddress
,
739 State
.DefaultServer
);
744 void InteractiveMode()
746 _tprintf( _T("Default Server: %s\n"), State
.DefaultServer
);
747 _tprintf( _T("Address: %s\n\n"), State
.DefaultServerAddress
);
749 /* TODO: Implement interactive mode. */
751 _tprintf( _T("ERROR: Feature not implemented.\n") );
754 int main( int argc
, char* argv
[] )
758 PFIXED_INFO pNetInfo
= NULL
;
762 ProcessHeap
= GetProcessHeap();
765 /* Set up the initial state. */
767 State
.defname
= TRUE
;
769 State
.recurse
= TRUE
;
772 State
.ignoretc
= FALSE
;
774 State
.type
= TypeBoth
;
775 State
.Class
= ClassIN
;
781 RtlZeroMemory( State
.root
, 256 );
782 RtlZeroMemory( State
.domain
, 256 );
783 for( i
= 0; i
< 6; i
+= 1 ) RtlZeroMemory( State
.srchlist
[i
], 256 );
784 RtlZeroMemory( State
.DefaultServer
, 256 );
785 RtlZeroMemory( State
.DefaultServerAddress
, 16 );
787 memcpy( State
.root
, DEFAULT_ROOT
, sizeof(DEFAULT_ROOT
) );
789 /* We don't know how long of a buffer it will want to return. So we'll
790 pass an empty one now and let it fail only once, instead of guessing. */
791 Status
= GetNetworkParams( pNetInfo
, &NetBufLen
);
792 if( Status
== ERROR_BUFFER_OVERFLOW
)
794 pNetInfo
= (PFIXED_INFO
)HeapAlloc( ProcessHeap
, 0, NetBufLen
);
795 if( pNetInfo
== NULL
)
797 _tprintf( _T("ERROR: Out of memory\n") );
802 /* For real this time. */
803 Status
= GetNetworkParams( pNetInfo
, &NetBufLen
);
804 if( Status
!= NO_ERROR
)
806 _tprintf( _T("Error in GetNetworkParams call\n") );
808 HeapFree( ProcessHeap
, 0, pNetInfo
);
814 strncpy( State
.domain
, pNetInfo
->DomainName
, 255 );
815 strncpy( State
.srchlist
[0], pNetInfo
->DomainName
, 255 );
816 strncpy( State
.DefaultServerAddress
,
817 pNetInfo
->DnsServerList
.IpAddress
.String
,
820 HeapFree( ProcessHeap
, 0, pNetInfo
);
822 WSAStartup( MAKEWORD(2,2), &wsaData
);
824 switch( ParseCommandLine( argc
, argv
) )
827 /* This means that it was a /? parameter. */
831 /* Anything else means we enter interactive mode. The only exception
832 to this is when the host to resolve was provided on the command