#include "copyright.h"
#include "config.h"
#ifdef SOLARIS
# ifndef _POSIX_SOURCE
# define _POSIX_SOURCE /* Solaris needs this */
# endif
#endif
#include <sys/socket.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#ifdef USE_IPV6
# ifdef HAVE_NETINET6_IN6_H
# include <netinet6/in6.h>
# elif defined(HAVE_LINUX_IN6_H)
# include <linux/in6.h>
# elif defined(HAVE_IN6_H)
# include <in6.h>
# endif
#endif
/*
* SunOS can't include signal.h and sys/signal.h, stupid broken OS.
*/
#if defined(HAVE_SYS_SIGNAL_H) && !defined(SUN_OS)
# include <sys/signal.h>
#endif
/* number of hostnames cached in an LRU queue */
#define HOST_CACHE_SIZE 8192
/* Time before retrying to resolve a previously unresolved IP address. */
/* 1800 seconds == 30 minutes */
#define EXPIRE_TIME 1800
/* Time before the resolver gives up on a identd lookup. Prevents hangs. */
#define IDENTD_TIMEOUT 60
int
notify(int player, const char* msg)
{
return printf("%s\n", msg);
}
#ifdef USE_IPV6
const char *addrout(struct in6_addr *, unsigned short, unsigned short);
#else
const char *addrout(long, unsigned short, unsigned short);
#endif
#define MALLOC(result, type, number) do { \
if (!((result) = (type *) malloc ((number) * sizeof (type)))) \
abort(); \
} while (0)
#define FREE(x) (free((void *) x))
struct hostcache {
#ifdef USE_IPV6
struct in6_addr ipnum;
#else
long ipnum;
#endif
char name[128];
time_t time;
struct hostcache *next;
struct hostcache **prev;
} *hostcache_list = 0;
#ifdef USE_IPV6
int
ipcmp(struct in6_addr *a, struct in6_addr *b)
{
int i = 128;
char *A = (char *) a;
char *B = (char *) b;
while (i) {
i--;
if (*A++ != *B++)
break;
}
return i;
}
#endif
void
#ifdef USE_IPV6
hostdel(struct in6_addr *ip)
#else
hostdel(long ip)
#endif
{
struct hostcache *ptr;
for (ptr = hostcache_list; ptr; ptr = ptr->next) {
#ifdef USE_IPV6
if (!ipcmp(&(ptr->ipnum), ip)) {
#else
if (ptr->ipnum == ip) {
#endif
if (ptr->next) {
ptr->next->prev = ptr->prev;
}
*ptr->prev = ptr->next;
FREE(ptr);
return;
}
}
}
const char *
#ifdef USE_IPV6
hostfetch(struct in6_addr *ip)
#else
hostfetch(long ip)
#endif
{
struct hostcache *ptr;
for (ptr = hostcache_list; ptr; ptr = ptr->next) {
#ifdef USE_IPV6
if (!ipcmp(&(ptr->ipnum), ip)) {
#else
if (ptr->ipnum == ip) {
#endif
if (time(NULL) - ptr->time > EXPIRE_TIME) {
hostdel(ip);
return NULL;
}
if (ptr != hostcache_list) {
*ptr->prev = ptr->next;
if (ptr->next) {
ptr->next->prev = ptr->prev;
}
ptr->next = hostcache_list;
if (ptr->next) {
ptr->next->prev = &ptr->next;
}
ptr->prev = &hostcache_list;
hostcache_list = ptr;
}
return (ptr->name);
}
}
return NULL;
}
void
hostprune(void)
{
struct hostcache *ptr;
struct hostcache *tmp;
int i = HOST_CACHE_SIZE;
ptr = hostcache_list;
while (i-- && ptr) {
ptr = ptr->next;
}
if (i < 0 && ptr) {
*ptr->prev = NULL;
while (ptr) {
tmp = ptr->next;
FREE(ptr);
ptr = tmp;
}
}
}
void
#ifdef USE_IPV6
hostadd(struct in6_addr *ip, const char *name)
#else
hostadd(long ip, const char *name)
#endif
{
struct hostcache *ptr;
MALLOC(ptr, struct hostcache, 1);
ptr->next = hostcache_list;
if (ptr->next) {
ptr->next->prev = &ptr->next;
}
ptr->prev = &hostcache_list;
hostcache_list = ptr;
#ifdef USE_IPV6
memcpy(&(ptr->ipnum), ip, sizeof(struct in6_addr));
#else
ptr->ipnum = ip;
#endif
ptr->time = 0;
strcpy(ptr->name, name);
hostprune();
}
void
#ifdef USE_IPV6
hostadd_timestamp(struct in6_addr *ip, const char *name)
#else
hostadd_timestamp(long ip, const char *name)
#endif
{
hostadd(ip, name);
hostcache_list->time = time(NULL);
}
void set_signals(void);
#ifdef _POSIX_VERSION
void our_signal(int signo, void (*sighandler) (int));
#else
# define our_signal(s,f) signal((s),(f))
#endif
/*
* our_signal(signo, sighandler)
*
* signo - Signal #, see defines in signal.h
* sighandler - The handler function desired.
*
* Calls sigaction() to set a signal, if we are posix.
*/
#ifdef _POSIX_VERSION
void
our_signal(int signo, void (*sighandler) (int))
{
struct sigaction act, oact;
act.sa_handler = sighandler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
/* Restart long system calls if a signal is caught. */
#ifdef SA_RESTART
act.sa_flags |= SA_RESTART;
#endif
/* Make it so */
sigaction(signo, &act, &oact);
}
#endif /* _POSIX_VERSION */
/*
* set_signals()
* set_sigs_intern(bail)
*
* Traps a bunch of signals and reroutes them to various
* handlers. Mostly bailout.
*
* If called from bailout, then reset all to default.
*
* Called from main() and bailout()
*/
void
set_signals(void)
{
/* we don't care about SIGPIPE, we notice it in select() and write() */
our_signal(SIGPIPE, SIG_IGN);
/* didn't manage to lose that control tty, did we? Ignore it anyway. */
our_signal(SIGHUP, SIG_IGN);
}
void
make_nonblocking(int s)
{
#if !defined(O_NONBLOCK) || defined(ULTRIX) /* POSIX ME HARDER */
# ifdef FNDELAY /* SUN OS */
# define O_NONBLOCK FNDELAY
# else
# ifdef O_NDELAY /* SyseVil */
# define O_NONBLOCK O_NDELAY
# endif /* O_NDELAY */
# endif /* FNDELAY */
#endif
if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) {
perror("make_nonblocking: fcntl");
abort();
}
}
const char *
#ifdef USE_IPV6
get_username(struct in6_addr *a, int prt, int myprt)
#else
get_username(long a, int prt, int myprt)
#endif
{
int fd, len, result;
char *ptr, *ptr2;
static char buf[1024];
int lasterr;
int timeout = IDENTD_TIMEOUT;
#ifdef USE_IPV6
struct sockaddr_in6 addr;
if ((fd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {
#else
struct sockaddr_in addr;
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
#endif
perror("resolver ident socket");
return (0);
}
make_nonblocking(fd);
len = sizeof(addr);
#ifdef USE_IPV6
addr.sin6_family = AF_INET6;
memcpy(&addr.sin6_addr.s6_addr, a, sizeof(struct in6_addr));
addr.sin6_port = htons((short) 113);
#else
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = a;
addr.sin_port = htons((short) 113);
#endif
do {
result = connect(fd, (struct sockaddr *) &addr, len);
lasterr = errno;
if (result < 0) {
if (!timeout--)
break;
sleep(1);
}
} while (result < 0 && lasterr == EINPROGRESS);
if (result < 0 && lasterr != EISCONN) {
goto bad;
}
snprintf(buf, sizeof(buf), "%d,%d\n", prt, myprt);
do {
result = write(fd, buf, strlen(buf));
lasterr = errno;
if (result < 0) {
if (!timeout--)
break;
sleep(1);
}
} while (result < 0 && lasterr == EAGAIN);
if (result < 0)
goto bad2;
do {
result = read(fd, buf, sizeof(buf));
lasterr = errno;
if (result < 0) {
if (!timeout--)
break;
sleep(1);
}
} while (result < 0 && lasterr == EAGAIN);
if (result < 0)
goto bad2;
ptr = index(buf, ':');
if (!ptr)
goto bad2;
ptr++;
if (*ptr)
ptr++;
if (strncmp(ptr, "USERID", 6))
goto bad2;
ptr = index(ptr, ':');
if (!ptr)
goto bad2;
ptr = index(ptr + 1, ':');
if (!ptr)
goto bad2;
ptr++;
/* if (*ptr) ptr++; */
shutdown(fd, 2);
close(fd);
if ((ptr2 = index(ptr, '\r')))
*ptr2 = '\0';
if (!*ptr)
return (0);
return ptr;
bad2:
shutdown(fd, 2);
bad:
close(fd);
return (0);
}
/* addrout -- Translate address 'a' from int to text. */
const char *
#ifdef USE_IPV6
addrout(struct in6_addr *a, unsigned short prt, unsigned short myprt)
#else
addrout(long a, unsigned short prt, unsigned short myprt)
#endif
{
static char buf[128];
char tmpbuf[128];
const char *ptr, *ptr2;
struct hostent *he;
#ifdef USE_IPV6
struct in6_addr addr;
memcpy(&addr.s6_addr, a, sizeof(struct in6_addr));
ptr = hostfetch(a);
#else
struct in_addr addr;
addr.s_addr = a;
ptr = hostfetch(ntohl(a));
#endif
if (ptr) {
ptr2 = get_username(a, prt, myprt);
if (ptr2) {
snprintf(buf, sizeof(buf), "%s(%s)", ptr, ptr2);
} else {
snprintf(buf, sizeof(buf), "%s(%d)", ptr, prt);
}
return buf;
}
#ifdef USE_IPV6
he = gethostbyaddr(((char *) &addr), sizeof(addr), AF_INET6);
#else
he = gethostbyaddr(((char *) &addr), sizeof(addr), AF_INET);
#endif
if (he) {
strcpy(tmpbuf, he->h_name);
#ifdef USE_IPV6
hostadd(a, tmpbuf);
#else
hostadd(ntohl(a), tmpbuf);
#endif
ptr = get_username(a, prt, myprt);
if (ptr) {
snprintf(buf, sizeof(buf), "%s(%s)", tmpbuf, ptr);
} else {
snprintf(buf, sizeof(buf), "%s(%d)", tmpbuf, prt);
}
return buf;
}
#ifdef USE_IPV6
inet_ntop(AF_INET6, a, tmpbuf, 128);
hostadd_timestamp(a, tmpbuf);
ptr = get_username(a, prt, myprt);
#else
a = ntohl(a);
snprintf(tmpbuf, sizeof(tmpbuf), "%ld.%ld.%ld.%ld",
(a >> 24) & 0xff, (a >> 16) & 0xff, (a >> 8) & 0xff, a & 0xff);
hostadd_timestamp(a, tmpbuf);
ptr = get_username(htonl(a), prt, myprt);
#endif
if (ptr) {
snprintf(buf, sizeof(buf), "%s(%s)", tmpbuf, ptr);
} else {
snprintf(buf, sizeof(buf), "%s(%d)", tmpbuf, prt);
}
return buf;
}
#define erreturn { \
return 0; \
}
int
do_resolve(void)
{
int ip1, ip2, ip3, ip4;
int prt, myprt;
int doagain;
char *result;
const char *ptr;
char buf[1024];
#ifdef USE_IPV6
char ipv6addr[128];
struct in6_addr fullip;
char *bufptr;
#else
long fullip;
#endif
ip1 = ip2 = ip3 = ip4 = prt = myprt = -1;
do {
doagain = 0;
*buf = '\0';
result = fgets(buf, sizeof(buf), stdin);
if (!result) {
if (errno == EAGAIN) {
doagain = 1;
sleep(1);
} else {
if (feof(stdin))
erreturn;
perror("fgets");
erreturn;
}
}
} while (doagain || !strcmp(buf, "\n"));
#ifdef USE_IPV6
bufptr = strchr(buf, '(');
if (!bufptr)
erreturn;
sscanf(bufptr, "(%d)%d", &prt, &myprt);
*bufptr = '\0';
if (myprt > 65535 || myprt < 0)
erreturn;
if (inet_pton(AF_INET6, buf, &fullip) <= 0)
erreturn;
ptr = addrout(&fullip, prt, myprt);
printf("%s(%d)|%s\n", buf, prt, ptr);
#else
sscanf(buf, "%d.%d.%d.%d(%d)%d", &ip1, &ip2, &ip3, &ip4, &prt, &myprt);
if (ip1 < 0 || ip2 < 0 || ip3 < 0 || ip4 < 0 || prt < 0)
erreturn;
if (ip1 > 255 || ip2 > 255 || ip3 > 255 || ip4 > 255 || prt > 65535)
erreturn;
if (myprt > 65535 || myprt < 0)
erreturn;
fullip = (ip1 << 24) | (ip2 << 16) | (ip3 << 8) | ip4;
fullip = htonl(fullip);
ptr = addrout(fullip, prt, myprt);
printf("%d.%d.%d.%d(%d):%s\n", ip1, ip2, ip3, ip4, prt, ptr);
#endif
fflush(stdout);
return 1;
}
int
main(int argc, char **argv)
{
if (argc > 1) {
fprintf(stderr, "Usage: %s\n", *argv);
exit(1);
return 1;
}
/* remember to ignore certain signals */
set_signals();
/* go do it */
while (do_resolve()) ;
fprintf(stderr, "Resolver exited.\n");
exit(0);
return 0;
}