Net2/usr/src/contrib/isode/snmp/snmpd.c

Compare this file to the similar file:
Show the results in this format:

/* snmpd.c - SNMP agent for 4BSD/ISODE */

/* Someday add sets for these objects...
	ifAdminStatus
	arptable
	ipForwarding
	route table
	clnpForwarding
	clnp route table
	clnp es-is table
	smux stuff
	exprExpr
 */

#ifndef	lint
static char *rcsid = "$Header: /f/osi/snmp/RCS/snmpd.c,v 7.51 91/03/09 11:57:29 mrose Exp $";
#endif

/*
 * $Header: /f/osi/snmp/RCS/snmpd.c,v 7.51 91/03/09 11:57:29 mrose Exp $
 *
 * Contributed by NYSERNet Inc.  This work was partially supported by the
 * U.S. Defense Advanced Research Projects Agency and the Rome Air Development
 * Center of the U.S. Air Force Systems Command under contract number
 * F30602-88-C-0016.
 *
 *
 * $Log:	snmpd.c,v $
 * Revision 7.51  91/03/09  11:57:29  mrose
 * update
 * 
 * Revision 7.49  91/02/20  17:25:55  mrose
 * stuff
 *
 * Revision 7.48  91/01/12  11:43:24  mrose
 * stuff
 *
 * Revision 7.47  91/01/11  15:35:10  mrose
 * sets
 *
 * Revision 7.46  91/01/07  12:40:55  mrose
 * update
 *
 * Revision 7.45  90/12/23  18:43:00  mrose
 * update
 *
 * Revision 7.44  90/12/18  10:13:50  mrose
 * update
 *
 * Revision 7.43  90/12/17  22:08:59  mrose
 * split-up
 *
 * Revision 7.42  90/11/20  15:32:16  mrose
 * update
 *
 * Revision 7.41  90/10/29  18:38:51  mrose
 * updates
 *
 * Revision 7.40  90/10/23  20:36:29  mrose
 * update
 *
 * Revision 7.39  90/09/03  12:57:40  mrose
 * update
 *
 * Revision 7.38  90/08/30  15:11:15  mrose
 * ho-hum
 *
 * Revision 7.37  90/08/29  12:23:44  mrose
 * touch-up
 *
 * Revision 7.36  90/08/08  14:01:07  mrose
 * stuff
 *
 * Revision 7.35  90/07/09  14:49:04  mrose
 * sync
 *
 * Revision 7.34  90/07/01  21:07:26  mrose
 * pepsy
 *
 * Revision 7.33  90/06/23  17:07:38  mrose
 * loopback
 *
 * Revision 7.32  90/06/23  17:01:24  mrose
 * update
 *
 * Revision 7.31  90/06/23  01:33:12  mrose
 * proxy again
 *
 * Revision 7.30  90/06/21  21:27:14  mrose
 * proxy and snmpt
 *
 * Revision 7.29  90/06/20  23:52:57  mrose
 * again
 *
 * Revision 7.28  90/06/20  21:38:33  mrose
 * update
 *
 * Revision 7.27  90/06/15  16:58:39  mrose
 * update
 *
 * Revision 7.26  90/06/13  17:58:44  mrose
 * defaultView
 *
 * Revision 7.25  90/06/12  05:19:03  mrose
 * again
 *
 * Revision 7.24  90/06/12  02:21:43  mrose
 * again
 *
 * Revision 7.23  90/06/12  02:05:33  mrose
 * views ...
 *
 * Revision 7.22  90/06/05  20:47:10  mrose
 * touch-up
 *
 * Revision 7.21  90/05/21  10:07:39  mrose
 * bug-fix
 *
 * Revision 7.20  90/05/15  16:56:20  mrose
 * bump COMM_RDWRITE
 *
 * Revision 7.19  90/05/14  19:55:48  mrose
 * optimize views
 *
 * Revision 7.18  90/05/13  18:13:46  mrose
 * update
 *
 * Revision 7.17  90/05/13  17:54:39  mrose
 * views again
 *
 * Revision 7.16  90/05/13  16:18:17  mrose
 * views
 *
 * Revision 7.15  90/04/18  08:51:53  mrose
 * oid_normalize
 *
 * Revision 7.14  90/04/09  08:50:16  mrose
 * update
 *
 * Revision 7.13  90/02/27  18:49:55  mrose
 * unix stuff
 *
 * Revision 7.12  90/02/23  17:47:49  mrose
 * update
 *
 * Revision 7.11  90/02/19  16:25:56  mrose
 * typo
 *
 * Revision 7.10  90/02/19  15:38:50  mrose
 * one more time
 *
 * Revision 7.9  90/02/17  17:18:48  mrose
 * touch-up
 *
 * Revision 7.8  90/01/11  18:34:33  mrose
 * real-sync
 *
 * Revision 7.7  89/12/19  22:01:52  mrose
 * touch-up
 *
 * Revision 7.6  89/12/19  16:18:23  mrose
 * dgram
 *
 * Revision 7.5  89/12/11  16:22:29  mrose
 * more clts
 *
 * Revision 7.4  89/12/09  21:07:41  mrose
 * touch-up
 *
 * Revision 7.3  89/12/08  14:20:27  mrose
 * touch-up
 *
 * Revision 7.2  89/12/07  22:15:12  mrose
 * touch-up
 *
 * Revision 7.1  89/12/01  10:42:15  mrose
 * clts
 *
 * Revision 7.0  89/11/23  22:23:26  mrose
 * Release 6.0
 *
 */

/*
 *				  NOTICE
 *
 *    Acquisition, use, and distribution of this module and related
 *    materials are subject to the restrictions of a license agreement.
 *    Consult the Preface in the User's Manual for the full terms of
 *    this agreement.
 *
 */


#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <varargs.h>
#include "mib.h"
#include <sys/ioctl.h>
#ifdef	BSD42
#include <sys/file.h>
#endif
#ifdef	SYS5
#include <fcntl.h>
#endif
#include <sys/stat.h>
#include "tailor.h"

#include "dgram.h"
#include "tsap.h"
#ifdef	TCP
#define	SMUX
#include "internet.h"
#endif
#ifdef	X25
#include "x25.h"
#define	COTS
#endif
#ifdef	TP4
#include "tp4.h"
#if	!defined(CLTS) && !defined(COTS)
#define	COTS
#endif
#endif

#if	defined(SNMPT) || defined(SNMPC)
#undef	SMUX
#endif


#define	MAXSNMP		484
#define	IDLE_TIME	(3 * 60)

/*    DATA */

int	debug = 0;
static	int	tooBig = MAXSNMP;
static	int	nbits = FD_SETSIZE;
#ifndef	SNMPT
static	int	rflag = 0;
#endif


#define	LLOG_XXX	(LLOG_PDUS | LLOG_DEBUG)

static LLog _pgm_log = {
#ifndef	SNMPT
#ifndef	SNMPC
    "snmpd.log",
#else
    "snmpc.log",
#endif
#else
    "snmpt.log",
#endif
    NULLCP, NULLCP, LLOG_FATAL | LLOG_EXCEPTIONS | LLOG_NOTICE,
    LLOG_FATAL, -1, LLOGCLS | LLOGCRT | LLOGZER, NOTOK
};
static LLog *pgm_log = &_pgm_log;

#ifndef	SNMPT
#ifndef	SNMPC
static char *myname = "snmpd";
#else
static char *myname = "snmpc";
#endif
#else
static char *myname = "snmpt";
#endif

	int	tcpservice = 1;
static	int	x25service = 1;
static	int	tp4service = 1;


#define	NTADDRS	FD_SETSIZE

static fd_set	ifds;

static struct TSAPaddr *tz;
static struct TSAPaddr  tas[NTADDRS];

#ifdef	COTS
static fd_set	cfds;
static struct TSAPaddr  taddrs[FD_SETSIZE];
static struct timeval   lru[FD_SETSIZE];
#endif
static char	source[BUFSIZ];


#ifdef	DEBUG
static	int	didhup = OK;

SFD	hupser ();
#endif

void	adios (), advise ();
void	ts_advise ();


extern int  errno;

/*  */

int	nd = NOTOK;

#ifdef	TCP
static	int	udp = NOTOK;
#ifndef	SNMPT
	int	udport;
	int	traport;
#endif
#endif

#ifdef	CLTS
static	int	clts = NOTOK;
#endif

/*  */

#ifndef	SNMPT
int	quantum = 0;


#include "snmp-g.h"

struct snmpstat snmpstat;
int	unix_netstat = 1;


/* PROXY */

#define	NPQ	10

static struct proxyque {
    integer pq_quantum;
    int	    pq_age;

    int	    pq_fd;
    IFP	    pq_closefnx;
    PS	    pq_ps;

    struct qbuf pq_community;
    integer pq_request;
}	pips[NPQ];

static	int	pqs = 0;

static struct proxyque *pqr = NULL;


/* VIEWS */

#include "view-g.h"


static OID viewTree = NULLOID;

static struct view viewque;
struct view *VHead = &viewque;


/* COMMUNITIES */

static struct community commque;
struct community *CHead = &commque;

struct community *str2comm ();


/* TRAPS */

static struct trap trapque;
struct trap *UHead = &trapque;

#ifdef	TCP
static struct type_SNMP_Message *trap = NULL;
#endif
#ifdef	SMUX
static struct qbuf *loopback_addr = NULL;
#endif


/* SMUX GROUP */

#ifdef	SMUX
#include "smux-g.h"


static	int	smux_enabled = 1;
static	int	smux = NOTOK;

static fd_set	sfds;

static struct smuxPeer peerque;
struct smuxPeer *PHead = &peerque;

static struct smuxTree treeque;
struct smuxTree *THead = &treeque;

static struct smuxReserved {
    char   *rb_text;
    OID	    rb_name;
}    reserved[] = {
    "snmp", NULLOID,
    "view", NULLOID,
    "smux", NULLOID,

    NULL
};
#endif

#else	/* SNMPT */

/*  */

#define	proxy_clear(fd)


static	PS	audit = NULLPS;

#endif	/* SNMPT */

/*    MAIN */

/* ARGSUSED */

