* Copyright (c) 1985,1989 Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
"@(#) Copyright (c) 1985,1989 Regents of the University of California.\n\
static char sccsid
[] = "@(#)main.c 5.42 (Berkeley) 3/3/91";
*******************************************************************************
* Main routine and some action routines for the name server
* U.C. Berkeley Computer Science Div.
*******************************************************************************
#include <arpa/nameser.h>
* 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 "ns.nic.ddn.mil."
char rootServerName
[NAME_LEN
] = ROOT_SERVER
;
* 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.
extern void 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"
ReadRC(); /* look for options file */
++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 */
* Use an explicit name server. If the hostname lookup fails,
* default to the server(s) in resolv.conf.
addr
.s_addr
= inet_addr(*++argv
);
if (addr
.s_addr
!= (unsigned long)-1) {
_res
.nsaddr
.sin_addr
= addr
;
hp
= gethostbyname(*argv
);
fprintf(stderr
, "*** Can't find server address for '%s': ",
bcopy(hp
->h_addr
, (char *)&_res
.nsaddr
.sin_addr
, hp
->h_length
);
for (i
= 0; i
< MAXNS
&& hp
->h_addr_list
[i
] != NULL
; i
++) {
bcopy(hp
->h_addr_list
[i
],
(char *)&_res
.nsaddr_list
[i
].sin_addr
,
if (_res
.nscount
== 0 || useLocalServer
) {
for (i
= 0; i
< _res
.nscount
; i
++) {
if (_res
.nsaddr_list
[i
].sin_addr
.s_addr
== INADDR_ANY
) {
result
= GetHostInfoByAddr(&(_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.
fprintf(stderr
, "*** Default servers are not available\n");
strcpy(defaultServer
, defaultPtr
->name
);
_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
= GetHostInfoByName(&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) GetHostInfoByName(&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");
*******************************************************************************
* Returns TRUE if the string looks like an Internet address.
* A string with a trailing dot is not an address, even if it looks
* XXX doesn't treat 255.255.255.255 as an address.
*******************************************************************************
unsigned long *addrPtr
; /* If return TRUE, contains IP address */
/* Make sure it has only digits and dots. */
for (cp
= host
; *cp
; ++cp
) {
if (!isdigit(*cp
) && *cp
!= '.')
/* If it has a trailing dot, don't treat it as an address. */
if ((addr
= inet_addr(host
)) != (unsigned long) -1) {
/* XXX Check for 255.255.255.255 case */
*******************************************************************************
* 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
;
struct in_addr
*servAddrPtr
;
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
* Check to see if we have the address of the server or the
* address of a server who knows about this domain.
* XXX For now, just use the first address in the list.
servAddrPtr
= &defaultAddr
;
} else if (defaultPtr
->addrList
!= NULL
) {
servAddrPtr
= (struct in_addr
*) defaultPtr
->addrList
[0];
servAddrPtr
= (struct in_addr
*) defaultPtr
->servers
[0]->addrList
[0];
if (IsAddr(newServer
, &addr
.s_addr
)) {
result
= GetHostInfoByAddr(servAddrPtr
, &addr
, newDefPtr
);
/* If we can't get the name, fall through... */
if (result
!= SUCCESS
&& result
!= NONAUTH
) {
result
= GetHostInfoByName(servAddrPtr
, 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
));
*******************************************************************************
* Common subroutine for LookupHost and LookupHostWithServer.
* SUCCESS - the lookup was successful.
* Misc. Errors - an error message is printed if the lookup failed.
*******************************************************************************
DoLookup(host
, servPtr
, serverName
)
struct in_addr
*servAddrPtr
;
/* Skip escape character */
* If the user gives us an address for an address query,
* silently treat it as a PTR query. If the query type is already
* PTR, then convert the address into the in-addr.arpa format.
* Use the address of the server if it exists, otherwise use the
* address of a server who knows about this domain.
* XXX For now, just use the first address in the list.
if (servPtr
->addrList
!= NULL
) {
servAddrPtr
= (struct in_addr
*) servPtr
->addrList
[0];
servAddrPtr
= (struct in_addr
*) servPtr
->servers
[0]->addrList
[0];
* RFC1123 says we "SHOULD check the string syntactically for a
* dotted-decimal number before looking it up [...]" (p. 13).
if (queryType
== T_A
&& IsAddr(host
, &addr
.s_addr
)) {
result
= GetHostInfoByAddr(servAddrPtr
, &addr
, &curHostInfo
);
if (queryType
== T_PTR
) {
result
= GetHostInfoByName(servAddrPtr
, queryClass
, queryType
, host
,
* 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 (%s) records available for %s\n",
DecodeType(queryType
), p_type(queryType
), host
);
fprintf(stderr
, "*** Request to %s timed-out\n", serverName
);
fprintf(stderr
, "*** %s can't find %s: %s\n", serverName
, host
,
*******************************************************************************
* Asks the default name server for information about the
* specified host or domain. The information is printed
* if the lookup was successful.
* ERROR - the output file could not be opened.
*******************************************************************************
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
);
result
= DoLookup(host
, defaultPtr
, defaultServer
);
*******************************************************************************
* 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.
* ERROR - the output file could not be opened.
*******************************************************************************
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
);
result
= GetHostInfoByName(
(struct in_addr
*) defaultPtr
->addrList
[0] :
(struct in_addr
*) defaultPtr
->servers
[0]->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
);
result
= DoLookup(host
, &serverInfo
, server
);
*******************************************************************************
* 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:
* all lists current values of options.
* ALL lists current values of options, including
* [no]d2 turn on/off extra debugging mode.
* [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.
* 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.
* port TCP/UDP port to server.
* [no]primary use/don't use primary server.
* 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
, "d2", 2) == 0) { /* d2 (more debug) */
_res
.options
|= (RES_DEBUG
| RES_DEBUG2
);
} else if (strncmp(option
, "nod2", 4) == 0) {
_res
.options
&= ~RES_DEBUG2
;
printf("d2 mode disabled; still in debug mode\n");
} 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
= strchr(option
, '=');
sscanf(++ptr
, "%s", _res
.defdname
);
} else if (strncmp(option
, "deb", 1) == 0) { /* debug */
_res
.options
|= RES_DEBUG
;
} else if (strncmp(option
, "nodeb", 5) == 0) {
_res
.options
&= ~(RES_DEBUG
| RES_DEBUG2
);
} else if (strncmp(option
, "ig", 2) == 0) { /* ignore */
_res
.options
|= RES_IGNTC
;
} else if (strncmp(option
, "noig", 4) == 0) {
_res
.options
&= ~RES_IGNTC
;
} else if (strncmp(option
, "po", 2) == 0) { /* port */
ptr
= strchr(option
, '=');
sscanf(++ptr
, "%hu", &nsport
);
} else if (strncmp(option
, "pri", 3) == 0) { /* primary */
_res
.options
|= RES_PRIMARY
;
} else if (strncmp(option
, "nopri", 5) == 0) {
_res
.options
&= ~RES_PRIMARY
;
} else if (strncmp(option
, "q", 1) == 0 || /* querytype */
strncmp(option
, "ty", 2) == 0) { /* type */
ptr
= strchr(option
, '=');
sscanf(++ptr
, "%s", type
);
queryType
= StringToType(type
, queryType
);
} else if (strncmp(option
, "cl", 2) == 0) { /* query class */
ptr
= strchr(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
= strchr(option
, '=');
sscanf(++ptr
, "%d", &tmp
);
} else if (strncmp(option
, "ro", 2) == 0) { /* root */
ptr
= strchr(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
, "srchl", 5) == 0) { /* domain search list */
ptr
= strchr(option
, '=');
} else if (strncmp(option
, "ti", 2) == 0) { /* timeout */
ptr
= strchr(option
, '=');
sscanf(++ptr
, "%d", &tmp
);
} 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
+ MAXDFLSRCH
; n
--) {
_res
.options
|= RES_INIT
;
(void)strncpy(_res
.defdname
, cp
, sizeof(_res
.defdname
) - 1);
if ((cp
= strchr(_res
.defdname
, '\n')) != NULL
)
* Set search list to be blank-separated strings
for (n
= 0; *cp
&& pp
< _res
.dnsrch
+ MAXDNSRCH
; cp
++) {
if (*cp
== SRCHLIST_SEP
) {
if ((cp
= strchr(pp
[-1], SRCHLIST_SEP
)) != NULL
) {
*******************************************************************************
* 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\n", (_res
.options
& RES_RECURSE
) ? "" : "no");
printf(" %sd2\t\t", (_res
.options
& RES_DEBUG2
) ? "" : "no");
printf(" %svc\t\t", (_res
.options
& RES_USEVC
) ? "" : "no");
printf(" %signoretc\t", (_res
.options
& RES_IGNTC
) ? "" : "no");
printf(" port=%u\n", nsport
);
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(" root=%s\n", rootServerName
);
printf(" domain=%s\n", _res
.defdname
);
printf(" srchlist=%s", *cp
);
printf("%c%s", SRCHLIST_SEP
, *cp
);
*******************************************************************************
* Prints out the help file.
* (Code taken from Mail.)
*******************************************************************************
register FILE *helpFilePtr
;
if ((helpFilePtr
= fopen(_PATH_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).
* Assumes the argument buffer is large enougth to hold the result.
*******************************************************************************
if (IsAddr(name
, &addr
.s_addr
)) {
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]);
*******************************************************************************
* Use the contents of ~/.nslookuprc as options.
*******************************************************************************
if ((cp
= getenv("HOME")) != NULL
) {
(void) strcat(buf
, "/.nslookuprc");
if ((fp
= fopen(buf
, "r")) != NULL
) {
while (fgets(buf
, sizeof(buf
), fp
) != NULL
) {
if ((cp
= strchr(buf
, '\n')) != NULL
) {