* Copyright (c) 1985 Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
"@(#) Copyright (c) 1985 Regents of the University of California.\n\
static char sccsid
[] = "@(#)main.c 5.23 (Berkeley) 11/17/88";
*******************************************************************************
* Main routine and some action routines for the name server
* U.C. Berkeley Computer Science Div.
*******************************************************************************
#include <arpa/nameser.h>
* Location of the help file.
#define HELPFILE "/usr/local/nslookup.help"
* Default Internet address of the current host.
#define LOCALHOST "127.0.0.1"
* Name of a top-level name server. Can be changed with
* the "set root" command.
#define ROOT_SERVER "sri-nic.arpa."
char rootServerName
[NAME_LEN
];
* Import the state information from the resolver library.
extern struct state _res
;
* Info about the most recently queried host.
int curHostValid
= FALSE
;
* Info about the default name server.
HostInfo
*defaultPtr
= NULL
;
char defaultServer
[NAME_LEN
];
struct in_addr defaultAddr
;
* Initial name server query type is Address.
* Stuff for Interrupt (control-C) signal handler.
* SockFD is the file descriptor for sockets used to
* connect with the name servers. It has to be global to
* allow the interrupt handler can close open sockets.
extern int IntrHandler();
static void CvtAddrToPtr();
*******************************************************************************
* Initializes the resolver library and determines the address
* of the initial name server. The yylex routine is used to
* read and perform commands.
*******************************************************************************
* Initialize the resolver library routines.
fprintf(stderr
,"*** Can't initialize resolver.\n");
* Allocate space for the default server's host info and
* find the server's address and name. If the resolver library
* already has some addresses for a potential name server,
* then use them. Otherwise, see if the current host has a server.
* Command line arguments may override the choice of initial server.
defaultPtr
= (HostInfo
*) Calloc(1, sizeof(HostInfo
));
* no args = go into interactive mode, use default host as server
* 1 arg = use as host name to be looked up, default host will be server
* ignore but go into interactive mode
* use as host name to be looked up,
* go into non-interactive mode
* 2nd arg: name or inet address of server
* "Set" options are specified with a leading - and must come before
* any arguments. For example, to find the well-known services for
* a host, type "nslookup -query=wks host"
++argv
; --argc
; /* skip prog name */
while (argc
&& *argv
[0] == '-' && argv
[0][1]) {
(void) SetOption (&(argv
[0][1]));
if (argc
&& *argv
[0] != '-') {
wantedHost
= *argv
; /* name of host to be looked up */
* Set explicit name server address.
_res
.nsaddr
.sin_addr
.s_addr
= inet_addr(*++argv
);
if (_res
.nsaddr
.sin_addr
.s_addr
== (unsigned)-1) {
hp
= gethostbyname(*argv
);
fprintf(stderr
, "nslookup: %s: ", *argv
);
bcopy(hp
->h_addr
, &_res
.nsaddr
.sin_addr
, hp
->h_length
);
bcopy(hp
->h_addr_list
[0], &_res
.nsaddr
.sin_addr
, hp
->h_length
);
if (_res
.nscount
== 0 || useLocalServer
) {
for (i
= 0; i
< _res
.nscount
; i
++) {
if (_res
.nsaddr_list
[i
].sin_addr
.s_addr
== INADDR_ANY
) {
result
= FindHostInfo(&(_res
.nsaddr_list
[i
].sin_addr
),
&(_res
.nsaddr_list
[i
].sin_addr
),
"*** Can't find server name for address %s: %s\n",
inet_ntoa(_res
.nsaddr_list
[i
].sin_addr
),
defaultAddr
= _res
.nsaddr_list
[i
].sin_addr
;
* If we have exhausted the list, tell the user about the
* command line argument to specify an address.
"*** Default servers are not available\n");
strcpy(defaultServer
, defaultPtr
->name
);
strcpy(rootServerName
, ROOT_SERVER
);
_res
.options
|= RES_DEBUG2
;
_res
.options
|= RES_DEBUG
;
* If we're in non-interactive mode, look up the wanted host and quit.
* Otherwise, print the initial server's name and continue with
if (wantedHost
!= (char *) NULL
) {
LookupHost(wantedHost
, 0);
PrintHostInfo(stdout
, "Default Server:", defaultPtr
);
* Setup the environment to allow the interrupt handler to return here.
* Return here after a longjmp.
signal(SIGINT
, IntrHandler
);
signal(SIGPIPE
, SIG_IGN
);
* Read and evaluate commands. The commands are described in commands.l
* Yylex returns 0 when ^D or 'exit' is typed.
gethostname(hostName
, sizeof(hostName
));
defaultAddr
.s_addr
= inet_addr(LOCALHOST
);
result
= GetHostInfo(&defaultAddr
, C_IN
, T_A
, hostName
, defaultPtr
, 1);
"*** Can't find initialize address for server %s: %s\n",
defaultServer
, DecodeError(result
));
defaultAddr
.s_addr
= htonl(INADDR_ANY
);
(void) GetHostInfo(&defaultAddr
, C_IN
, T_A
, "0.0.0.0", defaultPtr
, 1);
defaultPtr
->name
= Calloc(1, sizeof(hostName
)+1);
strcpy(defaultPtr
->name
, hostName
);
*******************************************************************************
* Lists the proper methods to run the program and exits.
*******************************************************************************
fprintf(stderr
, "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");
*******************************************************************************
* Changes the default name server to the one specified by
* the first argument. The command "server name" uses the current
* default server to lookup the info for "name". The command
* "lserver name" uses the original server to lookup "name".
* This routine will cause a core dump if the allocation requests fail.
* SUCCESS The default server was changed successfully.
* NONAUTH The server was changed but addresses of
* other servers who know about the requested server
* Errors No info about the new server was found or
* requests to the current server timed-out.
*******************************************************************************
SetDefaultServer(string
, local
)
register HostInfo
*newDefPtr
;
char newServer
[NAME_LEN
];
* Parse the command line. It maybe of the form "server name",
* "lserver name" or just "name".
i
= sscanf(string
, " lserver %s", newServer
);
i
= sscanf(string
, " server %s", newServer
);
i
= sscanf(string
, " %s", newServer
);
fprintf(stderr
,"SetDefaultServer: invalid name: %s\n", string
);
* Allocate space for a HostInfo variable for the new server. Don't
* overwrite the old HostInfo struct because info about the new server
* might not be found and we need to have valid default server info.
newDefPtr
= (HostInfo
*) Calloc(1, sizeof(HostInfo
));
* A 'local' lookup uses the original server that the program was
result
= GetHostInfo(&defaultAddr
, C_IN
, T_A
, newServer
, newDefPtr
, 1);
* Check to see if we have the address of the server or the
* address of a server who knows about this domain.
* For now, just use the first address in the list.
if (defaultPtr
->addrList
== NULL
) {
(struct in_addr
*) defaultPtr
->servers
[0]->addrList
[0],
C_IN
, T_A
, newServer
, newDefPtr
, 1);
result
= GetHostInfo((struct in_addr
*) defaultPtr
->addrList
[0],
C_IN
, T_A
, newServer
, newDefPtr
, 1);
if (result
== SUCCESS
|| result
== NONAUTH
) {
* Found info about the new server. Free the resources for
FreeHostInfoPtr(defaultPtr
);
free((char *)defaultPtr
);
strcpy(defaultServer
, defaultPtr
->name
);
PrintHostInfo(stdout
, "Default Server:", defaultPtr
);
fprintf(stderr
, "*** Can't find address for server %s: %s\n",
newServer
, DecodeError(result
));
*******************************************************************************
* Asks the default name server for information about the
* specified host or domain. The information is printed
* if the lookup was successful.
* SUCCESS - the lookup was successful.
* ERROR - the output file could not be opened.
* Misc. Errors - an error message is printed if the lookup failed.
*******************************************************************************
LookupHost(string
, putToFile
)
* Invalidate the current host information to prevent Finger
* Parse the command string into the host and
* optional output file name.
sscanf(string
, " %s", host
); /* removes white space */
filePtr
= OpenFile(string
, file
);
fprintf(stderr
, "*** Can't open %s for writing\n", file
);
fprintf(filePtr
,"> %s\n", string
);
PrintHostInfo(filePtr
, "Server:", defaultPtr
);
* Check to see if we have the address of the server or the
* address of a server who knows about this domain.
* For now, just use the first address in the list.
if (queryType
== T_PTR
) {
if (defaultPtr
->addrList
== NULL
) {
(struct in_addr
*) defaultPtr
->servers
[0]->addrList
[0],
queryClass
, queryType
, host
, &curHostInfo
, 0);
result
= GetHostInfo((struct in_addr
*) defaultPtr
->addrList
[0],
queryClass
, queryType
, host
, &curHostInfo
, 0);
* If the query was for an address, then the curHostInfo
* variable can be used by Finger.
* There's no need to print anything for other query types
* because the info has already been printed.
PrintHostInfo(filePtr
, "Name:", &curHostInfo
);
* No Authoritative answer was available but we got names
* of servers who know about the host.
PrintHostInfo(filePtr
, "Name:", &curHostInfo
);
fprintf(stderr
, "*** No %s information is available for %s\n",
DecodeType(queryType
), host
);
fprintf(stderr
, "*** Request to %s timed-out\n", defaultServer
);
fprintf(stderr
, "*** %s can't find %s: %s\n", defaultServer
, host
,
*******************************************************************************
* LookupHostWithServer --
* Asks the name server specified in the second argument for
* information about the host or domain specified in the first
* argument. The information is printed if the lookup was successful.
* Address info about the requested name server is obtained
* from the default name server. This routine will return an
* error if the default server doesn't have info about the
* requested server. Thus an error return status might not
* mean the requested name server doesn't have info about the
* Comments from LookupHost apply here, too.
* SUCCESS - the lookup was successful.
* ERROR - the output file could not be opened.
* Misc. Errors - an error message is printed if the lookup failed.
*******************************************************************************
LookupHostWithServer(string
, putToFile
)
static HostInfo serverInfo
;
sscanf(string
, " %s %s", host
, server
);
filePtr
= OpenFile(string
, file
);
fprintf(stderr
, "*** Can't open %s for writing\n", file
);
fprintf(filePtr
,"> %s\n", string
);
if (defaultPtr
->addrList
== NULL
) {
(struct in_addr
*) defaultPtr
->servers
[0]->addrList
[0],
C_IN
, T_A
, server
, &serverInfo
, 1);
result
= GetHostInfo((struct in_addr
*) defaultPtr
->addrList
[0],
C_IN
, T_A
, server
, &serverInfo
, 1);
fprintf(stderr
,"*** Can't find address for server %s: %s\n", server
,
PrintHostInfo(filePtr
, "Server:", &serverInfo
);
if (queryType
== T_PTR
) {
if (serverInfo
.addrList
== NULL
) {
(struct in_addr
*) serverInfo
.servers
[0]->addrList
[0],
queryClass
, queryType
, host
, &curHostInfo
, 0);
result
= GetHostInfo((struct in_addr
*) serverInfo
.addrList
[0],
queryClass
, queryType
, host
, &curHostInfo
, 0);
PrintHostInfo(filePtr
, "Name:", &curHostInfo
);
PrintHostInfo(filePtr
, "Name:", &curHostInfo
);
fprintf(stderr
, "*** No %s information is available for %s\n",
DecodeType(queryType
), host
);
fprintf(stderr
, "*** Request to %s timed-out\n", server
);
fprintf(stderr
, "*** %s can't find %s: %s\n", server
, host
,
*******************************************************************************
* This routine is used to change the state information
* that affect the lookups. The command format is
* Most keywords can be abbreviated. Parsing is very simplistic--
* A value must not be separated from its keyword by white space.
* Valid keywords: Meaning:
* [no]aaonly authoritative query only or not (hidden).
* all lists current values of options.
* ALL lists current values of options, including
* [no]d2 turn on/off extra debugging mode (hidden).
* [no]debug turn on/off debugging mode.
* [no]defname use/don't use default domain name.
* [no]search use/don't use domain search list.
* domain=NAME set default domain name to NAME.
* [no]ignore ignore/don't ignore trunc. errors (hidden).
* [no]primary use/don't use primary server (hidden).
* query=value set default query type to value,
* value is one of the query types in RFC883
* without the leading T_. (e.g. A, HINFO)
* [no]recurse use/don't use recursive lookup.
* retry=# set number of retries to #.
* root=NAME change root server to NAME.
* time=# set timeout length to #.
* [no]vc use/don't use virtual circuit.
* SUCCESS the command was parsed correctly.
* ERROR the command was not parsed correctly.
*******************************************************************************
if (strncmp (option
, "set ", 4) == 0)
fprintf(stderr
, "*** Invalid set command\n");
if (strncmp(option
, "all", 3) == 0) {
} else if (strncmp(option
, "ALL", 3) == 0) {
} else if (strncmp(option
, "aa", 2) == 0) { /* aaonly */
_res
.options
|= RES_AAONLY
;
} else if (strncmp(option
, "noaa", 4) == 0) {
_res
.options
&= ~RES_AAONLY
;
} else if (strncmp(option
, "d2", 2) == 0) { /* d2 (more debug) */
_res
.options
|= (RES_DEBUG
| RES_DEBUG2
);
} else if (strncmp(option
, "nod2", 4) == 0) {
_res
.options
&= ~RES_DEBUG2
;
} else if (strncmp(option
, "def", 3) == 0) { /* defname */
_res
.options
|= RES_DEFNAMES
;
} else if (strncmp(option
, "nodef", 5) == 0) {
_res
.options
&= ~RES_DEFNAMES
;
} else if (strncmp(option
, "do", 2) == 0) { /* domain */
ptr
= index(option
, '=');
sscanf(++ptr
, "%s", _res
.defdname
);
} else if (strncmp(option
, "deb", 1) == 0) { /* debug */
_res
.options
|= RES_DEBUG
;
} else if (strncmp(option
, "nodeb", 3) == 0) {
_res
.options
&= ~(RES_DEBUG
| RES_DEBUG2
);
} else if (strncmp(option
, "i", 1) == 0) { /* ignore */
_res
.options
|= RES_IGNTC
;
} else if (strncmp(option
, "noi", 3) == 0) {
_res
.options
&= ~RES_IGNTC
;
} else if (strncmp(option
, "p", 1) == 0) { /* primary */
_res
.options
|= RES_PRIMARY
;
} else if (strncmp(option
, "nop", 3) == 0) {
_res
.options
&= ~RES_PRIMARY
;
} else if (strncmp(option
, "q", 1) == 0 || /* querytype */
strncmp(option
, "ty", 2) == 0) {
ptr
= index(option
, '=');
sscanf(++ptr
, "%s", type
);
queryType
= StringToType(type
, queryType
);
} else if (strncmp(option
, "cl", 2) == 0) { /* query class */
ptr
= index(option
, '=');
sscanf(++ptr
, "%s", type
);
queryClass
= StringToClass(type
, queryClass
);
} else if (strncmp(option
, "rec", 3) == 0) { /* recurse */
_res
.options
|= RES_RECURSE
;
} else if (strncmp(option
, "norec", 5) == 0) {
_res
.options
&= ~RES_RECURSE
;
} else if (strncmp(option
, "ret", 3) == 0) { /* retry */
ptr
= index(option
, '=');
sscanf(++ptr
, "%d", &_res
.retry
);
} else if (strncmp(option
, "ro", 2) == 0) { /* root */
ptr
= index(option
, '=');
sscanf(++ptr
, "%s", rootServerName
);
} else if (strncmp(option
, "sea", 3) == 0) { /* search list */
_res
.options
|= RES_DNSRCH
;
} else if (strncmp(option
, "nosea", 5) == 0) {
_res
.options
&= ~RES_DNSRCH
;
} else if (strncmp(option
, "t", 1) == 0) { /* timeout */
ptr
= index(option
, '=');
sscanf(++ptr
, "%d", &_res
.retrans
);
} else if (strncmp(option
, "v", 1) == 0) { /* vc */
_res
.options
|= RES_USEVC
;
} else if (strncmp(option
, "nov", 3) == 0) {
_res
.options
&= ~RES_USEVC
;
fprintf(stderr
, "*** Invalid option: %s\n", option
);
* Fake a reinitialization when the domain is changed.
/* find components of local domain that might be searched */
for (cp
= _res
.defdname
, n
= 0; *cp
; cp
++)
for (; n
>= LOCALDOMAINPARTS
&& pp
< _res
.dnsrch
+ MAXDNSRCH
; n
--) {
_res
.options
|= RES_INIT
;
*******************************************************************************
* Prints out the state information used by the resolver
* library and other options set by the user.
*******************************************************************************
PrintHostInfo(stdout
, "Default Server:", defaultPtr
);
PrintHostInfo(stdout
, "Host:", &curHostInfo
);
printf("Set options:\n");
printf(" %sdebug \t", (_res
.options
& RES_DEBUG
) ? "" : "no");
printf(" %sdefname\t", (_res
.options
& RES_DEFNAMES
) ? "" : "no");
printf(" %ssearch\t", (_res
.options
& RES_DNSRCH
) ? "" : "no");
printf(" %srecurse\t", (_res
.options
& RES_RECURSE
) ? "" : "no");
printf(" %svc\n", (_res
.options
& RES_USEVC
) ? "" : "no");
printf(" %saa\t\t", (_res
.options
& RES_AAONLY
) ? "" : "no");
printf(" %sd2\t\t", (_res
.options
& RES_DEBUG2
) ? "" : "no");
printf(" %signoretc\t", (_res
.options
& RES_IGNTC
) ? "" : "no");
printf(" %sprimary\n", (_res
.options
& RES_PRIMARY
) ? "" : "no");
printf(" querytype=%s\t", p_type(queryType
));
printf(" class=%s\t", p_class(queryClass
));
printf(" timeout=%d\t", _res
.retrans
);
printf(" retry=%d\n", _res
.retry
);
printf(" domain=%s\n", _res
.defdname
);
printf(" search list: ");
for (cp
= _res
.dnsrch
; *cp
; cp
++)
printf("\n root=%s\n", rootServerName
);
printf(" current packet id: %d\n", (int)_res
.id
);
printf(" number of name servers: %d\n", _res
.nscount
);
printf(" name server addresses: %s\n",
inet_ntoa(_res
.nsaddr_list
[0].sin_addr
));
for (i
= 1; i
< _res
.nscount
; i
++) {
inet_ntoa(_res
.nsaddr_list
[i
].sin_addr
));
*******************************************************************************
* Prints out the help file.
* (Code taken from Mail.)
*******************************************************************************
register FILE *helpFilePtr
;
if ((helpFilePtr
= fopen(HELPFILE
, "r")) == NULL
) {
while ((c
= getc(helpFilePtr
)) != EOF
) {
*******************************************************************************
* Convert a dotted-decimal Internet address into the standard
* PTR format (reversed address with .in-arpa. suffix).
*******************************************************************************
if (index(name
, '.') != NULL
) {
* Look for the in-addr.arpa suffix with and without a trailing .
(strncasecmp(p
, "in-addr.arpa", sizeof("in-addr.arpa")-1) != 0)) {
* Canonicalize the address so, for example, 127.1 works.
if (sscanf(p
, "%d.%d.%d.%d",
&ip
[0], &ip
[1], &ip
[2], &ip
[3]) == 4) {
sprintf(name
, "%d.%d.%d.%d.in-addr.arpa.",
ip
[3], ip
[2], ip
[1], ip
[0]);