main (argc, argv, envp)
int	argc;
char  **argv,
      **envp;
{
    int	    failed,
	    listening,
	    nfds;
    register struct TSAPaddr  *ta;
    struct TSAPdisconnect   tds;
    register struct TSAPdisconnect  *td = &tds;

    arginit (argv);
    envinit ();

    failed = listening = 0;
    nfds = 0;
    FD_ZERO (&ifds);

    for (ta = tas; ta < tz; ta++) {
	if (ta -> ta_naddr == 0) {
	    if (!tp4service)
		continue;
#ifdef	CLTS
	    goto do_clts;
#endif
	}
	else {
	    register struct NSAPaddr *na = ta -> ta_addrs;

	    switch (na -> na_stack) {
		case NA_TCP:
		    if (!tcpservice)
			continue;
#ifdef	TCP
		    {
			struct sockaddr_in lo_socket;
			register struct sockaddr_in *lsock = &lo_socket;

			bzero ((char *) lsock, sizeof *lsock);
			lsock -> sin_family = AF_INET;
			lsock -> sin_port = na -> na_port;

			if ((udp = start_udp_server (lsock, 0, 0, 0))
			        == NOTOK) {
			    advise (LLOG_EXCEPTIONS, "failed",
				    "start_udp_server");
			    failed++;
			    continue;
			}
			if (udp >= nfds)
			    nfds = udp + 1;
			FD_SET (udp, &ifds);
			if (nd == NOTOK)
			    nd = udp;

			advise (LLOG_NOTICE, NULLCP,
				"listening on UDP port %d",
				(int) ntohs (na -> na_port));
			listening++;
			continue;
		    }
#else
		    advise (LLOG_EXCEPTIONS, NULLCP,
			    "UDP support not configured");
		    failed++;
		    continue;
#endif

	        case NA_X25:
		    if (!x25service)
			continue;
		    break;

	        case NA_NSAP:
		    if (!tp4service)
			continue;
#ifdef	CLTS
do_clts: ;
		    {
			union sockaddr_osi lo_socket;
			register union sockaddr_osi *lsock = &lo_socket;

			(void) gen2tp4 (ta, lsock);
			if ((clts = start_clts_server (lsock, 0, 0, 0))
			        == NOTOK) {
			    advise (LLOG_EXCEPTIONS, "failed",
				    "start_clts_server");
			    failed++;
			    continue;
			}
			if (clts >= nfds)
			    nfds = clts + 1;
			FD_SET (clts, &ifds);
			if (nd == NOTOK)
			    nd = clts;

			advise (LLOG_NOTICE, NULLCP,
				"listening on %s", taddr2str (ta));
			listening++;
			continue;
		    }
#else
		    break;
#endif

		default:
		    adios (NULLCP, "unknown network type 0x%x", na -> na_stack);
		    /* NOT REACHED */
	    }
	}

	advise (LLOG_NOTICE, NULLCP, "listening on %s", taddr2str (ta));

	if (TNetListen (ta, td) == NOTOK) {
	    ts_advise (td, LLOG_EXCEPTIONS, "listen failed");
	    failed++;
	}
	else
	    listening++;
    }

    if (!listening)
	adios (NULLCP, failed ? "no successful listens"
			      : "no network services selected");

#ifndef	SNMPT
    do_trap (int_SNMP_generic__trap_coldStart, 0,
	     (struct type_SNMP_VarBindList *) 0);
#endif

#ifdef	SMUX
    {
	struct sockaddr_in lo_socket;
	register struct sockaddr_in *lsock = &lo_socket;
	register struct servent *sp;
	register struct smuxReserved *sr;
	OT	ot;

	PHead -> pb_forw = PHead -> pb_back = PHead;
	THead -> tb_forw = THead -> tb_back = THead;
	for (sr = reserved; sr -> rb_text; sr++)
	    if (ot = text2obj (sr -> rb_text))
		sr -> rb_name = ot -> ot_name;

	bzero ((char *) lsock, sizeof *lsock);
	lsock -> sin_family = AF_INET;
	lsock -> sin_port = (sp = getservbyname ("smux", "tcp"))
						    ? sp -> s_port
						    : htons ((u_short) 199);

	if (smux_enabled) {
	    if ((smux = start_tcp_server (lsock, SOMAXCONN, 0, 0)) == NOTOK)
		adios ("failed", "start_tcp_server for SMUX");
	    if (smux >= nfds)
		nfds = smux + 1;
	    FD_SET (smux, &ifds);
	}
    }

    FD_ZERO (&sfds);
#endif

#ifdef	COTS
    FD_ZERO (&cfds);
#endif

    for (;;) {
	int	fd,
		secs;
#ifdef	COTS
	struct timeval tvs;
	register struct timeval *tv = &tvs;
#endif
	int	vecp;
	fd_set  rfds;
	char   *vec[4];

	secs = NOTOK;
#ifdef	COTS
	for (fd = 0; fd < nfds; fd++)
	    if (FD_ISSET (fd, &cfds)) {
		secs = IDLE_TIME + 10;
		break;
	    }
#endif

	rfds = ifds;	/* struct copy */
	if (TNetAcceptAux (&vecp, vec, &fd, NULLTA, nfds, &rfds, NULLFD,
			   NULLFD, secs, td) == NOTOK) {
	    ts_advise (td, LLOG_EXCEPTIONS, "TNetAccept failed");
	    continue;
	}

#ifdef	TCP
	if (udp != NOTOK && FD_ISSET (udp, &rfds))
	    doit_udp (udp);
#endif

#ifdef	SMUX
	if (smux != NOTOK
		&& FD_ISSET (smux, &rfds)
	        && (fd = start_smux ()) != NOTOK) {
	    if (fd >= nfds)
		nfds = fd + 1;
	    FD_SET (fd, &ifds);
	    FD_SET (fd, &sfds);
	}

	for (fd = 0; fd < nfds; fd++)
	    if (FD_ISSET (fd, &rfds) && FD_ISSET (fd, &sfds))
		doit_smux (fd);
#endif

#ifdef	CLTS
	if (clts != NOTOK && FD_ISSET (clts, &rfds))
	    doit_clts (clts);
#endif

#ifdef	COTS
	if (vecp > 0 && (fd = start_tsap (vecp, vec)) != NOTOK) {
	    if (fd >= nfds)
		nfds = fd + 1;
	    FD_SET (fd, &ifds);
	    FD_SET (fd, &cfds);
	}

	for (fd = 0; fd < nfds; fd++)
	    if (FD_ISSET (fd, &rfds) && FD_ISSET (fd, &cfds))
		doit_cots (fd);

	(void) gettimeofday (tv, (struct timezone *) 0);
	tv -> tv_sec -= (long) IDLE_TIME;

	for (fd = 0; fd < nfds; fd++)
	    if (FD_ISSET (fd, &cfds)) {
		if (timercmp (tv, &lru[fd], >)) {
		    advise (LLOG_EXCEPTIONS, NULLCP,
			    "clearing connection from %d: %s", fd,
			    taddr2str (taddrs + fd));
		    (void) TDiscRequest (fd, NULLCP, 0, td);

		    FD_CLR (fd, &ifds);
		    FD_CLR (fd, &cfds);
		    proxy_clear (fd);
		}
	    }

	for (fd = nfds - 1; fd >= 0; fd--)
	    if (FD_ISSET (fd, &ifds))
		break;
	nfds = fd + 1;
#endif
    }
}

/*  */

static void  ts_advise (td, code, event)
register struct TSAPdisconnect *td;
int	code;
char   *event;
{
    char    buffer[BUFSIZ];

    if (td -> td_cc > 0)
	(void) sprintf (buffer, "[%s] %*.*s",
		TErrString (td -> td_reason),
		td -> td_cc, td -> td_cc, td -> td_data);
    else
	(void) sprintf (buffer, "[%s]", TErrString (td -> td_reason));

    advise (code, NULLCP, "%s: %s", event, buffer);
}

/*    DOIT */

#ifdef	TCP
static	doit_udp (pd)
int	pd;
{
    int	    fd;
    char   *cp;
    struct sockaddr_in in_socket;
    register struct sockaddr_in *isock = &in_socket;
    struct NSAPaddr nas;
    register struct NSAPaddr *na = &nas;

    if ((fd = join_udp_client (pd, isock)) == NOTOK) {
	if (errno == EWOULDBLOCK)
	    return;
	adios ("failed", "join_udp_client");
    }

    (void) sprintf (source, "Internet=%s+%d+2",
		    cp = inet_ntoa (isock -> sin_addr),
		    (int) ntohs (isock -> sin_port));

    bzero ((char *) na, sizeof *na);
    na -> na_stack = NA_TCP;
    na -> na_community = ts_comm_tcp_default;
    (void) strncpy (na -> na_domain, cp, sizeof na -> na_domain - 1);
    na -> na_port = isock -> sin_port;

    doit_aux (fd, na, read_udp_socket, write_udp_socket, check_udp_socket);
#ifndef	SNMPT
    if (pqr)
	pqr -> pq_fd = fd, pqr -> pq_closefnx = close_udp_socket;
    else
#endif
	(void) close_udp_socket (fd);
}
#endif

/*  */

#ifdef	CLTS
static	doit_clts (pd)
int	pd;
{
    int	    fd;
    char   *cp;
    union sockaddr_osi in_socket;
    register union sockaddr_osi *isock = &in_socket;
    struct TSAPaddr tas;
    register struct TSAPaddr *ta = &tas;

    if ((fd = join_clts_client (pd, isock)) == NOTOK) {
	if (errno == EWOULDBLOCK)
	    return;
	adios ("failed", "join_tcp_client");
    }
    (void) tp42gen (ta, isock);

    (void) strcpy (source, taddr2str (ta));

    doit_aux (fd, ta -> ta_addrs, read_clts_socket, write_clts_socket,
	      check_clts_socket);
#ifndef	SNMPT
    if (pqr)
	pqr -> pq_fd = fd, pqr -> pq_closefnx = close_clts_socket;
    else
#endif
	(void) close_clts_socket (fd);
}
#endif

/*  */

#ifdef	COTS
static int  start_tsap (vecp, vec)
int	vecp;
char  **vec;
{
    struct TSAPstart tss;
    register struct TSAPstart *ts = &tss;
    struct TSAPdisconnect   tds;
    register struct TSAPdisconnect  *td = &tds;

    if (TInit (vecp, vec, ts, td) == NOTOK) {
	ts_advise (td, LLOG_EXCEPTIONS, "T-CONNECT.INDICATION");
	return NOTOK;
    }

    advise (LLOG_XXX, NULLCP,
	    "T-CONNECT.INDICATION: <%d, %s, %s, %d, %d>",
	    ts -> ts_sd,
	    taddr2str (&ts -> ts_calling), taddr2str (&ts -> ts_called),
	    ts -> ts_expedited, ts -> ts_tsdusize);

    if (TConnResponse (ts -> ts_sd, NULLTA, 0, NULLCP, 0, NULLQOS, td)
	    == NOTOK) {
	ts_advise (td, LLOG_EXCEPTIONS, "T-CONNECT.RESPONSE");
	return NOTOK;
    }

    taddrs[ts -> ts_sd] = ts -> ts_calling;	/* struct copy */
    (void) gettimeofday (lru + ts -> ts_sd, (struct timezone *) 0);

    return ts -> ts_sd;
}

/*  */

static	doit_cots (fd)
int	fd;
{
    (void) strcpy (source, taddr2str (taddrs + fd));
    doit_aux (fd, &(taddrs[fd].ta_addrs[0]), ts_read, ts_write, NULLIFP);
#ifndef	SNMPT
    if (pqr)
	pqr -> pq_fd = fd, pqr -> pq_closefnx = NULLIFP;
#endif

    (void) gettimeofday (lru + fd, (struct timezone *) 0);
}
#endif

/*  */

