/*
 * traptime.c - It is an example of how to modify snmptrapd to
 *              measure the time and reliability of the trap system.
 *
 * Modified from the Carnegie Mellon University
 * snmpd.
 * E. Pasqualucci 7-11-1996
 * 
 */

/* Standard include files */

#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <nlist.h>
#include <errno.h>
#include <syslog.h>

/* SNMP standard include files */

#include "compat.h"
#include "snmp.h"
#include "asn1.h"
#include "snmp_impl.h"
#include "snmp_api.h"
#include "snmp_client.h"
#include "party.h"
#include "context.h"
#include "view.h"
#include "acl.h"

/* Other definitions */

#define NUM_NETWORKS	16   /* max number of interfaces to check */
#ifndef IFF_LOOPBACK
#define IFF_LOOPBACK 0
#endif
#define LOOPBACK    0x7f000001

/* Specific include files */

#include "traptime_protos.h"
#include "traptime.h"

/* Global variables */

int nrec = 0;
int nsend = 0;
int nerror = 0;
int nlast = 10000;

extern int errno;
int snmp_dump_packet = 0;
int Print = 0;

char default_community[] = "public";
char description[] = "0";

char *gateway = NULL;
char *community = default_community;

struct snmp_session *ss;
struct snmp_session *send_ss;
struct snmp_pdu *send_pdu;

oid objid_enterprise[] = {1, 3, 6, 1, 4, 1, 3, 1, 1};
oid objid_sysdescr[] = {1, 3, 6, 1, 2, 1, 1, 1, 0};

struct timeval t0, t1;

char *traptime_trap_description (int trap)
{
    switch(trap){
	case SNMP_TRAP_COLDSTART:
	    return "Cold Start";
	case SNMP_TRAP_WARMSTART:
	    return "Warm Start";
	case SNMP_TRAP_LINKDOWN:
	    return "Link Down";
	case SNMP_TRAP_LINKUP:
	    return "Link Up";
	case SNMP_TRAP_AUTHFAIL:
	    return "Authentication Failure";
	case SNMP_TRAP_EGPNEIGHBORLOSS:
	    return "EGP Neighbor Loss";
	case SNMP_TRAP_ENTERPRISESPECIFIC:
	    return "Enterprise Specific";
	default:
	    return "Unknown Type";
    }
}

char *uptime_string(register u_int timeticks, char *buf)
{
    int	seconds, minutes, hours, days;

    timeticks /= 100;
    days = timeticks / (60 * 60 * 24);
    timeticks %= (60 * 60 * 24);

    hours = timeticks / (60 * 60);
    timeticks %= (60 * 60);

    minutes = timeticks / 60;
    seconds = timeticks % 60;

    if (days == 0){
	sprintf(buf, "%d:%02d:%02d", hours, minutes, seconds);
    } else if (days == 1) {
	sprintf(buf, "%d day, %d:%02d:%02d", days, hours, minutes, seconds);
    } else {
	sprintf(buf, "%d days, %d:%02d:%02d", days, hours, minutes, seconds);
    }
    return buf;
}

void traptime_exit (void)
{
    struct timeval tdiff;

    gettimeofday (&t1, (struct timezone *) 0);
    printf (" nrec %d nsend %d nerror %d\n", nrec, nsend, nerror);
    snmp_close (ss);
    snmp_close (send_ss);

    printf (" time needed = %d sec %d usec\n", t1.tv_sec-t0.tv_sec,
            t1.tv_usec-t0.tv_usec);
    exit (1);
}

int traptime_input (int op, struct snmp_session *session, int reqid,
                    struct snmp_pdu *pdu, void *magic)
{
    struct variable_list *vars;
    char buf[TRAPTIME_MAXBUFLEN];

    static int flag = 0;

    if (!flag)
    {
        gettimeofday (&t0, (struct timezone *) 0);
        ++flag;
    }

    if (op == RECEIVED_MESSAGE && pdu->command == TRP_REQ_MSG)
    {
	if (Print)
        {
	    printf("%s: %s Trap (%d) Uptime: %s\n",
                   inet_ntoa(pdu->agent_addr.sin_addr),
	           traptime_trap_description(pdu->trap_type),
                   pdu->specific_type, uptime_string(pdu->time, buf));

	    for(vars = pdu->variables; vars; vars = vars->next_variable)
		print_variable(vars->name, vars->name_length, vars);
	}
#ifndef __Lynx__
        else
        {
	    syslog(LOG_WARNING, "%s: %s Trap (%d) Uptime: %s\n",
                   inet_ntoa(pdu->agent_addr.sin_addr),
		   traptime_trap_description(pdu->trap_type),
                   pdu->specific_type, uptime_string(pdu->time, buf));
	}
#endif
        ++nrec;

        if (snmp_send(send_ss, send_pdu)== 0)
            ++nerror;
        else
            ++nsend;
    }
    else if (op == TIMED_OUT)
    {
	printf("Timeout: This shouldn't happen!\n");
    }

    if (nrec == nlast)
        traptime_exit ();
}

void init_syslog (void)
{
#ifndef __Lynx__
/*
 * These definitions handle 4.2 systems without additional syslog facilities.
 */
#ifndef LOG_CONS
#define LOG_CONS	0	/* Don't bother if not defined... */
#endif
#ifndef LOG_LOCAL0
#define LOG_LOCAL0	0
#endif
    /*
     * All messages will be logged to the local0 facility and will be sent to
     * the console if syslog doesn't work.
     */
    openlog("snmptrapd", LOG_CONS, LOG_LOCAL0);
    syslog(LOG_INFO, "Starting snmptrapd");
#endif
}

void usage (void)
{
    printf("Usage: traptime [-p] [-d] -g gateway [-c community] [-n number]\n");
}

