/*
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.
 *
 * 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.
 */

#ifndef lint
static char Version[] = "@(#)aslookup.c	e07@nikhef.nl (Eric Wassenaar) 991603";
#endif

#if defined(apollo) && defined(lint)
#define __attribute(x)
#endif

#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <signal.h>
#include <setjmp.h>
#include <netdb.h>

#include <sys/types.h>		/* not always automatically included */
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include "port.h"
#include "aslookup.h"
#include "exit.h"

#ifndef MAXDNAME
#define MAXDNAME	256	/* maximum length of domain name */
#endif

#ifdef lint
#define EXTERN
#else
#define EXTERN extern
#endif

EXTERN int errno;
EXTERN int h_errno;
extern char *version;		/* program version number */

#define NOT_DOTTED_QUAD	((ipaddr_t)-1)

#define MAXINT16	65535

#define MAXADDRS	35	/* max address count from gethostnamadr.c */

#include "defs.h"		/* declarations of functions */

#define sameword(a,b)	(strcasecmp(a,b) == 0)

#define is_space(c)	(isascii(c) && isspace(c))

#define setalarm(n)	(void) alarm((unsigned int)(n))

/*
** AS_LOOKUP -- Lookup the AS-number of the given IP address
** ---------------------------------------------------------
**
**	Returns:
**		Pointer to AS-number ascii string, if found.
**		NULL if it could not be determined.
**
**	Side effects:
**		Keep the status of the connection cached as follows:
**		EX_NOINPUT	Not connected (initial state)
**		EX_SUCCESS	Connected
**		EX_TEMPFAIL	Temporary failure on previous connect
**		EX_NOHOST	Permanent failure (unknown server)
**		EX_UNAVAILABLE	Permanent failure (service not running)
*/

#define MAXAS	80		/* maximum size of AS numbers and names */

char *
as_lookup(inaddr, server, port, keepopen)
struct in_addr inaddr;			/* IP address to look up */
char *server;				/* explicit whois server */
int port;				/* specific tcp port */
bool keepopen;				/* keep server connection alive */
{
	static int status = EX_NOINPUT;	/* cached connection status */
	static char *options = NULL;	/* default whois server options */
	bool newline = FALSE;		/* set if newline encountered */
	bool inobject = FALSE;		/* set if examining db object */

	static char numberbuf[MAXAS+1];	/* AS-number string if found */
	char *number = NULL;		/* set to numberbuf if found */
	static char namebuf[MAXAS+1];	/* AS-name string if found */
	char *name = NULL;		/* set to namebuf if found */
	static char buf[2*MAXAS+3+1];
	register char *p;

/*
 * Determine server if not explicitly given.
 */
	if (server == NULL && status != EX_SUCCESS)
	{
		/* check environment */
		p = getenv("AS_SERVER_HOST");
		if (p != NULL && *p != '\0')
			server = p;
#ifdef AS_SERVER_HOST
		/* use default server if defined */
		if (server == NULL)
			server = AS_SERVER_HOST;
#endif
		/* at this point it must exist */
		if (server == NULL)
		{	
			/* result = EX_NOINPUT; */
			return(NULL);
		}
	}

/*
 * Determine service port if not explicitly given.
 * If still unspecified, the default service port will be used.
 */
	if (port == 0 && status != EX_SUCCESS)
	{
		/* check environment */
		p = getenv("AS_SERVER_PORT");
		if (p != NULL && *p != '\0')
			port = atoi(p);

		/* ensure it is in range */
		if (port < 0 || port > MAXINT16)
			port = 0;
	}

/*
 * Fetch specific whois server options.
 * If still unspecified, use default options if available.
 */
	if (options == NULL)
	{
		/* check environment */
		p = getenv("AS_SERVER_OPTIONS");
		if (p != NULL && *p != '\0')
			options = p;
#ifdef AS_SERVER_OPTIONS
		/* use default options if defined */
		if (options == NULL)
			options = AS_SERVER_OPTIONS;
#endif
		/* in case there are no options */
		if (options == NULL)
			options = "";
	}

/*
 * Connect to the server, unless already connected.
 */
	/* do not re-connect after permament failures */
	if (status == EX_NOINPUT || status == EX_TEMPFAIL)
		status = as_connect(server, port);

	/* give up if connect failed -- cache status */
	if (status != EX_SUCCESS)
		return(NULL);

/*
 * Construct and issue the AS-number lookup command.
 */
	if ((options[0] == '-' && options[1] == 'k') || !keepopen)
		(void) sprintf(buf, "%.80s %s", options, as_netof(inaddr));
	else
		(void) sprintf(buf, "-k %.80s %s", options, as_netof(inaddr));

	if (buf[0] == '-' && buf[1] == 'k')
		keepopen = TRUE;

	/* send the command */
	as_put(buf);

/*
 * Try to filter out the AS-number from the reply.
 */
	while ((p = as_get(buf, sizeof(buf))) != NULL)
	{
		while (is_space(buf[0]))
			(void) strcpy(buf, buf+1);

		/* double newline terminates input */
		if ((buf[0] == '\0') && newline)
			break;
		newline = (buf[0] == '\0') ? TRUE : FALSE;

		/* single newline terminates database object */
		if (newline)
			inobject = FALSE;

		/* split keyword and parameters */
		p = index(buf, ':');
		if (p == NULL)
			continue;
		*p++ = '\0';
		while (is_space(*p))
			p++;

	/*
	 * Extract AS-number from the first route object found.
	 * Kludge: ensure enough CIDR bits, and skip dummy AS-number.
	 */
		if (!inobject && sameword(buf, "route"))
		{
			p = rindex(p, '/');
			if (p != NULL && atoi(p+1) >= 8)
				inobject = TRUE;
		}
		else if (inobject && sameword(buf, "origin"))
		{
			if ((number == NULL) && !sameword(p, "AS0"))
				number = strncpy(numberbuf, p, MAXAS);
		}
		else if (inobject && sameword(buf, "descr"))
		{
			if (name == NULL)
				name = strncpy(namebuf, p, MAXAS);
		}
	}

/*
 * Disconnect from server if necessary.
 */
	/* always disconnect on errors */
	if ((p == NULL) || !keepopen)
	{
		as_disconnect();
		status = EX_NOINPUT;
	}

	/* return partial result */
	if (number == NULL || name == NULL)
		return(number != NULL ? number : name);

	/* combine both answers */
	(void) sprintf(buf, "%s - %s", number, name);
	return(buf);
}

