/*
 *	etherd.c
 *
 * Changes:
 * V0.7 - fiddled with Makefile to adapt to newer version of rpcgen on
 *        Linux (and still dont break compiling on other systems)
 * V0.6 - added dlpi support for Solaris (strauss@ibr.cs.tu-bs.de, Oct 1997)
 * V0.5 - cosmetic change in debugging code for linux
 *        cosmetic change in man page
 * V0.4 - use syslog(3) instead of stderr (S.P.)
 * V0.3 - clean up machine-specific vs. machine-dependant code for
 *	  the packet counting stuff (S.P.)
 * V0.2 - ports for Linux and FreeBSD, port to SunOS unfinished (S.P.)
 *      - added -d option
 * V0.1 - the original from Guy Cardwell
 */

#include <stdio.h>
#include <rpc/rpc.h>
#include <sys/time.h>
#include <sys/types.h>
#include <errno.h>
#include <signal.h>
#include <syslog.h>
#include "etherd.h"

static char version[] = "@(#)rpc.etherd V0.7 Oct'99 S.P.";
static char *ifname;

extern void etherprog_1();

static void death() {
  pmap_unset(ETHERPROG, ETHERVERS);
  packet_shutdown();
  exit(0);
}

#ifdef DEBUG
int debug = 0;
#endif

static void usage(s) char *s; {
#ifdef DEBUG
  fprintf(stderr, "usage: %s interface\n", s);
#else
  fprintf(stderr, "usage: %s [-d] interface\n", s);
#endif
  exit(1);
}
  

int main(argc, argv)
int argc;
char *argv[];
{
  int n;
  register SVCXPRT *transp;
  fd_set readfds;
  int dtbsz = getdtablesize();
  
  for (n = 1; n < argc; n++) {
    if ('-' == *argv[n]) switch(argv[n][1]) {
    case '?':
    case 'h': usage(*argv); break;
    case 'd': 
#ifdef DEBUG
      debug++;
#else
      fprintf(stderr, "%s compiled w/o DEBUG: -d ignored\n", *argv);
#endif
      break;
    default: 
      fprintf(stderr, "%s: unknown option %s\n", *argv, argv[n]);
      usage(*argv);
      break;
    }
    else break;
  }
  if (n == argc) {
    fprintf(stderr, "no interface name given\n");
    usage(*argv);
  }
  ifname = argv[n];

  if (getuid() != 0) {
    fprintf(stderr, "You must be root to run %s\n", argv[0]);
    exit(2);
  }

  /* TODO: go into background */
  signal(SIGINT, death);

#ifdef LOG_DAEMON
  openlog(*argv, LOG_PID|LOG_NOWAIT, LOG_DAEMON);
#else
  openlog(*argv, LOG_PID|LOG_NOWAIT);
#endif

  /* this just checks to see if the interface is valid, etc */
  /* etherproc_on_1_svc actually turns the whole thing on.  This just
     checks the interface */
  packet_setup(ifname);
  packet_shutdown();
  (void) pmap_unset(ETHERPROG, ETHERVERS);
  
  transp = svcudp_create(RPC_ANYSOCK);
  if (transp == NULL) {
    syslog(LOG_ERR, "cannot create udp service");
    exit(1);
  }
  if (!svc_register(transp, ETHERPROG, ETHERVERS, etherprog_1, IPPROTO_UDP)) {
    syslog(LOG_ERR, "unable to register (ETHERPROG, ETHERVERS, udp)");
    exit(1);
  }
  
  transp = svctcp_create(RPC_ANYSOCK, 0, 0);
  if (transp == NULL) {
    syslog(LOG_ERR, "cannot create tcp service.");
    exit(1);
  }
  if (!svc_register(transp, ETHERPROG, ETHERVERS, etherprog_1, IPPROTO_TCP)) {
    syslog(LOG_ERR, "unable to register (ETHERPROG, ETHERVERS, tcp).");
    exit(1);
  }
  /* this is code from svc_run */
  for (;;) {
    readfds = svc_fdset;
    if (pffd != -1) {
      FD_SET(pffd,&readfds);
    }
    switch ((n=select(dtbsz,&readfds,NULL,NULL,NULL))) {
    case -1:
      if (errno!=EBADF) continue;
      syslog(LOG_ERR, "select: %m");
      perror("rpc.etherd: select");
      exit(1);
    case 0:
      continue;
    default: 
      if (pffd != -1 && FD_ISSET(pffd,&readfds)) {
	FD_CLR(pffd, &readfds);
	process_packet();
      }
      svc_getreqset(&readfds);
    }
  }
  /* NOTREACHED */
}

