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>
24 _tprintf( _T("Default Server: (null)\n\n") );
25 _tprintf( _T("Set options:\n") );
28 if( !State
.debug
) _tprintf( _T("no") );
29 _tprintf( _T("debug\n") );
32 if( !State
.defname
) _tprintf( _T("no") );
33 _tprintf( _T("defname\n") );
36 if( !State
.search
) _tprintf( _T("no") );
37 _tprintf( _T("search\n") );
40 if( !State
.recurse
) _tprintf( _T("no") );
41 _tprintf( _T("recurse\n") );
44 if( !State
.d2
) _tprintf( _T("no") );
45 _tprintf( _T("d2\n") );
48 if( !State
.vc
) _tprintf( _T("no") );
49 _tprintf( _T("vc\n") );
52 if( !State
.ignoretc
) _tprintf( _T("no") );
53 _tprintf( _T("ignoretc\n") );
55 _tprintf( _T(" port=%d\n"), State
.port
);
56 _tprintf( _T(" type=%s\n"), State
.type
);
57 _tprintf( _T(" class=%s\n"), State
.Class
);
58 _tprintf( _T(" timeout=%d\n"), (int)State
.timeout
);
59 _tprintf( _T(" retry=%d\n"), (int)State
.retry
);
60 _tprintf( _T(" root=%s\n"), State
.root
);
61 _tprintf( _T(" domain=%s\n"), State
.domain
);
64 if( !State
.MSxfr
) _tprintf( _T("no") );
65 _tprintf( _T("MSxfr\n") );
67 _tprintf( _T(" IXFRversion=%d\n"), (int)State
.ixfrver
);
69 _tprintf( _T(" srchlist=%s\n\n"), State
.srchlist
[0] );
74 _tprintf( _T("Usage:\n"
75 " nslookup [-opt ...] # interactive mode using"
76 " default server\n nslookup [-opt ...] - server #"
77 " interactive mode using 'server'\n nslookup [-opt ...]"
78 " host # just look up 'host' using default server\n"
79 " nslookup [-opt ...] host server # just look up 'host'"
80 " using 'server'\n") );
83 BOOL
PerformInternalLookup( PCHAR pAddr
, PCHAR pResult
)
85 /* Needed to issue DNS packets and parse them. */
86 PCHAR Buffer
= NULL
, RecBuffer
= NULL
;
88 ULONG BufferLength
= 0, RecBufferLength
= 512;
89 int i
= 0, j
= 0, k
= 0, d
= 0;
92 /* Makes things easier when parsing the response packet. */
93 UCHAR Header1
, Header2
;
100 if( (strlen( pAddr
) + 1) > 255 ) return FALSE
;
103 if( IsValidIP( pAddr
) ) Type
= TYPE_PTR
;
105 /* If it's a PTR lookup then append the ARPA sig to the end. */
106 if( Type
== TYPE_PTR
)
108 ReverseIP( pAddr
, pResolve
);
109 strcat( pResolve
, ARPA_SIG
);
113 strcpy( pResolve
, pAddr
);
116 /* Base header length + length of QNAME + length of QTYPE and QCLASS */
117 BufferLength
= 12 + (strlen( pResolve
) + 2) + 4;
119 /* Allocate memory for the buffer. */
120 Buffer
= HeapAlloc( ProcessHeap
, 0, BufferLength
);
123 _tprintf( _T("ERROR: Out of memory\n") );
127 /* Allocate the receiving buffer. */
128 RecBuffer
= HeapAlloc( ProcessHeap
, 0, RecBufferLength
);
131 _tprintf( _T("ERROR: Out of memory\n") );
135 /* Insert the ID field. */
136 ((PSHORT
)&Buffer
[i
])[0] = htons( RequestID
);
139 /* Bits 0-7 of the second 16 are all 0, except for when recursion is
142 if( State
.recurse
) Buffer
[i
] |= 0x01;
145 /* Bits 8-15 of the second 16 are 0 for a query. */
149 /* Only 1 question. */
150 ((PSHORT
)&Buffer
[i
])[0] = htons( 1 );
153 /* We aren't sending a response, so 0 out the rest of the header. */
155 Buffer
[i
+ 1] = 0x00;
156 Buffer
[i
+ 2] = 0x00;
157 Buffer
[i
+ 3] = 0x00;
158 Buffer
[i
+ 4] = 0x00;
159 Buffer
[i
+ 5] = 0x00;
162 /* Walk through the query address. Split each section delimited by '.'.
163 Format of the QNAME section is length|data, etc. Last one is null */
167 for( k
= 0; k
< strlen( pResolve
); k
+= 1 )
169 if( pResolve
[k
] != '.' )
171 Buffer
[i
] = pResolve
[k
];
176 Buffer
[j
] = (i
- j
) - 1;
182 Buffer
[j
] = (i
- j
) - 1;
187 ((PSHORT
)&Buffer
[i
])[0] = htons( Type
);
191 ((PSHORT
)&Buffer
[i
])[0] = htons( CLASS_IN
);
193 /* Ship the request off to the DNS server. */
194 bOk
= SendRequest( Buffer
,
198 if( !bOk
) goto cleanup
;
200 /* Start parsing the received packet. */
201 Header1
= RecBuffer
[2];
202 Header2
= RecBuffer
[3];
203 NumQuestions
= ntohs( ((PSHORT
)&RecBuffer
[4])[0] );
204 NumAnswers
= ntohs( ((PSHORT
)&RecBuffer
[6])[0] );
205 NumAuthority
= ntohs( ((PUSHORT
)&RecBuffer
[8])[0] );
206 NumAdditional
= ntohs( ((PUSHORT
)&RecBuffer
[10])[0] );
210 /* We don't care about the questions section, blow through it. */
213 for( i
= 0; i
< NumQuestions
; i
+= 1 )
215 /* Quick way to skip the domain name section. */
216 k
+= ExtractName( RecBuffer
, pResult
, k
, 0 );
221 /* Skip the answer name. */
222 k
+= ExtractName( RecBuffer
, pResult
, k
, 0 );
224 Type
= ntohs( ((PUSHORT
)&RecBuffer
[k
])[0] );
227 d
= ntohs( ((PUSHORT
)&RecBuffer
[k
])[0] );
230 if( TYPE_PTR
== Type
)
232 k
+= ExtractName( RecBuffer
, pResult
, k
, d
);
234 else if( TYPE_A
== Type
)
236 k
+= ExtractIP( RecBuffer
, pResult
, k
);
241 if( Buffer
) HeapFree( ProcessHeap
, 0, Buffer
);
242 if( RecBuffer
) HeapFree( ProcessHeap
, 0, RecBuffer
);
249 void PerformLookup( PCHAR pAddr
)
251 /* Needed to issue DNS packets and parse them. */
252 PCHAR Buffer
= NULL
, RecBuffer
= NULL
;
255 ULONG BufferLength
= 0, RecBufferLength
= 512;
256 int i
= 0, j
= 0, k
= 0, d
= 0;
259 /* Makes things easier when parsing the response packet. */
260 UCHAR Header1
, Header2
;
264 USHORT NumAdditional
;
267 if( (strlen( pAddr
) + 1) > 255 ) return;
269 _tprintf( _T("Server: %s\n"), State
.DefaultServer
);
270 _tprintf( _T("Address: %s\n\n"), State
.DefaultServerAddress
);
272 if( !strcmp( TypeA
, State
.type
)
273 || !strcmp( TypeAAAA
, State
.type
)
274 || !strcmp( TypeBoth
, State
.type
) )
277 if( IsValidIP( pAddr
) ) Type
= TYPE_PTR
;
280 Type
= TypeNametoTypeID( State
.type
);
282 /* If it's a PTR lookup then append the ARPA sig to the end. */
283 if( (Type
== TYPE_PTR
) && IsValidIP( pAddr
) )
285 ReverseIP( pAddr
, pResolve
);
286 strcat( pResolve
, ARPA_SIG
);
290 strcpy( pResolve
, pAddr
);
293 /* Base header length + length of QNAME + length of QTYPE and QCLASS */
294 BufferLength
= 12 + (strlen( pResolve
) + 2) + 4;
296 /* Allocate memory for the buffer. */
297 Buffer
= HeapAlloc( ProcessHeap
, 0, BufferLength
);
300 _tprintf( _T("ERROR: Out of memory\n") );
304 /* Allocate memory for the return buffer. */
305 RecBuffer
= HeapAlloc( ProcessHeap
, 0, RecBufferLength
);
308 _tprintf( _T("ERROR: Out of memory\n") );
312 /* Insert the ID field. */
313 ((PSHORT
)&Buffer
[i
])[0] = htons( RequestID
);
316 /* Bits 0-7 of the second 16 are all 0, except for when recursion is
319 if( State
.recurse
) Buffer
[i
] |= 0x01;
322 /* Bits 8-15 of the second 16 are 0 for a query. */
326 /* Only 1 question. */
327 ((PSHORT
)&Buffer
[i
])[0] = htons( 1 );
330 /* We aren't sending a response, so 0 out the rest of the header. */
332 Buffer
[i
+ 1] = 0x00;
333 Buffer
[i
+ 2] = 0x00;
334 Buffer
[i
+ 3] = 0x00;
335 Buffer
[i
+ 4] = 0x00;
336 Buffer
[i
+ 5] = 0x00;
339 /* Walk through the query address. Split each section delimited by '.'.
340 Format of the QNAME section is length|data, etc. Last one is null */
344 for( k
= 0; k
< strlen( pResolve
); k
+= 1 )
346 if( pResolve
[k
] != '.' )
348 Buffer
[i
] = pResolve
[k
];
353 Buffer
[j
] = (i
- j
) - 1;
359 Buffer
[j
] = (i
- j
) - 1;
364 ((PSHORT
)&Buffer
[i
])[0] = htons( Type
);
368 ((PSHORT
)&Buffer
[i
])[0] = htons( ClassNametoClassID( State
.Class
) );
370 /* Ship off the request to the DNS server. */
371 bOk
= SendRequest( Buffer
,
375 if( !bOk
) goto cleanup
;
377 /* Start parsing the received packet. */
378 Header1
= RecBuffer
[2];
379 Header2
= RecBuffer
[3];
380 NumQuestions
= ntohs( ((PSHORT
)&RecBuffer
[4])[0] );
381 NumAnswers
= ntohs( ((PSHORT
)&RecBuffer
[6])[0] );
382 NumAuthority
= ntohs( ((PUSHORT
)&RecBuffer
[8])[0] );
383 NumAdditional
= ntohs( ((PUSHORT
)&RecBuffer
[10])[0] );
386 /* Check the RCODE for failure. */
388 if( d
!= RCODE_NOERROR
)
393 _tprintf( _T("*** %s can't find %s: Non-existant domain\n"), State
.DefaultServer
, pAddr
);
397 _tprintf( _T("*** %s can't find %s: Query refused\n"), State
.DefaultServer
, pAddr
);
401 _tprintf( _T("*** %s can't find %s: Unknown RCODE\n"), State
.DefaultServer
, pAddr
);
411 /* Blow through the questions section since we don't care about it. */
412 for( i
= 0; i
< NumQuestions
; i
+= 1 )
414 k
+= ExtractName( RecBuffer
, pResult
, k
, 0 );
422 k
+= ExtractName( RecBuffer
, pResult
, k
, 0 );
424 Type
= ntohs( ((PUSHORT
)&RecBuffer
[k
])[0] );
427 d
= ntohs( ((PUSHORT
)&RecBuffer
[k
])[0] );
430 if( TYPE_PTR
== Type
)
432 k
+= ExtractName( RecBuffer
, pResult
, k
, d
);
434 else if( TYPE_A
== Type
)
436 k
+= ExtractIP( RecBuffer
, pResult
, k
);
440 /* FIXME: This'll need to support more than PTR and A at some point. */
441 if( !strcmp( State
.type
, TypePTR
) )
443 if( TYPE_PTR
== Type
)
445 _tprintf( _T("%s name = %s\n"), pResolve
, pResult
);
451 else if( !strcmp( State
.type
, TypeA
)
452 || !strcmp( State
.type
, TypeAAAA
)
453 || !strcmp( State
.type
, TypeBoth
) )
455 if( (TYPE_A
== Type
) /*|| (TYPE_AAAA == Type)*/ )
457 if( 0 == NumAuthority
)
458 _tprintf( _T("Non-authoritative answer:\n") );
460 _tprintf( _T("Name: %s\n"), pAddr
);
461 _tprintf( _T("Address: %s\n\n"), pResult
);
465 _tprintf( _T("Name: %s\n"), pResult
);
466 _tprintf( _T("Address: %s\n\n"), pAddr
);
472 if( Buffer
) HeapFree( ProcessHeap
, 0, Buffer
);
473 if( RecBuffer
) HeapFree( ProcessHeap
, 0, RecBuffer
);
478 BOOL
ParseCommandLine( int argc
, char* argv
[] )
481 BOOL NoMoreOptions
= FALSE
;
482 BOOL Interactive
= FALSE
;
483 CHAR AddrToResolve
[256];
486 RtlZeroMemory( AddrToResolve
, 256 );
487 RtlZeroMemory( Server
, 256 );
491 /* In the Windows nslookup, usage is only displayed if /? is the only
492 option specified on the command line. */
493 if( !strncmp( "/?", argv
[1], 2 ) )
502 for( i
= 1; i
< argc
; i
+= 1 )
506 strncpy( Server
, argv
[i
], 255 );
508 /* Determine which one to resolve. This is based on whether the
509 DNS server provided was an IP or an FQDN. */
510 if( IsValidIP( Server
) )
512 strncpy( State
.DefaultServerAddress
, Server
, 16 );
514 PerformInternalLookup( State
.DefaultServerAddress
,
515 State
.DefaultServer
);
519 strncpy( State
.DefaultServer
, Server
, 255 );
521 PerformInternalLookup( State
.DefaultServer
,
522 State
.DefaultServerAddress
);
525 if( Interactive
) return 1;
527 PerformLookup( AddrToResolve
);
533 if( !strncmp( "-all", argv
[i
], 4 ) )
537 else if( !strncmp( "-type=", argv
[i
], 6 ) )
539 if( !strncmp( TypeA
, &argv
[i
][6], strlen( TypeA
) ) )
543 else if( !strncmp( TypeAAAA
, &argv
[i
][6], strlen( TypeAAAA
) ) )
545 State
.type
= TypeAAAA
;
547 else if( !strncmp( TypeBoth
, &argv
[i
][6], strlen( TypeBoth
) ) )
549 State
.type
= TypeBoth
;
551 else if( !strncmp( TypeAny
, &argv
[i
][6], strlen( TypeAny
) ) )
553 State
.type
= TypeAny
;
555 else if( !strncmp( TypeCNAME
, &argv
[i
][6], strlen( TypeCNAME
) ) )
557 State
.type
= TypeCNAME
;
559 else if( !strncmp( TypeMX
, &argv
[i
][6], strlen( TypeMX
) ) )
563 else if( !strncmp( TypeNS
, &argv
[i
][6], strlen( TypeNS
) ) )
567 else if( !strncmp( TypePTR
, &argv
[i
][6], strlen( TypePTR
) ) )
569 State
.type
= TypePTR
;
571 else if( !strncmp( TypeSOA
, &argv
[i
][6], strlen( TypeSOA
) ) )
573 State
.type
= TypeSOA
;
575 else if( !strncmp( TypeSRV
, &argv
[i
][6], strlen( TypeSRV
) ) )
577 State
.type
= TypeSRV
;
581 _tprintf( _T("unknown query type: %s"), &argv
[i
][6] );
584 else if( !strncmp( "-domain=", argv
[i
], 8 ) )
586 strcpy( State
.domain
, &argv
[i
][8] );
588 else if( !strncmp( "-srchlist=", argv
[i
], 10 ) )
591 else if( !strncmp( "-root=", argv
[i
], 6 ) )
593 strcpy( State
.root
, &argv
[i
][6] );
595 else if( !strncmp( "-retry=", argv
[i
], 7 ) )
598 else if( !strncmp( "-timeout=", argv
[i
], 9 ) )
601 else if( !strncmp( "-querytype=", argv
[i
], 11 ) )
603 if( !strncmp( TypeA
, &argv
[i
][11], strlen( TypeA
) ) )
607 else if( !strncmp( TypeAAAA
, &argv
[i
][11], strlen( TypeAAAA
) ) )
609 State
.type
= TypeAAAA
;
611 else if( !strncmp( TypeBoth
, &argv
[i
][11], strlen( TypeBoth
) ) )
613 State
.type
= TypeBoth
;
615 else if( !strncmp( TypeAny
, &argv
[i
][11], strlen( TypeAny
) ) )
617 State
.type
= TypeAny
;
619 else if( !strncmp( TypeCNAME
, &argv
[i
][11], strlen( TypeCNAME
) ) )
621 State
.type
= TypeCNAME
;
623 else if( !strncmp( TypeMX
, &argv
[i
][11], strlen( TypeMX
) ) )
627 else if( !strncmp( TypeNS
, &argv
[i
][11], strlen( TypeNS
) ) )
631 else if( !strncmp( TypePTR
, &argv
[i
][11], strlen( TypePTR
) ) )
633 State
.type
= TypePTR
;
635 else if( !strncmp( TypeSOA
, &argv
[i
][11], strlen( TypeSOA
) ) )
637 State
.type
= TypeSOA
;
639 else if( !strncmp( TypeSRV
, &argv
[i
][11], strlen( TypeSRV
) ) )
641 State
.type
= TypeSRV
;
645 _tprintf( _T("unknown query type: %s"), &argv
[i
][6] );
648 else if( !strncmp( "-class=", argv
[i
], 7 ) )
650 if( !strncmp( ClassIN
, &argv
[i
][7], strlen( ClassIN
) ) )
652 State
.Class
= ClassIN
;
654 else if( !strncmp( ClassAny
, &argv
[i
][7], strlen( ClassAny
) ) )
656 State
.Class
= ClassAny
;
660 _tprintf( _T("unknown query class: %s"), &argv
[i
][7] );
663 else if( !strncmp( "-ixfrver=", argv
[i
], 9 ) )
666 else if( !strncmp( "-debug", argv
[i
], 6 ) )
670 else if( !strncmp( "-nodebug", argv
[i
], 8 ) )
675 else if( !strncmp( "-d2", argv
[i
], 3 ) )
680 else if( !strncmp( "-nod2", argv
[i
], 5 ) )
682 if( State
.debug
) _tprintf( _T("d2 mode disabled; still in debug mode\n") );
686 else if( !strncmp( "-defname", argv
[i
], 8 ) )
688 State
.defname
= TRUE
;
690 else if( !strncmp( "-noddefname", argv
[i
], 10 ) )
692 State
.defname
= FALSE
;
694 else if( !strncmp( "-recurse", argv
[i
], 8 ) )
696 State
.recurse
= TRUE
;
698 else if( !strncmp( "-norecurse", argv
[i
], 10 ) )
700 State
.recurse
= FALSE
;
702 else if( !strncmp( "-search", argv
[i
], 7 ) )
706 else if( !strncmp( "-nosearch", argv
[i
], 9 ) )
708 State
.search
= FALSE
;
710 else if( !strncmp( "-vc", argv
[i
], 3 ) )
714 else if( !strncmp( "-novc", argv
[i
], 5 ) )
718 else if( !strncmp( "-msxfr", argv
[i
], 6 ) )
722 else if( !strncmp( "-nomsxfr", argv
[i
], 8 ) )
726 else if( !strncmp( "-", argv
[i
], 1 ) && (strlen( argv
[i
] ) == 1) )
728 /* Since we received just the plain - switch, we are going
729 to be entering interactive mode. We also will not be
730 parsing any more options. */
731 NoMoreOptions
= TRUE
;
736 /* Grab the address to resolve. No more options accepted
738 strncpy( AddrToResolve
, argv
[i
], 255 );
739 NoMoreOptions
= TRUE
;
744 if( NoMoreOptions
&& !Interactive
)
746 /* Get the FQDN of the DNS server. */
747 PerformInternalLookup( State
.DefaultServerAddress
,
748 State
.DefaultServer
);
750 PerformLookup( AddrToResolve
);
756 /* Get the FQDN of the DNS server. */
757 PerformInternalLookup( State
.DefaultServerAddress
,
758 State
.DefaultServer
);
763 void InteractiveMode()
765 _tprintf( _T("Default Server: %s\n"), State
.DefaultServer
);
766 _tprintf( _T("Address: %s\n\n"), State
.DefaultServerAddress
);
768 /* TODO: Implement interactive mode. */
770 _tprintf( _T("ERROR: Feature not implemented.\n") );
773 int main( int argc
, char* argv
[] )
777 PFIXED_INFO pNetInfo
= NULL
;
781 ProcessHeap
= GetProcessHeap();
784 /* Set up the initial state. */
786 State
.defname
= TRUE
;
788 State
.recurse
= TRUE
;
791 State
.ignoretc
= FALSE
;
793 State
.type
= TypeBoth
;
794 State
.Class
= ClassIN
;
800 RtlZeroMemory( State
.root
, 256 );
801 RtlZeroMemory( State
.domain
, 256 );
802 for( i
= 0; i
< 6; i
+= 1 ) RtlZeroMemory( State
.srchlist
[i
], 256 );
803 RtlZeroMemory( State
.DefaultServer
, 256 );
804 RtlZeroMemory( State
.DefaultServerAddress
, 16 );
806 strncpy( State
.root
, DEFAULT_ROOT
, strlen( DEFAULT_ROOT
) );
808 /* We don't know how long of a buffer it will want to return. So we'll
809 pass an empty one now and let it fail only once, instead of guessing. */
810 Status
= GetNetworkParams( pNetInfo
, &NetBufLen
);
811 if( Status
== ERROR_BUFFER_OVERFLOW
)
813 pNetInfo
= (PFIXED_INFO
)HeapAlloc( ProcessHeap
, 0, NetBufLen
);
814 if( pNetInfo
== NULL
)
816 _tprintf( _T("ERROR: Out of memory\n") );
821 /* For real this time. */
822 Status
= GetNetworkParams( pNetInfo
, &NetBufLen
);
823 if( Status
!= NO_ERROR
)
825 _tprintf( _T("Error in GetNetworkParams call\n") );
827 HeapFree( ProcessHeap
, 0, pNetInfo
);
833 strncpy( State
.domain
, pNetInfo
->DomainName
, 255 );
834 strncpy( State
.srchlist
[0], pNetInfo
->DomainName
, 255 );
835 strncpy( State
.DefaultServerAddress
,
836 pNetInfo
->DnsServerList
.IpAddress
.String
,
839 HeapFree( ProcessHeap
, 0, pNetInfo
);
841 WSAStartup( MAKEWORD(2,2), &wsaData
);
843 switch( ParseCommandLine( argc
, argv
) )
846 /* This means that it was a /? parameter. */
850 /* Anything else means we enter interactive mode. The only exception
851 to this is when the host to resolve was provided on the command