/*
** AS_CONNECT -- Establish connection to WHOIS server
** --------------------------------------------------
**
**	Returns:
**		Status code indicating success or failure.
**
**	Outputs:
**		Sets OutChannel and InChannel.
*/

int ConnTimeout = CONNTIMEOUT;	/* timeout in secs for connect */
int ReadTimeout = READTIMEOUT;	/* timeout in secs for read reply */
FILE *InChannel = NULL;		/* input channel from server */
FILE *OutChannel = NULL;	/* output channel to server */

static jmp_buf Timeout;

static sigtype_t /*ARGSUSED*/
timer(sig)
int sig;
{
	longjmp(Timeout, 1);
	/*NOTREACHED*/
}

int
as_connect(server, port)
char *server;				/* host name to connect to */
int port;				/* tcp port on server (host order) */
{
	struct in_addr inaddr[MAXADDRS];/* all server addresses */
	int naddrs;			/* number of server addresses */
	struct hostent *hp;
	struct sockaddr_in sin;
	ipaddr_t addr;
	int sock;
	register int i;

/*
 * Reset state.
 */
	bzero((char *)&sin, sizeof(sin));

	errno = 0;
	h_errno = 0;

	if (server == NULL || server[0] == '\0')
		server = "localhost";

	/* use default port if unspecified */
	if (port == 0)
		port = AS_SERVER_PORT;

/*
 * Fetch the ip addresses of the given host.
 */
	addr = inet_addr(server);

	if (addr == NOT_DOTTED_QUAD)
	{
		hp = gethostbyname(server);
		if (hp == NULL)
		{
			/* cannot contact nameserver, force retry */
			if (errno == ETIMEDOUT || errno == ECONNREFUSED)
				h_errno = TRY_AGAIN;

			/* nameserver could not resolve name properly */
			if (h_errno == TRY_AGAIN)
				return(EX_TEMPFAIL);

			/* no address found by nameserver */
			return(EX_NOHOST);
		}

		for (i = 0; i < MAXADDRS && hp->h_addr_list[i]; i++)
			bcopy(hp->h_addr_list[i], (char *)&inaddr[i], INADDRSZ);
		naddrs = i;
	}
	else
	{
		inaddr[0].s_addr = addr;
		naddrs = 1;
	}

/*
 * Try to make connection to each of the addresses in turn.
 */
	for (i = 0; i < naddrs; i++)
	{
		sin.sin_family = AF_INET;
		sin.sin_port = htons((u_short)port);
		sin.sin_addr = inaddr[i];

		sock = socket(AF_INET, SOCK_STREAM, 0);
		if (sock < 0)
		{
			/* cannot get socket */
			return(EX_TEMPFAIL);
		}

		if (setjmp(Timeout) != 0)
		{
			(void) close(sock);
			errno = ETIMEDOUT;
			continue;
		}

	/*
	 * Connect to the server using a short timeout.
	 * Exit immediately if the service is not running on the server.
	 */
		(void) signal(SIGALRM, timer);
		setalarm(ConnTimeout);
		if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
		{
			int save_errno = errno;
			setalarm(0);
			(void) close(sock);
			errno = save_errno;
			if (errno == EINTR)
				errno = ETIMEDOUT;
			if (errno == ECONNREFUSED)
			{
				/* service is not running */
				return(EX_UNAVAILABLE);
			}
			continue;
		}
		setalarm(0);

	/*
	 * Put connection in canonical form.
	 */
		OutChannel = fdopen(sock, "w");
		InChannel  = fdopen(dup(sock), "r");

		if (InChannel == NULL || OutChannel == NULL)
		{
			int save_errno = errno;
			(void) close(sock);
			errno = save_errno;
			as_disconnect();

			/* could not setup channel */
			return(EX_TEMPFAIL);
		}

		errno = 0;
		h_errno = 0;

		/* connection established */
		return(EX_SUCCESS);
	}

	/* all connection attempts failed */
	return(EX_TEMPFAIL);
}

