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

#ifndef	lint
static char *rcsid = "$Header: /var/lib/cvsd/repos/CSRG/43BSDReno/contrib/isode-beta/snmp/snmpd.c,v 1.1.1.1 2018/04/24 16:12:56 root Exp $";
#endif

/* 
 * $Header: /var/lib/cvsd/repos/CSRG/43BSDReno/contrib/isode-beta/snmp/snmpd.c,v 1.1.1.1 2018/04/24 16:12:56 root 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 1.1.1.1  2018/04/24 16:12:56  root
 * BSD 4.3reno
 *
 * 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

#ifdef	SNMPT
#undef	SMUX
#endif


#define	IDLE_TIME	(3 * 60)

/*    DATA */

int	debug = 0;
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
    "snmpd.log",
#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
static char *myname = "snmpd";
#else
static char *myname = "snmpt";
#endif

static	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];


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


extern int  errno;

/*  */

int	nd = NOTOK;

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

#ifdef	CLTS
static	int	clts = NOTOK;
#endif

/*  */

#ifndef	SNMPT
int	quantum = 0;


struct subtree {
    struct subtree *s_forw;	/* doubly-linked list */
    struct subtree *s_back;	/* doubly-linked list */

    OID	    s_subtree;		/* subtree */
};


struct view {
    struct view *v_forw;	/* doubly-linked list */
    struct view *v_back;	/*   .. */

    OID	    v_name;		/* view name */
    u_long  v_mask;		/* view mask */

    struct subtree v_subtree;	/* list of subtrees */

    struct qbuf *v_community;	/* for proxy, traps... */
    struct sockaddr v_sa;
};

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

static int viewmask = 0x1;
static OID localAgent = NULLOID;
static OID rfc1157Domain = NULLOID;


struct community {
    struct community *c_forw;	/* doubly-linked list */
    struct community *c_back;	/*   .. */

    char   *c_name;		/* community name */
    struct NSAPaddr c_addr;	/* network address */

    int	    c_permission;	/* same as ot_access */
#define	OT_YYY	0x08

    OID	    c_vu;		/* associated view */
    struct view *c_view;	/*   .. */

    unsigned int *c_instance;	/* object instance */
    int	    c_insize;		/*   .. */
    struct community *c_next;	/* next in lexi-order */
};

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

struct community *str2comm ();


struct trap {
    struct trap *t_forw;	/* doubly-linked list */
    struct trap *t_back;	/*   .. */

    char   *t_name;		/* trap name */

    struct view  t_vu;		/* associated view */
    struct view *t_view;	/*   .. */

    u_long  t_generics;		/* generic traps enabled */
};

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

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


#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;

/*  */

struct snmpstat {
    integer	s_inpkts;
    integer	s_outpkts;
    integer	s_badversions;
    integer	s_badcommunitynames;
    integer	s_badcommunityuses;
    integer	s_asnparseerrs;
    integer	s_badtypes;
    integer	s_totalreqvars;
    integer	s_totalsetvars;
    integer	s_ingetrequests;
    integer	s_ingetnexts;
    integer	s_insetrequests;
    integer	s_ingetresponses;
    integer	s_intraps;
    integer	s_outgetresponses;
    integer	s_outtraps;
    integer	s_toobigs;
    integer	s_nosuchnames;
    integer	s_badvalues;
    integer	s_readonlys;
    integer	s_generrs;
    integer	s_enableauthtraps;
#define	TRAPS_ENABLED	1			/* snmpEnableAuthTraps */
#define	TRAPS_DISABLED	2			/*   .. */
};


static struct snmpstat snmpstat;


static	int	unix_netstat = 1;

#else	/* SNMPT */

/*  */

static	PS	audit = NULLPS;

#endif	/* SNMPT */

/*  */

#ifdef	SMUX
static	int	smux_enabled = 1;
static	int	smux = NOTOK;

static fd_set	sfds;


struct smuxPeer {
    struct smuxPeer *pb_forw;		/* doubly-linked list */
    struct smuxPeer *pb_back;		/*   .. */

    int	    pb_fd;			/* smuxPindex */
    struct sockaddr_in pb_address;
    char    pb_source[30];

    OID	    pb_identity;		/* smuxPidentity */
    char   *pb_description;		/* smuxPdescription */

    PS	    pb_ps;

    int	    pb_priority;		/* minimum allowed priority */
};

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


struct smuxTree {
    struct smuxTree *tb_forw;		/* doubly-linked list */
    struct smuxTree *tb_back;		/*   .. */

#define	TB_SIZE	30			/* object instance */
    unsigned int    tb_instance[TB_SIZE + 1];
    int	    tb_insize;

    OT	    tb_subtree;			/* smuxTsubtree */
    int	    tb_priority;		/* smuxTpriority */
    struct smuxPeer *tb_peer;		/* smuxTindex */

    struct smuxTree *tb_next;		/* linked list for ot_smux */
};

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

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

    NULL
};
#endif

/*    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);
#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);
#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);
#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)
int	fd;
struct NSAPaddr *na;
IFP	rfx,
	wfx;
{
    int	    result;
    PE	    pe;
    PS	    ps;
    struct type_SNMP_Message *msg;

    result = NOTOK;
    pe = NULLPE;
    msg = NULL;
#ifndef	SNMPT
    pqr = NULL;
#endif
    if ((ps = ps_alloc (dg_open)) == NULLPS
	    || dg_setup (ps, fd, MAXDGRAM, rfx, wfx) == 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;
    }

    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;
    }
#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);

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

/*    PROCESS */

#ifndef	SNMPT
static int  process (ps, msg, na)
PS	ps;
register struct type_SNMP_Message *msg;
struct NSAPaddr *na;
{
    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_enableauthtraps == TRAPS_ENABLED)
	    do_trap (int_SNMP_generic__trap_authenticationFailure, 0,
		     (struct type_SNMP_VarBindList *) 0);
	goto out;
    }

    if ((result = do_operation (ps, msg, comm)) != 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)
PS	ps;
struct type_SNMP_Message *msg;
struct community *comm;
{
    int	    idx,
	    offset,
	    status;
    object_instance ois;
    struct view *vu = comm -> c_view;
    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;

    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... */
		idx = 0;
		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;
	    /* else fall... */
	case type_SNMP_PDUs_trap:
	    snmpstat.s_intraps++;
	    if (vu == NULL)
		goto access_denied;
	    advise (LLOG_EXCEPTIONS, NULLCP,
		    "unexpectedOperation: %d (%s)", pdu -> offset, source);
	    snmpstat.s_badtypes++;
	    return NOTOK;

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

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

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

    quantum++;
    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
	            || ((ot = oi -> oi_type) -> ot_getfnx == NULLIFP
			    && ot -> ot_smux == NULL)) {
no_name: ;
		parm -> error__status = int_SNMP_error__status_noSuchName;
		goto out;
	    }

try_again: ;
	switch (offset) {
	    case type_SNMP_PDUs_get__request:
	        if (!(vu -> v_mask & ot -> ot_views)) {
		    snmpstat.s_badcommunityuses++;
		    parm -> error__status = int_SNMP_error__status_noSuchName;
		    goto out;
		}
		break;

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

	    case type_SNMP_PDUs_set__request:
	        if (!(vu -> v_mask & ot -> ot_views)) {
		    snmpstat.s_badcommunityuses++;
		    parm -> error__status = int_SNMP_error__status_readOnly;
		    goto out;
		}
		break;
	}

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

	switch (status) {
	    case NOTOK:	    /* get-next wants a bump */
get_next: ;
		oi = &ois;
		for (;;) {
		    if ((ot = ot -> ot_next) == NULLOT) {
			parm -> error__status =
					    int_SNMP_error__status_noSuchName;
			goto out;
		    }
		    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;
		goto out;
	}
    }
    idx = 0;