void init_options (int argc, char **argv)
{
    int arg;

    for(arg = 1; arg < argc; arg++)
	if (argv[arg][0] == '-')
	{
            switch(argv[arg][1])
            {
		case 'd':
		    snmp_dump_packet++;
		    break;
		case 'p':
		    Print++;
		    break;
		case 'g':
		    gateway = argv[++arg];
		    break;
		case 'c':
		    community = argv[++arg];
		    break;
		case 'n':
		    nlast = atoi(argv[++arg]);
		    break;
		default:
		    printf("invalid option: -%c\n", argv[arg][1]);
                    usage ();
		    break;
	    }
        }
}

struct snmp_session *traptime_init_snmp_trap_session (void)
{
    struct snmp_session send_session;
    struct snmp_session session, *traptime_ss;

    bzero((char *)&session, sizeof(struct snmp_session));

    session.peername = NULL;
    session.community = NULL;
    session.community_len = 0;
    session.retries = SNMP_DEFAULT_RETRIES;
    session.timeout = SNMP_DEFAULT_TIMEOUT;
    session.authenticator = NULL;
    session.callback = traptime_input;
    session.callback_magic = NULL;
    session.local_port = TRAPTIME_PORT;

    traptime_ss = snmp_open(&session);

    bzero((char *)&send_session, sizeof(struct snmp_session));

    send_session.peername = gateway;
    send_session.community = (u_char *)community;
    send_session.community_len = strlen(community);
    send_session.retries = SNMP_DEFAULT_RETRIES;
    send_session.timeout = SNMP_DEFAULT_TIMEOUT;
    send_session.authenticator = NULL;
    send_session.callback = NULL;
    send_session.callback_magic = NULL;
    send_session.remote_port = TRAPTIME_PORT;

    send_ss = snmp_open(&send_session);

    if (send_ss == NULL){
	printf("Couldn't open snmp to send traps\n");
	exit(-1);
    }

    return traptime_ss;
}

int get_myaddr (void)
{
    int sd;
    struct ifconf ifc;
    struct ifreq conf[NUM_NETWORKS], *ifrp, ifreq;
    struct sockaddr_in *in_addr;
    int count;
    int interfaces;		/* number of interfaces returned by ioctl */

    if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
	return 0;
    ifc.ifc_len = sizeof(conf);
    ifc.ifc_buf = (caddr_t)conf;
    if (ioctl(sd, SIOCGIFCONF, (char *)&ifc) < 0){
	close(sd);
	return 0;
    }
    ifrp = ifc.ifc_req;
    interfaces = ifc.ifc_len / sizeof(struct ifreq);
    for(count = 0; count < interfaces; count++, ifrp++){
	ifreq = *ifrp;
	if (ioctl(sd, SIOCGIFFLAGS, (char *)&ifreq) < 0)
	    continue;
	in_addr = (struct sockaddr_in *)&ifrp->ifr_addr;
	if ((ifreq.ifr_flags & IFF_UP)
	    && (ifreq.ifr_flags & IFF_RUNNING)
	    && !(ifreq.ifr_flags & IFF_LOOPBACK)
	    && in_addr->sin_addr.s_addr != LOOPBACK){
		close(sd);
		return in_addr->sin_addr.s_addr;
	    }
    }
    close(sd);
    return 0;
}

void init_send_pdu (void)
{
    struct variable_list *vars;

    send_pdu = snmp_pdu_create(TRP_REQ_MSG);
    send_pdu->enterprise = (oid *)malloc(sizeof(objid_enterprise));
    bcopy((char *)objid_enterprise, (char *)send_pdu->enterprise,
          sizeof(objid_enterprise));
    send_pdu->enterprise_length = sizeof(objid_enterprise) / sizeof(oid);
    send_pdu->agent_addr.sin_addr.s_addr = get_myaddr();
    send_pdu->trap_type = SNMP_TRAP_ENTERPRISESPECIFIC;
    send_pdu->specific_type = 0;
    send_pdu->time = 0;

    send_pdu->variables = vars = (struct variable_list *)malloc(sizeof(struct variable_list));
    vars->next_variable = NULL;
    vars->name = (oid *)malloc(sizeof(objid_sysdescr));
    bcopy((char *)objid_sysdescr, (char *)vars->name, sizeof(objid_sysdescr));
    vars->name_length = sizeof(objid_sysdescr) / sizeof(oid);
    vars->type = ASN_OCTET_STR;
    vars->val.string = (u_char *)malloc(strlen(description) + 1);
    strcpy((char *)vars->val.string, description);
    vars->val_len = strlen(description);
}

void traptime_init (int argc, char **argv)
{
    init_syslog();
    init_mib();
    init_options (argc, argv);

    if ((ss = traptime_init_snmp_trap_session()) == NULL)
    {
	printf("Couldn't open snmp\n");
	exit(-1);
    }

    init_send_pdu();
}

int traptime_main_loop (void)
{
    int count, numfds, block;
    fd_set fdset;
    struct timeval timeout, *tvp;

    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 */

#if defined (_HPUX_SOURCE) && defined (V9)
	count = select (numfds, (int *) &fdset, 0, 0, tvp);
#else
	count = select (numfds, &fdset, 0, 0, tvp);
#endif

	if (count > 0)
            snmp_read(&fdset);
	else
            switch(count)
            {
                case 0:
                    snmp_timeout();
		    break;
	        case -1:
		    if (errno != EINTR)
		        perror("select");
		    return -1;
	        default:
		    printf("select returned %d\n", count);
		    return -1;
	    }
    }
}

main (int argc, char **argv)
{
    traptime_init (argc, argv);
    traptime_main_loop();
}