static	doit_aux (fd, na, rfx, wfx, cfx)
int	fd;
struct NSAPaddr *na;
IFP	rfx,
	wfx,
	cfx;
{
    int	    result,
    	    size;
    PE	    pe;
    PS	    ps;
    struct type_SNMP_Message *msg;

#ifdef	DEBUG
    didhup = NOTOK;
#endif

    result = NOTOK;
    pe = NULLPE;
    msg = NULL;
#ifndef	SNMPT
    pqr = NULL;
#endif
    if ((ps = ps_alloc (dg_open)) == NULLPS
	    || dg_setup (ps, fd, MAXSNMP, rfx, wfx, cfx) == NOTOK) {
	if (ps == NULLPS)
	    advise (LLOG_EXCEPTIONS, NULLCP, "ps_alloc: out of memory (%s)",
		    source);
	else
	    advise (LLOG_EXCEPTIONS, NULLCP, "dg_setup: %s (%s)",
		    ps_error (ps -> ps_errno), source);

	goto out;
    }

    size = ps -> ps_byteno;
    if ((pe = ps2pe (ps)) == NULLPE) {
#ifdef	COTS
	if (rfx == ts_read) {
	    FD_CLR (fd, &ifds);
	    FD_CLR (fd, &cfds);
	    proxy_clear (fd);

	    if (ps -> ps_errno == PS_ERR_NONE) {
		advise (LLOG_XXX, NULLCP,
			"T-DISCONNECT.INDICATION: %d (%s)", fd, source);
		goto out;
	    }
	}
#endif
	advise (LLOG_EXCEPTIONS, NULLCP, "ps2pe: %s (%s)",
		ps_error (ps -> ps_errno), source);
#ifndef	SNMPT
	snmpstat.s_inpkts++;
	snmpstat.s_asnparseerrs++;
#endif
	goto out;
    }
    size = ps -> ps_byteno - size;
#ifdef	COTS
    if (rfx == ts_read)
	advise (LLOG_XXX, NULLCP, "T-DATA.INDICATION: %d (%s)", fd, source);
    else
#endif
	advise (LLOG_XXX, NULLCP, "packet from %s", source);

#ifndef	SNMPT
    snmpstat.s_inpkts++;
#endif

    if (decode_SNMP_Message (pe, 1, NULLIP, NULLVP, &msg) == NOTOK) {
	advise (LLOG_EXCEPTIONS, NULLCP, "decode_SNMP_Message: %s (%s)",
		PY_pepy, source);
#ifndef	SNMPT
	snmpstat.s_asnparseerrs++;
#endif
#ifdef	COTS
	if (rfx == ts_read) {
	    struct TSAPdisconnect tds;

	    advise (LLOG_EXCEPTIONS, NULLCP,
		    "clearing connection from %d: %s", fd,
		    taddr2str (taddrs + fd));
	    (void) TDiscRequest (fd, NULLCP, 0, &tds);

	    FD_CLR (fd, &ifds);
	    FD_CLR (fd, &cfds);
	    proxy_clear (fd);
	}
#endif
	goto out;
    }

    PLOGP (pgm_log,SNMP_Message, pe, "Message", 1);

    result = process (ps, msg, na, size);

out: ;
    if (msg) {
/* BEGIN MOBY PEPSY HACK... */

	register struct type_SNMP_VarBindList *vp =
			msg -> data -> un.get__request -> variable__bindings;

	free_SNMP_VarBindList (vp);

	msg -> data -> un.get__request -> variable__bindings = NULL;

/* END MOBY PEPSY HACK... */

	free_SNMP_Message (msg);
    }
    if (pe)
	pe_free (pe);
    if (result != OK && ps)
	ps_free (ps);

#ifdef	DEBUG
    hupser (SIGHUP);
#endif
}

/*    PROCESS */

#ifndef	SNMPT
static int  process (ps, msg, na, size)
PS	ps;
register struct type_SNMP_Message *msg;
struct NSAPaddr *na;
int	size;
{
    int	    result;
    char   *commname;
    struct community *comm;
    PE	    pe;

    if (msg -> version != int_SNMP_version_version__1) {
	advise (LLOG_EXCEPTIONS, NULLCP, "badVersion: %d (%s)",
		msg -> version, source);
	snmpstat.s_badversions++;
	return DONE;
    }

    if ((commname = qb2str (msg -> community)) == NULLCP) {
	advise (LLOG_EXCEPTIONS, NULLCP, "qb2str: out of memory (%s)", source);
	return DONE;
    }

    result = NOTOK;
    if ((comm = str2comm (commname, na)) == NULL) {
	advise (LLOG_EXCEPTIONS, NULLCP, "badCommunity: %s (%s)",
		commname, source);
	snmpstat.s_badcommunitynames++;
	if (snmpstat.s_enableauthentraps == TRAPS_ENABLED)
	    do_trap (int_SNMP_generic__trap_authenticationFailure, 0,
		     (struct type_SNMP_VarBindList *) 0);
	goto out;
    }

    if ((result = do_operation (ps, msg, comm, size)) != DONE)
	goto out;

    pe = NULLPE;

    if (encode_SNMP_Message (&pe, 1, 0, NULLCP, msg) != NOTOK) {
	PLOGP (pgm_log,SNMP_Message, pe, "Message", 0);

	if (pe2ps (ps, pe) == NOTOK)
	    advise (LLOG_EXCEPTIONS, NULLCP, "pe2ps: %s (%s)",
		    ps_error (ps -> ps_errno), source);
	else
	    snmpstat.s_outpkts++;
    }
    else
	advise (LLOG_EXCEPTIONS, NULLCP, "encode_SNMP_Message: %s (%s)",
		PY_pepy, source);

    if (pe)
	pe_free (pe);

out: ;
    free (commname);

    return result;
}

/*  */

static int  do_operation (ps, msg, comm, size)
PS	ps;
struct type_SNMP_Message *msg;
struct community *comm;
int	size;
{
    int	    idx,
	    offset;
    struct view *vu = comm -> c_view;
    register struct type_SNMP_PDUs *pdu = msg -> data;
    register struct type_SNMP_VarBindList *vp;
    struct type_SNMP_VarBindList *op = NULL;
    register struct type_SNMP_GetResponse__PDU *parm = pdu -> un.get__response;

    idx = 0;
    switch (pdu -> offset) {
	case type_SNMP_PDUs_get__request:
	    snmpstat.s_ingetrequests++;
	    if (vu == NULL || !(comm -> c_permission & OT_RDONLY)) {
access_denied: ;
		advise (LLOG_EXCEPTIONS, NULLCP,
			"authorizationFailure: %d (%s)", pdu -> offset,
			source);
/* no trap for this... */
		offset = pdu -> offset;
		pdu -> offset = type_SNMP_PDUs_get__response;
		parm -> error__status = int_SNMP_error__status_noSuchName;
		goto out;
	    }
	    break;
	case type_SNMP_PDUs_get__next__request:
	    snmpstat.s_ingetnexts++;
	    if (vu == NULL || !(comm -> c_permission & OT_RDONLY))
		goto access_denied;
	    break;

	case type_SNMP_PDUs_set__request:
	    snmpstat.s_insetrequests++;
	    if (vu == NULL || !(comm -> c_permission & OT_WRONLY))
		goto access_denied;
	    break;

	case type_SNMP_PDUs_get__response:
	    snmpstat.s_ingetresponses++;
	    if (vu == NULL)
		goto access_denied;
	    if ((comm -> c_permission & OT_YYY) && proxy2 (msg) != OK)
		return NOTOK;
	    goto bad_operation;

	case type_SNMP_PDUs_trap:
	    snmpstat.s_intraps++;
	    if (vu == NULL)
		goto access_denied;
	    advise (LLOG_EXCEPTIONS, NULLCP,
		    "unexpectedOperation: %d (%s)", pdu -> offset, source);
	    return NOTOK;

	default:
	    if (vu == NULL)
		goto access_denied;
bad_operation: ;
	    advise (LLOG_EXCEPTIONS, NULLCP,
		    "badOperation: %d (%s)", pdu -> offset, source);
	    return NOTOK;
    }

    if (vu -> v_community)
	return proxy1 (ps, msg, comm);

    offset = pdu -> offset;
    pdu -> offset = type_SNMP_PDUs_get__response;

    {
	register struct type_SNMP_VarBindList **opp;

	opp = &op;
	for (vp = msg -> data -> un.get__request -> variable__bindings;
	         vp;
	         vp = vp -> next) {
	    register struct type_SNMP_VarBindList *bind;
	    register struct type_SNMP_VarBind *v;

	    if ((bind = (struct type_SNMP_VarBindList *)
					calloc (1, sizeof *bind)) == NULL) {
no_mem: ;
		free_SNMP_VarBindList (op);
		op = NULL;
		advise (LLOG_EXCEPTIONS, NULLCP, "out of memory");

		parm -> error__status = int_SNMP_error__status_genErr;
		goto out;
	    }
	    *opp = bind, opp = &bind -> next;

	    if ((v = (struct type_SNMP_VarBind *) calloc (1, sizeof *v))
		    == NULL)
		goto no_mem;
	    bind -> VarBind = v;

	    if ((v -> name = oid_cpy (vp -> VarBind -> name)) == NULLOID)
		goto no_mem;
	    (v -> value = vp -> VarBind -> value) -> pe_refcnt++;
	}
    }

    quantum++;

    switch (offset) {
	case type_SNMP_PDUs_get__request:
	case type_SNMP_PDUs_get__next__request:
	default:
	    idx = do_pass (msg, offset, vu);
	    break;

	case type_SNMP_PDUs_set__request:
	    if (idx = do_pass (msg, offset, vu)) {
		int	status = parm -> error__status;

		(void) do_pass (msg, type_SNMP_PDUs_rollback, vu);

		parm -> error__status = status;
	    }
	    else {
		(void) do_pass (msg, type_SNMP_PDUs_commit, vu);
		gc_set ();
	    }
	    break;
    }

out: ;
    parm -> error__index = idx;
    switch (parm -> error__status) {
	case int_SNMP_error__status_noError:
	    for (vp = op; vp; vp = vp -> next)
		size -= ps_get_abs (vp -> VarBind -> value);
	    for (vp = msg -> data -> un.get__request -> variable__bindings;
		     vp;
		     vp = vp -> next)
		if ((size += ps_get_abs (vp -> VarBind -> value)) >= tooBig) {
		    parm -> error__status = int_SNMP_error__status_tooBig;
		    goto out;
		}	

	    idx = 0;
	    for (vp = msg -> data -> un.get__request -> variable__bindings;
		     vp;
		     vp = vp -> next)
		idx++;
	    switch (offset) {
	        case type_SNMP_PDUs_get__request:
	        case type_SNMP_PDUs_get__next__request:
		    snmpstat.s_totalreqvars += idx;
		    break;

	        case type_SNMP_PDUs_set__request:
		    snmpstat.s_totalsetvars += idx;
		    break;
	    }
	    free_SNMP_VarBindList (op);
	    break;

	case int_SNMP_error__status_tooBig:
	    snmpstat.s_toobigs++;
	    goto losing;

	case int_SNMP_error__status_readOnly:
	    advise (LLOG_NOTICE, NULLCP, "lurking readOnly");
	    /* and fall... */
	case int_SNMP_error__status_noSuchName:
	    snmpstat.s_nosuchnames++;
	    goto losing;

	case int_SNMP_error__status_badValue:
	    snmpstat.s_badvalues++;
	    goto losing;

	case int_SNMP_error__status_genErr:
	    snmpstat.s_generrs++;
	    goto losing;

	default:
losing: ;
	    if (op) {
		free_SNMP_VarBindList (msg -> data -> un.get__request
							-> variable__bindings);
		msg -> data -> un.get__request -> variable__bindings = op;
	    }
	    break;
    }
    snmpstat.s_outgetresponses++;

    return DONE;
}

/*  */

static int  do_pass (msg, offset, vu)
struct type_SNMP_Message *msg;
int	offset;
struct view *vu;
{
    int	    idx,
	    status;
    object_instance ois;
    register struct type_SNMP_PDUs *pdu = msg -> data;
    register struct type_SNMP_VarBindList *vp;
    register struct type_SNMP_GetResponse__PDU *parm = pdu -> un.get__response;
    IFP	    method;