etherstat *etherproc_getdata_1_svc(dummy, req)
void *dummy;
struct svc_req *req;
{
  struct timeval tp;
  struct timezone tzp;
  static etherstat data;
#ifdef DEBUG
  if (debug) fprintf(stderr, "getdata_1_svc\n");
#endif
  /* set the time in the structure */
  data = estatdata;
  gettimeofday(&tp, &tzp);
  data.e_time.tv_seconds=tp.tv_sec;
  data.e_time.tv_useconds=tp.tv_usec;
  return (&data);
}



etheraddrs *etherproc_getsrcdata_1_svc(dummy, req)
void *dummy;
struct svc_req *req;
{
  struct timeval tp;
  struct timezone tzp;
  static etheraddrs data;
#ifdef DEBUG
  if (debug) fprintf(stderr, "getsrcdata_1_svc\n");
#endif
  data = srcaddrs;
  /* set the time in the structure */
  gettimeofday(&tp, &tzp);
  data.e_time.tv_seconds = tp.tv_sec;
  data.e_time.tv_useconds = tp.tv_usec;
  data.e_bytes = estatdata.e_bytes;
  data.e_packets = estatdata.e_packets;
  data.e_bcast = estatdata.e_bcast;
  return(&data);
}


etheraddrs *etherproc_getdstdata_1_svc(dummy, req)
void *dummy;
struct svc_req *req;
{
  struct timeval tp;
  struct timezone tzp;
  static etheraddrs data;
#ifdef DEBUG
  if (debug) fprintf(stderr, "getdstdata_1_svc\n");
#endif
  data = destaddrs;
  /* set the time in the structure */
  gettimeofday(&tp, &tzp);
  data.e_time.tv_seconds = tp.tv_sec;
  data.e_time.tv_useconds = tp.tv_usec;
  data.e_bytes = estatdata.e_bytes;
  data.e_packets = estatdata.e_packets;
  data.e_bcast = estatdata.e_bcast;
  return(&data);
}  

void *etherproc_on_1_svc(dummy, req)
void *dummy;
struct svc_req *req;
{
#ifdef DEBUG
  if (debug) fprintf(stderr, "on_1_svc\n");
#endif
  if (pffd == -1) packet_setup(ifname);
  return ((void *) 1);
}

void *etherproc_off_1_svc(dummy, req)
void *dummy;
struct svc_req *req;
{
#ifdef DEBUG
  if (debug) fprintf(stderr, "off_1_svc\n");
#endif
  if (pffd != -1) packet_shutdown();
  return ((void *)1);
}

void *etherproc_selectsrc_1_svc(mask, req)
addrmask *mask;
struct svc_req *req;
{
  syslog(LOG_INFO, "selectsrc_1_svc: mask=%d srcmask=0x%x\n",
	mask->a_mask, mask->a_addr);
  if (mask->a_mask) srcmask = mask->a_addr;
  else srcmask = 0;
  return ((void *)1);
}

void *etherproc_selectdst_1_svc(mask, req)
addrmask *mask;
struct svc_req *req;
{
  syslog(LOG_INFO, "selectdst_1_svc: mask=%d destmask=0x%x\n",
	mask->a_mask, mask->a_addr);
  if (mask->a_mask) destmask = mask->a_addr;
  else destmask = 0;
  return ((void *)1);
}

void *etherproc_selectproto_1_svc(mask, req)
addrmask *mask;
struct svc_req *req;
{
  syslog(LOG_INFO, "selectproto_1_svc: mask=%d proto=%d\n",
	mask->a_mask, mask->a_addr);
  if (mask->a_mask) prot_mask = mask->a_addr;
  else prot_mask = 0;
  return ((void *)1);
}

void *etherproc_selectlnth_1_svc(mask, req)
addrmask *mask;
struct svc_req *req;
{
  syslog(LOG_INFO, "selectlnth_1_svc: small=%d large=%d\n",
	mask->a_addr, mask->a_mask);
  if (mask->a_mask) {
    small_size = mask->a_addr;
    large_size = mask->a_mask;
  } else {
    small_size = 0;
    large_size = 0;
  }
  return ((void *)1);
}
