/*
** identify.c            Identify a connection with the Identification Protocol
**
** Version: 0.6
**
** Author: Peter Eriksson <pen@lysator.liu.se>
**
** This program is in the public domain.
*/

#include <stdio.h>
#include <signal.h>
#include <syslog.h>
#include <authuser.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>


int debug    = 0;
int fd       = 0;
char *name   = "ident";
char *msg    = NULL;
int logopt   = 0;
#ifdef LOG_AUTH
int facility = LOG_AUTH;
#endif
int priority = LOG_NOTICE;
int background = 0;
int child_pid = 0;
char *force_log = NULL;
int noidentify = 0;
int bits = 0;
int reject_flag = 0;
unsigned long inlocal;
unsigned long inremote;
int timeout = 120;


static char *host_address(ad)
  unsigned long ad;
{
  int a, b, c, d;
  static char addr[20];
  
  d = ad % 256;
  ad /= 256;
  c = ad % 256;
  ad /= 256;
  b = ad % 256;
  a = ad / 256;
  sprintf(addr, "%d.%d.%d.%d", a, b, c, d);
  
  return addr;
}


char *ident_get_identifier(fd, host, len)
  int fd;
  char *host;
  int len;
{
  unsigned short local;
  unsigned short remote;


  if (auth_fd2(fd, &inlocal, &inremote,
	       &local, &remote) == -1)
  {
    if (debug)
      perror("auth_fd2");
    
    return NULL;
  }

  if (host)
  {
    struct hostent *hp;

  
    hp = gethostbyaddr(&inremote,sizeof(struct in_addr),AF_INET);
    if (hp)
      strncpy(host, hp->h_name, len);
    else
      strncpy(host, host_address(inremote), len);
    
    host[len] = '\0';
  }

  if (noidentify)
    return NULL;
  else
    if (timeout)
      return auth_tcpuser2(inlocal, inremote, local, remote, timeout);
    else
      return auth_tcpuser2(inlocal, inremote, local, remote);
}


main(argc,argv)
  int argc;
  char *argv[];
{
  int i;
  char *id;
  char host[1024];

  
  if (argv[0])
    name = argv[0];

  for (i = 1; i < argc && argv[i][0] == '-'; i++)
    switch (argv[i][1])
    {
      case 'x':
        noidentify = 1;
	force_log = "";
	break;

      case 't':
	timeout = atoi(argv[i]+2);
	break;
	
      case 'R':
	if (!argv[i][2])
	  reject_flag = 1;
	else
	  reject_flag = atoi(argv[i]+2);
	break;
	
      case 'r':
	bits = 32 - atoi(argv[i]+2);
	break;
	
      case 'o':
        logopt = atoi(argv[i]+2);
	break;

#ifdef LOG_AUTH
      case 'f':
        facility = atoi(argv[i]+2)<<3;
	break;
#endif
	
      case 'p':
        priority = atoi(argv[i]+2);
	break;

      case 'n':
	name = argv[i]+2;
	break;

      case 'd':
        fd = atoi(argv[i]+2);
	break;

      case 'm':
	msg = argv[i]+2;
	break;

      case 'b':
	background = 1;
	break;

      case 'i':
	if (!argv[i][2])
	  force_log = "";
	else
	  force_log = argv[i]+2;
	break;
	
      case 'D':
	debug = 1;
	break;
    }

  /*
  ** Can do the background stuff if using -R or -r
  */
  if (reject_flag || bits)
    background = 0;
  
  if (background)
  {
    signal(SIGCHLD, SIG_IGN);
    
    child_pid = fork();
    if (!child_pid)
    {
      /* In child, let's fork again so we can forget about this child */
      if (fork())
	_exit();
    }
    else
    {
      /* Do this so we can forget about this child */
      int status;
      wait(&status);
    }
  }

  if (!child_pid)
  {
#ifdef LOG_AUTH
    openlog(name, logopt, facility);
#else
    openlog(name, logopt);
#endif
    id = ident_get_identifier(fd, host, sizeof(host)-1);
    if (id || force_log)
    {
      if (!id)
	id = force_log;
      
      if (debug)
	fprintf(stderr, "syslog: %s@%s: %s",
		id,
		host,
		msg ? msg : argv[i]);
    
      syslog(priority, "%s@%s: %s", id, host, msg ? msg : argv[i]);
    }
    else if (debug)
      perror("ident error");
    
    if (background)
    {
      closelog();
      _exit(0);
    }
  }

  if ((bits && (inremote >> bits != inlocal >> bits)) ||
      (reject_flag && !id))
  {
    if (id)
      syslog(priority, "Rejecting from %s@%s", id, host_address(inremote));
    else
      syslog(priority, "Rejecting from %s", host_address(inremote));
    exit(1);
  }
  
  execv(argv[i], argv+i+1);
  
  if (debug)
    fprintf(stderr, "execv(%s, ..) failed\r\n", argv[i]);
  
  exit(1);
}