    idx = 0;
    for (vp = msg -> data -> un.get__request -> variable__bindings;
	     vp;
	     vp = vp -> next) {
	register OI	oi;
	register OT	ot;
	register struct type_SNMP_VarBind *v = vp -> VarBind;

	idx++;
	
	if (offset == type_SNMP_PDUs_get__next__request) {
	    if ((oi = name2inst (v -> name)) == NULLOI
		    && (oi = next2inst (v -> name)) == NULLOI)
		goto no_name;

	    if ((ot = oi -> oi_type) -> ot_getfnx == NULLIFP
		    && ot -> ot_smux == NULL)
		goto get_next;
	}
	else {
	    if ((oi = name2inst (v -> name)) == NULLOI)
		goto no_name;
	    ot = oi -> oi_type;
	    if ((offset == type_SNMP_PDUs_get__request
		 	    ? ot -> ot_getfnx : ot -> ot_setfnx) == NULLIFP
		    && ot -> ot_smux == NULL) {
no_name: ;
		parm -> error__status = int_SNMP_error__status_noSuchName;
		return idx;
	    }
	}

try_again: ;
	switch (offset) {
	    case type_SNMP_PDUs_get__request:
	        if (!(vu -> v_mask & ot -> ot_views)) {
losing_name: ;
		    snmpstat.s_badcommunityuses++;
		    goto no_name;
		}
		method = ot -> ot_getfnx;
		break;

	    case type_SNMP_PDUs_get__next__request:
	        if (!(vu -> v_mask & ot -> ot_views))
		    goto get_next;
		method = ot -> ot_getfnx;
		break;

	    case type_SNMP_PDUs_set__request:
	    case type_SNMP_PDUs_commit:
	    case type_SNMP_PDUs_rollback:
	        if (!(vu -> v_mask & ot -> ot_views))
		    goto losing_name;
		method = ot -> ot_setfnx;
		break;
	}

#ifdef	SMUX
	if (ot -> ot_smux)
	    status = smux_method (pdu, ot,
				  ((struct smuxTree *) ot -> ot_smux)
								    -> tb_peer,
				  v, offset);
	else
#endif
	    status = (*method) (oi, v, offset);

	switch (status) {
	    case NOTOK:	    /* get-next wants a bump */
#ifdef SMUX
	        /* If current object is being handled by a SMUX sub-agent, then
		   step over the entire subtree before proceeding with the
		   'get-next' search.  This is needed to avoid descending into
		   the portion of the object tree that the SMUX sub-agent has
		   'mounted over'. (EJP) */
	        if (ot -> ot_smux) {
		    int level = ot -> ot_name -> oid_nelem;
		    
 		    while (ot -> ot_next
			       && ot -> ot_next -> ot_name -> oid_nelem
					> level)
		      ot = ot -> ot_next;
		  }
#endif
get_next: ;
		oi = &ois;
		for (;;) {
		    if ((ot = ot -> ot_next) == NULLOT) {
			parm -> error__status =
					    int_SNMP_error__status_noSuchName;
			return idx;
		    }
		    oi -> oi_name = (oi -> oi_type = ot) -> ot_name;
		    if (ot -> ot_getfnx || ot -> ot_smux)
			goto try_again;
		}

	    case int_SNMP_error__status_noError:
		break;

	    default:
		parm -> error__status = status;
		return idx;
	}
    }

    return 0;
}

/*  */

static	gc_set ()
{
#ifdef	SMUX
    register struct smuxPeer *pb,
			     *qb;
    register struct smuxTree *tb,
			     *ub;

    for (pb = PHead -> pb_forw; pb != PHead; pb = qb) {
	qb = pb -> pb_forw;

	if (pb -> pb_invalid)
	    pb_free (pb);
    }

    for (tb = THead -> tb_forw; tb != THead; tb = ub) {
	ub = tb -> tb_forw;
	
	if (tb -> tb_invalid)
	    tb_free (tb);
    }
#endif
}

/*    PROXY */

static int  proxy1 (psp, msg, comm)
PS	psp;
struct type_SNMP_Message *msg;
struct community *comm;
{
    int	    result;
    register struct view *v = comm -> c_view;
    register struct proxyque *pq;
    PE	    pe;
    PS	    ps;

    if (qb_pullup (msg -> community) == NOTOK) {
	advise (LLOG_EXCEPTIONS, NULLCP, "qb_pullup: out of memory (proxy %s)",
		source);
	return NOTOK;
    }
    if (pqs >= NPQ) {
	register struct proxyque *qp;

	for (qp = (pq = pips) + NPQ; pq < qp; pq++)
	    if (pq -> pq_age < quantum) {
		advise (LLOG_NOTICE, NULLCP, "proxy flush");
		if (pq -> pq_closefnx) {
		    ps_free (pq -> pq_ps);
		    (void) (*pq -> pq_closefnx) (pq -> pq_fd);
		}
		QBFREE (&pq -> pq_community);
		break;
	    }
	if (pq >= qp) {
	    advise (LLOG_EXCEPTIONS, NULLCP,
		    "proxy for view %s, but no proxyque slots available (%s)",
		    oid2ode (v -> v_name), source);
	    return NOTOK;
	}
    }
    else
	pq = pips + pqs++;

    pq -> pq_quantum = quantum;
    pq -> pq_age = quantum + 20;	/* who knows what a good value is?!? */
    pq -> pq_ps = psp;
    pq -> pq_community.qb_forw = pq -> pq_community.qb_back
		= &pq -> pq_community;
    insque (msg -> community -> qb_forw, &pq -> pq_community);
    free ((char *) msg -> community);
    pq -> pq_request = msg -> data -> un.get__request -> request__id;

    msg -> community = v -> v_community;
    msg -> data -> un.get__request -> request__id = pq -> pq_quantum;

    result = NOTOK;
    pe = NULLPE;
    if (encode_SNMP_Message (&pe, 1, 0, NULLCP, msg) == NOTOK) {
	advise (LLOG_EXCEPTIONS, NULLCP, "encode_SNMP_Message: %s (proxy %s)",
		PY_pepy, source);
	if (pe)
	    pe_free (pe);
	goto out;
    }
    PLOGP (pgm_log,SNMP_Message, pe, "Message", 0);

    if ((ps = ps_alloc (dg_open)) == NULLPS
	    || dg_setup (ps, udp, MAXSNMP, read_udp_socket, write_udp_socket,
 			 check_udp_socket) == NOTOK) {
	if (ps == NULLPS)
	    advise (LLOG_EXCEPTIONS, NULLCP,
		    "ps_alloc: out of memory (proxy %s)", source);
	else
	    advise (LLOG_EXCEPTIONS, NULLCP, "dg_setup: %s (proxy %s)",
		    ps_error (ps -> ps_errno), source);
    }
    else
	if (hack_dgram_socket (udp, &v -> v_sa) == NOTOK)
	    advise (LLOG_EXCEPTIONS, "failed",
		    "hack_dgram_socket(1) (proxy %s)", source);
	else
	    if (pe2ps (ps, pe) == NOTOK)
		advise (LLOG_EXCEPTIONS, NULLCP, "pe2ps: %s (proxy %s)",
			ps_error (ps -> ps_errno), source);
	    else {
		result = OK;

		if (hack_dgram_socket (udp, (struct sockaddr *) NULL) == NOTOK)
		    advise (LLOG_EXCEPTIONS, "failed",
			    "hack_dgram_socket(2) (proxy %s)", source);
	    }

    pe_free (pe);

    if (ps)
	ps_free (ps);

out: ;
    msg -> community = NULL;
    if (result == NOTOK) {
	register struct proxyque *qp = pips + --pqs;

	if (pq -> pq_closefnx) {
	    ps_free (pq -> pq_ps);
	    (void) (*pq -> pq_closefnx) (pq -> pq_fd);
	}
	QBFREE (&pq -> pq_community);

	if (pq != qp)
	    *pq = *qp;		/* struct copy */
    }
    else
	pqr = pq;

    return result;
}

/*  */

static int  proxy2 (msg)
struct type_SNMP_Message *msg;
{
    integer  request;
    register struct proxyque *pq,
			     *qp;
    PE	    pe;

    request = msg -> data -> un.get__request -> request__id;
    for (qp = (pq = pips) + pqs; pq < qp; pq++)
	if (pq -> pq_quantum == request)
	    break;
    if (pq >= qp)
	return OK;

    qb_free (msg -> community);
    msg -> community = &pq -> pq_community;
    msg -> data -> un.get__request -> request__id = pq -> pq_request;

    pe = NULLPE;
    if (encode_SNMP_Message (&pe, 1, 0, NULLCP, msg) == NOTOK) {
	advise (LLOG_EXCEPTIONS, NULLCP, "encode_SNMP_Message: %s (proxy %s)",
		PY_pepy, source);
	if (pe)
	    pe_free (pe);
	goto out;
    }
    PLOGP (pgm_log,SNMP_Message, pe, "Message", 0);

    if (pe2ps (pq -> pq_ps, pe) == NOTOK)
	advise (LLOG_EXCEPTIONS, NULLCP, "pe2ps: %s (proxy %s)",
		ps_error (pq -> pq_ps -> ps_errno), source);
    snmpstat.s_outgetresponses++;

    pe_free (pe);

out: ;
    msg -> community = NULL;

    qp = pips + --pqs;

    if (pq -> pq_closefnx) {
	ps_free (pq -> pq_ps);
	(void) (*pq -> pq_closefnx) (pq -> pq_fd);
    }
    QBFREE (&pq -> pq_community);

    if (pq != qp)
	*pq = *qp;	/* struct copy */

    return DONE;
}

/*  */

#ifdef	COTS
static	proxy_clear (fd)
int	fd;
{
    register struct proxyque *pq,
			     *qp;

again: ;
    for (qp = (pq = pips) + pqs; pq < qp; pq++)
	if (pq -> pq_fd == fd) {
	    qp = pips + --pqs;

	    if (pq -> pq_closefnx) {
		ps_free (pq -> pq_ps);
		(void) (*pq -> pq_closefnx) (pq -> pq_fd);
	    }
	    QBFREE (&pq -> pq_community);

	    if (pq != qp)
		*pq = *qp;
	    goto again;
	}
}
#endif

/*    SMUX */

#ifdef	SMUX
#include "smux.h"


static int  start_smux ()
{
    int	    fd;
    struct sockaddr_in in_socket;
    register struct sockaddr_in *isock = &in_socket;
    register struct smuxPeer *pb,
			     *qb;
    static int smux_peerno = 0;

    if ((fd = join_tcp_client (smux, isock)) == NOTOK) {
	if (errno == EWOULDBLOCK)
	    return NOTOK;
	adios ("failed", "join_tcp_client");
    }

    if ((pb = (struct smuxPeer *) calloc (1, sizeof *pb)) == NULL) {
	advise (LLOG_EXCEPTIONS, NULLCP, "doit_smux: out of memory");
out: ;
	(void) close_tcp_socket (fd);
	return NOTOK;
    }

    pb -> pb_address = *isock;		/* struct copy */

    /* Format sockets consistantly with other places in this program,
       with a plus sign between the internet address and port number.  (EJP) */
    (void) sprintf (pb -> pb_source, "%s+%d",
		    inet_ntoa (pb -> pb_address.sin_addr),
		    (int) ntohs (pb -> pb_address.sin_port));
    (void) strcpy (source, pb -> pb_source);

    if ((pb -> pb_ps = ps_alloc (fdx_open)) == NULLPS
	    || fdx_setup (pb -> pb_ps, fd) == NOTOK) {
	if (pb -> pb_ps == NULLPS)
	    advise (LLOG_EXCEPTIONS, NULLCP,
		    "ps_alloc: out of memory (SMUX %s)", source);
	else
	    advise (LLOG_EXCEPTIONS, NULLCP, "fdx_setup: %s (SMUX %s)",
		    ps_error (pb -> pb_ps -> ps_errno), source);

	pb_free (pb);
	goto out;
    }

    /* Insert new smuxPeer structure at the end of the doubly-linked
       list anchored at PHead and assign it the next sequential index
       number for use as smuxPindex in o_smuxPeer() and smuxTindex in
       o_smuxTree().  (EJP) */

    insque (pb, PHead -> pb_back);
    pb -> pb_index = ++smux_peerno;

    return (pb -> pb_fd = fd);
}

/*  */

