#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <syslog.h>
#include <errno.h>
#include <unistd.h>
#include <sys/un.h>

#include <glib.h>
#include <snmp.h>

#include "dae.h"

#ifndef BSD4_3

#ifndef NFDBITS
typedef long	fd_mask;
#define NFDBITS	(sizeof(fd_mask) * NBBY)	/* bits per mask */

#define	FD_SET(n, p)	((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
#define	FD_CLR(n, p)	((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
#define	FD_ISSET(n, p)	((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
#define FD_ZERO(p)	bzero((char *)(p), sizeof(*(p)))
#endif

#endif

char version[] = "$Id: pdud.c,v 1.11 1999/09/14 10:26:59 remlali Exp $"; /*usefull to 'strings' the binary*/

aclist trapfilter;
glb_enviroment enviroment;
int pdudsock;

char buffer[2000];
int pz;			//buffer pointer;
struct {
  unsigned char type;
  unsigned int len;
}block;

struct _trapinfo{
  char IP[20];
  char ts[80];
  char enterprise[200];
  int g;
  int s;
  ulong uptime;
  int argc;
  int argv[3][200];
} trap;

extern int  errno;
int snmp_input(int op, struct snmp_session *session, int reqid, struct snmp_pdu *pdu, void *magic){
char *ptr, *opt;
int i, a, u;
size_t traplen;
struct variable_list *vars;
time_t ts; 	/*timestamp*/

  if (op == RECEIVED_MESSAGE && pdu->command == TRP_REQ_MSG){
/* parse buffer */
    memset(buffer,'\0',500);	/*this is due to a parse bug in trapd*/
    trap.enterprise[0] = 0;
    for (i = 0; i < pdu->enterprise_length; i++){
      sprintf(&trap.enterprise[strlen(trap.enterprise)], ".%d", pdu->enterprise[i]);
    }

	strcpy(buffer,"TRAP#");
	traplen = strlen(buffer);
	ptr = buffer + strlen(buffer);
	*(ptr) = '\123';								/* type start of trap*/
	ptr = ptr+sizeof(char);
	ptr += sizeof(int);								/* severity (assigned first in trapd)*/
	ts = time(NULL);
	memcpy(ptr,&ts,sizeof(int));							/* time */
	ptr = ptr+ sizeof(int);
	memcpy(ptr,&(pdu->agent_addr.sin_addr),sizeof(struct sockaddr_in));		/* IP */
	ptr = ptr+sizeof(struct in_addr);
	memcpy(ptr,&(pdu->trap_type),sizeof(int));					/* generic */
	ptr = ptr+sizeof(int);
	memcpy(ptr,&(pdu->specific_type),sizeof(int));					/* specific */
	ptr = ptr+sizeof(int);
	u = a = strlen(trap.enterprise);
	memcpy(ptr,&a,sizeof(int));							/* OID len */
	ptr = ptr+sizeof(int);
	ptr = memccpy(ptr,&(trap.enterprise),'\0',130);					/* OID */
	traplen += sizeof(char)+sizeof(int)+sizeof(int)+sizeof(struct in_addr)+sizeof(int)+sizeof(int)+sizeof(int)+a;
	ptr -= 1;
	opt = ptr;

/* all variables in trap */
	for (trap.argc = 0, vars = pdu->variables;  vars;  trap.argc++, vars = vars->next_variable){
		trap.enterprise[0] = 0;
		for (i = 0; i < vars->name_length; i++){						/*variable OID*/
			sprintf(&trap.enterprise[strlen(trap.enterprise)], ".%d", vars->name[i]);
		}
		
		switch(vars->type){									/*variable value*/
		case ASN_INTEGER:

			a = strlen(trap.enterprise);
			traplen += sizeof(int)+a+sizeof(char)+sizeof(int)+sizeof(int);
			memcpy(ptr,&a,sizeof(int));						/* var OID len */
			ptr = ptr+sizeof(int);
			ptr = memccpy(ptr,&(trap.enterprise),'\0',130);				/* var OID */
			ptr -= 1;
			*(ptr) = '\1';								/* var OID type: integer */
			ptr = ptr+sizeof(char);
			a = sizeof(int);
			memcpy(ptr,&a,sizeof(int));						/* var OID len */
			ptr = ptr+sizeof(int);
			a = *(vars->val.integer);
			memcpy(ptr,&a,sizeof(int));						/* var value*/
			ptr = ptr+sizeof(int);
		break;
		case ASN_OCTET_STR:

			a = strlen(trap.enterprise);
			traplen += sizeof(int)+a+sizeof(char)+sizeof(int)+vars->val_len; 		/*traplen is set to end of this arg*/
			memcpy(ptr,&a,sizeof(int));							/* var OID len */
			ptr = ptr+sizeof(int);
			ptr = memccpy(ptr,&(trap.enterprise),'\0',130);					/* var OID */
			ptr -= 1;
			*(ptr) = '\2';									/* var OID type: string */
			ptr = ptr+sizeof(char);
			a = vars->val_len;
			memcpy(ptr,&a,sizeof(int));							/* var OID len */
			ptr = ptr+sizeof(int);
			memcpy(ptr, vars->val.string, a);						/* var value */
			ptr += a;

		break;

/*case ASN_OBJECT_ID: .. vars->val.objid .. vars->val_len*/
/*case IPADDRESS: .. bcopy(vars->val.string, &sin_addr.s_addr, sizeof(sin_addr.s_addr));*/
		}

	}
	traplen++;
	*(ptr) = '\xff';							/*type end of trap*/
	traplen++;
	traplen += 20;

/*forward trap to trapd*/
	senddsocket(pdudsock, enviroment.trapd, buffer, traplen);

    }
   if (op == TIMED_OUT)
    {
      fprintf(stderr,"Timeout: This shouldn't happen!\n");
    }
  return 0;
}