/*
** AS_DISCONNECT -- Disconnect from WHOIS server
** ---------------------------------------------
**
**	Returns:
**		None.
**
**	Outputs:
**		Resets OutChannel and InChannel.
*/

void
as_disconnect()
{
	int save_errno = errno;		/* preserve state */

	if (InChannel != NULL)
	{
		(void) fclose(InChannel);
		InChannel = NULL;
	}

	if (OutChannel != NULL)
	{
		(void) fclose(OutChannel);
		OutChannel = NULL;
	}

	/* restore state */
	errno = save_errno;
}

/*
** AS_PUT -- Issue command to WHOIS server
** ---------------------------------------
**
**	Returns:
**		None.
**
**	Side effects:
**		Appends CRLF to the command.
*/

void
as_put(buf)
char *buf;				/* output buffer with command */
{
	if (OutChannel != NULL)
	{
		/* send the message over the channel */
		(void) fprintf(OutChannel, "%s\r\n", buf);

		/* immediately flush the output channel */
		(void) fflush(OutChannel);
	}
}

/*
** AS_GET -- Read an input line, using timeout
** -------------------------------------------
**
**	Returns:
**		Pointer to start of input line, if read ok.
**		NULL on error (including timeout).
**
**	Side effects:
**		Strips trailing CRLF combination.
*/

char *
as_get(buf, size)
char *buf;				/* input buffer */
int size;				/* size of input buffer */
{
	register char *p = NULL;

	/* in case not connected */
	if (InChannel == NULL)
	{
		errno = ECONNRESET;
		return(NULL);
	}

	/* arrange for timeout */
	if (setjmp(Timeout) != 0)
	{
		errno = ETIMEDOUT;
		return(NULL);
	}

/*
 * Read a reply from the input channel with timeout.
 */
	(void) signal(SIGALRM, timer);
	setalarm(ReadTimeout);
	while ((p == NULL) && !feof(InChannel) && !ferror(InChannel))
	{
		errno = 0;
		p = fgets(buf, size, InChannel);
		if (errno == EINTR)
			clearerr(InChannel);
	}
	setalarm(0);

/*
 * Determine possible error condition.
 */
	if (p == NULL)
	{
		if (feof(InChannel) && (errno == 0))
			errno = ECONNRESET;
		if (ferror(InChannel) && (errno == 0))
			errno = EIO;
		return(NULL);
	}

/*
 * Remove trailing CRLF combination.
 */
	p = index(buf, '\n');
	if (p != NULL)
	{
		if (p > buf && p[-1] == '\r')
			p--;
		*p = '\0';
	}

	/* valid reply received */
	return(buf);
}

/*
** AS_NETOF -- Get ascii representation of network number
** ------------------------------------------------------
**
**	Returns:
**		Pointer to string in static storage.
*/