static int  doit_smux (fd)
int	fd;
{
    PE	    pe;
    register struct smuxPeer *pb;
    struct type_SNMP_SMUX__PDUs *pdu;

    for (pb = PHead -> pb_forw; pb != PHead; pb = pb -> pb_forw)
	if (pb -> pb_fd == fd)
	    break;
    if (pb == PHead) {
	advise (LLOG_EXCEPTIONS, NULLCP, "lost smuxPeer block for %d", fd);
	FD_CLR (fd, &ifds);
	FD_CLR (fd, &sfds);

	return;
    }

    (void) strcpy (source, pb -> pb_source);

    if ((pe = ps2pe (pb -> pb_ps)) == NULLPE) {
	advise (LLOG_EXCEPTIONS, NULLCP, "ps2pe: %s (SMUX %s)",
		ps_error (pb -> pb_ps -> ps_errno), source);

out: ;
	if (pe)
	    pe_free (pe);
	pb_free (pb);
	return;
    }

    advise (LLOG_XXX, NULLCP, "SMUX packet from %s", source);

    pdu = NULL;

    if (decode_SNMP_SMUX__PDUs (pe, 1, NULLIP, NULLVP, &pdu) == NOTOK) {
	advise (LLOG_EXCEPTIONS, NULLCP,
		"decode_SNMP_SMUX__PDUs: %s (SMUX %s)", PY_pepy, source);
	goto out;
    }

    PLOGP (pgm_log,SNMP_SMUX__PDUs, pe, "SMUX Message", 1);

    if (smux_process (pb, pdu) == NOTOK)
	pb_free (pb);

    if (pdu)
	free_SNMP_SMUX__PDUs (pdu);
    if (pe)
	pe_free (pe);
}

/*  */

static	smux_process (pb, pdu)
register struct smuxPeer *pb;
struct type_SNMP_SMUX__PDUs *pdu;
{
    int	    result = OK;

    switch (pdu -> offset) {
	case type_SNMP_SMUX__PDUs_simple:
	    if (pb -> pb_identity)
		goto unexpected;
	    {
		register struct type_SNMP_SimpleOpen *simple =
							    pdu -> un.simple;
		register struct smuxEntry *se;

		if (simple -> version != int_SNMP_version_version__1) {
		    advise (LLOG_EXCEPTIONS, NULLCP,
			    "badVersion: %d (SMUX %s)",
			    simple -> version, source);
		    return NOTOK;
		}

		pb -> pb_identity = simple -> identity;
		simple -> identity = NULL;

		if ((pb -> pb_description = qb2str (simple -> description))
		        == NULL) {
		    advise (LLOG_EXCEPTIONS, NULLCP,
			    "qb2str: out of memory (SMUX %s)", source);
		    return NOTOK;
		}

		if (qb_pullup (simple -> password) == NOTOK) {
		    advise (LLOG_EXCEPTIONS, NULLCP,
			    "qb_pullup: out of memory (SMUX %s)", source);
		    return NOTOK;
		}

		if ((se = getsmuxEntrybyidentity (pb -> pb_identity)) == NULL
			|| strcmp (se -> se_password,
				   simple -> password -> qb_forw -> qb_data)) {
		    advise (LLOG_EXCEPTIONS, NULLCP,
			    "%s: %s (SMUX %s)",
			    se ? "badPassword" : "badIdentity",
			    oid2ode (simple -> identity), source);
		    if (snmpstat.s_enableauthentraps == TRAPS_ENABLED)
			do_trap (int_SNMP_generic__trap_authenticationFailure,
				 0, (struct type_SNMP_VarBindList *) 0);
		    return NOTOK;
		}

		if ((pb -> pb_priority = se -> se_priority) < 0)
		    pb -> pb_priority = 0;
		
		advise (LLOG_NOTICE, NULLCP,
			"SMUX open: %d %s \"%s\" (%d/ %s)",
			pb -> pb_index, oid2ode (pb -> pb_identity),
			pb -> pb_description, pb -> pb_fd, source);
	    }
	    break;

	case type_SNMP_SMUX__PDUs_close:
	    if (!pb -> pb_identity)
		goto unexpected;
	    advise (LLOG_NOTICE, NULLCP,
		    "SMUX close: %s (%s)",
		    smux_error (pdu -> un.close -> parm), source);
	    return NOTOK;

	case type_SNMP_SMUX__PDUs_registerRequest:
	    if (!pb -> pb_identity)
		goto unexpected;
	    {
		register struct type_SNMP_RReqPDU *rreq =
						    pdu -> un.registerRequest;
		struct type_SNMP_RRspPDU rrsp;
		struct type_SNMP_SMUX__PDUs rsp;
		register struct smuxReserved *sr;
		register struct smuxTree *tb = NULL;
		register struct smuxTree *qb;
		register OID	oid = rreq -> subtree;
		OT	ot = NULLOT;
		PE	pe;

		for (sr = reserved; sr -> rb_text; sr++)
		    if (sr -> rb_name
			    && bcmp ((char *) sr -> rb_name -> oid_elements,
				     (char *) oid -> oid_elements,
				     (sr -> rb_name -> oid_nelem
				         <= oid -> oid_nelem
				     		? sr -> rb_name -> oid_nelem
						: oid -> oid_nelem)
				         * sizeof oid -> oid_elements[0])
				    == 0) {
			advise (LLOG_EXCEPTIONS, NULLCP,
				"reservedSubTree: %s %s %s (SMUX %s)",
				oid2ode (oid),
				sr -> rb_name -> oid_nelem
				    <= oid -> oid_nelem
					? "under" : "contains",
				sr -> rb_text, source);
			goto no_dice;
		    }

		if ((ot = name2obj (oid)) == NULLOT) {
		    if (rreq -> operation == int_SNMP_operation_delete) {
			advise (LLOG_EXCEPTIONS, NULLCP,
				"noSuchSubTree: %s (SMUX %s)",
				oid2ode (oid), source);
			goto no_dice;
		    }
		
		    if ((ot = (OT) calloc (1, sizeof *ot)) == NULL
			    || (ot -> ot_text = ot -> ot_id =
					strdup (sprintoid (oid)))
			        == NULL) {
			advise (LLOG_EXCEPTIONS, NULLCP,
				"out of memory (SMUX %s)", source);
			if (ot)
			    free ((char *) ot);
			return NOTOK;
		    }
		    ot -> ot_name = rreq -> subtree;
		    rreq -> subtree = NULL;
		    ot -> ot_access = rreq -> operation;
		    ot -> ot_status = OT_OPTIONAL;
		    export_view (ot);

		    (void) add_objects (ot);
		}
		else {
		    if (rreq -> operation == int_SNMP_operation_delete) {
			for (tb = (struct smuxTree *) ot -> ot_smux;
			         tb;
			         tb = tb -> tb_next)
			    if (tb -> tb_peer == pb
				    && (rreq -> priority < 0
					    || rreq -> priority
							== tb -> tb_priority))
				break;
			if (tb)
			    tb_free (tb);
			else {
			    advise (LLOG_EXCEPTIONS, NULLCP,
				    "noSuchRegistration: %s (SMUX %s)",
				    oid2ode (oid), source);
			    ot = NULLOT;
			}
			goto no_dice;
		    }

		    if (ot -> ot_name -> oid_nelem > oid -> oid_nelem) {
			advise (LLOG_EXCEPTIONS, NULLCP,
				"badSubTree: %s (SMUX %s)",
				oid2ode (oid), source);
			ot = NULL;
			goto no_dice;
		    }

		    export_view (ot);
		}

		if ((tb = (struct smuxTree *) calloc (1, sizeof *tb))
		        == NULL) {
		    advise (LLOG_EXCEPTIONS, NULLCP,
			    "out of memory (SMUX %s)", source);
		    return NOTOK;
		}

		if ((tb -> tb_priority = rreq -> priority) < pb -> pb_priority)
		    tb -> tb_priority = pb -> pb_priority;
		for (qb = (struct smuxTree *) ot -> ot_smux;
		         qb;
		         qb = qb -> tb_next)
		    if (qb -> tb_priority > tb -> tb_priority)
			break;
		    else
			if (qb -> tb_priority == tb -> tb_priority)
			    tb -> tb_priority++;

		tb -> tb_peer = pb;

no_dice: ;
		bzero ((char *) &rsp, sizeof rsp);
		rsp.offset = type_SNMP_SMUX__PDUs_registerResponse;
		rsp.un.registerResponse = &rrsp;

		bzero ((char *) &rrsp, sizeof rrsp);
		rrsp.parm = tb ? tb -> tb_priority
				  : int_SNMP_RRspPDU_failure;

		pe = NULLPE;

		if (encode_SNMP_SMUX__PDUs (&pe, 1, 0, NULLCP, &rsp)
			!= NOTOK) {
		    PLOGP (pgm_log,SNMP_SMUX__PDUs, pe, "SMUX Message", 0);

		    if (pe2ps (pb -> pb_ps, pe) == NOTOK) {
			advise (LLOG_EXCEPTIONS, NULLCP, "pe2ps: %s (SMUX %s)",
				ps_error (pb -> pb_ps -> ps_errno), source);
			result = NOTOK;
		    }
		    else {
			if (ot)
			    advise (LLOG_NOTICE, NULLCP,
				    "SMUX register: %s %s in=%d out=%d (%s)",
				    rreq -> operation
				        == int_SNMP_operation_delete ? "delete"
					: rreq -> operation
				    		== int_SNMP_operation_readOnly
				        ? "readOnly" : "readWrite",
				    oid2ode (ot -> ot_name),
				    rreq -> priority,
				    tb ? tb -> tb_priority : -1, source);

			if (tb
			        && rreq -> operation
					!= int_SNMP_operation_delete) {
			    register int    i;
			    register unsigned int *ip,
						  *jp;
			    register struct smuxTree **qpp;

			    tb -> tb_subtree = ot;

		    /* Insert the new element into the single-linked chain
		       of smuxTree structures for this object type that is
		       anchored in the object tree.  Elements are chained
		       in order of increasing priority.  (EJP) */

 			    for (qpp = (struct smuxTree **) &ot -> ot_smux;
				     qb = *qpp;
				     qpp = &qb -> tb_next)
				if (qb -> tb_priority > tb -> tb_priority)
				    break;
			    tb -> tb_next = qb;
			    *qpp = tb;

		    /* Fill in the tb_instance field of the new element
		       with the concatenation of the size of the object name
		       followed by the object name itself followed by
		       its priority.  (EJP) */

			    ip = tb -> tb_instance;
			    jp = ot -> ot_name -> oid_elements;
			    *ip++ = ot -> ot_name -> oid_nelem;
			    for (i = ot -> ot_name -> oid_nelem; i > 0; i--)
				*ip++ = *jp++;
			    *ip++ = tb -> tb_priority;
			    tb -> tb_insize = ip - tb -> tb_instance;

		    /* Insert the new element into the doubly-linked chain
		       of all smuxTree structures that is anchored in THead.
		       Elements are chained in lexicographic order of
		       tb_instance so that the get_tbent() function will
		       work correctly for 'get next' operations.  (EJP) */

			    for (qb = THead -> tb_forw;
				     qb != THead;
				     qb = qb -> tb_forw)
			      if (elem_cmp (tb -> tb_instance, tb -> tb_insize,
					    qb -> tb_instance, qb -> tb_insize)
				      < 0)
				  break;
			    insque (tb, qb -> tb_back);
			}
		    }
		}
		else {
		    advise (LLOG_EXCEPTIONS, NULLCP,
			    "encode_SNMP_SMUX__PDUs: %s (SMUX %s)",
			    PY_pepy, source);
		    result = NOTOK;
		}

		if (pe)
		    pe_free (pe);
	    }
	    break;

	case type_SNMP_SMUX__PDUs_trap:
	    if (!pb -> pb_identity)
		goto unexpected;
	    {
		struct qbuf *qb;
		struct type_SNMP_Message msgs;
		register struct type_SNMP_Message *msg = &msgs;
		struct type_SNMP_PDUs datas;
		register struct type_SNMP_PDUs *data = &datas;
		register struct type_SNMP_Trap__PDU *parm = pdu -> un.trap;

		advise (LLOG_NOTICE, NULLCP,
			"SMUX trap: %d %d (%s)",
			parm -> generic__trap, parm -> specific__trap, source);

		bzero ((char *) msg, sizeof *msg);
		msg -> version = int_SNMP_version_version__1;
		msg -> data = data;

		bzero ((char *) data, sizeof *data);
		data -> offset = type_SNMP_PDUs_trap;
		data -> un.trap = parm;

		if (loopback_addr
			&& qb_pullup (qb = parm -> agent__addr) != NOTOK
		        && qb -> qb_len == loopback_addr -> qb_len
		        && bcmp (qb -> qb_forw -> qb_data,
				 loopback_addr -> qb_forw -> qb_data,
				 qb -> qb_len) == 0)
		    parm -> agent__addr = trap -> data -> un.trap->agent__addr;
		do_traps (msg, parm -> generic__trap, parm -> specific__trap);
		parm -> agent__addr = qb;
	    }
	    break;

	case type_SNMP_SMUX__PDUs_registerResponse:
	case type_SNMP_SMUX__PDUs_get__request:
	case type_SNMP_SMUX__PDUs_get__next__request:
	case type_SNMP_SMUX__PDUs_get__response:
	case type_SNMP_SMUX__PDUs_set__request:
unexpected: ;
	    advise (LLOG_EXCEPTIONS, NULLCP,
		    "unexpectedOperation: %d (SMUX %s)", pdu -> offset,
		    source);
	    return NOTOK;

	default:
	    advise (LLOG_EXCEPTIONS, NULLCP,
		    "badOperation: %d (SMUX %s)", pdu -> offset, source);
	    return NOTOK;
    }

