Dean Gaudet's async hostname/identd lookup code

From: Jeremy Elson (jelson@blaze.cs.jhu.edu)
Date: 06/30/96


Here it is (written for Arctic).. if anyone feels like porting it to Circle,
I'm sure a lot of people would appreciate it.....


-Jeremy


Article 11714 of rec.games.mud.diku:
Newsgroups: rec.games.mud.diku
Path: blaze.cs.jhu.edu!mozart.amil.jhu.edu!europa.eng.gtefsd.com!howland.reston.ans.net!cs.utexas.edu!utnut!torn!watserv2.uwaterloo.ca!undergrad.math.uwaterloo.ca!dgaudet
From: dgaudet@undergrad.math.uwaterloo.ca (Dean Gaudet)
Subject: asynch gethostname() code (was Re: gethostname() hangs circlemud, SLS linux .99)
Message-ID: <CrBIuB.CwL@undergrad.math.uwaterloo.ca>
Sender: news@undergrad.math.uwaterloo.ca (news spool owner)
Organization: University of Waterloo
References: <2t7drp$4dm@giga.bga.com> <Cr5Iqq.12u@athena.ulaval.ca> <2t95qa$fbi@mailhost.interaccess.com>
Date: Mon, 13 Jun 1994 04:22:59 GMT
Lines: 571

In article <2t95qa$fbi@mailhost.interaccess.com>,
Sonic Death <thc@interaccess.com> wrote:
>Another alternative is setting up a seperate server to retrieve the
>proper address it send it back to the dmserver. hmm, is this
>possible? I do not know much about interproccess communication.

It's not only possible... it's how I did it in Arctic's code.  It
was a one day hack... I've included the skeleton stuff in hopes
that other folks will merge it into the main diku codebases.

BTW, not only does this do asynch hostname lookup, it does asynch
rfc 1413 lookups.  That is: it grabs userids when it can.  I don't
feel like going into the details as to why this information can
be useless... you can find that argument in the unix security
newsgroups.  We found it fairly useful -- just watch out for folks
using PCs.