#define	CLASSA(a)	(((a) & (ipaddr_t)0x80000000) == (ipaddr_t)0x00000000)
#define	CLASSB(a)	(((a) & (ipaddr_t)0xC0000000) == (ipaddr_t)0x80000000)
#define	CLASSC(a)	(((a) & (ipaddr_t)0xE0000000) == (ipaddr_t)0xC0000000)

#define	CLASSD(a)	(((a) & (ipaddr_t)0xF0000000) == (ipaddr_t)0xE0000000)
#define	CLASSE(a)	(((a) & (ipaddr_t)0xF0000000) == (ipaddr_t)0xF0000000)
#define	CLASSL(a)	(((a) & (ipaddr_t)0xFF000000) == (ipaddr_t)0x7F000000)

#define	CLASSA_NET	(ipaddr_t)0xFF000000
#define	CLASSB_NET	(ipaddr_t)0xFFFF0000
#define	CLASSC_NET	(ipaddr_t)0xFFFFFF00
#define	CLASSD_NET	(ipaddr_t)0xFFFFFFFF	/* XXX */
#define	CLASSE_NET	(ipaddr_t)0xFFFFFFFF	/* XXX */

char *
as_netof(inaddr)
struct in_addr inaddr;			/* IP address */
{
	register ipaddr_t address = ntohl(inaddr.s_addr);
	register ipaddr_t netmask;

	if (CLASSA(address))
		netmask = CLASSA_NET;
	else if (CLASSB(address))
		netmask = CLASSB_NET;
	else if (CLASSC(address))
		netmask = CLASSC_NET;
	else if (CLASSD(address))
		netmask = CLASSD_NET;
	else
		netmask = CLASSE_NET;

	/* mask network part */
	address &= netmask;

#ifdef AS_NETMASK
	inaddr.s_addr = htonl(address);
#endif
	return(inet_ntoa(inaddr));
}

/*
** Special definitions if compiled stand-alone.
*/

#ifdef STANDALONE

/* main.c */
int main		PROTO((int, char **));
int lookup		PROTO((char *, char *, int, bool));
void fatal		PROTO((char *, ...));
void error		PROTO((char *, ...));

/* misc.c */
char *itoa		PROTO((int));

static char Usage[] = "Usage: %s [-h server] [-k] [-p port] host ...";

/*
** MAIN -- Start of program aslookup
** ---------------------------------
**
**	Standalone wrapper test program for as_lookup().
**
**	Exits:
**		Various possibilities from <sysexits.h>
**		EX_SUCCESS if all lookups were successful.
*/

int
main(argc, argv)
int argc;
char *argv[];
{
	int excode = EX_NOINPUT;	/* overall result status */
	int result;			/* result status of action taken */
	register int optvalue;
	register char *option;
	register int i;
	char *program;			/* name of program */
	char *server = NULL;		/* explicit whois server */
	int port = 0;			/* specific tcp port */
	bool keepopen = FALSE;		/* keep connection open */

/*
 * Check command line options.
 */
	if (argc < 1 || argv[0] == NULL)
		exit(EX_USAGE);

	program = rindex(argv[0], '/');
	if (program++ == NULL)
		program = argv[0];

	while (argc > 1 && argv[1] != NULL && argv[1][0] == '-')
	{
	    for (option = &argv[1][1]; *option != '\0'; option++)
	    {
		switch (*option)
		{
		    case 'h':
			/* name of whois server host */
			if (argv[2] == NULL || argv[2][0] == '-')
				fatal("Missing server name");
			server = argv[2];
			argc--, argv++;
			break;

		    case 'k':
			/* keep connection to server open */
			keepopen = TRUE;
			break;

		    case 'p':
			/* port number of whois service */
			if (argv[2] == NULL || argv[2][0] == '-')
				fatal("Missing port number");
			optvalue = atoi(argv[2]);
			if (optvalue < 1)
				fatal("Invalid port number %s", argv[2]);
			if (optvalue > MAXINT16)
				fatal("Maximum port number %s", itoa(MAXINT16));
			port = optvalue;
			argc--, argv++;
			break;

		    case 'V':
			printf("Version %s\n", version);
			exit(EX_SUCCESS);

		    default:
			fatal(Usage, program);
		}
	    }

	    argc--, argv++;
	}

/*
 * Fetch (mandatory) remote host address(es) to look up.
 */
	if (argc < 2 || argv[1] == NULL)
		fatal(Usage, program);

	/* process each of the command line arguments */
	for (i = 1; i < argc && argv[i] != NULL; i++)
	{
		/* single host lookup */
		result = lookup(argv[i], server, port, keepopen);

		/* maintain overall result */
		if (result != EX_SUCCESS || excode == EX_NOINPUT)
			excode = result;
	}

	/* return overall result */
	return(excode);
	/*NOTREACHED*/
}