    return result;
}

/*  */

static int  smux_method (pdu, ot, pb, v, offset)
struct type_SNMP_PDUs *pdu;
OT	ot;
register struct smuxPeer *pb;
register struct type_SNMP_VarBind *v;
int	offset;
{
    int	    status,
	    orig_id;
    struct type_SNMP_VarBindList *orig_bindings,
				  vps;
    struct type_SNMP_SMUX__PDUs  req,
				*rsp;
    struct type_SNMP_SOutPDU cor;
    register struct type_SNMP_GetResponse__PDU *get;
    PE	    pe;

    status = int_SNMP_error__status_noError;

    bzero ((char *) &req, sizeof req);
    switch (offset) {
	case type_SNMP_PDUs_get__request:
	    req.offset = type_SNMP_SMUX__PDUs_get__request;
	    goto stuff_pdu;

	case type_SNMP_PDUs_get__next__request:
	    req.offset = type_SNMP_SMUX__PDUs_get__next__request;
	    goto stuff_pdu;

	case type_SNMP_PDUs_set__request:
	    req.offset = type_SNMP_SMUX__PDUs_set__request;
stuff_pdu: ;
	    orig_id = pdu -> un.get__request -> request__id;
	    orig_bindings = pdu -> un.get__request -> variable__bindings;

	    req.un.get__request = pdu -> un.get__request;
	    bzero ((char *) &vps, sizeof vps);
	    vps.VarBind = v;

	    pdu -> un.get__request -> request__id = quantum;
	    pdu -> un.get__request -> variable__bindings = &vps;
	    break;

	case type_SNMP_PDUs_commit:
	    cor.parm = int_SNMP_SOutPDU_commit;
	    goto stuff_cor;

	case type_SNMP_PDUs_rollback:
	    cor.parm = int_SNMP_SOutPDU_rollback;
stuff_cor: ;
	    req.offset = type_SNMP_SMUX__PDUs_commitOrRollback;
	    req.un.commitOrRollback = &cor;
	    break;
    }

    pe = NULLPE;

    if (encode_SNMP_SMUX__PDUs (&pe, 1, 0, NULLCP, &req) != NOTOK) {
	PLOGP (pgm_log,SNMP_SMUX__PDUs, pe, "SMUX Message", 0);

	if (pe2ps (pb -> pb_ps, pe) == NOTOK) {
	    advise (LLOG_EXCEPTIONS, NULLCP, "pe2ps: %s (%s, SMUX %s)",
		    ps_error (pb -> pb_ps -> ps_errno), source,
		    pb -> pb_source);

lost_peer: ;
	    pb_free (pb);
	    status = int_SNMP_error__status_genErr;
	}
    }
    else {
	advise (LLOG_EXCEPTIONS, NULLCP,
		"encode_SNMP_SMUX__PDUs: %s (%s)", PY_pepy, source);
	status = int_SNMP_error__status_genErr;
    }

    if (pe)
	pe_free (pe);

    switch (offset) {
	case type_SNMP_PDUs_get__request:
	case type_SNMP_PDUs_get__next__request:
	case type_SNMP_PDUs_set__request:
	    pdu -> un.get__request -> request__id = orig_id;
	    pdu -> un.get__request -> variable__bindings = orig_bindings;
	    break;

	default:
	    break;
    }

    if (status != int_SNMP_error__status_noError
	    || offset == type_SNMP_PDUs_rollback)
	return status;

    if ((pe = ps2pe (pb -> pb_ps)) == NULLPE) {
	advise (LLOG_EXCEPTIONS, NULLCP, "ps2pe: %s (%s, SMUX %s)",
		ps_error (pb -> pb_ps -> ps_errno), source,
		pb -> pb_source);

	goto lost_peer;
    }


    rsp = NULL;

    if (decode_SNMP_SMUX__PDUs (pe, 1, NULLIP, NULLVP, &rsp) == NOTOK) {
	advise (LLOG_EXCEPTIONS, NULLCP,
		"decode_SNMP_SMUX__PDUs: %s (%s, SMUX %s)", PY_pepy, source,
		pb -> pb_source);

lost_peer_again: ;
	pb_free (pb);
	status = int_SNMP_error__status_genErr;
	goto out;
    }

    PLOGP (pgm_log,SNMP_SMUX__PDUs, pe, "SMUX Message", 1);

    if (rsp -> offset != type_SNMP_SMUX__PDUs_get__response) {
	advise (LLOG_EXCEPTIONS, NULLCP,
		"unexpectedOperation: %d (%s, SMUX %s)", rsp -> offset,
		source, pb -> pb_source);

	goto lost_peer_again;
    }
    get = rsp -> un.get__response;

    switch (status = get -> error__status) {
	case int_SNMP_error__status_noError:
	    {
		register struct type_SNMP_VarBindList *vp;
		register struct type_SNMP_VarBind *v2;

		if ((vp = get -> variable__bindings) == NULL) {
		    advise (LLOG_EXCEPTIONS, NULLCP,
			    "missing variable in get response (%s, SMUX %s)",
			    source, pb -> pb_source);

		    goto lost_peer_again;
		}
		v2 = vp -> VarBind;

		if (offset == type_SNMP_PDUs_get__next__request
		        && (ot -> ot_name -> oid_nelem
			    	    > v2 -> name -> oid_nelem
			        || bcmp ((char *) ot -> ot_name ->oid_elements,
					 (char *) v2 -> name -> oid_elements,
					 ot -> ot_name -> oid_nelem
				           * sizeof ot -> ot_name ->
							oid_elements[0]))) {
			status = NOTOK;
			break;
		}
		free_SNMP_ObjectName (v -> name);
		v -> name = v2 -> name;
		v2 -> name = NULL;
		free_SNMP_ObjectSyntax (v -> value);
		v -> value = v2 -> value;
		v2 -> value = NULL;
	    }
	    break;

	case int_SNMP_error__status_noSuchName:
	    if (offset == type_SNMP_PDUs_get__next__request) {
		status = NOTOK;
		break;
	    }
	    /* else fall */

        default:
	    break;
    }

out: ;
    if (rsp)
	free_SNMP_SMUX__PDUs (rsp);
    if (pe)
	pe_free (pe);

    return status;
}

/*  */

static	pb_free (pb)
register struct smuxPeer *pb;
{
    register struct smuxTree *tb,
			     *ub;

    if (pb == NULL)
	return;

    for (tb = THead -> tb_forw; tb != THead; tb = ub) {
	ub = tb -> tb_forw;
	
	if (tb -> tb_peer == pb)
	    tb_free (tb);
    }

    if (pb -> pb_ps)
	ps_free (pb -> pb_ps);

    if (pb -> pb_fd != NOTOK) {
	(void) close_tcp_socket (pb -> pb_fd);
	FD_CLR (pb -> pb_fd, &ifds);
	FD_CLR (pb -> pb_fd, &sfds);
    }

    if (pb -> pb_identity)
	oid_free (pb -> pb_identity);
    if (pb -> pb_description)
	free (pb -> pb_description);

    remque (pb);

    free ((char *) pb);
}

/*  */

static	tb_free (tb)
register struct smuxTree *tb;
{
    register struct smuxTree *tp,
			    **tpp;

    if (tb == NULL)
	return;

    for (tpp = (struct smuxTree **) &tb -> tb_subtree -> ot_smux;
	     tp = *tpp;
	     tpp = &tp -> tb_next)
	if (tp == tb) {
	    *tpp = tb -> tb_next;
	    break;
	}

    remque (tb);

    free ((char *) tb);
}
#endif	/* SMUX */

/*    VIEWS */

static	start_view () {
    register OT	    ot;

    if (ot = text2obj ("view"))
	viewTree = ot -> ot_name;

    /* Initialize the view mask of all objects in the tree, not just
       the leaves.  This is necessary so that do_pass() will allow
       SMUX sub-agents to mount their subtrees over interior objects.  (EJP) */

    for (ot = text2obj ("ccitt"); ot; ot = ot -> ot_next)
        export_view (ot);
}

/*  */

static	export_view (ot)
register OT	ot;
{
    register struct subtree *s;
    register struct view  *v;
    OID	    name = ot -> ot_name;

    ot -> ot_views = 0;
    for (v = VHead -> v_forw; v != VHead; v = v -> v_forw)
	if ((s = v -> v_subtree.s_forw) != &v -> v_subtree) {
	    for (; s != &v -> v_subtree; s = s -> s_forw)
		if (inSubtree (s -> s_subtree, name))
		    goto mark_it;
	}
	else
	    if (!viewTree || !inSubtree (viewTree, name)) {
mark_it: ;
		ot -> ot_views |= v -> v_mask;
	    }
}

/*    COMMUNITIES */

static	struct community *str2comm (name, na)
char   *name;
register struct NSAPaddr *na;
{
    register struct community *c,
			      *d;

    d = NULL;
    for (c = CHead -> c_forw; c != CHead; c = c -> c_forw)
	if (strcmp (c -> c_name, name) == 0) {
	    if (c -> c_addr.na_stack == NA_TCP
		    && strcmp (c -> c_addr.na_domain, "0.0.0.0") == 0) {
		d = c;
		continue;
	    }
	    else {
		if (c -> c_addr.na_stack != na -> na_stack)
		    continue;
		switch (na -> na_stack) {
		    case NA_TCP:
		        if (strcmp (c -> c_addr.na_domain, na -> na_domain))
			    continue;
			break;

		   case NA_X25:
			if (c -> c_addr.na_dtelen != na -> na_dtelen
			        || bcmp (c -> c_addr.na_dte,
					 na -> na_dte, na -> na_dtelen))
			    continue;
			break;

		    case NA_NSAP:
			if (c -> c_addr.na_addrlen != na -> na_addrlen
			        || bcmp (c -> c_addr.na_address,
					 na -> na_address, na -> na_addrlen))
			    continue;
			break;

		    default:
			adios (NULLCP,
			       "unknown network type (0x%x) for community \"%s\"",
			       na -> na_stack, name);
			/* NOTREACHED */
		}
	    }

	    d = c;
	    break;
	}

    if (d) {
	remque (d);
	insque (d, CHead);
    }

    return d;
}

