/*
 * tnmWinSnmp.c --
 *
 *	This file contains all functions that handle Windows specific
 *	functions for the SNMP engine. This is basically the code
 *	required to receive SNMP traps via the straps(8) daemon.
 *
 * Copyright (c) 1994-1996 Technical University of Braunschweig.
 * Copyright (c) 1996-1997 University of Twente. 
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * @(#) $Id: tnmWinSnmp.c 890 1997-12-04 12:35:12Z  $
 */

#include "tnmSnmp.h"

/*
 * Local variables:
 */

extern int hexdump;		/* flag that controls hexdump */
static int trap_sock = -1;	/* socket to receive traps */
static int trap_count = 0;	/* reference counter for trap socket */

/*
 * Forward declarations for procedures defined later in this file:
 */

static void
TrapProc		_ANSI_ARGS_((ClientData clientData, int mask));

static int
TrapRecv		_ANSI_ARGS_((Tcl_Interp *interp, 
				     u_char *packet, int *packetlen, 
				     struct sockaddr_in *from));

/*
 *----------------------------------------------------------------------
 *
 * TnmSnmpTrapOpen --
 *
 *	This procedure creates a socket used to receive trap messages.
 *	Since traps are send to a privileged port, we start the straps
 *	trap multiplexer and connect to it via a UNIX domain socket.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
TnmSnmpTrapOpen(interp)
    Tcl_Interp *interp;
{
    struct sockaddr_in name;
    int code;

    trap_count++;

    if (trap_sock >= 0) {
	return TCL_OK;
    }

    trap_sock = TnmSocket(AF_INET, SOCK_DGRAM, 0);
    if (trap_sock == TNM_SOCKET_ERROR) {
        Tcl_AppendResult(interp, "can not create socket: ", 
			 Tcl_PosixError(interp), (char *) NULL);
	return TCL_ERROR;
    }
    
    name.sin_family = AF_INET;
    name.sin_port = 0;
    name.sin_addr.s_addr = INADDR_ANY;
    name.sin_port = htons(TNM_SNMP_TRAPPORT);

    code = TnmSocketBind(trap_sock, (struct sockaddr *) &name, sizeof(name));
    if (code == TNM_SOCKET_ERROR) {
        Tcl_AppendResult(interp, "can not bind socket: ", 
			 Tcl_PosixError(interp), (char *) NULL);
	TnmSocketClose(trap_sock);
	trap_sock = -1;
	return TCL_ERROR;
    }

    TnmCreateSocketHandler(trap_sock, TCL_READABLE, TrapProc,
			   (ClientData) interp);
    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * TnmSnmpTrapClose --
 *
 *	This procedure closes the socket for incoming traps.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void
TnmSnmpTrapClose()
{
    if (--trap_count == 0) {
	TnmDeleteSocketHandler(trap_sock);
	TnmSocketClose(trap_sock);
	trap_sock = -1;
	Tcl_ReapDetachedProcs();
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TrapRecv --
 *
 *	This procedure reads from the trap socket to process incoming
 *	trap messages.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
TrapRecv(interp, packet, packetlen, from)
    Tcl_Interp *interp;
    u_char *packet;
    int *packetlen;
    struct sockaddr_in *from;
{
    int	fromlen = sizeof(*from);

    *packetlen = TnmSocketRecvFrom(trap_sock, 
				   (char *) packet, *packetlen, 0,
				   (struct sockaddr *) from, &fromlen);
    if (*packetlen == TNM_SOCKET_ERROR) {
	Tcl_AppendResult(interp, "recvfrom failed: ",
			 Tcl_PosixError(interp), (char *) NULL);
	return TCL_ERROR;
    }

    if (hexdump) {
	TnmSnmpDumpPacket(packet, *packetlen, from, NULL);
    }

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * TrapProc --
 *
 *	This procedure is called from the event dispatcher whenever
 *	a trap message is received.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static void
TrapProc(clientData, mask)
    ClientData clientData;
    int mask;
{
    Tcl_Interp *interp = (Tcl_Interp *) clientData;
    u_char packet[TNM_SNMP_MAXSIZE];
    int code, packetlen = TNM_SNMP_MAXSIZE;
    struct sockaddr_in from;

    Tcl_ResetResult(interp);
    code = TrapRecv(interp, packet, &packetlen, &from);
    if (code != TCL_OK) return;

    code = Tnm_SnmpDecode(interp, packet, packetlen, &from, NULL, NULL);
    if (code == TCL_ERROR) {
	Tcl_AddErrorInfo(interp, "\n    (snmp trap event)");
	Tcl_BackgroundError(interp);
    }
    if (code == TCL_CONTINUE && hexdump) {
	TnmWriteMessage(interp->result);
	TnmWriteMessage("\n");
    }
}
