/
Archipelago/
Archipelago/doc/
Archipelago/lib/misc/
Archipelago/lib/plrobjs/
Archipelago/lib/plrobjs/P-T/
Archipelago/lib/world/mob/
Archipelago/lib/world/obj/
Archipelago/lib/world/shp/
Archipelago/lib/world/wld/
Archipelago/lib/world/zon/
Archipelago/slave/
/*
    This slave does iptoname conversions, and identquery lookups.

    The philosophy is to keep this program as simple/small as possible.
    It does normal fork()s, so the smaller it is, the faster it goes.
*/
#define RETSIGTYPE void 

#include <errno.h>
#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include "slave.h"
#include <arpa/inet.h>

void log_write(FILE *fp, char *fmt, ...);
int log_stderr(char *fmt);

pid_t parent_pid;

#define MAX_STRING 4096

char *arg_for_errors;

RETSIGTYPE child_timeout_signal()
{
  /* this just causes log spam -djg  
  log_write( stderr, "'%s' 5 minute timeout", arg_for_errors );  */
  exit( 1 );
}


int query( char *orig_arg )
{
  char *comma;
  char *port_pair;
  struct hostent *hp;
  struct sockaddr_in sin;
  int s;
  FILE *f;
  char result[ MAX_STRING ];
  char buf[ MAX_STRING ];
  char arg[ MAX_STRING ];
  size_t len;
  char *p;
  
  arg_for_errors = orig_arg;
  strcpy( arg, orig_arg );
  comma =(char *) strrchr( arg, ',' );
  if( comma == NULL ) {
    log_write( stderr, "invalid parm '%s'", orig_arg );
    return( -1 );
  }
  *comma = 0;
  port_pair =(char *) strrchr( arg, ',' );
  if( port_pair == NULL ) {
    log_write( stderr, "invalid parm '%s'", orig_arg );
    return( -1 );
  }
  *port_pair++ = 0;
  *comma = ',';
  
  hp = gethostbyname(arg);
  if( hp == NULL ) {
    static struct hostent def;
    static struct in_addr defaddr;
    static char *alist[1];
    static char namebuf[128];
    
    defaddr.s_addr = inet_addr(arg);
    if (defaddr.s_addr == -1) {
      log_write( stderr, "'%s': unknown host", orig_arg ); 
      return( -1 );
    }
    strcpy(namebuf, arg);
    def.h_name = namebuf;
    def.h_addr_list = alist;
    def.h_addr = (char *)&defaddr;
    def.h_length = sizeof (struct in_addr);
    def.h_addrtype = AF_INET;
    def.h_aliases = 0;
    hp = &def;
  }
  sin.sin_family = hp->h_addrtype;
  bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
  sin.sin_port = htons( 113 );        /* ident port */
  s = socket(hp->h_addrtype, SOCK_STREAM, 0);
  if( s < 0 ) {
    log_write( stderr, "'%s': socket: %s", orig_arg, strerror( errno ) );
    return (-1);
  }
  if( connect(s, (struct sockaddr *)&sin, sizeof (sin)) < 0 ) {
    if( errno != ECONNREFUSED
	&& errno != ETIMEDOUT
	&& errno != ENETUNREACH
	&& errno != EHOSTUNREACH ) {
      log_write( stderr, "'%s': connect: %s", orig_arg, strerror( errno ) ); 
    }
    close(s);
    return( -1 );
  }

  len = strlen( port_pair );
  if( write(s, port_pair, len) != len ) {
    log_write( stderr, "'%s': write: %s", orig_arg, strerror( errno ) ); 
    close(s);
    return( -1 );
  }
  if( write(s, "\r\n", 2) != 2 ) {
    log_write( stderr, "'%s': write: %s", orig_arg, strerror( errno ) );
    close(s);
    return( -1 );
  }
  f = fdopen(s, "r");
  {
    int c;

    p = result;
    while( ( c = fgetc( f ) ) != EOF ) {
      if( c == '\n' ) break;
      if( isprint( c ) ) {
	*p++ = c;
	if( p - result == MAX_STRING - 1 ) break;
      }
    }
    *p = 0;
  }
  (void)fclose(f);
  len = p - result;
  buf[0] = SLAVE_IDENTQ;
  p = (char *)format_inet_addr( buf+1, ntohl(sin.sin_addr.s_addr) );
  *p++ = ' ';
  if( len + ( p - buf ) >= sizeof( buf ) - 2 ) {
    log_write( stderr, "'%s': result too long", orig_arg );
    return( -1 );
  }
  p = (char *) stpcpy( p, result );
  *p++ = '\n';
  write( 1, buf, p - buf );
  return( 0 );
}