out: ;
    parm -> error__index = idx;
    switch (parm -> error__status) {
	case int_SNMP_error__status_noError:
	    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;
	    }
	    break;

	case int_SNMP_error__status_tooBig:
	    snmpstat.s_toobigs++;
	    break;

	case int_SNMP_error__status_noSuchName:
	    snmpstat.s_nosuchnames++;
	    break;

	case int_SNMP_error__status_badValue:
	    snmpstat.s_badvalues++;
	    break;

	case int_SNMP_error__status_readOnly:
	    snmpstat.s_readonlys++;
	    break;

	case int_SNMP_error__status_genErr:
	    snmpstat.s_generrs++;
	    break;

	default:
	    break;
    }
    snmpstat.s_outgetresponses++;

    return DONE;
}

/*    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, MAXDGRAM, read_udp_socket, write_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;

    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 */
    (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;
    }

    for (qb = PHead -> pb_forw; qb != PHead; qb = qb -> pb_forw)
	if (qb -> pb_fd > fd)
	    break;
    insque (pb, qb != PHead ? qb -> pb_forw : qb -> pb_back);

    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_enableauthtraps == 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\" (%s)",
			pb -> pb_fd, oid2ode (pb -> pb_identity),
			pb -> pb_description, 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);

		    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;
		    }
		}

		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;

			    for (qb = ot -> ot_smux
				 	? (struct smuxTree *) ot -> ot_smux
					: THead -> tb_forw;
				     qb != THead;
				     qb = qb -> tb_forw)
				if ((i = oid_cmp (qb -> tb_subtree -> ot_name,
						  ot -> ot_name)) > 0
				        || (i == 0
					        && qb -> tb_priority
							> tb -> tb_priority))
				    break;
			    insque (tb, qb != THead ? qb -> tb_forw
						    : qb -> tb_back);

			    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;

			    ip = tb -> tb_instance;
			    jp = ot -> ot_name -> oid_elements;
			    for (*ip++ = (i = ot -> ot_name -> oid_nelem);
				     i > 0;
				     i--)
				*ip++ = *jp++;
			    *ip++ = tb -> tb_priority;
			    tb -> tb_insize = ip - tb -> tb_instance;
			}
		    }
		}
		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_getfnx (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;
    register struct type_SNMP_GetResponse__PDU *get;
    PE	    pe;

    status = int_SNMP_error__status_noError;
    orig_id = pdu -> un.get__request -> request__id;
    orig_bindings = pdu -> un.get__request -> variable__bindings;

    bzero ((char *) &req, sizeof req);
    req.offset = offset == type_SNMP_PDUs_get__request
			? type_SNMP_SMUX__PDUs_get__request
			: type_SNMP_SMUX__PDUs_get__next__request;
    req.un.get__request = pdu -> un.get__request;
    
    pdu -> un.get__request -> request__id = quantum;

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

    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);

    pdu -> un.get__request -> request__id = orig_id;
    pdu -> un.get__request -> variable__bindings = orig_bindings;

    if (status != int_SNMP_error__status_noError)
	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

/*    SNMP GROUP */