Dean

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README comm.c slave.c slave.h
# Wrapped by dgaudet@mobius04.math on Mon Jun 13 00:17:07 1994
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(1046 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XThis is very rough code.  Slave was hacked together and never cleaned
Xup.  I'm almost ashamed to release it... but it's time that other
Xdikus get asynch hostname and username lookups.
X
Xslave.c probably needs some patching to not rely on my own personal library.
X
Xcomm.c is code that has to be merged into your comm.c and fiddled to fit
Xwhatever constants/etc you have in your code.  You'll have to change/add
Xfields into your descriptor structure.
X
Xslave.h is pretty obvious.
X
XBuild slave.c as a seperate executable, and place it in your path somewhere.
X
XOnce working you have asynchronous hostname/username lookups.  Enjoy.
XI suggest you modify your "users" command (hey is this stock diku?  It's
Xbeen so long for me since I saw stock...) so that it prints the new
Xuserid field from your descriptor structure.
X
XBugs: slave.c needs to set an alarm to wake itself up and test if it's
Xppid is 1 -- this happens when the mud crashes and orphans the slave.
XI never figured out why the read() doesn't fail -- I tried a few different
Xapproaches.
X
XDean
END_OF_FILE
if test 1046 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'comm.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'comm.c'\"
else
echo shar: Extracting \"'comm.c'\" \(5374 characters\)
sed "s/^X//" >'comm.c' <<'END_OF_FILE'
X#include "slave.h"
X
Xpid_t slave_pid;
Xstatic int slave_socket = -1;	/* handle for the slave task */
X
Xint new_connection(int s)
X{
X    /* lots of stuff deleted */
X
X    buf[0] = SLAVE_IPTONAME;
X	/* this effectively sprintfs %u.%u.%u.%u with the inet addr */
X    p = format_inet_addr( buf+1, addr );
X    if( slave_socket != -1 && !SET_is_member( mudmode, MUD_NO_IPTONAME ) ) {
X	/* ask slave to do a hostname lookup for us */
X	*p = '\n';
X	if( write( slave_socket, buf, p - buf + 1 ) != p - buf + 1 ) {
X	    syslogf( "{SLAVE} losing slave on write: %s", strerror( errno ));
X	    close( slave_socket );
X	    slave_socket = -1;
X	}
X	/* ask slave to do an identquery for us */
X	buf[0] = SLAVE_IDENTQ;
X	p += zsprintf( p, ",%d,%d\n", ntohs( sock.sin_port ), game_port );
X	if( write( slave_socket, buf, p - buf ) != p - buf ) {
X	    syslogf( "{SLAVE} losing slave on write: %s", strerror( errno ));
X	    close( slave_socket );
X	    slave_socket = -1;
X	}
X    }
X    /* lots deleted */
X}
X
X
X/* get a result from the slave */
Xstatic int get_slave_result( void )
X{
X    char buf[ MAX_STRING_LENGTH +1 ];
X    char token[ MAX_STRING_LENGTH ];
X    char os[ MAX_STRING_LENGTH ];
X    char userid[ MAX_STRING_LENGTH ];
X    int octet[4];
X    int local_port, remote_port;
X    char *p;
X    struct descriptor_data *d;
X    long addr;
X    int len;
X
X    len = read( slave_socket, buf, MAX_STRING_LENGTH );
X    if( len < 0 ) {
X	if( errno == EAGAIN || errno == EWOULDBLOCK ) return( -1 );
X	syslogf( "{SLAVE} losing slave on read: %s", strerror( errno ) );
X	close( slave_socket );
X	slave_socket = -1;
X	return( -1 );
X    } else if( len == 0 ) {
X	return( -1 );
X    }
X    buf[len] = 0;
X
X    switch( buf[0] ) {
X    case SLAVE_IDENTQ:
X	if( sscanf( buf+1, "%d.%d.%d.%d %d , %d : %[^:] : %[^:] : %s",
X	    &octet[0], &octet[1], &octet[2], &octet[3],
X	    &remote_port, &local_port,
X	    token, os, userid ) != 9 ) {
X	    /* don't bother logging "ERROR" result codes */
X	    if( sscanf( buf+1, "%d.%d.%d.%d %d , %d : %[^:] : %s",
X		&octet[0], &octet[1], &octet[2], &octet[3],
X		&remote_port, &local_port,
X		token, userid ) != 7 || stricmp( token, "ERROR" ) ) {
X		p = strchr( buf, '\n' );
X		*p = 0;
X		syslogf( "{SLAVE} invalid: '%s'", buf );
X	    }
X	    return( 0 );
X	}
X	if( local_port != game_port ) {
X	    syslogf( "{SLAVE} invalid local port: '%s'", buf );
X	    return( 0 );
X	}
X	addr = ( octet[0] << 24 )
X	    + ( octet[1] << 16 )
X	    + ( octet[2] << 8 )
X	    + ( octet[3] );
X	for( d = descriptor_list; d; d = d->next ) {
X	    if( d->port != remote_port ) continue;
X	    if( d->addr != addr ) continue;
X	    strncpy( d->userid, userid, MAX_USERID );
X	    d->userid[MAX_USERID] = 0;
X	    return( 0 );
X	}
X	break;
X    
X    case SLAVE_IPTONAME:
X	if( sscanf( buf+1, "%d.%d.%d.%d %s",
X		&octet[0], &octet[1], &octet[2], &octet[3], token ) != 5 ) {
X	    syslogf( "{SLAVE} invalid: %s", buf );
X	    return( 0 );
X	}
X	addr = ( octet[0] << 24 )
X	    + ( octet[1] << 16 )
X	    + ( octet[2] << 8 )
X	    + ( octet[3] );
X	for( d = descriptor_list; d; d = d->next ) {
X	    if( d->addr != addr ) continue;
X	    strncpy( d->host, token, MAX_HOSTNAME );
X	    d->host[MAX_HOSTNAME] = 0;
X	}
X	break;
X
X    default:
X	syslogf( "{SLAVE} invalid: %s", buf );
X	break;
X    }
X    return( 0 );
X}
X
X
Xvoid game_loop(int s)
X{
X    /* lots deleted ... inside the game loop now */
X	    FD_ZERO(&input_set);
X	    FD_ZERO(&output_set);
X	    FD_ZERO(&exc_set);
X	    FD_SET(s, &input_set);
X	    if( slave_socket != -1 ) {
X		FD_SET( slave_socket, &input_set );
X	    }
X		
X    /* snip snip... after the select() call now */
X
X		/* slave result? */
X		if( slave_socket != -1
X		    && FD_ISSET( slave_socket, &input_set ) ) {
X		    while( get_slave_result() == 0 )
X			;
X		}
X    /* snip snip... outside the game loop.. i.e. in the shutdown code */
X
X    /* deal with slave */
X    if( slave_socket != -1 ) {
X	kill( slave_pid, SIGKILL );
X    }
X}
X
X
Xvoid boot_slave( void )
X{
X    int sv[2];
X    int i;
X
X    if( slave_socket != -1 ) {
X	close( slave_socket );
X	slave_socket = -1;
X    }
X
X    syslogf( "{BOOT} booting slave" );
X    syslogf( "{BOOT} slave: ouch" );
X
X    if( socketpair( AF_UNIX, SOCK_DGRAM, 0, sv ) < 0 ) {
X	syslogf( "boot_slave: socketpair: %s", strerror( errno ) );
X	return;
X    }
X    /* set to nonblocking */
X    if( fcntl( sv[0], F_SETFL, FNDELAY ) == -1 ) {
X	syslogf( "boot_slave: fcntl( F_SETFL, FNDELAY ): %s",
X	    strerror( errno ) );
X	close(sv[0]);
X	close(sv[1]);
X	return;
X    }
X    slave_pid = vfork();
X    switch( slave_pid ) {
X    case -1:
X	syslogf( "boot_slave: vfork: %s", strerror( errno ) );
X	close( sv[0] );
X	close( sv[1] );
X	return;
X
X    case 0: /* child */
X	close( sv[0] );
X	close( 0 );
X	close( 1 );
X	if( dup2( sv[1], 0 ) == -1 ) {
X	    syslogf( "boot_slave: child: unable to dup stdin: %s", strerror( errno ) );
X	    _exit( 1 );
X	}
X	if( dup2( sv[1], 1 ) == -1 ) {
X	    syslogf( "boot_slave: child: unable to dup stdout: %s", strerror( errno ) );
X	    _exit( 1 );
X	}
X	for( i = 3; i < system_max_handle; ++i ) {
X	    close( i );
X	}
X	execlp( "slave", "slave", NULL );
X	syslogf( "boot_slave: child: unable to exec: %s", strerror( errno ) );
X	_exit( 1 );
X    }
X    close( sv[1] );
X
X    if( fcntl(sv[0], F_SETFL, FNDELAY ) == -1 ) {
X	syslogf( "boot_slave: fcntl: %s", strerror( errno ) );
X	close( sv[0] );
X	return;
X    }
X    slave_socket = sv[0];
X}
X
X
Xstatic void run_the_game(int port)
X{
X    /*... somewhere early on ... */
X    boot_slave();
X}
END_OF_FILE
if test 5374 -ne `wc -c <'comm.c'`; then
    echo shar: \"'comm.c'\" unpacked with wrong size!
fi
# end of 'comm.c'
fi
if test -f 'slave.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'slave.c'\"
else
echo shar: Extracting \"'slave.c'\" \(5669 characters\)
sed "s/^X//" >'slave.c' <<'END_OF_FILE'
X/*
X    This slave does iptoname conversions, and identquery lookups.
X
X    The philosophy is to keep this program as simple/small as possible.
X    It does normal fork()s, so the smaller it is, the faster it goes.
X*/
X
X#include "config.h"
X#include "kiwi/kiwi.h"
X#include "kiwi/string.h"
X#include "kiwi/logfile.h"
X#include "kiwi/inet.h"
X#include <errno.h>
X#include <ctype.h>
X#include <stdio.h>
X#include <sys/socket.h>
X#include <netdb.h>
X#include <unistd.h>
X#include <netinet/in.h>
X#include <sys/wait.h>
X#include <signal.h>
X#include "kiwi/time.h"
X#include "slave.h"
X#include <arpa/inet.h>
X
X#define MAX_STRING 4096
Xchar *arg_for_errors;
X
Xint my_write( int s, char *buf, size_t len )
X{
X    return( 1 ? write( s, buf, len ) : send( s, buf, len, 0 ) );
X}
X
X
Xint my_read( int s, char *buf, size_t len )
X{
X    return( 1 ? read( s, buf, len ) : recv( s, buf, len, 0 ) );
X}
X
X
XRETSIGTYPE timeout_signal()
X{
X    log_write( stderr, "'%s' 5 minute timeout", arg_for_errors );
X    exit( 1 );
X}
X
X
Xint query( char *orig_arg )
X{
X    char *comma;
X    char *port_pair;
X    struct hostent *hp;
X    struct sockaddr_in sin;
X    int s;
X    FILE *f;
X    char result[ MAX_STRING ];
X    char buf[ MAX_STRING ];
X    char arg[ MAX_STRING ];
X    size_t len;
X    char *p;
X
X    arg_for_errors = orig_arg;
X    strcpy( arg, orig_arg );
X    comma = strrchr( arg, ',' );
X    if( comma == NULL ) {
X	log_write( stderr, "invalid parm '%s'", orig_arg );
X	return( -1 );
X    }
X    *comma = 0;
X    port_pair = strrchr( arg, ',' );
X    if( port_pair == NULL ) {
X	log_write( stderr, "invalid parm '%s'", orig_arg );
X	return( -1 );
X    }
X    *port_pair++ = 0;
X    *comma = ',';
X
X    hp = gethostbyname(arg);
X    if( hp == NULL ) {
X	static struct hostent def;
X	static struct in_addr defaddr;
X	static char *alist[1];
X	static char namebuf[128];
X
X	defaddr.s_addr = inet_addr(arg);
X	if (defaddr.s_addr == -1) {
X	    log_write( stderr, "'%s': unknown host", orig_arg );
X	    return( -1 );
X	}
X	strcpy(namebuf, arg);
X	def.h_name = namebuf;
X	def.h_addr_list = alist;
X	def.h_addr = (char *)&defaddr;
X	def.h_length = sizeof (struct in_addr);
X	def.h_addrtype = AF_INET;
X	def.h_aliases = 0;
X	hp = &def;
X    }
X    sin.sin_family = hp->h_addrtype;
X    bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
X    sin.sin_port = htons( 113 );	/* ident port */
X    s = socket(hp->h_addrtype, SOCK_STREAM, 0);
X    if( s < 0 ) {
X	log_write( stderr, "'%s': socket: %s", orig_arg, strerror( errno ) );
X	return (-1);
X    }
X    if( connect(s, (char *)&sin, sizeof (sin)) < 0 ) {
X	if( errno != ECONNREFUSED && errno != ETIMEDOUT ) {
X	    log_write( stderr, "'%s': connect: %s", orig_arg, strerror( errno ) );
X	}
X	close(s);
X	return( -1 );
X    }
X
X    len = strlen( port_pair );
X    if( write(s, port_pair, len) != len ) {
X	log_write( stderr, "'%s': write: %s", orig_arg, strerror( errno ) );
X	close(s);
X	return( -1 );
X    }
X    if( write(s, "\r\n", 2) != 2 ) {
X	log_write( stderr, "'%s': write: %s", orig_arg, strerror( errno ) );
X	close(s);
X	return( -1 );
X    }
X    f = fdopen(s, "r");
X    {
X	int c;
X
X	p = result;
X	while( ( c = fgetc( f ) ) != EOF ) {
X	    if( c == '\n' ) break;
X	    if( isprint( c ) ) {
X		*p++ = c;
X		if( p - result == MAX_STRING - 1 ) break;
X	    }
X	}
X	*p = 0;
X    }
X    (void)fclose(f);
X    len = p - result;
X    buf[0] = SLAVE_IDENTQ;
X    p = format_inet_addr( buf+1, ntohl(sin.sin_addr.s_addr) );
X    *p++ = ' ';
X    if( len + ( p - buf ) >= sizeof( buf ) - 2 ) {
X	log_write( stderr, "'%s': result too long" );
X	return( -1 );
X    }
X    p = stpcpy( p, result );
X    *p++ = '\n';
X    my_write( 1, buf, p - buf );
X    return( 0 );
X}
X
X
Xint iptoname( char *arg )
X{
X    unsigned long addr;
X    struct hostent *hp;
X    char buf[ MAX_STRING ];
X    char *p;
X
X    addr = inet_addr( arg );
X    if( addr == -1 ) {
X	log_write( stderr, "%s is not a valid decimal ip address", arg );
X	return( -1 );
X    }
X
X    hp = gethostbyaddr( &addr, sizeof(addr), AF_INET );
X    if( hp ) {
X	buf[0] = SLAVE_IPTONAME;
X	p = stpcpy( buf+1, arg );
X	*p++ = ' ';
X	p = stpcpy( p, hp->h_name );
X	*p++ = '\n';
X	my_write( 1, buf, p - buf );
X    }
X    return( 0 );
X}
X
X
XRETSIGTYPE child_signal()
X{
X    /* collect any children */
X    while( waitpid( 0, NULL, WNOHANG ) > 0 )
X	;
X    signal( SIGCHLD, child_signal );
X}
X
X
Xvoid main( int argc, char **argv )
X{
X    char arg[ MAX_STRING +1 ];
X    char *p;
X    int len;
X
X    if( log_stderr( "slave_log" ) != 0 ) {
X	exit( 1 );
X    }
X    log_write( stderr, "{OUCH} slave booted" );
X    kiwi_logfile = stderr;
X    signal( SIGCHLD, child_signal );
X    signal( SIGPIPE, SIG_DFL );
X
X    for(;;) {
X	len = my_read( 0, arg, MAX_STRING );
X	if( len == 0 ) break;
X	if( len < 0 ) {
X	    if( errno == EINTR ) {
X		errno = 0;
X		continue;
X	    }
X	    log_write( stderr, "read: %s", strerror( errno ) );
X	    break;
X	}
X	arg[len] = 0;
X	p = strchr( arg, '\n' );
X	if( p ) *p = 0;
X	log_write( stderr, "received: '%s'", arg );
X	switch( fork() ) {
X	case -1:
X	    log_write( stderr, "'%s': fork: %s", arg, strerror( errno ) );
X	    exit( 1 );
X	
X	case 0: /* child */
X	    {
X		/* we don't want to try this for more than 5 minutes */
X		struct itimerval itime;
X		struct timeval interval;
X
X		interval.tv_sec = 300;      /* 5 minutes */
X		interval.tv_usec = 0;
X		itime.it_interval = interval;
X		itime.it_value = interval;
X		setitimer(ITIMER_VIRTUAL, &itime, 0);
X		signal(SIGVTALRM, timeout_signal);
X	    }
X	    switch( arg[0] ) {
X	    case SLAVE_IDENTQ:
X		exit( query( arg+1 ) != 0 );
X	    case SLAVE_IPTONAME:
X		exit( iptoname( arg+1 ) != 0 );
X	    default:
X		log_write( stderr, "invalid arg: %s", arg );
X	    }
X	}
X	/* collect any children */
X	while( waitpid( 0, NULL, WNOHANG ) > 0 )
X	    ;
X    }
X    log_write( stderr, "exiting" );
X    exit( 0 );
X}
END_OF_FILE
if test 5669 -ne `wc -c <'slave.c'`; then
    echo shar: \"'slave.c'\" unpacked with wrong size!
fi
# end of 'slave.c'
fi
if test -f 'slave.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'slave.h'\"
else
echo shar: Extracting \"'slave.h'\" \(59 characters\)
sed "s/^X//" >'slave.h' <<'END_OF_FILE'
Xenum {
X    SLAVE_IDENTQ = 'i',
X    SLAVE_IPTONAME = 'h'
X};
END_OF_FILE
if test 59 -ne `wc -c <'slave.h'`; then
    echo shar: \"'slave.h'\" unpacked with wrong size!
fi
# end of 'slave.h'
fi
echo shar: End of shell archive.
exit 0



This archive was generated by hypermail 2b30 : 12/18/00 PST