int main()
{
struct snmp_session session, *ss;
int count, numfds, block;
fd_set fdset;
struct timeval timeout, *tvp;
int i,j;

/*daemonize*/
  if(fork() != 0) _exit(0);
  if(setsid() == -1) _exit(0);
  if(fork() != 0) _exit(0);
  umask(777);
  if(chdir("/")<0) _exit(0);
  for(i=sysconf(_SC_OPEN_MAX),j=0;j<i;) close(j++);
  open("/dev/null",O_RDWR); dup(0); dup(0);
/***********/
  load_enviroment();
  unlink(enviroment.pdud);
  if(!(pdudsock = createsocket(enviroment.pdud,1))) _exit(0);

  //memset((char *)&session, '\0', sizeof(struct snmp_session));
  bzero((char *)&session,sizeof(struct snmp_session));
  session.authenticator = NULL;
  session.callback = snmp_input;
  session.callback_magic = NULL;
  session.community = NULL;
  session.community_len = 0;
  session.local_port = SNMP_TRAP_PORT;
  session.peername = NULL;
  session.retries = SNMP_DEFAULT_RETRIES;
  session.timeout = SNMP_DEFAULT_TIMEOUT;
  ss = snmp_open(&session);
  if(ss == NULL) _exit(-1);
  while(1){
    numfds = 0;
    FD_ZERO(&fdset);
    block = 1;
    tvp = &timeout;
    timerclear(tvp);
    snmp_select_info(&numfds, &fdset, tvp, &block);
    if (block == 1) tvp = NULL;	/* block without timeout */
    count = select(numfds, &fdset, 0, 0, tvp);
    if (count > 0)
      snmp_read(&fdset);
      /*snmp_sess_read(session, &fdset);  thought single session API supported this*/
      else switch(count){
/*FIX: log, and try to restart daemon*/
      case 0:
        snmp_timeout();
        break;
      case -1:
        if (errno == EINTR) continue;
        else perror("select");
        return -1;
      default:
        printf("select returned %d\n", count);
        return -1;
      }
    }
    /* not reached */
}