/*    TRAPS */

static	start_trap () {
#ifdef	TCP
    char    myhost[BUFSIZ];
    register struct hostent *hp;
    struct type_SNMP_Message *msg;
    register struct type_SNMP_PDUs *pdu;
    register struct type_SNMP_Trap__PDU *parm;

    if ((msg = (struct type_SNMP_Message *) calloc (1, sizeof *msg)) == NULL) {
no_mem: ;
	advise (LLOG_EXCEPTIONS, NULLCP,
		"unable to initialize trap structure: out of memory");
out: ;
	if (msg)
	    free_SNMP_Message (msg);

	return;
    }
    msg -> version = int_SNMP_version_version__1;

    if ((pdu = (struct type_SNMP_PDUs *) calloc (1, sizeof *pdu)) == NULL)
	goto no_mem;
    msg -> data = pdu;

    pdu -> offset = type_SNMP_PDUs_trap;

    if ((parm = (struct type_SNMP_Trap__PDU *) calloc (1, sizeof *parm))
	    == NULL)
	goto no_mem;
    pdu -> un.trap = parm;

    (void) strcpy (myhost, TLocalHostName ());
    if (hp = gethostbystring (myhost)) {
	struct sockaddr_in sin;

	inaddr_copy (hp, &sin);
	if ((parm -> agent__addr = str2qb ((char *) &sin.sin_addr, 4, 1))
	        == NULL)
	    goto no_mem;
    }
    else {
	advise (LLOG_EXCEPTIONS, NULLCP,
		"%s: unknown host, so no traps", myhost);
	goto out;
    }

    if ((parm -> time__stamp = (struct type_SNMP_TimeTicks *)
	 	calloc (1, sizeof *parm -> time__stamp)) == NULL)
	goto no_mem;

    trap = msg;

#ifdef	SMUX
    if (hp = gethostbystring ("localhost")) {
	struct sockaddr_in sin;

	inaddr_copy (hp, &sin);
	if ((loopback_addr = str2qb ((char *) &sin.sin_addr, 4, 1)) == NULL)
	    advise (LLOG_EXCEPTIONS, NULLCP,
		    "unable to initialize loopback address: out of memory");
    }
#endif
#endif
}

/*  */

#ifndef	TCP
/* ARGSUSED */
#endif

static	do_trap (generic, specific, bindings)
int	generic,
	specific;
struct type_SNMP_VarBindList *bindings;
{
#ifdef	TCP
    struct type_SNMP_Message *msg;
    register struct type_SNMP_Trap__PDU *parm;
    OT	    ot;

    if ((msg = trap) == NULL)
	return;
    parm = msg -> data -> un.trap;

    if ((ot = text2obj ("sysObjectID")) == NULLOT) {
	advise (LLOG_EXCEPTIONS, NULLCP,
		"unable to send trap: no such object: \"%s\"",
		"sysObjectID");
	return;
    }
    if ((parm -> enterprise = (OID) ot -> ot_info) == NULLOID) {
	advise (LLOG_EXCEPTIONS, NULLCP,
		"unable to send trap: no value defined for object \"%s\"",
		"sysObjectID");
	return;
    }

    parm -> generic__trap = generic;
    parm -> specific__trap = specific;
    {
	struct timeval now;

	if (gettimeofday (&now, (struct timezone *) 0) == NOTOK) {
	    advise (LLOG_EXCEPTIONS, "failed", "gettimeofday");
	    return;
	}

	parm -> time__stamp -> parm = (now.tv_sec - my_boottime.tv_sec) * 100
	    				+ ((now.tv_usec - my_boottime.tv_usec)
								      / 10000);
    }
    parm -> variable__bindings = bindings;

    do_traps (msg, (integer) generic, (integer) specific);
#endif
}

/*  */

#ifdef	TCP
static	do_traps (msg, generic, specific)
register struct type_SNMP_Message *msg;
integer	generic,
	specific;
{
    int	    mask = 1 << 7 - generic;
    register struct trap *t;

    for (t = UHead -> t_forw; t != UHead; t = t -> t_forw) {
	register struct view *v = t -> t_view;
	PE	pe;
	PS	ps;

	if (specific == 0 && !(t -> t_generics & mask))
	    continue;

	msg -> community = v -> v_community;

	pe = NULLPE;
	if (encode_SNMP_Message (&pe, 1, 0, NULLCP, msg) == NOTOK) {
	    advise (LLOG_EXCEPTIONS, NULLCP,
		    "encode_SNMP_Message: %s", PY_pepy);
	    if (pe)
		pe_free (pe);
	    continue;
	}
	PLOGP (pgm_log,SNMP_Message, pe, "Message", 0);

	if ((ps = ps_alloc (dg_open)) == NULLPS
	        || dg_setup (ps, udp, MAXSNMP, read_udp_socket,
			     write_udp_socket, check_udp_socket) == NOTOK) {
	    if (ps == NULLPS)
		advise (LLOG_EXCEPTIONS, NULLCP, "ps_alloc: out of memory");
	    else
		advise (LLOG_EXCEPTIONS, NULLCP, "dg_setup: %s",
			ps_error (ps -> ps_errno));
	}
	else {
	    if (hack_dgram_socket (udp, &v -> v_sa) == NOTOK)
		advise (LLOG_EXCEPTIONS, "failed", "hack_dgram_socket(1)");
	    else {
		if (pe2ps (ps, pe) == NOTOK)
		    advise (LLOG_EXCEPTIONS, NULLCP, "pe2ps: %s",
			    ps_error (ps -> ps_errno));
		else {
		    snmpstat.s_outpkts++, snmpstat.s_outtraps++;
 		}

		/* This function should always be called, regardless of whether
		   pe2ps() succeeds or fails.  (EJP) */
		if (hack_dgram_socket (udp, (struct sockaddr *) NULL) == NOTOK)
			advise (LLOG_EXCEPTIONS, "failed", "hack_dgram_socket(2)");
	    }
        }

	pe_free (pe);

	if (ps)
	    ps_free (ps);
	else
	    break;
    }
}
#endif
#else	/* SNMPT */

/*  */

/* ARGSUSED */

static int  process (ps, msg, na, size)
PS	ps;
register struct type_SNMP_Message *msg;
struct NSAPaddr *na;
{
    char   *cp;
    long    now;
    PE	    pe,
	    p;
    register struct type_SNMP_PDUs *pdu = msg -> data;
    register struct tm *tm;
    struct UTCtime uts;
    register struct UTCtime *ut = &uts;
    register struct type_SNMP_Audit *au;

    if (msg -> version != int_SNMP_version_version__1) {
	advise (LLOG_EXCEPTIONS, NULLCP, "badVersion: %d (%s)",
		msg -> version, source);
	return NOTOK;
    }

    if (pdu -> offset != type_SNMP_PDUs_trap) {
	advise (LLOG_EXCEPTIONS, NULLCP,
		"unexpectedOperation: %d (%s)", pdu -> offset, source);
	return NOTOK;
    }

    pe = p = NULLPE;
    au = NULL;

    if (encode_SNMP_Message (&p, 1, 0, NULLCP, msg) == NOTOK) {
	advise (LLOG_EXCEPTIONS, NULLCP, "encode_SNMP_Message: %s (%s)",
		PY_pepy, source);
	goto out;
    }

    if ((au = (struct type_SNMP_Audit *) calloc (1, sizeof *au)) == NULL)
	goto no_mem;
    au -> sizeOfEncodingWhichFollows = ps_get_abs (p);

    if ((au -> source = str2qb (source, strlen (source), 1)) == NULL) {
no_mem: ;
        advise (LLOG_EXCEPTIONS, NULLCP, "out of memory for audit (%s)",
		source);
	goto out;
    }
    (void) time (&now);

    if (tm = gmtime (&now))
	tm2ut (tm, ut);
    else {
	advise (LLOG_EXCEPTIONS, NULLCP, "gmtime failed");

	bzero ((char *) ut, sizeof *ut);
    }

    if ((cp = gent2str (ut)) == NULL
	    || (au -> dateAndTime = str2qb (cp, strlen (cp), 1)) == NULL)
	goto no_mem;

    if (encode_SNMP_Audit (&pe, 1, 0, NULLCP, au) != NOTOK) {
	PLOGP (pgm_log,SNMP_Audit, pe, "Audit", 0);
	PLOGP (pgm_log,SNMP_Message, p, "Message", 0);

	if (pe2ps (audit, pe) == NOTOK || pe2ps (audit, p) == NOTOK)
	    advise (LLOG_EXCEPTIONS, NULLCP, "pe2ps: %s (%s)",
		    ps_error (audit -> ps_errno), source);

	(void) ps_flush (audit);
    }
    else
	advise (LLOG_EXCEPTIONS, NULLCP, "encode_SNMP_Audit: %s (%s)",
		PY_pepy, source);

out: ;
    if (au)
	free_SNMP_Audit (au);
    if (pe)
	pe_free (pe);
    if (p)
	pe_free (p);

    return DONE;
}
#endif	/* SNMPT */

/*    MISCELLANY */

