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>
20 _tprintf( _T("Default Server: (null)\n\n") );
21 _tprintf( _T("Set options:\n") );
24 if( !State
.debug
) _tprintf( _T("no") );
25 _tprintf( _T("debug\n") );
28 if( !State
.defname
) _tprintf( _T("no") );
29 _tprintf( _T("defname\n") );
32 if( !State
.search
) _tprintf( _T("no") );
33 _tprintf( _T("search\n") );
36 if( !State
.recurse
) _tprintf( _T("no") );
37 _tprintf( _T("recurse\n") );
40 if( !State
.d2
) _tprintf( _T("no") );
41 _tprintf( _T("d2\n") );
44 if( !State
.vc
) _tprintf( _T("no") );
45 _tprintf( _T("vc\n") );
48 if( !State
.ignoretc
) _tprintf( _T("no") );
49 _tprintf( _T("ignoretc\n") );
51 _tprintf( _T(" port=%d\n"), State
.port
);
52 _tprintf( _T(" type=%s\n"), State
.type
);
53 _tprintf( _T(" class=%s\n"), State
.Class
);
54 _tprintf( _T(" timeout=%d\n"), (int)State
.timeout
);
55 _tprintf( _T(" retry=%d\n"), (int)State
.retry
);
56 _tprintf( _T(" root=%s\n"), State
.root
);
57 _tprintf( _T(" domain=%s\n"), State
.domain
);
60 if( !State
.MSxfr
) _tprintf( _T("no") );
61 _tprintf( _T("MSxfr\n") );
63 _tprintf( _T(" IXFRversion=%d\n"), (int)State
.ixfrver
);
65 _tprintf( _T(" srchlist=%s\n\n"), State
.srchlist
[0] );
70 _tprintf( _T("Usage:\n"
71 " nslookup [-opt ...] # interactive mode using"
72 " default server\n nslookup [-opt ...] - server #"
73 " interactive mode using 'server'\n nslookup [-opt ...]"
74 " host # just look up 'host' using default server\n"
75 " nslookup [-opt ...] host server # just look up 'host'"
76 " using 'server'\n") );
79 BOOL
PerformInternalLookup( PCHAR pAddr
, PCHAR pResult
)
81 /* Needed to issue DNS packets and parse them. */
82 PCHAR Buffer
= NULL
, RecBuffer
= NULL
;
84 ULONG BufferLength
= 0, RecBufferLength
= 512;
85 int i
= 0, j
= 0, k
= 0, d
= 0;
88 /* Makes things easier when parsing the response packet. */
92 if( (strlen( pAddr
) + 1) > 255 ) return FALSE
;
95 if( IsValidIP( pAddr
) ) Type
= TYPE_PTR
;
97 /* If it's a PTR lookup then append the ARPA sig to the end. */
98 if( Type
== TYPE_PTR
)
100 ReverseIP( pAddr
, pResolve
);
101 strcat( pResolve
, ARPA_SIG
);
105 strcpy( pResolve
, pAddr
);
108 /* Base header length + length of QNAME + length of QTYPE and QCLASS */
109 BufferLength
= 12 + (strlen( pResolve
) + 2) + 4;
111 /* Allocate memory for the buffer. */
112 Buffer
= HeapAlloc( ProcessHeap
, 0, BufferLength
);
115 _tprintf( _T("ERROR: Out of memory\n") );
119 /* Allocate the receiving buffer. */
120 RecBuffer
= HeapAlloc( ProcessHeap
, 0, RecBufferLength
);
123 _tprintf( _T("ERROR: Out of memory\n") );
127 /* Insert the ID field. */
128 ((PSHORT
)&Buffer
[i
])[0] = htons( RequestID
);
131 /* Bits 0-7 of the second 16 are all 0, except for when recursion is
134 if( State
.recurse
) Buffer
[i
] |= 0x01;
137 /* Bits 8-15 of the second 16 are 0 for a query. */
141 /* Only 1 question. */
142 ((PSHORT
)&Buffer
[i
])[0] = htons( 1 );
145 /* We aren't sending a response, so 0 out the rest of the header. */
147 Buffer
[i
+ 1] = 0x00;
148 Buffer
[i
+ 2] = 0x00;
149 Buffer
[i
+ 3] = 0x00;
150 Buffer
[i
+ 4] = 0x00;
151 Buffer
[i
+ 5] = 0x00;
154 /* Walk through the query address. Split each section delimited by '.'.
155 Format of the QNAME section is length|data, etc. Last one is null */
159 for( k
= 0; k
< strlen( pResolve
); k
+= 1 )
161 if( pResolve
[k
] != '.' )
163 Buffer
[i
] = pResolve
[k
];
168 Buffer
[j
] = (i
- j
) - 1;
174 Buffer
[j
] = (i
- j
) - 1;
179 ((PSHORT
)&Buffer
[i
])[0] = htons( Type
);
183 ((PSHORT
)&Buffer
[i
])[0] = htons( CLASS_IN
);
185 /* Ship the request off to the DNS server. */
186 bOk
= SendRequest( Buffer
,
190 if( !bOk
) goto cleanup
;
192 /* Start parsing the received packet. */
193 NumQuestions
= ntohs( ((PSHORT
)&RecBuffer
[4])[0] );
197 /* We don't care about the questions section, blow through it. */
200 for( i
= 0; i
< NumQuestions
; i
+= 1 )
202 /* Quick way to skip the domain name section. */
203 k
+= ExtractName( RecBuffer
, pResult
, k
, 0 );
208 /* Skip the answer name. */
209 k
+= ExtractName( RecBuffer
, pResult
, k
, 0 );
211 Type
= ntohs( ((PUSHORT
)&RecBuffer
[k
])[0] );
214 d
= ntohs( ((PUSHORT
)&RecBuffer
[k
])[0] );
217 if( TYPE_PTR
== Type
)
219 k
+= ExtractName( RecBuffer
, pResult
, k
, d
);
221 else if( TYPE_A
== Type
)
223 k
+= ExtractIP( RecBuffer
, pResult
, k
);
228 if( Buffer
) HeapFree( ProcessHeap
, 0, Buffer
);
229 if( RecBuffer
) HeapFree( ProcessHeap
, 0, RecBuffer
);
236 void PerformLookup( PCHAR pAddr
)
238 /* Needed to issue DNS packets and parse them. */
239 PCHAR Buffer
= NULL
, RecBuffer
= NULL
;
242 ULONG BufferLength
= 0, RecBufferLength
= 512;
243 int i
= 0, j
= 0, k
= 0, d
= 0;
246 /* Makes things easier when parsing the response packet. */
253 if( (strlen( pAddr
) + 1) > 255 ) return;
255 _tprintf( _T("Server: %s\n"), State
.DefaultServer
);
256 _tprintf( _T("Address: %s\n\n"), State
.DefaultServerAddress
);
258 if( !strcmp( TypeA
, State
.type
)
259 || !strcmp( TypeAAAA
, State
.type
)
260 || !strcmp( TypeBoth
, State
.type
) )
263 if( IsValidIP( pAddr
) ) Type
= TYPE_PTR
;
266 Type
= TypeNametoTypeID( State
.type
);
268 /* If it's a PTR lookup then append the ARPA sig to the end. */
269 if( (Type
== TYPE_PTR
) && IsValidIP( pAddr
) )
271 ReverseIP( pAddr
, pResolve
);
272 strcat( pResolve
, ARPA_SIG
);
276 strcpy( pResolve
, pAddr
);
279 /* Base header length + length of QNAME + length of QTYPE and QCLASS */
280 BufferLength
= 12 + (strlen( pResolve
) + 2) + 4;
282 /* Allocate memory for the buffer. */
283 Buffer
= HeapAlloc( ProcessHeap
, 0, BufferLength
);
286 _tprintf( _T("ERROR: Out of memory\n") );
290 /* Allocate memory for the return buffer. */
291 RecBuffer
= HeapAlloc( ProcessHeap
, 0, RecBufferLength
);
294 _tprintf( _T("ERROR: Out of memory\n") );
298 /* Insert the ID field. */
299 ((PSHORT
)&Buffer
[i
])[0] = htons( RequestID
);
302 /* Bits 0-7 of the second 16 are all 0, except for when recursion is
305 if( State
.recurse
) Buffer
[i
] |= 0x01;
308 /* Bits 8-15 of the second 16 are 0 for a query. */
312 /* Only 1 question. */
313 ((PSHORT
)&Buffer
[i
])[0] = htons( 1 );
316 /* We aren't sending a response, so 0 out the rest of the header. */
318 Buffer
[i
+ 1] = 0x00;
319 Buffer
[i
+ 2] = 0x00;
320 Buffer
[i
+ 3] = 0x00;
321 Buffer
[i
+ 4] = 0x00;
322 Buffer
[i
+ 5] = 0x00;
325 /* Walk through the query address. Split each section delimited by '.'.
326 Format of the QNAME section is length|data, etc. Last one is null */
330 for( k
= 0; k
< strlen( pResolve
); k
+= 1 )
332 if( pResolve
[k
] != '.' )
334 Buffer
[i
] = pResolve
[k
];
339 Buffer
[j
] = (i
- j
) - 1;
345 Buffer
[j
] = (i
- j
) - 1;
350 ((PSHORT
)&Buffer
[i
])[0] = htons( Type
);
354 ((PSHORT
)&Buffer
[i
])[0] = htons( ClassNametoClassID( State
.Class
) );
356 /* Ship off the request to the DNS server. */
357 bOk
= SendRequest( Buffer
,
361 if( !bOk
) goto cleanup
;
363 /* Start parsing the received packet. */
364 Header2
= RecBuffer
[3];
365 NumQuestions
= ntohs( ((PSHORT
)&RecBuffer
[4])[0] );
366 NumAnswers
= ntohs( ((PSHORT
)&RecBuffer
[6])[0] );
367 NumAuthority
= ntohs( ((PUSHORT
)&RecBuffer
[8])[0] );
370 /* Check the RCODE for failure. */
372 if( d
!= RCODE_NOERROR
)
377 _tprintf( _T("*** %s can't find %s: Non-existant domain\n"), State
.DefaultServer
, pAddr
);
381 _tprintf( _T("*** %s can't find %s: Query refused\n"), State
.DefaultServer
, pAddr
);
385 _tprintf( _T("*** %s can't find %s: Unknown RCODE\n"), State
.DefaultServer
, pAddr
);
395 /* Blow through the questions section since we don't care about it. */
396 for( i
= 0; i
< NumQuestions
; i
+= 1 )
398 k
+= ExtractName( RecBuffer
, pResult
, k
, 0 );
406 k
+= ExtractName( RecBuffer
, pResult
, k
, 0 );
408 Type
= ntohs( ((PUSHORT
)&RecBuffer
[k
])[0] );
411 d
= ntohs( ((PUSHORT
)&RecBuffer
[k
])[0] );
414 if( TYPE_PTR
== Type
)
416 k
+= ExtractName( RecBuffer
, pResult
, k
, d
);
418 else if( TYPE_A
== Type
)
420 k
+= ExtractIP( RecBuffer
, pResult
, k
);
424 /* FIXME: This'll need to support more than PTR and A at some point. */
425 if( !strcmp( State
.type
, TypePTR
) )
427 if( TYPE_PTR
== Type
)
429 _tprintf( _T("%s name = %s\n"), pResolve
, pResult
);
435 else if( !strcmp( State
.type
, TypeA
)
436 || !strcmp( State
.type
, TypeAAAA
)
437 || !strcmp( State
.type
, TypeBoth
) )
439 if( (TYPE_A
== Type
) /*|| (TYPE_AAAA == Type)*/ )
441 if( 0 == NumAuthority
)
442 _tprintf( _T("Non-authoritative answer:\n") );
444 _tprintf( _T("Name: %s\n"), pAddr
);
445 _tprintf( _T("Address: %s\n\n"), pResult
);
449 _tprintf( _T("Name: %s\n"), pResult
);
450 _tprintf( _T("Address: %s\n\n"), pAddr
);
456 if( Buffer
) HeapFree( ProcessHeap
, 0, Buffer
);
457 if( RecBuffer
) HeapFree( ProcessHeap
, 0, RecBuffer
);
462 BOOL
ParseCommandLine( int argc
, char* argv
[] )
465 BOOL NoMoreOptions
= FALSE
;
466 BOOL Interactive
= FALSE
;
467 CHAR AddrToResolve
[256];
470 RtlZeroMemory( AddrToResolve
, 256 );
471 RtlZeroMemory( Server
, 256 );
475 /* In the Windows nslookup, usage is only displayed if /? is the only
476 option specified on the command line. */
477 if( !strncmp( "/?", argv
[1], 2 ) )
486 for( i
= 1; i
< argc
; i
+= 1 )
490 strncpy( Server
, argv
[i
], 255 );
492 /* Determine which one to resolve. This is based on whether the
493 DNS server provided was an IP or an FQDN. */
494 if( IsValidIP( Server
) )
496 strncpy( State
.DefaultServerAddress
, Server
, 16 );
498 PerformInternalLookup( State
.DefaultServerAddress
,
499 State
.DefaultServer
);
503 strncpy( State
.DefaultServer
, Server
, 255 );
505 PerformInternalLookup( State
.DefaultServer
,
506 State
.DefaultServerAddress
);
509 if( Interactive
) return 1;
511 PerformLookup( AddrToResolve
);
517 if( !strncmp( "-all", argv
[i
], 4 ) )
521 else if( !strncmp( "-type=", argv
[i
], 6 ) )
523 if( !strncmp( TypeA
, &argv
[i
][6], strlen( TypeA
) ) )
527 else if( !strncmp( TypeAAAA
, &argv
[i
][6], strlen( TypeAAAA
) ) )
529 State
.type
= TypeAAAA
;
531 else if( !strncmp( TypeBoth
, &argv
[i
][6], strlen( TypeBoth
) ) )
533 State
.type
= TypeBoth
;
535 else if( !strncmp( TypeAny
, &argv
[i
][6], strlen( TypeAny
) ) )
537 State
.type
= TypeAny
;
539 else if( !strncmp( TypeCNAME
, &argv
[i
][6], strlen( TypeCNAME
) ) )
541 State
.type
= TypeCNAME
;
543 else if( !strncmp( TypeMX
, &argv
[i
][6], strlen( TypeMX
) ) )
547 else if( !strncmp( TypeNS
, &argv
[i
][6], strlen( TypeNS
) ) )
551 else if( !strncmp( TypePTR
, &argv
[i
][6], strlen( TypePTR
) ) )
553 State
.type
= TypePTR
;
555 else if( !strncmp( TypeSOA
, &argv
[i
][6], strlen( TypeSOA
) ) )
557 State
.type
= TypeSOA
;
559 else if( !strncmp( TypeSRV
, &argv
[i
][6], strlen( TypeSRV
) ) )
561 State
.type
= TypeSRV
;
565 _tprintf( _T("unknown query type: %s"), &argv
[i
][6] );
568 else if( !strncmp( "-domain=", argv
[i
], 8 ) )
570 strcpy( State
.domain
, &argv
[i
][8] );
572 else if( !strncmp( "-srchlist=", argv
[i
], 10 ) )
575 else if( !strncmp( "-root=", argv
[i
], 6 ) )
577 strcpy( State
.root
, &argv
[i
][6] );
579 else if( !strncmp( "-retry=", argv
[i
], 7 ) )
582 else if( !strncmp( "-timeout=", argv
[i
], 9 ) )
585 else if( !strncmp( "-querytype=", argv
[i
], 11 ) )
587 if( !strncmp( TypeA
, &argv
[i
][11], strlen( TypeA
) ) )
591 else if( !strncmp( TypeAAAA
, &argv
[i
][11], strlen( TypeAAAA
) ) )
593 State
.type
= TypeAAAA
;
595 else if( !strncmp( TypeBoth
, &argv
[i
][11], strlen( TypeBoth
) ) )
597 State
.type
= TypeBoth
;
599 else if( !strncmp( TypeAny
, &argv
[i
][11], strlen( TypeAny
) ) )
601 State
.type
= TypeAny
;
603 else if( !strncmp( TypeCNAME
, &argv
[i
][11], strlen( TypeCNAME
) ) )
605 State
.type
= TypeCNAME
;
607 else if( !strncmp( TypeMX
, &argv
[i
][11], strlen( TypeMX
) ) )
611 else if( !strncmp( TypeNS
, &argv
[i
][11], strlen( TypeNS
) ) )
615 else if( !strncmp( TypePTR
, &argv
[i
][11], strlen( TypePTR
) ) )
617 State
.type
= TypePTR
;
619 else if( !strncmp( TypeSOA
, &argv
[i
][11], strlen( TypeSOA
) ) )
621 State
.type
= TypeSOA
;
623 else if( !strncmp( TypeSRV
, &argv
[i
][11], strlen( TypeSRV
) ) )
625 State
.type
= TypeSRV
;
629 _tprintf( _T("unknown query type: %s"), &argv
[i
][6] );
632 else if( !strncmp( "-class=", argv
[i
], 7 ) )
634 if( !strncmp( ClassIN
, &argv
[i
][7], strlen( ClassIN
) ) )
636 State
.Class
= ClassIN
;
638 else if( !strncmp( ClassAny
, &argv
[i
][7], strlen( ClassAny
) ) )
640 State
.Class
= ClassAny
;
644 _tprintf( _T("unknown query class: %s"), &argv
[i
][7] );
647 else if( !strncmp( "-ixfrver=", argv
[i
], 9 ) )
650 else if( !strncmp( "-debug", argv
[i
], 6 ) )
654 else if( !strncmp( "-nodebug", argv
[i
], 8 ) )
659 else if( !strncmp( "-d2", argv
[i
], 3 ) )
664 else if( !strncmp( "-nod2", argv
[i
], 5 ) )
666 if( State
.debug
) _tprintf( _T("d2 mode disabled; still in debug mode\n") );
670 else if( !strncmp( "-defname", argv
[i
], 8 ) )
672 State
.defname
= TRUE
;
674 else if( !strncmp( "-noddefname", argv
[i
], 10 ) )
676 State
.defname
= FALSE
;
678 else if( !strncmp( "-recurse", argv
[i
], 8 ) )
680 State
.recurse
= TRUE
;
682 else if( !strncmp( "-norecurse", argv
[i
], 10 ) )
684 State
.recurse
= FALSE
;
686 else if( !strncmp( "-search", argv
[i
], 7 ) )
690 else if( !strncmp( "-nosearch", argv
[i
], 9 ) )
692 State
.search
= FALSE
;
694 else if( !strncmp( "-vc", argv
[i
], 3 ) )
698 else if( !strncmp( "-novc", argv
[i
], 5 ) )
702 else if( !strncmp( "-msxfr", argv
[i
], 6 ) )
706 else if( !strncmp( "-nomsxfr", argv
[i
], 8 ) )
710 else if( !strncmp( "-", argv
[i
], 1 ) && (strlen( argv
[i
] ) == 1) )
712 /* Since we received just the plain - switch, we are going
713 to be entering interactive mode. We also will not be
714 parsing any more options. */
715 NoMoreOptions
= TRUE
;
720 /* Grab the address to resolve. No more options accepted
722 strncpy( AddrToResolve
, argv
[i
], 255 );
723 NoMoreOptions
= TRUE
;
728 if( NoMoreOptions
&& !Interactive
)
730 /* Get the FQDN of the DNS server. */
731 PerformInternalLookup( State
.DefaultServerAddress
,
732 State
.DefaultServer
);
734 PerformLookup( AddrToResolve
);
740 /* Get the FQDN of the DNS server. */
741 PerformInternalLookup( State
.DefaultServerAddress
,
742 State
.DefaultServer
);
747 void InteractiveMode()
749 _tprintf( _T("Default Server: %s\n"), State
.DefaultServer
);
750 _tprintf( _T("Address: %s\n\n"), State
.DefaultServerAddress
);
752 /* TODO: Implement interactive mode. */
754 _tprintf( _T("ERROR: Feature not implemented.\n") );
757 int main( int argc
, char* argv
[] )
761 PFIXED_INFO pNetInfo
= NULL
;
765 ProcessHeap
= GetProcessHeap();
768 /* Set up the initial state. */
770 State
.defname
= TRUE
;
772 State
.recurse
= TRUE
;
775 State
.ignoretc
= FALSE
;
777 State
.type
= TypeBoth
;
778 State
.Class
= ClassIN
;
784 RtlZeroMemory( State
.root
, 256 );
785 RtlZeroMemory( State
.domain
, 256 );
786 for( i
= 0; i
< 6; i
+= 1 ) RtlZeroMemory( State
.srchlist
[i
], 256 );
787 RtlZeroMemory( State
.DefaultServer
, 256 );
788 RtlZeroMemory( State
.DefaultServerAddress
, 16 );
790 memcpy( State
.root
, DEFAULT_ROOT
, sizeof(DEFAULT_ROOT
) );
792 /* We don't know how long of a buffer it will want to return. So we'll
793 pass an empty one now and let it fail only once, instead of guessing. */
794 Status
= GetNetworkParams( pNetInfo
, &NetBufLen
);
795 if( Status
== ERROR_BUFFER_OVERFLOW
)
797 pNetInfo
= (PFIXED_INFO
)HeapAlloc( ProcessHeap
, 0, NetBufLen
);
798 if( pNetInfo
== NULL
)
800 _tprintf( _T("ERROR: Out of memory\n") );
805 /* For real this time. */
806 Status
= GetNetworkParams( pNetInfo
, &NetBufLen
);
807 if( Status
!= NO_ERROR
)
809 _tprintf( _T("Error in GetNetworkParams call\n") );
811 HeapFree( ProcessHeap
, 0, pNetInfo
);
817 strncpy( State
.domain
, pNetInfo
->DomainName
, 255 );
818 strncpy( State
.srchlist
[0], pNetInfo
->DomainName
, 255 );
819 strncpy( State
.DefaultServerAddress
,
820 pNetInfo
->DnsServerList
.IpAddress
.String
,
823 HeapFree( ProcessHeap
, 0, pNetInfo
);
825 WSAStartup( MAKEWORD(2,2), &wsaData
);
827 switch( ParseCommandLine( argc
, argv
) )
830 /* This means that it was a /? parameter. */
834 /* Anything else means we enter interactive mode. The only exception
835 to this is when the host to resolve was provided on the command