/*
NAME
ipw - get IP address whois information
SYNOPSIS
ipw [-a] [-n] [-c] [-C] [-t] [-v] [-T secs] address
ipw [-a] [-n] [-c] [-C] [-t] [-v] [-T secs] host
ipw [-a] [-n] [-c] [-C] [-t] [-v] [-T secs] handle
DESCRIPTION
The ipw program attempts to obtain the most relevant IP address regis-
tration record for a given IP address. It does so by trying each of
several major WHOIS servers, in turn, until it finds a relevant record.
The WHOIS servers that are consulted for IP address registration
records are:
whois.arin.net
whois.ripe.net
whois.apnic.net
whois.aunic.net
If any of these contains a relevant registration record for the given
IP address, that record will be printed to stdout. By default, the
entire registration record is printed to stdout, unless any combination
of the -a, -n, or -t options are given; in which case only the specific
information requested is displayed.
If the case of ARIN registration records, if there are multiple regis-
tration records covering address ranges which include the given IP
address, then the record relating to the numerically smallest such
IP address range is selected and then printed to stdout.
Note that ``handle'' may be prefixed with "ARIN:", "RIPE:", "APNIC:",
or "AUNIC:" (case sensitive) in order to manually select a whois
server when searching by nic handle. Normally, each of the whois
servers is consulted consecutively until a match is found for a given
nic handle, because a handle by itself does not include enough
information to automatically select the most appropriate server.
These prefixes are included in the output generated by the -n option.
The -a option selects IP address range mode. In this mode, the
smallest enclosing address range is printed to stdout, rather than
the entire registration record.
The -n option selects nic-handle mode, where the "handle", or name,
for the specified netblock is printed to stdout. The -N option also
selects nic-handle mode, but generates a prefix to the handle that
indicates which registry the handle belongs to (see above).
The -c option selects contacts mode. In this mode, the relevant
contact E-mail addresses are printed to stdout, rather than the entire
registration record. If there is more than one contact E-mail address
in the relevant registration record, then sequential addresses will
be separated by a comma and a space on stdout.
The -C option is just like the -c option, except that the block con-
tact E-mail addresses are output one per line, rather than all on a
single line separated by commas.
The -t option is present only for reasons of backward compatability.
It has the exact same effect as the -c option described above.
The -T option may be used to adjust the timeout period (in seconds)
used when attempting to connect to the various WHOIS servers. The
default timeout used when no -t option is specified is 0, which is
treated as actually representing infinity (i.e. no timeout). Note
however that the underlying TCP protocol may generate a timeout in
some cases.
NOTES
A valid Internet hostname may be given in place of the IP address
argument, in which case that hostname will be looked-up using DNS
and the registration record search will be applied to the first
registered IP address associated with that hostname.
There are many valid IP addresses for which no relevant registration
records exist. For example, addresses in the 10.0.0.0/8 address
block and addresses in the 192.168/16 address block have no relevant
registration records. There are many other such ranges.
Ideally, when the input is an ARIN, RIPE, or APNIC handle, we should
check to see if it has a prefix or suffix that might tip us off as
to which of these three registries we should look up the handle in
first. Normally, we will attempt lookups in ARIN, then RIPE, and then
APNIC, but the following handle suffixes and prefixes could help us
to avoid many pointless lookups:
RIPE suffixes and prefixes:
*-RIPE
*-NO
AT-*
SE-*
FR-*
DE-*
IT-*
RU-*
SK-*
APNIC suffixes:
*-AP
*-JP
*-AU (Data actually in the AUNIC!)
*-TW
*-CN
*-NZ
*-TH
*-MY
*-MN
*-ID
*-HK
*-SG
RETURN VALUE
Ipw will exit with a zero (0) status code if all goes well, or with a
one (1) if no relevant registration records for the given IP address
were found, or two (2) if there were any sort of internal or communica-
tions errors.
VERSION
3.3a
AUTHOR
Ronald F. Guilmette <rfg [at] monkeys.com>
Contributions by Marty Bower <marty [at] mjhb.marina-del-rey.ca.us>
mjhb $Id: ipw.c,v 1.13.2.9 1999/02/04 06:41:13 marty Exp $
COPYRIGHT
Copyright (c) 1998 Ronald F. Guilmette; All rights reserved.
*/
#include <sys/types.h>
#if defined(WIN32)
#include <winsock.h>
#include <io.h>
#else /* !defined(WIN32) */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pwd.h>
#include <unistd.h>
#include <stdarg.h>
#endif /* defined(WIN32) */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>
#include <setjmp.h>
#include <signal.h>
#include <limits.h>
static char const tcp[] = "tcp";
static char const whois_service[] = "whois";
static char const arin_server[] = "whois.arin.net";
static char const ripe_server[] = "whois.ripe.net";
static char const apnic_server[] = "whois.apnic.net";
static char const aunic_server[] = "whois.aunic.net";
static char const arin_handle[] = "ARIN:";
static char const ripe_handle[] = "RIPE:";
static char const apnic_handle[] = "APNIC:";
static char const aunic_handle[] = "AUNIC:";
#ifdef __cplusplus
#define argname(x)
#else
#define argname(x) x
#endif
#if !defined(__GNUC__) && !defined(__GNUG__)
#define __attribute__(x)
#endif
#if defined(SunOS4)
extern int printf (const char *, ...);
extern int fprintf (FILE *, const char *, ...);
extern int socket (int, int, int);
extern int connect (int, struct sockaddr *, int);
extern int _filbuf (FILE *);
extern int _flsbuf (int, FILE *);
extern char *sys_errlist[];
static char *
strerror (int err)
{
return sys_errlist[err];
}
#endif /* defined(SunOS4) */
typedef enum Bool { False, True } Bool;
typedef struct in_addr in_addr;
typedef struct in_addr_range {
in_addr start;
in_addr end;
} in_addr_range;
static char *pname;
static Bool contacts1_mode = False;
static Bool contacts2_mode = False;
static Bool verbose_mode = False;
static Bool addr_mode = False;
static Bool handle_mode = False;
static Bool add_handle_prefixes = False;
static jmp_buf recovery;
static struct protoent *protocol;
static struct servent *service;
static unsigned timeout = 0; /* Default */
#if !defined(RANGE_FUNC)
/* Getopt & inet_aton code borrowed from BSD; ANSIfied and also reformatted
to meet GNU coding conventions. Also modified to meet RFG coding style. */
/*
* Copyright (c) 1987, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 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
* SUCH DAMAGE.
*/
static int my_opterr = 1; /* if error message should be printed */
static int my_optind = 1; /* index into parent argv vector */
static int my_optopt; /* character checked for validity */
static int optreset; /* reset getopt */
static char *my_optarg; /* argument associated with option */
#define BADCH (int)'?'
#define BADARG (int)':'
#define EMSG ""
/*
* getopt --
* Parse argc/argv argument vector.
*/
static int
my_getopt (register int const nargc, register char *const *const nargv,
register const char *const ostr)
{
static char *place = EMSG; /* option letter processing */
register char *oli = NULL; /* option letter list index */
if (optreset || ! *place)
{ /* update scanning pointer */
optreset = 0;
if (my_optind >= nargc || *(place = nargv[my_optind]) != '-')
{
place = EMSG;
return -1;
}
if (place[1] && * ++place == '-')
{ /* found "--" */
++my_optind;
place = EMSG;
return -1;
}
} /* option letter okay? */
if ((my_optopt = (int) *place++) == (int) ':'
|| ! (oli = strchr (ostr, my_optopt)))
{
/*
* if the user didn't specify '-' as an option,
* assume it means -1.
*/
if (my_optopt == (int) '-')
return -1;
if ( ! *place)
++my_optind;
if (my_opterr && *ostr != ':')
fprintf (stderr, "%s: illegal option -- %c\n", pname, my_optopt);
return BADCH;
}
if (* ++oli != ':')
{ /* don't need argument */
my_optarg = NULL;
if ( ! *place)
++my_optind;
}
else
{ /* need an argument */
if (*place) /* no white space */
my_optarg = place;
else if (nargc <= ++my_optind)
{ /* no arg */
place = EMSG;
if (*ostr == ':')
return BADARG;
if (my_opterr)
fprintf (stderr, "%s: option requires an argument -- %c\n",
pname, my_optopt);
return BADCH;
}
else /* white space */
my_optarg = nargv[my_optind];
place = EMSG;
++my_optind;
}
return my_optopt; /* dump back option letter */
}
#endif /* !defined(RANGE_FUNC) */
/* We provide our own version on inet_aton here because Solaris/SunOS
doesn't have one and also because I'm not 100% sure that the one on
Linux always yields the correct results.
Note that we can't name this just `inet_aton' because if we do, the
Linux standard library version of gethostbyname will malfunction for
unknown reasons. */
/*
* Portions Copyright (c) 1993 by Digital Equipment Corporation.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies, and that
* the name of Digital Equipment Corporation not be used in advertising or
* publicity pertaining to distribution of the document or software without
* specific, written prior permission.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
/*
static char sccsid[] = " [at] (#)inet_addr.c 8.1 (Berkeley) 6/17/93";
*/
/*
* Check whether "cp" is a valid ascii representation
* of an Internet address and convert to a binary address.
* Returns 1 if the address is valid, 0 if not.
* This replaces inet_addr, the return value from which
* cannot distinguish between failure and a local broadcast address.
*/
static int
my_inet_aton (register const char *cp, register struct in_addr *addr)
{
register unsigned long val;
register int base, n;
register char c;
u_int parts[4];
register u_int *pp = parts;
c = *cp;
for (;;) {
/*
* Collect number up to ``.''.
* Values are specified as for C:
* 0x=hex, 0=octal, isdigit=decimal.
*/
if (!isdigit(c))
return (0);
val = 0; base = 10;
if (c == '0') {
c = *++cp;
if (c == 'x' || c == 'X')
base = 16, c = *++cp;
else
base = 8;
}
for (;;) {
if (isascii(c) && isdigit(c)) {
val = (val * base) + (c - '0');
c = *++cp;
} else if (base == 16 && isascii(c) && isxdigit(c)) {
val = (val << 4) |
(c + 10 - (islower(c) ? 'a' : 'A'));
c = *++cp;
} else
break;
}
if (c == '.') {
/*
* Internet format:
* a.b.c.d
* a.b.c (with c treated as 16 bits)
* a.b (with b treated as 24 bits)
*/
if (pp >= parts + 3)
return (0);
*pp++ = val;
c = *++cp;
} else
break;
}
/*
* Check for trailing characters.
*/
if (c != '\0' && (!isascii(c) || !isspace(c)))
return (0);
/*
* Concoct the address according to
* the number of parts specified.
*/
n = pp - parts + 1;
switch (n) {
case 0:
return (0); /* initial nondigit */
case 1: /* a -- 32 bits */
break;
case 2: /* a.b -- 8.24 bits */
if (val > 0xffffff)
return (0);
val |= parts[0] << 24;
break;
case 3: /* a.b.c -- 8.8.16 bits */
if (val > 0xffff)
return (0);
val |= (parts[0] << 24) | (parts[1] << 16);
break;
case 4: /* a.b.c.d -- 8.8.8.8 bits */
if (val > 0xff)
return (0);
val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
break;
}
if (addr)
addr->s_addr = htonl(val);
return (1);
}
/* Initialize and shutdown MS Windows Sockets interface. */
static void
mswinsock_init (void)
{
#if defined(WIN32)
auto WSADATA WSAData;
register int const status = WSAStartup (MAKEWORD (1,1), &WSAData);
if (status != 0)
{
fprintf (stderr, "%s: WSAStartup: %d", pname, status);
exit(2);
}
#endif /* defined(WIN32) */
}
static void
mswinsock_shutdown (void)
{
#if defined(WIN32)
register int const status = WSACleanup ();
if (status != 0)
{
fprintf (stderr, "%s: WSAShutdown: %d", pname, status);
exit(2);
}
#endif /* defined(WIN32) */
}
/* Print error, properly terminate access to winsock (WIN32), then exit. */
static void fatal (register int const exit_code, const char* fmt, ...)
__attribute__ ((noreturn));
static void
fatal (register int const exit_code, const char* fmt, ...)
{
va_list ap;
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
va_end (ap);
exit (exit_code);
}
#if !defined(RANGE_FUNC)
static void
usage (void)
{
fprintf (stderr, "%s: usage `%s [-a] [-n] [-t] [-v] [-T secs] address'\n",
pname, pname);
exit (2);
}
#endif /* !defined(RANGE_FUNC) */
static void *
xmalloc (register unsigned size)
{
register void *p;
if ((p = malloc (size)) == 0)
fatal (2, "%s: memory exhausted\n", pname);
return p;
}
/* substr(s1,s2) - return a pointer to the first instance of the string
s2 within the string s1, or NULL if s2 does not appear within s1. */
static char const *
substr (register const char *s1, register char const *s2)
{
for (; *s1 ; s1++)
{
register char const *p1;
register char const *p2;
register int c;
for (p1 = s1, p2 = s2; (c = *p2); p1++, p2++)
if (*p1 != c)
goto outer;
return s1;
outer:
;
}
return 0;
}
/* subnstr(s1,s2,n) - return a pointer to the first instance of the string
s2 within the first n characters of string s1, or NULL if s2 does not
appear within the first n characters of s1. Note that if the first n
characters of the string pointed to by s1 includes a nul bytes, then
the search for the subscring s2 terminates at that point, even if the
value of n would otherwise suggest that the search should continue
further. */
static char const *
subnstr (register char const *s1, register char const *s2,
register unsigned long n)
{
register char const *const s1_limit = s1 + n;
for (; *s1 && s1 < s1_limit ; s1++)
{
register char const *p1;
register char const *p2;
register int c;
for (p1 = s1, p2 = s2; (c = *p2); p1++, p2++)
if (*p1 != c)
goto outer;
return s1;
outer:
;
}
return 0;
}
/* skip whitespace characters, blank & \t; return pointer to next char.
note: isspace() also includes cr & lf, which we don't want */
static char const *
skip_ws (register char const *startp)
{
register char const *p = startp;
while (*p)
{
register int const ch = *p;
if ((ch != ' ') && (ch != '\t'))
break;
p++;
}
return p;
}
#if !defined(RANGE_FUNC)
/* Return True if the given string consists of only digits,
and at least one period. */
static Bool
vaguely_ipish (register char const *s)
{
register char const *p;
register int ndots = 0;
for (p = s; *p; p++)
{
register int const ch = *p;
if (!isdigit (ch))
{
if (ch == '.')
ndots++;
else
return False;
}
}
return ndots ? True : False;
}
/* We provide our own version of gethostbyname here because the one on
Linux seems to improperly yield a non-null (success) result if and when
it is passed such bogus strings as "24" or "24.0". We avoid that here. */
static struct hostent *
my_gethostbyname (register char const *const name)
{
return vaguely_ipish (name) ? (struct hostent *)0 : gethostbyname (name);
}
#endif /* !defined(RANGE_FUNC) */
#if !defined(WIN32)
static void
alarm_handler (register int argname(signo))
{
longjmp (recovery, 1);
}
#endif /* !defined(WIN32) */
/* Try connecting to (and sending a specified query to) either a WHOIS server
or an RWHOIS server.
If we can't connect for any reason, log a suitable error message to stderr
and then return NULL. */
static char const *
query_server (register char const *const server,
register int const portno,
register char const *const arg)
{
register struct hostent *hp;
struct sockaddr_in sin;
register int sock;
register int con_result;
auto unsigned long server_addr;
/* Lookup the server's IP address. */
if (!(hp = gethostbyname (server)))
fatal (2, "%s: Error: Unable to find %s server: %s\n",
pname, whois_service, server);
/* Create socket. */
if ((sock = socket (AF_INET, SOCK_STREAM, protocol->p_proto)) < 0)
fatal (2, "%s: Error opening %s socket: %s\n",
pname, tcp, strerror (errno));
sin.sin_family = AF_INET;
memcpy (&server_addr, hp->h_addr, sizeof server_addr);
sin.sin_addr.s_addr = server_addr;
sin.sin_port = htons (portno);
if (verbose_mode)
fprintf (stderr, "%s: Connecting to server: %s:%d\n",
pname, server, portno);
#if !defined(WIN32) /* no SIGALRM on WIN32 */
signal (SIGALRM, alarm_handler);
if (setjmp (recovery) != 0)
{
fprintf (stderr, "%s: Timeout connecting to server `%s'\n",
pname, server);
return NULL;
}
else
#endif /* !defined(WIN32) */
{
#if !defined(WIN32)
if (timeout)
alarm (timeout);
#endif /* !defined(WIN32) */
con_result = connect (sock, (struct sockaddr *)&sin, sizeof sin);
#if !defined(WIN32)
if (timeout)
alarm (0);
#endif /* !defined(WIN32) */
if (con_result < 0)
{
fprintf (stderr, "%s: Error connecting to server `%s': %s\n",
pname, server, strerror (errno));
return NULL;
}
else
{
enum { ibuf_size = (1 << 16) };
char ibuf[ibuf_size];
register char const *const ibuf_limit = &ibuf[ibuf_size];
register char *inp = ibuf;
register unsigned long space_left = ibuf_size;
register unsigned long used;
register char *result;
if (verbose_mode)
fprintf (stderr, "%s: Query: %s\n", pname, arg);
send (sock, arg, strlen (arg), 0);
send (sock, "\n", 1, 0);
for (;;)
{
register int result;
if ((result = recv (sock, inp, space_left, 0)) <= 0)
break;
if ((inp += result) >= ibuf_limit)
fatal (2, "%s: Response from server `%s' too large\n",
pname, server);
space_left -= result;
}
close (sock);
used = inp - ibuf;
result = (char *) xmalloc (used + 1);
memcpy (result, ibuf, used);
result[used] = '\0';
return result;
}
}
}
static char const *
whois (register char const *const server, register char const *const arg)
{
return query_server (server, 43, arg);
}
/* The following routine allows us to get more specific data in the case
of some specific networks that happen to put all of their sub-block
registration data into their own local RWHOIS server, rather than
SWIP'ing it all to ARIN. Some examples include Digex (e.g. 209.116.1.1),
Epoch (e.g. 207.168.1.1), and Exodus (e.g. 209.1.1.1).
Note that CAIS Internet (cais.net) _claims_ to be running an rwhois server
(e.g. in their ARIN registration record for their 207.226.0.0/16 address
block) however the reality is that this this rwhois servers is dead, and
not accepting connections. In cases such as that, the following function
will yield a NULL pointer.
*/
static char const *
fetch_rwhois_data (register char const *const whois_data,
register char const *const dotted_quad)
{
static char const rwhois_prefix[] = "rwhois.";
enum { rwhois_prefix_len = sizeof rwhois_prefix - 1 };
register char const *const p = substr (whois_data, rwhois_prefix);
register char const *q;
register unsigned srvr_len;
register char *server;
register int portno = 0;
register char const *result;
if (!p)
return NULL;
for (q = p + rwhois_prefix_len; *q && !isspace (*q); q++)
continue;
srvr_len = q - p;
server = (char *) xmalloc (srvr_len + 1);
memcpy (server, p, srvr_len);
server[srvr_len] = '\0';
while (isspace (*q))
q++;
while (isdigit (*q))
portno = (portno * 10) + (*q++ - '0');
if (!portno)
portno = 4321; /* Default rwhois port. */
/* The following line will cause us to return NULL in cases where the
designated rwhois server is either dead or not responding. */
if ((result = query_server (server, portno, dotted_quad)) == NULL)
return NULL;
/* In general, the first line of output from most rwhois servers will be
a header line like "%rwhois V-blah blah blah". We skip over that
useless jizz here. */
if (*result == '%')
{
do
result++;
while (*result && *result != '\n');
result++;
}
/* The Exodus.Net rwhois server sometimes gives %error (and then no useful
data) for IP addresses that do exist, that are in use, and that reside
within Exodus's address blocks, like for example 216.32.210.46. We
deal with this bit of stupidity here. */
if (strncmp (result, "%error", 6) == 0)
return NULL;
return result;
}
static void
print_range (register in_addr_range const *rangep)
{
/* WARNING: Do not try to combine the following two calls to printf into
one. doing so will result in wrong results because there is only one
buffer for the results of the calls to inet_ntoa(). */
printf ("%s-", inet_ntoa (rangep->start));
printf ("%s\n", inet_ntoa (rangep->end));
}
static char const *
containing_block (register char const *const dotted)
{
register char const *const last_dot = strrchr (dotted, '.');
if (last_dot)
{
register unsigned const len = last_dot - dotted;
register char *const result = (char *) xmalloc (len + 1);
memcpy (result, dotted, len);
result[len] = '\0';
return result;
}
else
return NULL;
}
/* Return True if we were in fact able to find (or deduce) a valid IP address
range in the individual summary record that was passed to us, or else
return False if we were not able to find such an address range. */
static Bool
get_in_addr_range (register char const *const rec_start,
register char const *const rec_end,
register unsigned const top_byte_digits,
register in_addr_range *resultp)
{
static char aborting_search[] = "Aborting search";
char dotted_quad_buf[16];
auto in_addr first_addr;
auto in_addr last_addr;
register char const *p;
register char const *start_addr_end;
register char const *end_addr_end;
register unsigned len;
register unsigned periods_seen;
/* The ARIN whois server has the annoying property that it will only yield
a maximum of 256 records in response to any query. Also annoying is the
fact that when you hit this limit, the output is prefixed by a line
telling you that you hit that limit. We check for such lines here and
return False in order to be able to ignore them when and if we come
upon them.
Note that in cases where we make a query which requries more than 256
records from the ARIN as a response, this program may end up incorrectly
saying (via a result status of 1) that there is no registration record
pertaining to the IP address which the user asked for info about. This
can be seen to happen (for example) when doing `ipw 12.0.0.0'. There
really ain't much we can do about this problem. If ARIN won't give us
the data then we're basically screwed. */
if (strncmp (rec_start, aborting_search, sizeof aborting_search - 1) == 0)
return False;
for (p = rec_end; isspace (*p); p--)
continue;
end_addr_end = p + 1;
/* Did I mention that the people running the ARIN are turkeys? Try doing
a lookup using their whois server on 205.182.50.5 and you'll see what
I mean. They have a tendency to allow the name part of records to be
output in such a way that they come out jammed right up against either
the starting address or else the end address of the range, which makes
it just a leetle difficult to tell where those addresses actually start.
*/
periods_seen = 0;
for ( ; p > rec_start; p--)
{
if (!isdigit (*p) && *p != '.')
goto found_end_addr_start;
if (*p == '.')
if (++periods_seen == 3)
{
p -= (1 + top_byte_digits);
goto found_end_addr_start;
}
}
fatal (2, "%s: Can't find start of ending IP addr in ARIN record\n", pname);
found_end_addr_start:
p++;
len = end_addr_end - p;
if (len == 0)
fatal (2, "%s: Ending IP addr missing in ARIN record\n", pname);
if (len > 15)
fatal (2, "%s: Ending IP too long in ARIN record\n", pname);
memcpy (dotted_quad_buf, p, len);
dotted_quad_buf[len] = '\0';
#if 0 /* We now filter out records for ASNs before this function is called. */
if (len <= 4 && strchr (dotted_quad_buf, '.') == NULL)
/* We have found an ARIN record for an ASN. We want to ignore this. */
return False;
#endif /* 0 */
if (!my_inet_aton (dotted_quad_buf, &last_addr))
fatal (2, "%s: Bad ending address in ARIN record: %s\n",
pname, dotted_quad_buf);
resultp->end = last_addr;
p--; /* Point to space or non-digit/non-period again. */
if (*p != ' ' || *--p != '-')
{
register unsigned long address = ntohl (last_addr.s_addr);
register unsigned long byte_mask = 0xff;
register unsigned long const low_order_byte = address & byte_mask;
if (low_order_byte)
/* This is an annoying case that comes up rather infrequently. The
record we were given most probably represents a single host, but
one whose handle fails to have the standard -HST suffix. An
example of one such host can be found by doing an ARIN lookup
on the address 192.70.34.15. We will just ignore such records,
because that is the Right Thing To Do. */
return False;
else
{
/* This is fairly screwed, but another major fuck up in the ARIN
records is that some of them only contain the starting address
for the relevant netblock, rather than specifying the range
entirely in the normal "first - last" format. Of course this
leaves us to try to intuit what the ending address of the
range might be. I believe that in all cases we can do that
reliably by just substituting .255 for any and all of the
trailing .0 components in the starting address spec. */
first_addr = last_addr;
do
{
address |= byte_mask;
byte_mask <<= 8;
}
while ((address & byte_mask) == 0);
last_addr.s_addr = htonl (address);
resultp->start = first_addr;
resultp->end = last_addr;
goto done;
}
}
if (*--p != ' ')
fatal (2, "%s: Missing space before hyphen in ARIN record\n", pname);
start_addr_end = p;
periods_seen = 0;
for (p--; p > rec_start; p--)
{
if (!isdigit (*p) && *p != '.')
goto found_start_addr_start;
if (*p == '.')
if (++periods_seen == 3)
{
p -= (1 + top_byte_digits);
goto found_start_addr_start;
}
}
fatal (2, "%s: Missing space before starting IP in ARIN record\n", pname);
found_start_addr_start:
p++;
/* p should now be pointing to the first digit of the first component of
the dotted quad representing the starting address of the IP address
range. */
len = start_addr_end - p;
if (len > 15)
fatal (2, "%s: Starting IP too long in ARIN record\n", pname);
memcpy (dotted_quad_buf, p, len);
dotted_quad_buf[len] = '\0';
if (!my_inet_aton (dotted_quad_buf, &first_addr))
fatal (2, "%s: Bad starting address in ARIN record: %s\n",
pname, dotted_quad_buf);
resultp->start = first_addr;
p--;
done:
#if 0 /* This code is for debugging only. */
fprintf (stderr, "Name: ");
{
register char const *pp;
while (isspace (*p))
p--;
for (pp = rec_start; pp <= p; pp++)
putc (*pp, stderr);
putc ('\n', stderr);
}
/* WARNING: Do not try to combine the following two calls to fprintf into
one. doing so will result in wrong results because there is only one
buffer for the results of the calls to inet_ntoa(). */
fprintf (stderr, "Range: %s - ", inet_ntoa (resultp->start));
fprintf (stderr, "%s\n", inet_ntoa (resultp->end));
#endif
return True;
}
/* Quite a lot of address ranges are specified wrong in the ARIN data base.
(A few are also screwed up in RIPE data base.) Here we clean up any
problems we see with the end address of the range. */
static void
normalize_in_addr_range (register in_addr_range *const rangep)
{
register unsigned long rstart = ntohl (rangep->start.s_addr);
register unsigned long rend = ntohl (rangep->end.s_addr);
if ((rend & 0xff000000) != (rstart & 0xff000000)) /* Have at least one /8. */
if ((rend & 0x00ff0000) == 0)
{
rangep->end.s_addr = htonl (rend | 0xffffff);
return;
}
if ((rend & 0x00ff0000) != (rstart & 0x00ff0000)) /* Have at least one /16. */
if ((rend & 0x0000ff00) == 0)
{
rangep->end.s_addr = htonl (rend | 0xffff);
return;
}
if ((rend & 0x0000ff00) != (rstart & 0x0000ff00)) /* Have at least one /24. */
if ((rend & 0x000000ff) == 0)
rangep->end.s_addr = htonl (rend | 0xff);
}
static unsigned long
sizeof_in_addr_range (register in_addr_range const range)
{
return (range.end.s_addr - range.start.s_addr) + 1;
}
static int
within_in_addr_range (register in_addr_range const *const rangep,
register in_addr addr)
{
register unsigned long rstart = ntohl (rangep->start.s_addr);
register unsigned long rend = ntohl (rangep->end.s_addr);
register unsigned long address = ntohl (addr.s_addr);
#if 0
printf ("checking %s <= ", inet_ntoa (rangep->start));
printf ("%s", inet_ntoa (addr));
printf (" <= %s\n", inet_ntoa (rangep->end));
#endif
return (address >= rstart && address <= rend);
}
/* parse IP address, and convert into in_addr.
Return pointer to next non-space char if successful, null if not. */
static char const *
scan_ip (register char const *const data, register in_addr *const addrp)
{
register char const *startp;
register char const *p;
register unsigned len;
auto char ip_buf[16];
startp = skip_ws (data);
p = startp;
while (*p)
{
register int const ch = *p;
if (!isdigit (ch) && ch != '.')
break;
p++;
}
len = p - startp;
if (len < sizeof (ip_buf))
{
memcpy(ip_buf, startp, len);
ip_buf[len] = '\0';
if(!my_inet_aton (ip_buf, addrp))
return NULL;
}
else
return NULL;
return skip_ws (p);
}
/* Search whois data for address range or starting IP, prefixed with
specified string on same line (e.g. "inetnum: 1.2.3.4 - 5.6.7.8").
Return range, concocting ending IP if necessary. */
static Bool
parse_range (register char const *const data, register char const *const prefix,
register in_addr_range *const rangep)
{
/* find prefix */
register char const *p = substr (data, prefix);
if(p)
{
p += strlen (prefix);
if (! (p = scan_ip (p, &rangep->start)))
fatal (2, "%s: Bad netblock starting address\n", pname);
if (*p == '-')
{
if (! (p = scan_ip (++p, &rangep->end)))
fatal (2, "%s: Bad netblock ending address\n", pname);
}
else
{
/* Lifted from get_in_addr_range(). */
register unsigned long address = ntohl (rangep->start.s_addr);
register unsigned long byte_mask = 0xff;
do
{
address |= byte_mask;
byte_mask <<= 8;
}
while ((address & byte_mask) == 0);
rangep->end.s_addr = htonl (address);
}
}
else
return False;
return True;
}
static char const *
arin_grunge (register char const *const old_data,
register struct in_addr const addr)
{
register char const *this_rec_start = old_data;
register char const *this_rec_end;
register char const *best_rec_start = NULL;
register char const *best_rec_end = NULL;
register unsigned long best_size = 0xffffffff;
register in_addr_range best_range;
register unsigned long const top_byte = (ntohl (addr.s_addr) >> 24) & 0xff;
register unsigned const top_byte_chars =
(top_byte >= 100) ? 3 : (top_byte >= 10) ? 2 : 1;
for (; *this_rec_start;)
{
auto in_addr_range this_range;
register unsigned long rec_len;
this_rec_end = this_rec_start;
for (;; this_rec_end++)
{
if (this_rec_end[0] == '\n' && this_rec_end[1] != '\t')
break;
}
rec_len = this_rec_end - this_rec_start;
/* The first blank line we hit signals the end of the useful ARIN
record data. */
if (!rec_len)
break;
/* Ignore records that describe individual hosts and/or ASNs. For all
other records, try to get the IP address range associated with that
record, and if it is smaller than the smallest containing address
range we have seen so far which contains the IP address of interest,
then remember it as our new smallest containing address range. */
if (subnstr (this_rec_start, "-ASN)", rec_len) == 0
&& subnstr (this_rec_start, "-HST)", rec_len) == 0
&& get_in_addr_range (this_rec_start, this_rec_end,
top_byte_chars, &this_range))
{
normalize_in_addr_range (&this_range);
if (within_in_addr_range (&this_range, addr))
{
register unsigned long size = sizeof_in_addr_range (this_range);
if (size < best_size)
{
best_range = this_range;
best_size = size;
best_rec_start = this_rec_start;
best_rec_end = this_rec_end;
}
}
}
this_rec_start = this_rec_end + 1;
}
if (!best_rec_start || !best_rec_end)
/* The address in question isn't actually registered to anybody. */
return NULL;
{
register char const *p;
register char const *handle_end;
register unsigned long len;
register char *handle;
for (p = best_rec_end; p > best_rec_start; p--)
if (*p == ')')
goto rparen;
fatal (2, "%s: Missing right paren in ARIN record\n", pname);
rparen:
handle_end = p;
for (p--; p > best_rec_start; p--)
if (*p == '(')
goto lparen;
fatal (2, "%s: Missing left paren in ARIN record\n", pname);
lparen:
p++;
len = handle_end - p;
/* NOTE: We need to prefix ARIN handles with `!' in order to avoid
having the ARIN WHOIS server improperly try to match the handle
against things that are not in fact handles (and thus perhaps
yielding screwy results. For an example of this screwyness in
action, just ask the ARIN WHOIS server about "NET-O2TECH". */
handle = (char *) xmalloc (1 + len + 1);
handle[0] = '!';
memcpy (&handle[1], p, len);
handle[1+len] = '\0';
return whois (arin_server, handle);
}
}
static int
present_arin_style_results (register char const *const data)
{
if (!data)
return 1;
if (handle_mode)
{
static char const prefix[] = "Netname:";
register char const *p = substr (data, prefix);
if (p)
{
if (add_handle_prefixes)
printf ("%s", arin_handle);
p = skip_ws (p + sizeof prefix);
while (!isspace (*p))
putchar (*p++);
putchar ('\n');
}
else
fatal (2, "%s: Unable to parse NIC handle from ARIN whois results\n",
pname);
}
if (addr_mode)
{
auto in_addr_range range;
if (parse_range (data, "Netblock:", &range))
{
normalize_in_addr_range (&range);
print_range (&range);
}
else if (parse_range (data, "Netnumber:", &range))
{
normalize_in_addr_range (&range);
print_range (&range);
}
else
fatal (2, "%s: Unable to find address range in ARIN whois results\n",
pname);
}
if (contacts1_mode || contacts2_mode)
{
register Bool not_first = False;
register char const *atsign;
/* We have to allow here for screwy ARIN records for the [at] Home network,
like for example the one covering the address 24.0.26.58. */
for (atsign = data + 1; *atsign; atsign++)
{
if (atsign[0] == ' [at] ' && atsign[-1] != ' ')
{
register char const *startp;
register char const *endp;
register char const *p;
for (startp = atsign - 1; !isspace (*startp); startp--)
continue;
startp++;
for (endp = atsign + 1; *endp && !isspace (*endp); endp++)
continue;
if (not_first && contacts1_mode)
putchar (','), putchar (' ');
for (p = startp; p < atsign; p++)
putchar (*p);
putchar (' [at] ');
for (p = atsign + 1; p < endp; p++)
putchar (tolower (*p));
if (contacts2_mode)
putchar ('\n');
not_first = True;
}
}
if (not_first && contacts1_mode)
putchar ('\n');
}
if (!contacts1_mode && !contacts2_mode && !addr_mode && !handle_mode)
puts (data);
return 0;
}
static int
present_ripe_style_results (register char const *const data,
register char const *which_registry)
{
register char const *inetnum_line;
static char inetnum[] = "\ninetnum:";
register char const *last_inetnum;
register char const *next_inetnum;
/* In the case of RIPE style results, we will often get a hunk of output
relating to the containing IP address block, as well as a hunk of
output relating to the smaller and more specific block that we are
actually interested in. In such cases, we want to skip over the
part of the output relating to the larger IP address block (which
always appears first). */
last_inetnum = data;
next_inetnum = substr (last_inetnum, inetnum);
while (next_inetnum != NULL)
{
last_inetnum = next_inetnum;
next_inetnum = substr (last_inetnum + sizeof inetnum - 1, inetnum);
}
inetnum_line = last_inetnum + 1;
if (handle_mode)
{
static char const prefix[] = "netname:";
register char const *p = substr (data, prefix);
if (p)
{
if (add_handle_prefixes)
printf ("%s", which_registry);
p = skip_ws (p + sizeof prefix);
while (!isspace (*p))
putchar (*p++);
putchar ('\n');
}
else
fatal (2, "%s: Unable to parse NIC handle from %s whois results\n",
pname, which_registry);
}
if (addr_mode)
{
auto in_addr_range range;
if (parse_range (inetnum_line, inetnum + 1, &range))
{
normalize_in_addr_range (&range);
print_range (&range);
}
else
fatal (2, "%s: Unable to parse address range from %s whois results\n",
pname, which_registry);
}
if (contacts1_mode || contacts2_mode)
{
register Bool not_first = False;
register char const *tail;
for (tail = inetnum_line;;)
{
register char const *addr_line = substr (tail, "\ne-mail:");
register char const *p;
if (!addr_line)
break;
for (p = addr_line + sizeof "\ne-mail:" - 1; isspace (*p); p++)
continue;
if (not_first && contacts1_mode)
putchar (','), putchar (' ');
do
putchar (*p), p++;
while (!isspace (*p));
if (contacts2_mode)
putchar ('\n');
not_first = True;
tail = p;
}
if (not_first && contacts1_mode)
putchar ('\n');
}
if (!contacts1_mode && !contacts2_mode && !addr_mode && !handle_mode)
puts (inetnum_line);
return 0;
}
#define BAD_CIDR(SPEC) \
do { \
fprintf (stderr, "%s: Invalid CIDR spec: %s\n", pname, SPEC); \
return NULL; \
} while (0)
static char const *
cidr_to_range (register char const *const cidr_spec)
{
static char range_buf[60];
auto char ip_buf[30];
auto in_addr start_addr;
auto in_addr end_addr;
register char *q;
register char const *p;
register unsigned mask_bits = 0;
register unsigned anti_mask_bits;
for (p = cidr_spec, q = ip_buf; *p && *p != '/'; )
*q++ = *p++;
*q = '\0';
if (*p != '/' || my_inet_aton (ip_buf, &start_addr) == 0)
BAD_CIDR (cidr_spec);
if (!*++p)
BAD_CIDR (cidr_spec);
for (; *p; p++)
{
register int ch = *p;
if (!isdigit (ch))
BAD_CIDR (cidr_spec);
mask_bits = (mask_bits * 10) + (ch - '0');
}
if (mask_bits > 32)
BAD_CIDR (cidr_spec);
anti_mask_bits = 32u - mask_bits;
if (anti_mask_bits == 0u)
end_addr.s_addr = start_addr.s_addr;
else
{
register unsigned long tmp1 = ntohl (start_addr.s_addr);
register unsigned long tmp2 = tmp1 + ((1lu << anti_mask_bits) - 1);
if (tmp2 < tmp1) /* Wrap around. */
BAD_CIDR (cidr_spec);
end_addr.s_addr = htonl (tmp2);
}
q = range_buf;
for (p = inet_ntoa (start_addr); *p; )
*q++ = *p++;
*q++ = '-';
for (p = inet_ntoa (end_addr); *p; )
*q++ = *p++;
*q = '\0';
return range_buf;
}
static int
present_rwhois_style_results (register char const *const rwhois_data_start,
register char const *const whois_data_start)
{
register char const *rwhois_data = rwhois_data_start;
if (*rwhois_data == '\n')
rwhois_data++;
if (!contacts1_mode && !contacts2_mode && !addr_mode && !handle_mode)
puts (rwhois_data);
if (handle_mode)
{
static char const handle[] = "network:Handle:";
enum { handle_length = sizeof handle - 1 };
static char const network_name[] = "network:Network-Name:";
enum { network_name_length = sizeof network_name - 1 };
register char const *p;
if ((p = substr (rwhois_data, handle)))
{
for (p += handle_length; *p && !isspace(*p); p++)
putchar (*p);
putchar ('\n');
}
else if ((p = substr (rwhois_data, network_name)))
{
for (p += network_name_length; *p && !isspace(*p); p++)
putchar (*p);
putchar ('\n');
}
}
if (addr_mode)
{
static char const ip_network[] = "network:IP-Network:";
enum { ip_network_length = sizeof ip_network - 1 };
static char const network[] = "network:Network:";
enum { network_length = sizeof network - 1 };
register char const *p;
auto char cidr_buf[100];
register char *q;
if ((p = substr (rwhois_data, ip_network)))
{
q = cidr_buf;
for (p += ip_network_length; *p && !isspace(*p); )
*q++ = *p++;
*q = '\0';
puts (cidr_to_range (cidr_buf));
}
else if ((p = substr (rwhois_data, network)))
{
q = cidr_buf;
for (p += network_length; *p && !isspace(*p); )
*q++ = *p++;
*q = '\0';
puts (cidr_to_range (cidr_buf));
}
}
if (contacts1_mode || contacts2_mode)
{
/* Digex, at least, provides Domain: info. */
static char const domain[] = "network:Domain:";
enum { domain_length = sizeof domain - 1 };
register char const *p = substr (rwhois_data, domain);
/* We have to be a little bit clever here. If the rwhois data doesn't
contain any indication of anything that we might be able to turn
into a contact E-mail address. Then we drop back and display the
contact addresses given in the whois data instead. */
if (p)
{
fputs ("postmaster [at] ", stdout);
for (p += domain_length; *p && !isspace (*p); p++)
putchar (*p);
putchar ('\n');
}
else
return present_arin_style_results (whois_data_start);
}
return 0;
}
static int
try_ripe (register char const *const queryp)
{
register char const *const whois_data = whois (ripe_server, queryp);
if (!whois_data)
return 1;
/*if (!strncmp (&whois_data[0120], "% No entries", 12)) */
if (substr (whois_data, "No entries found"))
return 1;
return present_ripe_style_results (whois_data, ripe_handle);
}
/* APNIC has delegated registration authority for certain address ranges
(e.g. the one which includes 203.34.97.2) to the AUNIC, so if APNIC
says that a given address belongs to the AUNIC, we have to call the
following function, after we get the APNIC info. */
static int
try_aunic (register char const *const queryp)
{
register char const *const whois_data = whois (aunic_server, queryp);
if (!whois_data)
return 1;
if (!strncmp (&whois_data[1], "% No entries", 12))
return 1;
return present_ripe_style_results (whois_data, aunic_handle);
}
static int
try_apnic (register char const *const queryp)
{
register char const *const whois_data = whois (apnic_server, queryp);
if (!whois_data)
return 1;
if (!strncmp (&whois_data[1], "% No entries", 12))
return 1;
if (substr (whois_data, "\nnetname: AUNIC-AU\n") != NULL)
return try_aunic (queryp);
else
return present_ripe_style_results (whois_data, apnic_handle);
}
#if !defined(RANGE_FUNC)
static int
try_arin (register char const *const queryp)
{
register char const *const whois_data = whois (arin_server, queryp);
if (!whois_data)
return 1;
if (!strncmp (whois_data, "No match", 8))
return 1;
else
return present_arin_style_results (whois_data);
}
#endif /* !defined(RANGE_FUNC) */
static void
initialize (void)
{
mswinsock_init();
if (!(protocol = getprotobyname (tcp)))
fatal (1, "%s: Unknown protocol: %s\n", pname, tcp);
if (!(service = getservbyname (whois_service, tcp)))
fatal (1, "%s: Unknown service: %s\n", pname, whois_service);
}
static void
finalize (void)
{
mswinsock_shutdown();
}
static int
process_ip_address (register in_addr addr)
{
register char const *dotted_quad;
register char const *dotted_blockname;
register char const *whois_data;
register int block_kind;
register unsigned long top_byte;
register unsigned long second_byte;
dotted_quad = strdup (inet_ntoa (addr));
#if 0
printf ("Checking %s\n", dotted_quad);
#endif
/* Don't ask me why, but the ARIN record(s) for the entire 192.114.0.0 -
192.118.255.255 block are snafued. If you do a lookup on anything in
that whole block, you will just get back a record describing the Israeli
master NIC, but with a little note attached saying that the data that
you were actually looking for is over at RIPE. Go figure! We just
compensate for this bit of insanity here. */
top_byte = (ntohl (addr.s_addr) >> 24) & 0xff;
second_byte = (ntohl (addr.s_addr) >> 16) & 0xff;
if (top_byte == 192 && second_byte >= 114 && second_byte <= 118)
{
try_ripe (dotted_quad);
return 0;
}
#if 0
/* Don't ask me why, but ARIN lookups seem to be totally broken for both
the 12.0.0.0/8 block and also the 24.0.0.0/8 block. The lookups just
plain don't work right. Try it for yourself and see. Do a lookup
using `whois -h whois.arin.net' on "24.2.4" and you will get back just
a failure indication which indicates that no matches were found. But
if you do a lookup on just "24", you will get back a boatload of matches
one of which will be a record for the "24.2.4.0 - 24.2.4.255" address
range. Go figure! Of course this proves that ARIN is totally fucked
up, but then we knew that already, right? Anyway, we try to compensate
here for ARIN's brain damage. */
top_byte = (ntohl (addr.s_addr) >> 24) & 0xff;
if (top_byte == 12)
{
/* In cases where none of the ranges of IP addresses that ARIN told
us about contains the original IP address we asked about, provide
the user with the WHOIS information for the onwer of this entire
/8 block, because we _do_ know who really owns this /8 block. */
if ((whois_data = whois (ARIN, "net 12")) == NULL)
whois_data = whois (ARIN, "NET-ATT");
else if ((whois_data = arin_grunge (whois_data, addr)) == NULL)
whois_data = whois (ARIN, "NET-ATT");
if (!whois_data)
return 1;
return present_arin_style_results (whois_data);
}
else
if (top_byte == 24)
{
/* In cases where none of the ranges of IP addresses that ARIN told
us about contains the original IP address we asked about, provide
the user with the WHOIS information for the onwer of this entire
/8 block, because we _do_ know who really owns this whole /8 block. */
if ((whois_data = whois (ARIN, "net 24")) == NULL)
whois_data = whois (ARIN, "HOME-NOC-ARIN");
else if ((whois_data = arin_grunge (whois_data, addr)) == NULL)
whois_data = whois (ARIN, "HOME-NOC-ARIN");
if (!whois_data)
return 1;
return present_arin_style_results (whois_data);
}
#endif /* 0 */
dotted_blockname = dotted_quad;
for (block_kind = 'D'; block_kind >= 'A'; block_kind--)
{
static char const ripe1[] = "European Regional Internet Registry/RIPE";
static char const ripe2[] = "RIPE NCC (NET-RIPE-NCC-";
static char const apnic[] = "Asia Pacific Network Information Center";
static char const nomatch[] = "No match";
static char const updated[] = "Record last updated on ";
static char const hostname[] = "Hostname:";
auto char arin_query[200];
try_next_addr:
strcpy (arin_query, "net ");
strcat (arin_query, dotted_blockname);
whois_data = whois (arin_server, arin_query);
if (whois_data == NULL) /* Server must be down! */
return 1;
if (strncmp (whois_data, nomatch, sizeof nomatch -1))
{
register char const *rwhois_data;
if (!strncmp (whois_data, ripe1, sizeof ripe1 - 1))
{
if (try_ripe (dotted_quad))
break;
else
return 0;
}
if (!strncmp (whois_data, ripe2, sizeof ripe2 - 1))
{
if (try_ripe (dotted_quad))
break;
else
return 0;
}
if (!strncmp (whois_data, apnic, sizeof apnic - 1))
{
if (try_apnic (dotted_quad))
break;
else
return 0;
}
/* This is rather messy, but when we are doing a lookup on a
complete address with ARIN, we can sometimes get back the
registration record for just the host at that address. But
we really don't want that. We really want the record for
the containing IP address block. So we have to do some
fiddling here to handle such cases. */
if (block_kind == 'D' && substr (whois_data, hostname))
{
register unsigned long haddr = ntohl (addr.s_addr);
haddr++;
addr.s_addr = htonl (haddr);
dotted_blockname = strdup (inet_ntoa (addr));
goto try_next_addr;
}
if (!substr (whois_data, updated))
{
/* Handle a special case. ARIN has returned to us a list of
the various address blocks that may or may not be related to
the one we are interested in. Now we have to grunge around
in that list and find the smallest block that contains the
address we are interested in. */
if ((whois_data = arin_grunge (whois_data, addr)) == NULL)
return 1;
}
if ((rwhois_data = fetch_rwhois_data (whois_data, dotted_quad)))
return present_rwhois_style_results (rwhois_data, whois_data);
else
return present_arin_style_results (whois_data);
}
dotted_blockname = containing_block (dotted_blockname);
if (dotted_blockname == NULL)
break;
}
fprintf (stderr, "%s: No registration record(s) for %s\n",
pname, dotted_quad);
return 1;
}
#if defined(RANGE_FUNC)
extern int
get_containing_block_range (register in_addr const addr,
register in_addr *const startp,
register in_addr *const endp);
int
get_containing_block_range (register in_addr const addr,
register in_addr *const startp,
register in_addr *const endp)
{
register int result = 0;
initialize ();
process_ip_address (addr);
finalize ();
return result;
}
#else /* !defined(RANGE_FUNC) */
static int
process_nic_handle (register char const *const arg)
{
/* use as nic handle
may include a prefix that identifies which registry it belongs to */
if (!strncmp (arin_handle, arg, sizeof arin_handle - 1))
{
if (try_arin (&arg[sizeof arin_handle - 1]))
fatal (2, "%s: ARIN handle not found: %s\n", pname, arg);
}
else if (!strncmp (ripe_handle, arg, sizeof ripe_handle - 1))
{
if (try_ripe (&arg[sizeof ripe_handle - 1]))
fatal (2, "%s: RIPE handle not found: %s\n", pname, arg);
}
else if (!strncmp (apnic_handle, arg, sizeof apnic_handle - 1))
{
if (try_apnic (&arg[sizeof apnic_handle - 1]))
fatal (2, "%s: APNIC handle not found: %s\n", pname, arg);
}
else if (!strncmp (aunic_handle, arg, sizeof aunic_handle - 1))
{
if (try_aunic (&arg[sizeof aunic_handle - 1]))
fatal (2, "%s: AUNIC handle not found: %s\n", pname, arg);
}
else /* no hint given, so we'll have to try 'em all */
{
if (try_arin (arg))
if (try_ripe (arg))
if (try_apnic (arg))
fatal (2, "%s: Handle not found in any registry: %s\n", pname, arg);
}
return 0;
}
static int
process_cmd_arg (register char const *const arg)
{
auto struct in_addr addr;
if (my_inet_aton (arg, &addr))
return process_ip_address (addr);
/* This test avoids unecessary DNS lookups,
at a loss of lookups on domain-less hostnames
which will fall through and be treated as nic handles */
else if (strchr (arg, '.'))
{
register struct hostent *hp;
if ((hp = my_gethostbyname (arg)) == NULL)
fatal (2, "%s: Invalid hostname: %s\n", pname, arg);
memcpy (&addr, hp->h_addr, sizeof addr);
return process_ip_address (addr);
}
/* use as nic handle
may include a prefix that identifies which registry it belongs to */
else
return process_nic_handle (arg);
}
int
main (register int const argc, register char *const argv[])
{
register int ch;
register int option_errors = 0;
pname = strrchr (argv[0], '/');
pname = pname ? pname+1 : argv[0];
initialize ();
atexit (finalize);
while ((ch = my_getopt (argc, argv, "anNvctCT:")) != EOF)
{
switch (ch)
{
case 'a':
addr_mode = True;
break;
case 'N':
add_handle_prefixes = True;
/* fall through */
case 'n':
handle_mode = True;
break;
case 'v':
verbose_mode = True;
break;
case 'c':
case 't':
contacts1_mode = True;
break;
case 'C':
contacts2_mode = True;
break;
case 'T':
timeout = atoi (my_optarg);
break;
case '?':
fprintf (stderr, "%s: Invalid option: -%c\n", pname, ch);
option_errors++;
break;
}
}
if (option_errors)
usage ();
if (my_optind == argc)
{
fprintf (stderr, "%s: Missing argument\n", pname);
usage ();
}
else if ((my_optind + 1) < argc)
{
fprintf (stderr, "%s: Too many arguments\n", pname);
usage ();
}
return process_cmd_arg (argv[my_optind]);
}
#endif /* !defined(RANGE_FUNC) */