/*
** LOOKUP -- Perform AS lookup for given host specification
** --------------------------------------------------------
**
**	Returns:
**		Overall result of all address lookups.
**		EX_SUCCESS if all lookups were successful.
*/

int
lookup(host, server, port, keepopen)
char *host;				/* host name/address to look up */
char *server;				/* explicit whois server */
int port;				/* specific tcp port */
bool keepopen;				/* keep server connection alive */
{
	int excode = EX_NOINPUT;	/* overall result status */
	int result;			/* result status of action taken */
	struct in_addr inaddr[MAXADDRS];/* all host IP addresses */
	int naddrs;			/* number of host addresses */
	char hostbuf[MAXDNAME+1];	/* fully qualified host name */
	struct hostent *hp;
	ipaddr_t addr;
	register int i;

/*
 * Fetch the ip address(es) of the given host.
 */
	addr = inet_addr(host);

	if (addr == NOT_DOTTED_QUAD)
	{
		hp = gethostbyname(host);
		if (hp == NULL)
		{
			error("Unknown host: %s", host);
			return(EX_NOHOST);
		}

		for (i = 0; i < MAXADDRS && hp->h_addr_list[i]; i++)
			bcopy(hp->h_addr_list[i], (char *)&inaddr[i], INADDRSZ);
		naddrs = i;
	}
	else
	{
		inaddr[0].s_addr = addr;
		naddrs = 1;

		hp = gethostbyaddr((char *)&inaddr[0], INADDRSZ, AF_INET);
	}

/*
 * Fetch its canonical name.
 */
	if (hp != NULL)
	{
		host = strncpy(hostbuf, hp->h_name, MAXDNAME);
		host[MAXDNAME] = '\0';
	}

/*
 * Lookup each of the addresses in turn.
 */
	for (i = 0; i < naddrs; i++)
	{
		struct netent *np;
		char *asnumber;

		/* single address network name lookup */
		np = getnetbyaddr(inet_netof(inaddr[i]), AF_INET);

		/* single address AS-number lookup */
		asnumber = as_lookup(inaddr[i], server, port, keepopen);
		result = (asnumber != NULL) ? EX_SUCCESS : EX_UNAVAILABLE;

		printf("%s (%s)", host, inet_ntoa(inaddr[i]));
		if (np != NULL)
			printf(" (%s)", np->n_name);
		if (asnumber != NULL)
			printf(" [%s]", asnumber);
		printf("\n");

		/* maintain overall result */
		if (result != EX_SUCCESS || excode == EX_NOINPUT)
			excode = result;
	}

	/* return overall result */
	return(excode);
}

/*
** FATAL -- Abort program when illegal option encountered
** ------------------------------------------------------
**
**	Returns:
**		Aborts after issuing error message.
*/

void /*VARARGS1*/
fatal(fmt, a, b, c, d)
char *fmt;				/* format of message */
char *a, *b, *c, *d;			/* optional arguments */
{
	(void) fprintf(stderr, fmt, a, b, c, d);
	(void) fprintf(stderr, "\n");
	exit(EX_USAGE);
}


/*
** ERROR -- Issue error message to error output
** --------------------------------------------
**
**	Returns:
**		None.
*/

void /*VARARGS1*/
error(fmt, a, b, c, d)
char *fmt;				/* format of message */
char *a, *b, *c, *d;			/* optional arguments */
{
	(void) fprintf(stderr, fmt, a, b, c, d);
	(void) fprintf(stderr, "\n");
}

/*
** ITOA -- Convert integer value to ascii string
** ---------------------------------------------
**
**	Returns:
**		Pointer to string.
*/

char *
itoa(n)
int n;					/* value to convert */
{
	static char buf[30];		/* sufficient for 64-bit values */

	(void) sprintf(buf, "%d", n);
	return(buf);
}

#endif /*STANDALONE*/


syntax highlighted by Code2HTML, v. 0.9.1