static	init_snmp ()
{
    register OT	    ot;

    if (ot = text2obj ("snmpInPkts"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_inpkts;
    if (ot = text2obj ("snmpOutPkts"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_outpkts;
    if (ot = text2obj ("snmpInBadVersions"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_badversions;
    if (ot = text2obj ("snmpInBadCommunityNames"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_badcommunitynames;
    if (ot = text2obj ("snmpInBadCommunityUses"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_badcommunityuses;
    if (ot = text2obj ("snmpInASNParseErrs"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_asnparseerrs;
    if (ot = text2obj ("snmpInBadTypes"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_badtypes;
    if (ot = text2obj ("snmpInTotalReqVars"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_totalreqvars;
    if (ot = text2obj ("snmpInTotalSetVars"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_totalsetvars;
    if (ot = text2obj ("snmpInGetRequests"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_ingetrequests;
    if (ot = text2obj ("snmpInGetNexts"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_ingetnexts;
    if (ot = text2obj ("snmpInSetRequests"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_insetrequests;
    if (ot = text2obj ("snmpInGetResponses"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_ingetresponses;
    if (ot = text2obj ("snmpInTraps"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_intraps;
    if (ot = text2obj ("snmpOutTooBigs"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_toobigs;
    if (ot = text2obj ("snmpOutNoSuchNames"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_nosuchnames;
    if (ot = text2obj ("snmpOutBadValues"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_badvalues;
    if (ot = text2obj ("snmpOutReadOnlys"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_readonlys;
    if (ot = text2obj ("snmpOutGenErrs"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_generrs;
    if (ot = text2obj ("snmpOutGetResponses"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_outgetresponses;
    if (ot = text2obj ("snmpOutTraps"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_outtraps;
    if (ot = text2obj ("snmpEnableAuthTraps"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &snmpstat.s_enableauthtraps;

    if (ot = text2obj ("unixNetstat"))
	ot -> ot_getfnx = o_generic,
	ot -> ot_info = (caddr_t) &unix_netstat;
}

/*    SMUX GROUP */

#ifdef	SMUX
#define	smuxPindex	0
#define	smuxPidentity	1
#define	smuxPdescription 2
#define	smuxPstatus	3

#define	PB_VALID	1		/* smuxPstatus */
#define	PB_CONNECTING	2		/*   .. */


static int  o_smuxPeer (oi, v, offset)
OI	oi;
register struct type_SNMP_VarBind *v;
int	offset;
{
    int	    ifnum,
	    ifvar;
    register struct smuxPeer *pb;
    register OID    oid = oi -> oi_name;
    register OT	    ot = oi -> oi_type;

    ifvar = (int) ot -> ot_info;
    switch (offset) {
	case type_SNMP_PDUs_get__request:
	    if (oid -> oid_nelem != ot -> ot_name -> oid_nelem + 1)
		return int_SNMP_error__status_noSuchName;
	    ifnum = oid -> oid_elements[oid -> oid_nelem - 1];
	    for (pb = PHead -> pb_forw; pb != PHead; pb = pb -> pb_forw)
		if (pb -> pb_fd == ifnum)
		    break;
	    if (pb == PHead
		    || ((ifvar == smuxPidentity || ifvar == smuxPdescription)
			    && pb -> pb_identity == NULL))
		return int_SNMP_error__status_noSuchName;
	    break;

	case type_SNMP_PDUs_get__next__request:
again: ;
	    if (oid -> oid_nelem == ot -> ot_name -> oid_nelem) {
		OID	new;

		if ((pb = PHead -> pb_forw) == PHead)
		    return NOTOK;
		ifnum = pb -> pb_fd;

		if ((new = oid_extend (oid, 1)) == NULLOID)
		    return int_SNMP_error__status_genErr;
		new -> oid_elements[new -> oid_nelem - 1] = ifnum;

		if (v -> name)
		    free_SNMP_ObjectName (v -> name);
		v -> name = new;
	    }
	    else {
		int	i = ot -> ot_name -> oid_nelem;

		ifnum = oid -> oid_elements[i];
		for (pb = PHead -> pb_forw; pb != PHead; pb = pb -> pb_forw)
		    if (pb -> pb_fd >= ifnum)
			break;
		if (pb == PHead
		        || ((pb -> pb_fd == ifnum)
			        && (pb = pb -> pb_forw) == PHead))
		    return NOTOK;
		ifnum = pb -> pb_fd;

		oid -> oid_elements[i] = ifnum;
		oid -> oid_nelem = i + 1;
	    }
	    if ((ifvar == smuxPidentity || ifvar == smuxPdescription)
		    && pb -> pb_identity == NULL)
		goto again;
	    break;

	default:
	    return int_SNMP_error__status_genErr;
    }

    switch (ifvar) {
	case smuxPindex:
	    return o_integer (oi, v, pb -> pb_fd);

	case smuxPidentity:
	    return o_specific (oi, v, (caddr_t) pb -> pb_identity);

	case smuxPdescription:
	    return o_string (oi, v, pb -> pb_description,
			     strlen (pb -> pb_description));

	case smuxPstatus:
	    return o_integer (oi, v, pb -> pb_identity ? PB_VALID
						       : PB_CONNECTING);

	default:
	    return int_SNMP_error__status_noSuchName;
    }
}

/*  */

static struct smuxTree *get_tbent (ip, len, isnext)
register unsigned int *ip;
int	len;
int	isnext;
{
    register struct smuxTree *tb;

    for (tb = THead -> tb_forw; tb != THead; tb = tb -> tb_forw)
	switch (elem_cmp (tb -> tb_instance, tb -> tb_insize, ip, len)) {
	    case 0:
	        if (!isnext)
		    return tb;
		if ((tb = tb -> tb_forw) == THead)
		    return NULL;
		/* else fall... */

	    case 1:
		return (isnext ? tb : NULL);
	}

    return NULL;
}

/*  */

#define	smuxTsubtree	0
#define	smuxTpriority	1
#define	smuxTindex	2
#define	smuxTstatus	3

#define	TB_VALID	1		/* smuxTstatus */


static int  o_smuxTree (oi, v, offset)
OI	oi;
register struct type_SNMP_VarBind *v;
int	offset;
{
    int	    ifvar;
    register int    i;
    register unsigned int *ip,
			  *jp;
    register struct smuxTree *tb;
    register OID    oid = oi -> oi_name;
    register OT	    ot = oi -> oi_type;

    ifvar = (int) ot -> ot_info;
    switch (offset) {
	case type_SNMP_PDUs_get__request:
	    if (oid -> oid_nelem <= ot -> ot_name -> oid_nelem)
		return int_SNMP_error__status_noSuchName;
	    if ((tb = get_tbent (oid -> oid_elements
				     + ot -> ot_name -> oid_nelem,
				 oid -> oid_nelem
				     - ot -> ot_name -> oid_nelem, 0)) == NULL)
		return int_SNMP_error__status_noSuchName;
	    break;

	case type_SNMP_PDUs_get__next__request:
	    if (oid -> oid_nelem == ot -> ot_name -> oid_nelem) {
		OID	new;

		if ((tb = THead -> tb_forw) == THead)
		    return NOTOK;

		if ((new = oid_extend (oid, tb -> tb_insize)) == NULLOID)
		    return int_SNMP_error__status_genErr;
		ip = new -> oid_elements + new -> oid_nelem - tb -> tb_insize;
		jp = tb -> tb_instance;
		for (i = tb -> tb_insize; i > 0; i--)
		    *ip++ = *jp++;

		if (v -> name)
		    free_SNMP_ObjectName (v -> name);
		v -> name = new;
	    }
	    else {
		int	j;

		if ((tb = get_tbent (oid -> oid_elements
				         + ot -> ot_name -> oid_nelem,
				     j = oid -> oid_nelem
				     	     - ot -> ot_name -> oid_nelem, 1))
		         == NULL)
		    return NOTOK;

		if ((i = j - tb -> tb_insize) < 0) {
		    OID	    new;

		    if ((new = oid_extend (oid, -i)) == NULLOID)
			return int_SNMP_error__status_genErr;
		    if (v -> name)
			free_SNMP_ObjectName (v -> name);
		    v -> name = new;

		    oid = new;
		}
		else
		    if (i > 0)
			oid -> oid_nelem -= i;

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

	default:
	    return int_SNMP_error__status_genErr;
    }

    switch (ifvar) {
	case smuxTsubtree:
	    return o_specific (oi, v, (caddr_t) tb -> tb_subtree -> ot_name);

	case smuxTpriority:
	    return o_integer (oi, v, tb -> tb_priority);

	case smuxTindex:
	    return o_integer (oi, v, tb -> tb_peer -> pb_fd);

	case smuxTstatus:
	    return o_integer (oi, v, TB_VALID);

	default:
	    return int_SNMP_error__status_noSuchName;
    }
}

/*  */

static	init_smux ()
{
    register OT	    ot;

    if (ot = text2obj ("smuxPindex"))
	ot -> ot_getfnx = o_smuxPeer,
	ot -> ot_info = (caddr_t) smuxPindex;
    if (ot = text2obj ("smuxPidentity"))
	ot -> ot_getfnx = o_smuxPeer,
	ot -> ot_info = (caddr_t) smuxPidentity;
    if (ot = text2obj ("smuxPdescription"))
	ot -> ot_getfnx = o_smuxPeer,
	ot -> ot_info = (caddr_t) smuxPdescription;
    if (ot = text2obj ("smuxPstatus"))
	ot -> ot_getfnx = o_smuxPeer,
	ot -> ot_info = (caddr_t) smuxPstatus;

    if (ot = text2obj ("smuxTsubtree"))
	ot -> ot_getfnx = o_smuxTree,
	ot -> ot_info = (caddr_t) smuxTsubtree;
    if (ot = text2obj ("smuxTpriority"))
	ot -> ot_getfnx = o_smuxTree,
	ot -> ot_info = (caddr_t) smuxTpriority;
    if (ot = text2obj ("smuxTindex"))
	ot -> ot_getfnx = o_smuxTree,
	ot -> ot_info = (caddr_t) smuxTindex;
    if (ot = text2obj ("smuxTstatus"))
	ot -> ot_getfnx = o_smuxTree,
	ot -> ot_info = (caddr_t) smuxTstatus;
}
#endif

/*    VIEW MIB */

#define	viewPrimName	  0
#define	viewPrimTDomain   1
#define	viewPrimTAddr	  2
#define	viewPrimUser	  3
#define	viewPrimCommunity 4
#define	viewPrimType	  5

#define	P_VALID		  1		/* viewPrimType */


struct view *get_prent ();


static int  o_viewPrim (oi, v, offset)
OI	oi;
register struct type_SNMP_VarBind *v;
int	offset;
{
    int	    ifvar;
    register int    i;
    register unsigned int *ip,
			  *jp;
    register struct view *vu;
    register OID    oid = oi -> oi_name;
    register OT	    ot = oi -> oi_type;

    ifvar = (int) ot -> ot_info;
    switch (offset) {
	case type_SNMP_PDUs_get__request:
	    if (oid -> oid_nelem <= ot -> ot_name -> oid_nelem)
		return int_SNMP_error__status_noSuchName;
	    if ((vu = get_prent (oid -> oid_elements
				     + ot -> ot_name -> oid_nelem,
				 oid -> oid_nelem
				     - ot -> ot_name -> oid_nelem, 0)) == NULL)
		return int_SNMP_error__status_noSuchName;
	    break;

	case type_SNMP_PDUs_get__next__request:
	    if (oid -> oid_nelem == ot -> ot_name -> oid_nelem) {
		OID	new;

		if ((vu = VHead -> v_forw) == VHead)
		    return NOTOK;

		i = vu -> v_name -> oid_nelem + 1;
		if ((new = oid_extend (oid, i)) == NULLOID)
		    return int_SNMP_error__status_genErr;
		ip = new -> oid_elements + new -> oid_nelem - i;
		jp = vu -> v_name -> oid_elements;
		*ip++ = i;
		for (i--; i > 0; i--)
		    *ip++ = *jp++;

		if (v -> name)
		    free_SNMP_ObjectName (v -> name);
		v -> name = new;
	    }
	    else {
		int	j,
			k;

		if ((vu = get_prent (oid -> oid_elements
				         + ot -> ot_name -> oid_nelem,
				     j = oid -> oid_nelem
				     	     - ot -> ot_name -> oid_nelem, 1))
		         == NULL)
		    return NOTOK;

		k = vu -> v_name -> oid_nelem + 1;
		if ((i = j - k) < 0) {
		    OID	    new;

		    if ((new = oid_extend (oid, -i)) == NULLOID)
			return int_SNMP_error__status_genErr;
		    if (v -> name)
			free_SNMP_ObjectName (v -> name);
		    v -> name = new;

		    oid = new;
		}
		else
		    if (i > 0)
			oid -> oid_nelem -= i;

		ip = oid -> oid_elements + ot -> ot_name -> oid_nelem;
		jp = vu -> v_name -> oid_elements;
		*ip++ = k;
		for (k--; k > 0; k--)
		    *ip++ = *jp++;
	    }
	    break;

	default:
	    return int_SNMP_error__status_genErr;
    }

    switch (ifvar) {
	case viewPrimName:
	    return o_specific (oi, v, (caddr_t) vu -> v_name);

	case viewPrimTDomain:
		return o_specific (oi, v,
				   (caddr_t) (vu -> v_community ? rfc1157Domain
					      		        : localAgent));

	case viewPrimTAddr:
#ifdef	TCP
	    if (vu -> v_community) {
		struct sockaddr_in *sin = (struct sockaddr_in *) &vu -> v_sa;

		return o_string (oi, v, (char *) &sin -> sin_addr, 4);
	    }
	    else
#endif
		return o_string (oi, v, NULLCP, 0);

	case viewPrimUser:
#ifdef	TCP
	    if (vu -> v_community)
		return o_qbstring (oi, v,
				   trap -> data -> un.trap -> agent__addr);
	    else
#endif
		return o_string (oi, v, NULLCP, 0);

	case viewPrimCommunity:
	    if (vu -> v_community)
		return o_qbstring (oi, v, vu -> v_community);
	    else
		return o_string (oi, v, NULLCP, 0);

	case viewPrimType:
	    return o_integer (oi, v, P_VALID);

	default:
	    return int_SNMP_error__status_noSuchName;
    }
}

/*  */

static struct view *get_prent (ip, len, isnext)
register unsigned int *ip;
int	len;
int	isnext;
{
    register struct view *v;

    ip++, len--;
    for (v = VHead -> v_forw; v != VHead; v = v -> v_forw)
	switch (elem_cmp (v -> v_name -> oid_elements,v -> v_name -> oid_nelem,
			  ip, len)) {
	    case 0:
	        if (!isnext)
		    return v;
		if ((v = v -> v_forw) == VHead)
		    return NULL;
		/* else fall... */

	    case 1:
		return (isnext ? v : NULL);
	}

    return NULL;
}

/*  */

#define	viewAclView	  0
#define	viewAclCommunity  1
#define	viewAclUser	  2
#define	viewAclPrivileges 3
#define	viewAclType	  4

#define	A_VALID		  1		/* viewAclType */


struct community *get_acent ();


static int  o_viewAcl (oi, v, offset)
OI	oi;
register struct type_SNMP_VarBind *v;
int	offset;
{
    int	    ifvar;
    register int    i;
    register unsigned int *ip,
			  *jp;
    register struct community *c;
    register OID    oid = oi -> oi_name;
    register OT	    ot = oi -> oi_type;

    ifvar = (int) ot -> ot_info;
    switch (offset) {
	case type_SNMP_PDUs_get__request:
	    if (oid -> oid_nelem <= ot -> ot_name -> oid_nelem)
		return int_SNMP_error__status_noSuchName;
	    if ((c = get_acent (oid -> oid_elements
				    + ot -> ot_name -> oid_nelem,
				oid -> oid_nelem
				    - ot -> ot_name -> oid_nelem, 0)) == NULL)
		return int_SNMP_error__status_noSuchName;
	    break;

	case type_SNMP_PDUs_get__next__request:
	    if (oid -> oid_nelem == ot -> ot_name -> oid_nelem) {
		OID	new;

		if ((c = CLex) == NULL)
		    return NOTOK;

		if ((new = oid_extend (oid, c -> c_insize)) == NULLOID)
		    return int_SNMP_error__status_genErr;
		ip = new -> oid_elements + new -> oid_nelem - c -> c_insize;
		jp = c -> c_instance;
		for (i = c -> c_insize; i > 0; i--)
		    *ip++ = *jp++;

		if (v -> name)
		    free_SNMP_ObjectName (v -> name);
		v -> name = new;
	    }
	    else {
		int	j;

		if ((c = get_acent (oid -> oid_elements
				        + ot -> ot_name -> oid_nelem,
				    j = oid -> oid_nelem
				     	    - ot -> ot_name -> oid_nelem, 1))
		         == NULL)
		    return NOTOK;

		if ((i = j - c -> c_insize) < 0) {
		    OID	    new;

		    if ((new = oid_extend (oid, -i)) == NULLOID)
			return int_SNMP_error__status_genErr;
		    if (v -> name)
			free_SNMP_ObjectName (v -> name);
		    v -> name = new;

		    oid = new;
		}
		else
		    if (i > 0)
			oid -> oid_nelem -= i;

		ip = oid -> oid_elements + ot -> ot_name -> oid_nelem;
		jp = c -> c_instance;
		for (i = c -> c_insize; i > 0; i--)
		    *ip++ = *jp++;
	    }
	    break;

	default:
	    return int_SNMP_error__status_genErr;
    }

    switch (ifvar) {
	case viewAclView:
	    return o_specific (oi, v, (caddr_t) c -> c_view -> v_name);

	case viewAclCommunity:
	    return o_string (oi, v, c -> c_name, strlen (c -> c_name));

	case viewAclUser:
	    switch (c -> c_addr.na_type) {
		case NA_TCP:
		    { /* hard to believe this is the easiest way of doing it */
			register char *bp,
				      *ep;
			char buf[4];

			ip = c -> c_instance + 1 + strlen (c -> c_name) + 1;
			for (ep = (bp = buf + sizeof buf); bp < ep; )
			    *bp++ = *ip++ & 0xff;

			return o_string (oi, v, buf, sizeof buf);
		    }

		case NA_X25:
		    return o_string (oi, v, c -> c_addr.na_dte,
				     (int) c -> c_addr.na_dtelen);

		case NA_NSAP:
		    return o_string (oi, v, c -> c_addr.na_address,
				     (int) c -> c_addr.na_addrlen);

		default:
		    return o_string (oi, v, NULLCP, 0);
	    }

	case viewAclPrivileges:
	    return o_integer (oi, v,
			        ((c -> c_permission & OT_RDONLY) ? 3 : 0)
			      + ((c -> c_permission & OT_WRONLY) ? 8 : 0)
			      + ((c -> c_permission & OT_YYY) ? 4 : 0));

	case viewAclType:
	    return o_integer (oi, v, A_VALID);

	default:
	    return int_SNMP_error__status_noSuchName;
    }
}

/*  */

static struct community *get_acent (ip, len, isnext)
register unsigned int *ip;
int	len;
int	isnext;
{
    register struct community *c;

    for (c = CLex; c; c = c -> c_next)
	switch (elem_cmp (c -> c_instance, c -> c_insize, ip, len)) {
	    case 0:
	        return (isnext ? c -> c_next : c);

	    case 1:
		return (isnext ? c : NULL);
	}

    return NULL;
}

/*  */

#define	viewTrapView	  0
#define	viewTrapGenerics  1
#define	viewTrapSpecifics 2
#define	viewTrapType      3

#define	T_VALID		  1		/* viewTrapType */


struct trap *get_trent ();


static int  o_viewTrap (oi, v, offset)
OI	oi;
register struct type_SNMP_VarBind *v;
int	offset;
{
    int	    ifvar;
    register int    i;
    register unsigned int *ip,
			  *jp;
    register struct trap *t;
    register OID    oid = oi -> oi_name;
    register OT	    ot = oi -> oi_type;

    ifvar = (int) ot -> ot_info;
    switch (offset) {
	case type_SNMP_PDUs_get__request:
	    if (oid -> oid_nelem <= ot -> ot_name -> oid_nelem)
		return int_SNMP_error__status_noSuchName;
	    if ((t = get_trent (oid -> oid_elements
				    + ot -> ot_name -> oid_nelem,
				oid -> oid_nelem
				    - ot -> ot_name -> oid_nelem, 0)) == NULL)
		return int_SNMP_error__status_noSuchName;
	    break;

	case type_SNMP_PDUs_get__next__request:
	    if (oid -> oid_nelem == ot -> ot_name -> oid_nelem) {
		OID	new;

		if ((t = UHead -> t_forw) == UHead)
		    return NOTOK;

		i = t -> t_view -> v_name -> oid_nelem + 1;
		if ((new = oid_extend (oid, i)) == NULLOID)
		    return int_SNMP_error__status_genErr;
		ip = new -> oid_elements + new -> oid_nelem - i;
		jp = t -> t_view -> v_name -> oid_elements;
		*ip++ = i;
		for (i--; i > 0; i--)
		    *ip++ = *jp++;

		if (v -> name)
		    free_SNMP_ObjectName (v -> name);
		v -> name = new;
	    }
	    else {
		int	j,
			k;

		if ((t = get_trent (oid -> oid_elements
				        + ot -> ot_name -> oid_nelem,
				    j = oid -> oid_nelem
				     	    - ot -> ot_name -> oid_nelem, 1))
		         == NULL)
		    return NOTOK;

		k = t -> t_view -> v_name -> oid_nelem + 1;
		if ((i = j - k) < 0) {
		    OID	    new;

		    if ((new = oid_extend (oid, -i)) == NULLOID)
			return int_SNMP_error__status_genErr;
		    if (v -> name)
			free_SNMP_ObjectName (v -> name);
		    v -> name = new;

		    oid = new;
		}
		else
		    if (i > 0)
			oid -> oid_nelem -= i;

		ip = oid -> oid_elements + ot -> ot_name -> oid_nelem;
		jp = t -> t_view -> v_name -> oid_elements;
		*ip++ = k;
		for (k--; k > 0; k--)
		    *ip++ = *jp++;
	    }
	    break;

	default:
	    return int_SNMP_error__status_genErr;
    }

    switch (ifvar) {
	case viewTrapView:
	    return o_specific (oi, v, (caddr_t) t -> t_view -> v_name);

	case viewTrapGenerics:
	    {
		char   c = t -> t_generics & 0xff;
		
		return o_string (oi, v, &c, sizeof c);
	    }

	case viewTrapSpecifics:
	    return o_string (oi, v, NULLCP, 0);

	case viewTrapType:
	    return o_integer (oi, v, T_VALID);

	default:
	    return int_SNMP_error__status_noSuchName;
    }
}

/*  */

static struct trap *get_trent (ip, len, isnext)
register unsigned int *ip;
int	len;
int	isnext;
{
    register struct trap *t;

    ip++, len--;
    for (t = UHead -> t_forw; t != UHead; t = t -> t_forw)
	switch (elem_cmp (t -> t_view -> v_name -> oid_elements,
			  t -> t_view -> v_name -> oid_nelem,
			  ip, len)) {
	    case 0:
	        if (!isnext)
		    return t;
		if ((t = t -> t_forw) == UHead)
		    return NULL;
		/* else fall... */

	    case 1:
		return (isnext ? t : NULL);
	}

    return NULL;
}

/*  */

static int  view_compar (a, b)
struct view **a,
            **b;
{
    return oid_cmp ((*a) -> v_name, (*b) -> v_name);

}

static int  comm_compar (a, b)
struct community **a,
                 **b;
{
    return elem_cmp ((*a) -> c_instance, (*a) -> c_insize,
		     (*b) -> c_instance, (*b) -> c_insize);
}

static int  trap_compar (a, b)
struct trap **a,
            **b;
{
    return oid_cmp ((*a) -> t_view -> v_name, (*b) -> t_view -> v_name);
}


static	init_view ()
{
    register int    i;
    register OT	    ot;
    register struct community *c;
    register struct view *v;
    register struct trap *t;

    if (ot = text2obj ("viewPrimName"))
	ot -> ot_getfnx = o_viewPrim,
	ot -> ot_info = (caddr_t) viewPrimName;
    if (ot = text2obj ("viewPrimTDomain"))
	ot -> ot_getfnx = o_viewPrim,
	ot -> ot_info = (caddr_t) viewPrimTDomain;
    if (ot = text2obj ("viewPrimTAddr"))
	ot -> ot_getfnx = o_viewPrim,
	ot -> ot_info = (caddr_t) viewPrimTAddr;
    if (ot = text2obj ("viewPrimUser"))
	ot -> ot_getfnx = o_viewPrim,
	ot -> ot_info = (caddr_t) viewPrimUser;
    if (ot = text2obj ("viewPrimCommunity"))
	ot -> ot_getfnx = o_viewPrim,
	ot -> ot_info = (caddr_t) viewPrimCommunity;
    if (ot = text2obj ("viewPrimType"))
	ot -> ot_getfnx = o_viewPrim,
	ot -> ot_info = (caddr_t) viewPrimType;

    if (ot = text2obj ("viewAclView"))
	ot -> ot_getfnx = o_viewAcl,
	ot -> ot_info = (caddr_t) viewAclView;
    if (ot = text2obj ("viewAclCommunity"))
	ot -> ot_getfnx = o_viewAcl,
	ot -> ot_info = (caddr_t) viewAclCommunity;
    if (ot = text2obj ("viewAclUser"))
	ot -> ot_getfnx = o_viewAcl,
	ot -> ot_info = (caddr_t) viewAclUser;
    if (ot = text2obj ("viewAclPrivileges"))
	ot -> ot_getfnx = o_viewAcl,
	ot -> ot_info = (caddr_t) viewAclPrivileges;
    if (ot = text2obj ("viewAclType"))
	ot -> ot_getfnx = o_viewAcl,
	ot -> ot_info = (caddr_t) viewAclType;

    if (ot = text2obj ("viewTrapView"))
	ot -> ot_getfnx = o_viewTrap,
	ot -> ot_info = (caddr_t) viewTrapView;
    if (ot = text2obj ("viewTrapGenerics"))
	ot -> ot_getfnx = o_viewTrap,
	ot -> ot_info = (caddr_t) viewTrapGenerics;
    if (ot = text2obj ("viewTrapSpecifics"))
	ot -> ot_getfnx = o_viewTrap,
	ot -> ot_info = (caddr_t) viewTrapSpecifics;
    if (ot = text2obj ("viewTrapType"))
	ot -> ot_getfnx = o_viewTrap,
	ot -> ot_info = (caddr_t) viewTrapType;

    i = 0;
    for (v = VHead -> v_forw; v != VHead; v = v -> v_forw)
	i++;
    if (i > 1) {
	register struct view **base,
			     **bp,
			     **ep;

	if ((base = (struct view **) malloc ((unsigned) (i * sizeof *base)))
	        == NULL)
	    adios (NULLCP, "out of memory");
	ep = base;
	for (v = VHead -> v_forw; v != VHead; v = v -> v_forw)
	    remque (*ep++ = v);
	VHead -> v_forw = VHead -> v_back = VHead;

	qsort ((char *) base, i, sizeof *base, view_compar);

	bp = base;
	while (bp < ep)
	    insque (*bp++, VHead -> v_back);

	free ((char *) base);
    }

    i = 0;
    for (c = CHead -> c_forw; c != CHead; c = c -> c_forw)
	i++;
    if (i > 0) {
	int	j;
	register struct community **base,
			          **bp,
			          **ep;

	if ((base = (struct community **)
			    malloc ((unsigned) (i * sizeof *base))) == NULL)
	    adios (NULLCP, "out of memory");
	ep = base;
	for (c = CHead -> c_forw; c != CHead; c = c -> c_forw) {
	    register char *cp,
			  *dp;
	    register unsigned int *ip;

	    switch (c -> c_addr.na_stack) {
		case NA_TCP:
		    j = 4;
		    break;

		case NA_X25:
		    j = c -> c_addr.na_dtelen;
		    break;

		case NA_NSAP:
		    j = c -> c_addr.na_addrlen;
		    break;

		default:
		    j = 0;
		    break;
	    }

	    c -> c_insize = 1 + strlen (c -> c_name) + 1 + j;
	    if ((c -> c_instance =
		    (unsigned int *) calloc ((unsigned) c -> c_insize,
					     sizeof *c -> c_instance)) == NULL)
		adios (NULLCP, "out of memory");
	    ip = c -> c_instance;

	    *ip++ = strlen (c -> c_name);
	    for (cp = c -> c_name; *cp; cp++)
		*ip++ = *cp & 0xff;

	    *ip++ = j;
	    switch (c -> c_addr.na_stack) {
		case NA_TCP:
		    (void) sscanf (c -> c_addr.na_domain, "%u.%u.%u.%u",
				   ip, ip + 1, ip + 2, ip + 3);
		    break;

		case NA_X25:
		    dp = (cp = c -> c_addr.na_dte) + c -> c_addr.na_dtelen;
		    goto stuff_it;

		case NA_NSAP:
		    dp = (cp = c -> c_addr.na_address) + c ->c_addr.na_addrlen;
stuff_it: ;
		    while (cp < dp)
			*ip++ = *cp++ & 0xff;
		    break;

		default:
		    break;
	    }

	    *ep++ = c;
	}

	if (i > 1)
	    qsort ((char *) base, i, sizeof *base, comm_compar);

	bp = base;
	c = CLex = *bp++;
	while (bp < ep) {
	    c -> c_next = *bp;
	    c = *bp++;
	}
	c -> c_next = NULL;

	free ((char *) base);
    }
    else
	CLex = NULL;

    i = 0;
    for (t = UHead -> t_forw; t != UHead; t = t -> t_forw)
	i++;
    if (i > 1) {
	register struct trap **base,
			     **bp,
			     **ep;

	if ((base = (struct trap **) malloc ((unsigned) (i * sizeof *base)))
	        == NULL)
	    adios (NULLCP, "out of memory");
	ep = base;
	for (t = UHead -> t_forw; t != UHead; t = t -> t_forw)
	    remque (*ep++ = t);
	UHead -> t_forw = UHead -> t_back = UHead;

	qsort ((char *) base, i, sizeof *base, trap_compar);

	bp = base;
	while (bp < ep)
	    insque (*bp++, UHead -> t_back);

	free ((char *) base);
    }

if (debug) {
    fprintf (stderr, "///////\nprimitive view table\n");
    for (v = VHead -> v_forw; v != VHead; v = v -> v_forw) {
	fprintf (stderr, "name=%s ", sprintoid (v -> v_name));
	if (v -> v_community) {
	    register char *cp,
			  *ep;
	    char   *p;
	    register struct qbuf *qb;
#ifdef	TCP
	    struct qbuf *x = trap -> data -> un.trap -> agent__addr;
	    struct sockaddr_in *sin = (struct sockaddr_in *) &v -> v_sa;
#endif

	    fprintf (stderr, "tDomain=%s tAddr=", sprintoid (rfc1157Domain));
#ifdef	TCP
	    p = "0x";
	    for (ep = (cp = (char *) &sin -> sin_addr) + 4; cp < ep; cp++) {
		fprintf (stderr, "%s%02x", p, *cp & 0xff);
		p = ":";
	    }
	    for (ep = (cp = (char *) &sin -> sin_port) + 2; cp < ep; cp++)
		fprintf (stderr, ":%02x", *cp & 0xff);
#else
	    fprintf (stderr, "\"\"");
#endif
	    fprintf (stderr, " user=");
#ifdef	TCP
	    p = "0x";
	    for (qb = x -> qb_forw; qb != x; qb = qb -> qb_forw)
		for (ep = (cp = qb -> qb_data) + qb -> qb_len; cp < ep; cp++) {
		    fprintf (stderr, "%s%02x", p, *cp & 0xff);
		    p = ":";
		}
#else
	    fprintf (stderr, "\"\"");
#endif
	    fprintf (stderr," community=\"");
	    for (qb = v -> v_community -> qb_forw;
		     qb != v -> v_community;
		     qb = qb -> qb_forw)
		fprintf (stderr, "%*.*s", qb -> qb_len, qb -> qb_len,
			 qb -> qb_data);
	    fprintf (stderr, "\"");
	}
	else
	    fprintf (stderr, "tDomain=%s ...", sprintoid (localAgent));
	fprintf (stderr, "\n");
    }
    fprintf(stderr,"\nview access table\n");
    for (c = CLex; c; c = c -> c_next) {
	OIDentifier oids;
	oids.oid_elements = c -> c_instance, oids.oid_nelem = c -> c_insize;
	fprintf (stderr,"acl=%s ", sprintoid (&oids));
	fprintf (stderr, "view=%s community=%s user=%s privileges=%d\n",
		 sprintoid (c -> c_view -> v_name), c -> c_name,
		 na2str (&c -> c_addr),
		 ((c -> c_permission & OT_RDONLY) ? 3 : 0)
		     + ((c -> c_permission & OT_WRONLY) ? 8 : 0)
		     + ((c -> c_permission & OT_YYY) ? 4 : 0));
    }
    fprintf(stderr,"\ntrap table\n");
    for (t = UHead -> t_forw; t != UHead; t = t -> t_forw) {
	v = t -> t_view;
	fprintf (stderr, "view=%s generics=0x%x specifics=null\n",
		 sprintoid (v -> v_name), t -> t_generics);
    }
    fprintf(stderr,"///////\n");
    compat_log -> ll_events |= LLOG_TRACE;
    compat_log -> ll_stat |= LLOGTTY;
}
}

/*    VIEWS */

static	initview () {
    register OT	    ot;

    for (ot = text2obj ("ccitt"); ot; ot = ot -> ot_next)
	if (ot -> ot_getfnx != NULL && ot -> ot_access != OT_NONE)
	    export_view (ot);
}

/*  */

#define	inSubtree(tree,object) \
    	((tree) -> oid_nelem <= (object) -> oid_nelem \
	     && bcmp ((char *) (tree) -> oid_elements, \
		      (char *) (object) -> oid_elements, \
		      (tree) -> oid_nelem \
		          * sizeof ((tree) -> oid_elements[0])) == 0)


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 {
mark_it: ;
		ot -> ot_views |= v -> v_mask;
	}
}

/*    COMMUNITIES */

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	initrap () {
#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 boottime,
		       now;

	if (getkmem (nl + N_BOOTTIME, (caddr_t) &boottime, sizeof boottime)
	        == NOTOK) {
	    advise (LLOG_EXCEPTIONS, NULLCP,
		    "unable to send trap: read of boottime failed");
	    return;
	}
	if (gettimeofday (&now, (struct timezone *) 0) == NOTOK) {
	    advise (LLOG_EXCEPTIONS, "failed", "gettimeofday");
	    return;
	}

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

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

/*  */

#ifdef	TCP
static	do_traps (msg, generic, specific)
register struct type_SNMP_Message *msg;
int	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, MAXDGRAM, read_udp_socket,
			     write_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++;

		    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)
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_c_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 '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;
    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
    if (readobjects ("snmpd.defs") == NOTOK)
	adios (NULLCP, "readobjects: %s", PY_pepy);

    readmib ();
    readconfig ();

    init_snmp ();
#ifdef	SMUX
    init_smux ();
#endif
    init_view ();

    initrap ();
    initview ();
    checkmib ();

    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");
}

/*    CONFIG */

#ifndef	SNMPT

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

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,

    NULL
};


static struct wired {
    char  *w_args1;
    char  *w_args2;
}	wired[] = {
    "defViewWholeRW", NULL,
    "defViewWholeRO", NULL,
    "defViewStandardRW", "mgmt",
    "defViewStandardRO", "mgmt",

    NULL
};

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

    CHead -> c_forw = CHead -> c_back = CHead;
    UHead -> t_forw = UHead -> t_back = UHead;
    VHead -> v_forw = VHead -> v_back = VHead;

    if ((localAgent = text2oid ("localAgent")) == NULLOID)
	adios (NULLCP, "unknown OID \"localAgent\"");
    if ((rfc1157Domain = text2oid ("rfc1157Domain")) == NULLOID)
	adios (NULLCP, "unknown OID \"rfc1157Domain\"");

    vec[0] = "view";
    for (w = wired; w -> w_args1; w++) {
	vec[1] = w -> w_args1;
	if (vec[2] = w -> w_args2)
	    vec[3] = NULL;

	if (f_view (vec) == NOTOK)
	    adios (NULLCP, "you lose");
    }

    (void) strcpy (buffer, "defViewTrapDest.0");
    if ((trapview = text2oid (buffer)) == NULLOID)
	adios (NULLCP, "unknown OID \"defViewTrapDest.0\" for traps");
    trapview -> oid_nelem--;

    bzero ((char *) &snmpstat, sizeof snmpstat);
    snmpstat.s_enableauthtraps = TRAPS_ENABLED;

    if ((fp = fopen (cp = "snmpd.rc", "r")) == NULL
	    && (fp = fopen (cp = isodefile ("snmpd.rc", 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);

    if (CHead -> c_forw == CHead) {
	vec[0] = "community";
	vec[1] = "public";
	vec[2] = NULL;

	(void) f_community (vec);
    }

    for (c = CHead -> c_forw; c != CHead; c = c -> c_forw) {
	for (v = VHead -> v_forw; v != VHead; v = v -> v_forw)
	    if (oid_cmp (v -> v_name, c -> c_vu) == 0) {
		c -> c_view = v;
		break;
	    }
	if (v == VHead)
	    advise (LLOG_EXCEPTIONS, NULLCP,
		    "no such view as %s for community \"%s\"",
		    sprintoid (c -> c_vu), c -> c_name);
    }
}

/*  */

static int  f_community (vec)
char  **vec;
{
    register struct community *c;
    register struct NSAPaddr *na;

    vec++;

    if ((c = (struct community *) calloc (1, sizeof *c)) == NULL
	    || (c -> c_name = strdup (*vec)) == NULL)
	adios (NULLCP, "out of memory");
    vec++;

    na = &c -> c_addr;
    if (*vec) {
	if (str2sa (*vec, na, (struct sockaddr *) NULL, 0) == NOTOK)
	    adios (NULLCP, "unknown address \"%s\" for community \"%s\"",
		   *vec, c -> c_name);

	vec++;
    }
    else {
	na -> na_stack = NA_TCP;
	na -> na_community = ts_comm_tcp_default;
	(void) strcpy (na -> na_domain, "0.0.0.0");
    }

    if (*vec) {
	if (lexequ (*vec, "readOnly") == 0)
	    c -> c_permission = OT_RDONLY;
	else
	    if (lexequ (*vec, "readWrite") == 0)
		c -> c_permission = OT_RDWRITE;
	    else
		if (lexequ (*vec, "writeOnly") == 0)
		    c -> c_permission = OT_WRONLY;
		else
		    if (lexequ (*vec, "none")) {
			advise (LLOG_EXCEPTIONS, NULLCP,
				"invalid access mode \"%s\"", *vec);
			goto you_lose;
		    }

	vec++;
    }
    else
	c -> c_permission = OT_RDONLY;

    if (*vec) {
	char    buffer[BUFSIZ];

	(void) strcpy (buffer, *vec);
	if ((c -> c_vu = text2oid (buffer)) == NULLOID) {
	    advise (LLOG_EXCEPTIONS, NULLCP, "unknown OID \"%s\"", *vec);
	    goto you_lose;
	}

	if (*++vec)
	    goto you_lose;
    }
    else
	if ((c -> c_vu = text2oid ("defViewWholeRO")) == NULL) {
	    advise (LLOG_EXCEPTIONS, NULLCP, "unknown OID \"defViewWholeRO\"");
		goto you_lose;
	}

    insque (c, CHead -> c_back);

    return OK;

you_lose: ;
    free (c -> c_name);
    free ((char *) c);

    return NOTOK;
}

/*  */

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_proxy (vec)
char  **vec;
{
    char    buffer[BUFSIZ];
    register struct community *c;
    register struct view *v,
			 *u;
    register struct NSAPaddr *na;

    if ((v = (struct view *) calloc (1, sizeof *v)) == NULL)
	adios (NULLCP, "out of memory");
    v -> v_subtree.s_forw = v -> v_subtree.s_back = &v -> v_subtree;
    if ((c = (struct community *) calloc (1, sizeof *c)) == NULL)
	adios (NULLCP, "out of memory");
    c -> c_permission = OT_YYY;
    vec++;

    (void) strcpy (buffer, *vec);
    if ((v -> v_name = text2oid (buffer)) == NULL) {
	advise (LLOG_EXCEPTIONS, NULLCP, "unknown OID \"%s\"", *vec);
	goto you_lose;
    }
    c -> c_vu = v -> v_name;
    if (trapview && inSubtree (trapview, v -> v_name)) {
	advise (LLOG_EXCEPTIONS, NULLCP, "view \"%s\" is for traps", *vec);
	goto you_lose;
    }
    for (u = VHead -> v_forw; u != VHead; u = u -> v_forw)
	if (oid_cmp (u -> v_name, v -> v_name) == 0) {
	    advise (LLOG_EXCEPTIONS, NULLCP, "duplicate view \"%s\"", *vec);
	    goto you_lose;
	}
    vec++;

    if (lexequ (*vec, "rfc1157")) {
	advise (LLOG_EXCEPTIONS, NULLCP, "unsupported proxy domain \"%s\"",
		*vec);
	goto you_lose;
    }
    vec++;

    na = &c -> c_addr;
    if (*vec) {
	if (str2sa (*vec, na, &v -> v_sa, 1) == NOTOK)
	    adios (NULLCP, "unknown address \"%s\" for proxy %s",
		   *vec, oid2ode (v -> v_name));

	vec++;
    }
    else
	goto you_lose;

    if (*vec) {
	if ((v -> v_community = str2qb (*vec, strlen (*vec), 1)) == NULL)
	    adios (NULLCP, "out of memory");
	if ((c -> c_name = strdup (*vec)) == NULL)
	    adios (NULLCP, "out of memory");

	if (*++vec)
	    goto you_lose;
    }
    else
	goto you_lose;

    insque (v, VHead -> v_back);
    insque (c, CHead -> c_back);

    return OK;

you_lose: ;
    if (c -> c_name)
	free (c -> c_name);
    free ((char *) c);
    if (v -> v_name)
	oid_free (v -> v_name);
    if (v -> v_community)
	qb_free (v -> v_community);
    free ((char *) v);

    return NOTOK;
}

/*  */

static int  f_trap (vec)
char  **vec;
{
    register struct trap *t;
    register struct view *v;
    struct NSAPaddr nas;
    register struct NSAPaddr *na = &nas;
    static int trapno = 1;

    vec++;

    if ((t = (struct trap *) calloc (1, sizeof *t)) == NULL
	    || (t -> t_name = strdup (*vec)) == NULL)
	adios (NULLCP, "out of memory");
    v = t -> t_view = &t -> t_vu;
    v -> v_subtree.s_forw = v -> v_subtree.s_back = &v -> v_subtree;
    t -> t_generics = 0xfe;
    vec++;

    trapview -> oid_elements[trapview -> oid_nelem++] = trapno;
    v -> v_name = oid_cpy (trapview);
    trapview -> oid_nelem--;
    if (v -> v_name == NULLOID)
	adios (NULLCP, "out of memory");

    if ((v -> v_community = str2qb (t -> t_name, strlen (t -> t_name), 1))
	    == NULL)
	adios (NULLCP, "out of memory");

    bzero ((char *) na, sizeof *na);
    if (*vec) {
	if (str2sa (*vec, na, &v -> v_sa, 0) == NOTOK)
	    adios (NULLCP, "unknown address \"%s\" for trap sink \"%s\"",
		   *vec, t -> t_name);

	vec++;
    }
    else
	goto you_lose;

    if (*vec) {
	char    buffer[BUFSIZ];
	OID	name;
	register struct view *u;

	(void) strcpy (buffer, *vec);
	if ((name = text2oid (buffer)) == NULL) {
	    advise (LLOG_EXCEPTIONS, NULLCP, "unknown OID \"%s\"", *vec);
	    goto you_lose;
	}
	oid_free (v -> v_name);
	v -> v_name = name;

	for (u = VHead -> v_forw; u != VHead; u = u -> v_forw)
	    if (oid_cmp (u -> v_name, v -> v_name) == 0) {
		advise (LLOG_EXCEPTIONS, NULLCP,
			"duplicate view \"%s\" for trap sink \"%s\"",
			*vec, t -> t_name);
		goto you_lose;
	    }

	vec++;
    }
    else
	trapno++;

    if (*vec) {
	if (sscanf (*vec, "%lx", &t -> t_generics) != 1)
	    goto you_lose;

	if (*++vec)
	    goto you_lose;
    }

    insque (t, UHead -> t_back);
    insque (v, VHead -> v_back);

    return OK;

you_lose: ;
    oid_free (v -> v_name);
    qb_free (v -> v_community);
    free (t -> t_name);
    free ((char *) t);

    return NOTOK;
}

/*  */

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

    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;	
    }
    
    if (lexequ (*vec, "snmpEnableAuthTraps") == 0) {
	++vec;

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

	return OK;
    }

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

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

    return OK;
}

/*  */

static int  f_view (vec)
char  **vec;
{
    char    buffer[BUFSIZ];
    register struct subtree *s,
			    *x,
			    *y;
    register struct view *v,
			 *u;

    if (viewmask == 0) {
	advise (LLOG_EXCEPTIONS, NULLCP,
		"too many views starting with \"%s\"", *vec);
	return NOTOK;
    }

    if ((v = (struct view *) calloc (1, sizeof *v)) == NULL)
	adios (NULLCP, "out of memory");
    s = &v -> v_subtree;
    v -> v_subtree.s_forw = v -> v_subtree.s_back = s;
    vec++;

    (void) strcpy (buffer, *vec);
    if ((v -> v_name = text2oid (buffer)) == NULL) {
	advise (LLOG_EXCEPTIONS, NULLCP, "unknown OID \"%s\"", *vec);
	goto you_lose;
    }
    if (trapview && inSubtree (trapview, v -> v_name)) {
	advise (LLOG_EXCEPTIONS, NULLCP, "view \"%s\" is for traps", *vec);
	goto you_lose;
    }
    for (u = VHead -> v_forw; u != VHead; u = u -> v_forw)
	if (oid_cmp (u -> v_name, v -> v_name) == 0) {
	    advise (LLOG_EXCEPTIONS, NULLCP, "duplicate view \"%s\"", *vec);
	    goto you_lose;
	}

    for (vec++; *vec; vec++) {
	register struct subtree *z;
	OID	name;

	(void) strcpy (buffer, *vec);
	if ((name = text2oid (buffer)) == NULLOID) {
	    advise (LLOG_EXCEPTIONS, NULLCP, "unknown OID \"%s\"", *vec);
	    goto you_lose;
	}

	for (x = s -> s_forw; x != s; x = y) {
	    register int    i,
			    j;
	    y = x -> s_forw;

	    if (bcmp ((char *) x -> s_subtree -> oid_elements,
		      (char *) name -> oid_elements,
		      ((i = x -> s_subtree -> oid_nelem)
		           <= (j = name -> oid_nelem) ? i : j)
		          * sizeof name -> oid_elements[0]) == 0) {
		advise (LLOG_EXCEPTIONS, NULLCP,
			"%s %s %s",
			*vec,
			i <= j ? "already under" : "superceding",
			oid2ode (x -> s_subtree));
		if (i <= j)
		    goto another;

		remque (x);
		oid_free (x -> s_subtree);
		free ((char *) x);
	    }
	}

	if ((z = (struct subtree *) calloc (1, sizeof *z)) == NULL)
	    adios (NULLCP, "out of memory");
	z -> s_subtree = name;

	insque (z, s -> s_back);
another: ;
    }

    v -> v_mask = viewmask;
    viewmask <<= 1;
    insque (v, VHead -> v_back);

    return OK;

you_lose: ;
    for (x = s -> s_forw; x != s; x = y) {
	y = x -> s_forw;

	remque (x);
	oid_free (x -> s_subtree);
	free ((char *) x);
    }
    if (v -> v_name)
	oid_free (v -> v_name);
    free ((char *) v);

    return NOTOK;
}

/*  */

static int  str2sa (s, na, sock, proxy)
char   *s;
struct NSAPaddr *na;
struct sockaddr *sock;
int	proxy;
{
#ifdef	TCP
    register struct hostent *hp;
#endif
    struct TSAPaddr *ta;

#ifdef	TCP
    if (hp = gethostbystring (s)) {
	struct sockaddr_in    sin;

	na -> na_stack = NA_TCP;
	na -> na_community = ts_comm_tcp_default;
	inaddr_copy (hp, &sin);
	(void) strncpy (na -> na_domain, inet_ntoa (sin.sin_addr),
			sizeof na -> na_domain - 1);
    }
    else
#endif
	if ((ta = str2taddr (s)) && ta -> ta_naddr > 0) {
	    *na = ta -> ta_addrs[0];	/* struct copy */
	}
	else
	    return NOTOK;

    if (sock == NULL)
	return OK;

    switch (na -> na_stack) {
#ifdef	TCP
	case NA_TCP:
	    if (!tcpservice)
		goto you_lose;
	    {
		struct sockaddr_in sin;

		sin.sin_port = na -> na_port ? na -> na_port
					     : proxy ? udport : traport;

		if ((hp = gethostbystring (na -> na_domain)) == NULL)
		    return NOTOK;

		sin.sin_family = hp -> h_addrtype;
		inaddr_copy (hp, &sin);

		*((struct sockaddr_in *) sock) = sin;	/* struct copy */
	    }
	    break;
#endif

	default:
you_lose: ;
	    advise (LLOG_EXCEPTIONS, NULLCP, "address type unsupported");
	    return NOTOK;
    }

    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