static	arginit (vec)
char	**vec;
{
    register char  *ap;
#ifdef	SNMPT
    char   *file = "snmp.traps";
    FILE   *fp;
#endif
#ifdef	TCP
    int	    port;
    struct NSAPaddr *tcp_na;
    register struct servent *sp;
#endif
#ifdef	X25
    struct NSAPaddr *x25_na;
#endif

    if (myname = rindex (*vec, '/'))
	myname++;
    if (myname == NULL || *myname == NULL)
	myname = *vec;

    isodetailor (myname, 0);
    ll_hdinit (pgm_log, myname);

    bzero ((char *) tas, sizeof tas);
    tz = tas;

#ifdef	TCP
    if (!(ts_stacks & TS_TCP))
	tcpservice = 0;
    if ((sp = getservbyname ("snmp", "udp")) == NULL)
	advise (LLOG_EXCEPTIONS, NULLCP, "udp/snmp: unknown service");

    tcp_na = tz -> ta_addrs;
    tcp_na -> na_stack = NA_TCP;
    tcp_na -> na_community = ts_comm_tcp_default;
    tcp_na -> na_domain[0] = NULL;
#ifndef	SNMPT
    tcp_na -> na_port = sp ? sp -> s_port : htons ((u_short) 161);
    udport = tcp_na -> na_port;
#endif
    tz -> ta_naddr = 1;

    tz++;

    if ((sp = getservbyname ("snmp-trap", "udp")) == NULL)
	advise (LLOG_EXCEPTIONS, NULLCP, "udp/snmp-trap: unknown service");
#ifndef	SNMPT
    traport = sp ? sp -> s_port : htons ((u_short) 162);
#else
    tcp_na -> na_port = sp ? sp -> s_port : htons ((u_short) 162);
#endif
#endif

#ifdef	COTS
    bzero ((char *) taddrs, sizeof taddrs);
    bzero ((char *) lru, sizeof lru);
#endif

#ifdef	X25
    if (!(ts_stacks & TS_X25))
	x25service = 0;

    x25_na = tz -> ta_addrs;
    x25_na -> na_stack = NA_X25;
    x25_na -> na_community = ts_comm_x25_default;
    if (x25_local_dte && *x25_local_dte) {
	(void) strcpy (x25_na -> na_dte, x25_local_dte);
	x25_na -> na_dtelen = strlen (x25_na -> na_dte);
    }
#ifndef	SNMPT
    x25_na -> na_pidlen = str2sel ("03018200", -1, x25_na -> na_pid, NPSIZE);
#else
    x25_na -> na_pidlen = str2sel ("03019000", -1, x25_na -> na_pid, NPSIZE);
#endif
    tz -> ta_naddr = 1;

    tz++;
#endif

#ifdef	TP4
    if (!(ts_stacks & TS_TP4))
	tp4service = 0;

#ifndef	SNMPT
    bcopy ("snmp", tz -> ta_selector, tz -> ta_selectlen = sizeof "snmp" - 1);
#else
    bcopy ("snmp-trap", tz -> ta_selector,
	   tz -> ta_selectlen = sizeof "snmp-trap" - 1);
#endif
    tz -> ta_naddr = 0;

    tz++;
#endif

    for (vec++; ap = *vec; vec++) {
	if (*ap == '-')
	    switch (*++ap) {
		case 'b':
		    if ((ap = *++vec) == NULL
			    || *ap == '-'
			    || (tooBig = atoi (ap)) <= 128 /* MAXSNMP */)
			adios (NULLCP, "usage: %s -b size", myname);
		    continue;

		case 'd':
		    debug++;
		    continue;

#ifndef	SNMPT
		case 's':
#ifdef	SMUX
		    smux_enabled = 0;
#endif
		    continue;
#endif

		case 't':
		    ts_stacks = TS_TCP;
		    tcpservice = 1;
		    x25service = tp4service = 0;
		    continue;

		case 'x':
		    ts_stacks = TS_X25;
		    x25service = 1;
		    tcpservice = tp4service = 0;
		    continue;

		case 'z':
		    ts_stacks = TS_TP4;
		    tp4service = 1;
		    tcpservice = x25service = 0;
		    continue;

#ifndef	SNMPT
		case 'r':
		    rflag = 1;
		    continue;
#else
		case 'f':
		    if ((file = *++vec) == NULL || *file == '-')
			adios (NULLCP, "usage: %s -f audit-file", myname);
		    continue;
#endif

#ifdef	TCP
		case 'p':
		    if ((ap = *++vec) == NULL
			    || *ap == '-'
			    || (port = atoi (ap)) <= 0)
			adios (NULLCP, "usage: %s -p portno", myname);
		    tcp_na -> na_port = htons ((u_short) port);
		    continue;
#endif

#ifdef X25
		/* This permits listening on a specific subaddress. */
		case 'a':
		    if ((ap = *++vec) == NULL || *ap == '-')
			adios (NULLCP, "usage: %s -a x121address", myname);
		    (void) strcpy (x25_na -> na_dte, ap);
		    x25_na -> na_dtelen = strlen (ap);
		    continue;

		/* This permits listening on a specific protocol id.
		   In fact, SunLink X.25 lets you listen on a protocol
		   id mask, but let's keep it simple. */
		case 'i':
		    if ((ap = *++vec) == NULL || *ap == '-' )
			adios (NULLCP, "usage: %s -i pid", myname);
		    x25_na -> na_pidlen =
			str2sel (ap, -1, x25_na -> na_pid, NPSIZE);
		    continue;
#endif

		default:
		    adios (NULLCP, "-%s: unknown switch", ap);
	    }

	adios (NULLCP, "usage: %s [switches]", myname);
    }

    ps_len_strategy = PS_LEN_LONG;

#ifdef	SNMPT
    file = _isodefile (isodelogpath, file);
    if ((fp = fopen (file, "a")) == NULL)
	adios (file, "unable to append to");
    if ((audit = ps_alloc (std_open)) == NULLPS)
	adios (NULLCP, "ps_alloc(std_open): you lose");
    if (std_setup (audit, fp) == NOTOK)
	adios (NULLCP, "std_setup: %s", ps_error (audit -> ps_errno));
#endif
}

/*  */

static  envinit () {
    int     i,
            sd;
#ifndef	SNMPT
    char   *cp;
#endif
    char    file[BUFSIZ];
    FILE   *fp;

    nbits = getdtablesize ();

    if (debug == 0 && !(debug = isatty (2))) {
	for (i = 0; i < 5; i++) {
	    switch (fork ()) {
		case NOTOK:
		    sleep (5);
		    continue;

		case OK:
		    break;

		default:
		    _exit (0);
	    }
	    break;
	}

	(void) chdir ("/");

	if ((sd = open ("/dev/null", O_RDWR)) == NOTOK)
	    adios ("/dev/null", "unable to read");
	if (sd != 0)
	    (void) dup2 (sd, 0), (void) close (sd);
	(void) dup2 (0, 1);
	(void) dup2 (0, 2);

#ifdef	SETSID
	if (setsid () == NOTOK)
	    advise (LLOG_EXCEPTIONS, "failed", "setsid");
#endif
#ifdef	TIOCNOTTY
	if ((sd = open ("/dev/tty", O_RDWR)) != NOTOK) {
	    (void) ioctl (sd, TIOCNOTTY, NULLCP);
	    (void) close (sd);
	}
#else
#ifdef	SYS5
	(void) setpgrp ();
	(void) signal (SIGINT, SIG_IGN);
	(void) signal (SIGQUIT, SIG_IGN);
#endif
#endif
    }
    else
	ll_dbinit (pgm_log, myname);

#ifndef	sun		/* damn YP... */
    for (sd = 3; sd < nbits; sd++)
	if (pgm_log -> ll_fd != sd)
	    (void) close (sd);
#endif

    (void) signal (SIGPIPE, SIG_IGN);

    ll_hdinit (pgm_log, myname);

#ifndef	SNMPT
#ifndef	SNMPC
    cp = "snmpd.defs";
#else
    cp = "snmpc.defs";
#endif
    if (readobjects (cp) == NOTOK)
	adios (NULLCP, "readobjects: %s", PY_pepy);

    init_mib ();
#ifndef	SNMPC
    init_system ();	    /* Internet-standard MIB */
    init_interfaces ();
    init_ip ();
    init_icmp ();
    init_tcp ();
    init_udp ();
    init_clns ();	    /* experimental CLNS group */
#endif
    init_snmp ();
    init_view ();
#ifdef	SMUX
    init_smux ();
#endif
    init_eval ();

    readconfig ();

    fin_view ();
    fin_mib ();

    start_trap ();
    start_view ();

    o_advise = (IFP) advise;
#endif

    (void) sprintf (file, "/etc/%s.pid", myname);
    if (fp = fopen (file, "w")) {
	(void) fprintf (fp, "%d\n", getpid ());
	(void) fclose (fp);
    }

    advise (LLOG_NOTICE, NULLCP, "starting");
#ifdef	DEBUG
    (void) signal (SIGHUP, hupser);
#endif
}

/*  */

#ifdef	DEBUG
static SFD  hupser (sig)
int	sig;
{
    char    buffer[BUFSIZ];
    PE	    p;

    if (didhup == NOTOK) {
	didhup = OK;
	return;
    }

    advise (LLOG_EXCEPTIONS, NULLCP,
	    "sbrk=0x%x allocs=%d frees=%d most=%d",
	    sbrk (0), pe_allocs, pe_frees, pe_most);

    for (p = pe_active; p; p = p -> pe_link) {
	(void) sprintf (buffer, "active PE 0x%x (refcnt %d)", (caddr_t) p,
			p -> pe_refcnt);

	_vpdu (pgm_log, vunknown, p, buffer, -1);
    }
}
#endif

/*    CONFIG */

#ifndef	SNMPT

int	f_community (), f_logging (), f_proxy (), f_trap (), f_variable (),
	f_view (), f_expression ();

static struct pair {
    char   *p_name;		/* runcom directive */
    IFP	    p_handler;		/* dispatch */
}	pairs[] = {
    "community",    f_community,
    "logging",	    f_logging,
    "proxy",	    f_proxy,
    "trap",	    f_trap,
    "variable",	    f_variable,
    "view",	    f_view,
    "expression",    f_expression,

    NULL
};

/*  */

static	readconfig () {
    register char *cp;
    char    buffer[BUFSIZ],
	    line[BUFSIZ],
	   *vec[NVEC + NSLACK + 1];
    register struct pair *p;
    struct stat st;
    FILE   *fp;

#ifndef	SNMPC
    cp = "snmpd.rc";
#else
    cp = "snmpc.rc";
#endif
    if ((fp = fopen (cp, "r")) == NULL
	    && (fp = fopen (cp = isodefile (cp, 0), "r")) == NULL)
	adios (cp, "unable to read");

    if (!rflag
	    && getuid () == 0
	    && fstat (fileno (fp), &st) != NOTOK
	    && st.st_uid != 0)
	adios (NULLCP, "%s not owned by root", cp);

    while (fgets (buffer, sizeof buffer, fp)) {
	if (*buffer == '#')
	    continue;
	if (cp = index (buffer, '\n'))
	    *cp = NULL;
	(void) strcpy (line, buffer);

	bzero ((char *) vec, sizeof vec);
	if (str2vec (buffer, vec) < 1)
	    continue;
	for (p = pairs; p -> p_name; p++)
	    if (lexequ (p -> p_name, vec[0]) == 0) {
		if ((*p -> p_handler) (vec) == NOTOK)
		    advise (LLOG_EXCEPTIONS, NULLCP,
			    "malformed directive: \"%s\"", line);
		break;
	    }
	if (!p -> p_name)
	    advise (LLOG_EXCEPTIONS, NULLCP, "unknown directive: \"%s\"",
		    line);
    }

    (void) fclose (fp);
}

/*  */

static int  f_logging (vec)
char  **vec;
{
    register char  **vp;

    for (vp = ++vec; *vp; vp++)
	continue;

    log_tai (pgm_log, vec, vp - vec);

    return OK;
}

/*  */

static int  f_variable (vec)
char  **vec;
{
    if (*++vec == NULL)
	return NOTOK;

#ifndef	SNMPC
    if (lexequ (*vec, "interface") == 0) {
	char   *name;

	if ((name = *++vec) == NULL)
	    return NOTOK;
	for (vec++; *vec; vec++)
	    if (index (*vec, '='))
		set_interface (name, *vec);
	    else
		return NOTOK;

	return OK;	
    }
#endif

    if (lexequ (*vec, "snmpEnableAuthenTraps") == 0) {
	++vec;

	if (lexequ (*vec, "enabled") == 0)
	    snmpstat.s_enableauthentraps = TRAPS_ENABLED;
	else
	    if (lexequ (*vec, "disabled") == 0)
		snmpstat.s_enableauthentraps = TRAPS_DISABLED;

	return OK;
    }

    if (!vec[0] || !vec[1] || vec[2])
	return NOTOK;

    set_variable (vec[0], vec[1]);

    return OK;
}
#endif	/* SNMPT */

/*    ERRORS */

#ifndef	lint
void	adios (va_alist)
va_dcl
{
    va_list ap;

    va_start (ap);

    _ll_log (pgm_log, LLOG_FATAL, ap);

    va_end (ap);

    _exit (1);
}
#else
/* VARARGS */

void	adios (what, fmt)
char   *what,
       *fmt;
{
    adios (what, fmt);
}
#endif


#ifndef	lint
void	advise (va_alist)
va_dcl
{
    int	    code;
    va_list ap;

    va_start (ap);

    code = va_arg (ap, int);

    _ll_log (pgm_log, code, ap);

    va_end (ap);
}
#else
/* VARARGS */

void	advise (code, what, fmt)
char   *what,
       *fmt;
int	code;
{
    advise (code, what, fmt);
}
#endif