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. */
96 if( (strlen( pAddr
) + 1) > 255 ) return FALSE
;
99 if( IsValidIP( pAddr
) ) Type
= TYPE_PTR
;
101 /* If it's a PTR lookup then append the ARPA sig to the end. */
102 if( Type
== TYPE_PTR
)
104 ReverseIP( pAddr
, pResolve
);
105 strcat( pResolve
, ARPA_SIG
);
109 strcpy( pResolve
, pAddr
);
112 /* Base header length + length of QNAME + length of QTYPE and QCLASS */
113 BufferLength
= 12 + (strlen( pResolve
) + 2) + 4;
115 /* Allocate memory for the buffer. */
116 Buffer
= HeapAlloc( ProcessHeap
, 0, BufferLength
);
119 _tprintf( _T("ERROR: Out of memory\n") );
123 /* Allocate the receiving buffer. */
124 RecBuffer
= HeapAlloc( ProcessHeap
, 0, RecBufferLength
);
127 _tprintf( _T("ERROR: Out of memory\n") );
131 /* Insert the ID field. */
132 ((PSHORT
)&Buffer
[i
])[0] = htons( RequestID
);
135 /* Bits 0-7 of the second 16 are all 0, except for when recursion is
138 if( State
.recurse
) Buffer
[i
] |= 0x01;
141 /* Bits 8-15 of the second 16 are 0 for a query. */
145 /* Only 1 question. */
146 ((PSHORT
)&Buffer
[i
])[0] = htons( 1 );
149 /* We aren't sending a response, so 0 out the rest of the header. */
151 Buffer
[i
+ 1] = 0x00;
152 Buffer
[i
+ 2] = 0x00;
153 Buffer
[i
+ 3] = 0x00;
154 Buffer
[i
+ 4] = 0x00;
155 Buffer
[i
+ 5] = 0x00;
158 /* Walk through the query address. Split each section delimited by '.'.
159 Format of the QNAME section is length|data, etc. Last one is null */
163 for( k
= 0; k
< strlen( pResolve
); k
+= 1 )
165 if( pResolve
[k
] != '.' )
167 Buffer
[i
] = pResolve
[k
];
172 Buffer
[j
] = (i
- j
) - 1;
178 Buffer
[j
] = (i
- j
) - 1;
183 ((PSHORT
)&Buffer
[i
])[0] = htons( Type
);
187 ((PSHORT
)&Buffer
[i
])[0] = htons( CLASS_IN
);
189 /* Ship the request off to the DNS server. */
190 bOk
= SendRequest( Buffer
,
194 if( !bOk
) goto cleanup
;
196 /* Start parsing the received packet. */
197 NumQuestions
= ntohs( ((PSHORT
)&RecBuffer
[4])[0] );
201 /* We don't care about the questions section, blow through it. */
204 for( i
= 0; i
< NumQuestions
; i
+= 1 )
206 /* Quick way to skip the domain name section. */
207 k
+= ExtractName( RecBuffer
, pResult
, k
, 0 );
212 /* Skip the answer name. */
213 k
+= ExtractName( RecBuffer
, pResult
, k
, 0 );
215 Type
= ntohs( ((PUSHORT
)&RecBuffer
[k
])[0] );
218 d
= ntohs( ((PUSHORT
)&RecBuffer
[k
])[0] );
221 if( TYPE_PTR
== Type
)
223 k
+= ExtractName( RecBuffer
, pResult
, k
, d
);
225 else if( TYPE_A
== Type
)
227 k
+= ExtractIP( RecBuffer
, pResult
, k
);
232 if( Buffer
) HeapFree( ProcessHeap
, 0, Buffer
);
233 if( RecBuffer
) HeapFree( ProcessHeap
, 0, RecBuffer
);
240 void PerformLookup( PCHAR pAddr
)
242 /* Needed to issue DNS packets and parse them. */
243 PCHAR Buffer
= NULL
, RecBuffer
= NULL
;
246 ULONG BufferLength
= 0, RecBufferLength
= 512;
247 int i
= 0, j
= 0, k
= 0, d
= 0;
250 /* Makes things easier when parsing the response packet. */
257 if( (strlen( pAddr
) + 1) > 255 ) return;
259 _tprintf( _T("Server: %s\n"), State
.DefaultServer
);
260 _tprintf( _T("Address: %s\n\n"), State
.DefaultServerAddress
);
262 if( !strcmp( TypeA
, State
.type
)
263 || !strcmp( TypeAAAA
, State
.type
)
264 || !strcmp( TypeBoth
, State
.type
) )
267 if( IsValidIP( pAddr
) ) Type
= TYPE_PTR
;
270 Type
= TypeNametoTypeID( State
.type
);
272 /* If it's a PTR lookup then append the ARPA sig to the end. */
273 if( (Type
== TYPE_PTR
) && IsValidIP( pAddr
) )
275 ReverseIP( pAddr
, pResolve
);
276 strcat( pResolve
, ARPA_SIG
);
280 strcpy( pResolve
, pAddr
);
283 /* Base header length + length of QNAME + length of QTYPE and QCLASS */
284 BufferLength
= 12 + (strlen( pResolve
) + 2) + 4;
286 /* Allocate memory for the buffer. */
287 Buffer
= HeapAlloc( ProcessHeap
, 0, BufferLength
);
290 _tprintf( _T("ERROR: Out of memory\n") );
294 /* Allocate memory for the return buffer. */
295 RecBuffer
= HeapAlloc( ProcessHeap
, 0, RecBufferLength
);
298 _tprintf( _T("ERROR: Out of memory\n") );
302 /* Insert the ID field. */
303 ((PSHORT
)&Buffer
[i
])[0] = htons( RequestID
);
306 /* Bits 0-7 of the second 16 are all 0, except for when recursion is
309 if( State
.recurse
) Buffer
[i
] |= 0x01;
312 /* Bits 8-15 of the second 16 are 0 for a query. */
316 /* Only 1 question. */
317 ((PSHORT
)&Buffer
[i
])[0] = htons( 1 );
320 /* We aren't sending a response, so 0 out the rest of the header. */
322 Buffer
[i
+ 1] = 0x00;
323 Buffer
[i
+ 2] = 0x00;
324 Buffer
[i
+ 3] = 0x00;
325 Buffer
[i
+ 4] = 0x00;
326 Buffer
[i
+ 5] = 0x00;
329 /* Walk through the query address. Split each section delimited by '.'.
330 Format of the QNAME section is length|data, etc. Last one is null */
334 for( k
= 0; k
< strlen( pResolve
); k
+= 1 )
336 if( pResolve
[k
] != '.' )
338 Buffer
[i
] = pResolve
[k
];
343 Buffer
[j
] = (i
- j
) - 1;
349 Buffer
[j
] = (i
- j
) - 1;
354 ((PSHORT
)&Buffer
[i
])[0] = htons( Type
);
358 ((PSHORT
)&Buffer
[i
])[0] = htons( ClassNametoClassID( State
.Class
) );
360 /* Ship off the request to the DNS server. */
361 bOk
= SendRequest( Buffer
,
365 if( !bOk
) goto cleanup
;
367 /* Start parsing the received packet. */
368 Header2
= RecBuffer
[3];
369 NumQuestions
= ntohs( ((PSHORT
)&RecBuffer
[4])[0] );
370 NumAnswers
= ntohs( ((PSHORT
)&RecBuffer
[6])[0] );
371 NumAuthority
= ntohs( ((PUSHORT
)&RecBuffer
[8])[0] );
374 /* Check the RCODE for failure. */
376 if( d
!= RCODE_NOERROR
)
381 _tprintf( _T("*** %s can't find %s: Non-existant domain\n"), State
.DefaultServer
, pAddr
);
385 _tprintf( _T("*** %s can't find %s: Query refused\n"), State
.DefaultServer
, pAddr
);
389 _tprintf( _T("*** %s can't find %s: Unknown RCODE\n"), State
.DefaultServer
, pAddr
);
399 /* Blow through the questions section since we don't care about it. */
400 for( i
= 0; i
< NumQuestions
; i
+= 1 )
402 k
+= ExtractName( RecBuffer
, pResult
, k
, 0 );
410 k
+= ExtractName( RecBuffer
, pResult
, k
, 0 );
412 Type
= ntohs( ((PUSHORT
)&RecBuffer
[k
])[0] );
415 d
= ntohs( ((PUSHORT
)&RecBuffer
[k
])[0] );
418 if( TYPE_PTR
== Type
)
420 k
+= ExtractName( RecBuffer
, pResult
, k
, d
);
422 else if( TYPE_A
== Type
)
424 k
+= ExtractIP( RecBuffer
, pResult
, k
);
428 /* FIXME: This'll need to support more than PTR and A at some point. */
429 if( !strcmp( State
.type
, TypePTR
) )
431 if( TYPE_PTR
== Type
)
433 _tprintf( _T("%s name = %s\n"), pResolve
, pResult
);
439 else if( !strcmp( State
.type
, TypeA
)
440 || !strcmp( State
.type
, TypeAAAA
)
441 || !strcmp( State
.type
, TypeBoth
) )
443 if( (TYPE_A
== Type
) /*|| (TYPE_AAAA == Type)*/ )
445 if( 0 == NumAuthority
)
446 _tprintf( _T("Non-authoritative answer:\n") );
448 _tprintf( _T("Name: %s\n"), pAddr
);
449 _tprintf( _T("Address: %s\n\n"), pResult
);
453 _tprintf( _T("Name: %s\n"), pResult
);
454 _tprintf( _T("Address: %s\n\n"), pAddr
);
460 if( Buffer
) HeapFree( ProcessHeap
, 0, Buffer
);
461 if( RecBuffer
) HeapFree( ProcessHeap
, 0, RecBuffer
);
466 BOOL
ParseCommandLine( int argc
, char* argv
[] )
469 BOOL NoMoreOptions
= FALSE
;
470 BOOL Interactive
= FALSE
;
471 CHAR AddrToResolve
[256];
474 RtlZeroMemory( AddrToResolve
, 256 );
475 RtlZeroMemory( Server
, 256 );
479 /* In the Windows nslookup, usage is only displayed if /? is the only
480 option specified on the command line. */
481 if( !strncmp( "/?", argv
[1], 2 ) )
490 for( i
= 1; i
< argc
; i
+= 1 )
494 strncpy( Server
, argv
[i
], 255 );
496 /* Determine which one to resolve. This is based on whether the
497 DNS server provided was an IP or an FQDN. */
498 if( IsValidIP( Server
) )
500 strncpy( State
.DefaultServerAddress
, Server
, 16 );
502 PerformInternalLookup( State
.DefaultServerAddress
,
503 State
.DefaultServer
);
507 strncpy( State
.DefaultServer
, Server
, 255 );
509 PerformInternalLookup( State
.DefaultServer
,
510 State
.DefaultServerAddress
);
513 if( Interactive
) return 1;
515 PerformLookup( AddrToResolve
);
521 if( !strncmp( "-all", argv
[i
], 4 ) )
525 else if( !strncmp( "-type=", argv
[i
], 6 ) )
527 if( !strncmp( TypeA
, &argv
[i
][6], strlen( TypeA
) ) )
531 else if( !strncmp( TypeAAAA
, &argv
[i
][6], strlen( TypeAAAA
) ) )
533 State
.type
= TypeAAAA
;
535 else if( !strncmp( TypeBoth
, &argv
[i
][6], strlen( TypeBoth
) ) )
537 State
.type
= TypeBoth
;
539 else if( !strncmp( TypeAny
, &argv
[i
][6], strlen( TypeAny
) ) )
541 State
.type
= TypeAny
;
543 else if( !strncmp( TypeCNAME
, &argv
[i
][6], strlen( TypeCNAME
) ) )
545 State
.type
= TypeCNAME
;
547 else if( !strncmp( TypeMX
, &argv
[i
][6], strlen( TypeMX
) ) )
551 else if( !strncmp( TypeNS
, &argv
[i
][6], strlen( TypeNS
) ) )
555 else if( !strncmp( TypePTR
, &argv
[i
][6], strlen( TypePTR
) ) )
557 State
.type
= TypePTR
;
559 else if( !strncmp( TypeSOA
, &argv
[i
][6], strlen( TypeSOA
) ) )
561 State
.type
= TypeSOA
;
563 else if( !strncmp( TypeSRV
, &argv
[i
][6], strlen( TypeSRV
) ) )
565 State
.type
= TypeSRV
;
569 _tprintf( _T("unknown query type: %s"), &argv
[i
][6] );
572 else if( !strncmp( "-domain=", argv
[i
], 8 ) )
574 strcpy( State
.domain
, &argv
[i
][8] );
576 else if( !strncmp( "-srchlist=", argv
[i
], 10 ) )
579 else if( !strncmp( "-root=", argv
[i
], 6 ) )
581 strcpy( State
.root
, &argv
[i
][6] );
583 else if( !strncmp( "-retry=", argv
[i
], 7 ) )
586 else if( !strncmp( "-timeout=", argv
[i
], 9 ) )
589 else if( !strncmp( "-querytype=", argv
[i
], 11 ) )
591 if( !strncmp( TypeA
, &argv
[i
][11], strlen( TypeA
) ) )
595 else if( !strncmp( TypeAAAA
, &argv
[i
][11], strlen( TypeAAAA
) ) )
597 State
.type
= TypeAAAA
;
599 else if( !strncmp( TypeBoth
, &argv
[i
][11], strlen( TypeBoth
) ) )
601 State
.type
= TypeBoth
;
603 else if( !strncmp( TypeAny
, &argv
[i
][11], strlen( TypeAny
) ) )
605 State
.type
= TypeAny
;
607 else if( !strncmp( TypeCNAME
, &argv
[i
][11], strlen( TypeCNAME
) ) )
609 State
.type
= TypeCNAME
;
611 else if( !strncmp( TypeMX
, &argv
[i
][11], strlen( TypeMX
) ) )
615 else if( !strncmp( TypeNS
, &argv
[i
][11], strlen( TypeNS
) ) )
619 else if( !strncmp( TypePTR
, &argv
[i
][11], strlen( TypePTR
) ) )
621 State
.type
= TypePTR
;
623 else if( !strncmp( TypeSOA
, &argv
[i
][11], strlen( TypeSOA
) ) )
625 State
.type
= TypeSOA
;
627 else if( !strncmp( TypeSRV
, &argv
[i
][11], strlen( TypeSRV
) ) )
629 State
.type
= TypeSRV
;
633 _tprintf( _T("unknown query type: %s"), &argv
[i
][6] );
636 else if( !strncmp( "-class=", argv
[i
], 7 ) )
638 if( !strncmp( ClassIN
, &argv
[i
][7], strlen( ClassIN
) ) )
640 State
.Class
= ClassIN
;
642 else if( !strncmp( ClassAny
, &argv
[i
][7], strlen( ClassAny
) ) )
644 State
.Class
= ClassAny
;
648 _tprintf( _T("unknown query class: %s"), &argv
[i
][7] );
651 else if( !strncmp( "-ixfrver=", argv
[i
], 9 ) )
654 else if( !strncmp( "-debug", argv
[i
], 6 ) )
658 else if( !strncmp( "-nodebug", argv
[i
], 8 ) )
663 else if( !strncmp( "-d2", argv
[i
], 3 ) )
668 else if( !strncmp( "-nod2", argv
[i
], 5 ) )
670 if( State
.debug
) _tprintf( _T("d2 mode disabled; still in debug mode\n") );
674 else if( !strncmp( "-defname", argv
[i
], 8 ) )
676 State
.defname
= TRUE
;
678 else if( !strncmp( "-noddefname", argv
[i
], 10 ) )
680 State
.defname
= FALSE
;
682 else if( !strncmp( "-recurse", argv
[i
], 8 ) )
684 State
.recurse
= TRUE
;
686 else if( !strncmp( "-norecurse", argv
[i
], 10 ) )
688 State
.recurse
= FALSE
;
690 else if( !strncmp( "-search", argv
[i
], 7 ) )
694 else if( !strncmp( "-nosearch", argv
[i
], 9 ) )
696 State
.search
= FALSE
;
698 else if( !strncmp( "-vc", argv
[i
], 3 ) )
702 else if( !strncmp( "-novc", argv
[i
], 5 ) )
706 else if( !strncmp( "-msxfr", argv
[i
], 6 ) )
710 else if( !strncmp( "-nomsxfr", argv
[i
], 8 ) )
714 else if( !strncmp( "-", argv
[i
], 1 ) && (strlen( argv
[i
] ) == 1) )
716 /* Since we received just the plain - switch, we are going
717 to be entering interactive mode. We also will not be
718 parsing any more options. */
719 NoMoreOptions
= TRUE
;
724 /* Grab the address to resolve. No more options accepted
726 strncpy( AddrToResolve
, argv
[i
], 255 );
727 NoMoreOptions
= TRUE
;
732 if( NoMoreOptions
&& !Interactive
)
734 /* Get the FQDN of the DNS server. */
735 PerformInternalLookup( State
.DefaultServerAddress
,
736 State
.DefaultServer
);
738 PerformLookup( AddrToResolve
);
744 /* Get the FQDN of the DNS server. */
745 PerformInternalLookup( State
.DefaultServerAddress
,
746 State
.DefaultServer
);
751 void InteractiveMode()
753 _tprintf( _T("Default Server: %s\n"), State
.DefaultServer
);
754 _tprintf( _T("Address: %s\n\n"), State
.DefaultServerAddress
);
756 /* TODO: Implement interactive mode. */
758 _tprintf( _T("ERROR: Feature not implemented.\n") );
761 int main( int argc
, char* argv
[] )
765 PFIXED_INFO pNetInfo
= NULL
;
769 ProcessHeap
= GetProcessHeap();
772 /* Set up the initial state. */
774 State
.defname
= TRUE
;
776 State
.recurse
= TRUE
;
779 State
.ignoretc
= FALSE
;
781 State
.type
= TypeBoth
;
782 State
.Class
= ClassIN
;
788 RtlZeroMemory( State
.root
, 256 );
789 RtlZeroMemory( State
.domain
, 256 );
790 for( i
= 0; i
< 6; i
+= 1 ) RtlZeroMemory( State
.srchlist
[i
], 256 );
791 RtlZeroMemory( State
.DefaultServer
, 256 );
792 RtlZeroMemory( State
.DefaultServerAddress
, 16 );
794 memcpy( State
.root
, DEFAULT_ROOT
, sizeof(DEFAULT_ROOT
) );
796 /* We don't know how long of a buffer it will want to return. So we'll
797 pass an empty one now and let it fail only once, instead of guessing. */
798 Status
= GetNetworkParams( pNetInfo
, &NetBufLen
);
799 if( Status
== ERROR_BUFFER_OVERFLOW
)
801 pNetInfo
= (PFIXED_INFO
)HeapAlloc( ProcessHeap
, 0, NetBufLen
);
802 if( pNetInfo
== NULL
)
804 _tprintf( _T("ERROR: Out of memory\n") );
809 /* For real this time. */
810 Status
= GetNetworkParams( pNetInfo
, &NetBufLen
);
811 if( Status
!= NO_ERROR
)
813 _tprintf( _T("Error in GetNetworkParams call\n") );
815 HeapFree( ProcessHeap
, 0, pNetInfo
);
821 strncpy( State
.domain
, pNetInfo
->DomainName
, 255 );
822 strncpy( State
.srchlist
[0], pNetInfo
->DomainName
, 255 );
823 strncpy( State
.DefaultServerAddress
,
824 pNetInfo
->DnsServerList
.IpAddress
.String
,
827 HeapFree( ProcessHeap
, 0, pNetInfo
);
829 WSAStartup( MAKEWORD(2,2), &wsaData
);
831 switch( ParseCommandLine( argc
, argv
) )
834 /* This means that it was a /? parameter. */
838 /* Anything else means we enter interactive mode. The only exception
839 to this is when the host to resolve was provided on the command