int iptoname( char *arg )
{
  unsigned long addr;
  struct hostent *hp;
  char buf[ MAX_STRING ];
  char *p;
  
  addr = inet_addr( arg );
  if( addr == -1 ) {
    log_write( stderr, "%s is not a valid decimal ip address", arg );
    return( -1 );
    }
  
  hp = gethostbyaddr( (char *)&addr, sizeof(addr), AF_INET );
  if( hp ) {
    buf[0] = SLAVE_IPTONAME;
    p = (char *)stpcpy( buf+1, arg );
    *p++ = ' ';
    p = (char *) stpcpy( p, hp->h_name );
    *p++ = '\n';
    write( 1, buf, p - buf );
  }
  return( 0 );
}


RETSIGTYPE child_signal()
{
  /* collect any children */
  while( waitpid( 0, NULL, WNOHANG ) > 0 )
    ;
    signal( SIGCHLD, child_signal );
}


RETSIGTYPE alarm_signal()
{
  struct itimerval itime;
  struct timeval interval;
  
  if( getppid() != parent_pid ) {
    log_write( stderr, "shutdown since parent is 1" );
    exit( 1 );
  }
  signal( SIGALRM, alarm_signal );
  interval.tv_sec = 120;      /* 2 minutes */
  interval.tv_usec = 0;
  itime.it_interval = interval;
  itime.it_value = interval;
  setitimer(ITIMER_REAL, &itime, 0);
}


void main( int argc, char **argv )
{
  char arg[ MAX_STRING +1 ];
  char *p;
  int len;
  
  if( log_stderr( "slave_log" ) < 0 ) {
    exit( 1 );
  }
  log_write( stderr, "slave booted" ); 
  parent_pid = getppid();
  if( parent_pid == 1 ) {
    log_write( stderr, "parent is pid 1, shutting down" ); 
    exit( 1 );
  }
  alarm_signal();
  signal( SIGCHLD, child_signal );
  signal( SIGPIPE, SIG_DFL );
  
  for(;;) {
    len = read( 0, arg, MAX_STRING );
    if( len == 0 ) break;
    if( len < 0 ) {
      if( errno == EINTR ) {
	errno = 0;
	continue;
      }
      log_write( stderr, "read: %s", strerror( errno ) );
      break;
    }
    arg[len] = 0;
    p = (char *) strchr( arg, '\n' );
    if( p ) *p = 0;
    log_write( stderr, "received: '%s'", arg );
    switch( fork() ) {
    case -1:
      log_write( stderr, "'%s': fork: %s", arg, strerror( errno ) );
      exit( 1 );
        
    case 0: /* child */
      {
	/* we don't want to try this for more than 5 minutes */
	struct itimerval itime;
	struct timeval interval;
	
	interval.tv_sec = 300;      /* 5 minutes */
	interval.tv_usec = 0;
	itime.it_interval = interval;
	itime.it_value = interval;
	signal(SIGALRM, child_timeout_signal);
	setitimer(ITIMER_REAL, &itime, 0);
      }
      switch( arg[0] ) {
      case SLAVE_IDENTQ:
	exit( query( arg+1 ) != 0 );
      case SLAVE_IPTONAME:
	exit( iptoname( arg+1 ) != 0 );
      default:
	log_write( stderr, "invalid arg: %s", arg );
      }
    }
    /* collect any children */
        while( waitpid( 0, NULL, WNOHANG ) > 0 )
	  ;
  }
  log_write( stderr, "exiting" );
  exit( 0 );
}
void log_write(FILE *fp, char *fmt, ...)
{
  va_list args;
  
  va_start(args, fmt);
  fprintf(fp, "RFC 1413 Slave: ");
  vfprintf(fp, fmt, args);
  fprintf(fp, "\n");
  va_end(args);
  return;

}

int log_stderr(char *fmt)
{
  return(fprintf(stderr, "%s\n", fmt));

}