DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T s

⟦fab553190⟧ TextFile

    Length: 32952 (0x80b8)
    Types: TextFile
    Names: »snmpd.c«

Derivation

└─⟦2d1937cfd⟧ Bits:30007241 EUUGD22: P.P 5.0
    └─⟦35176feda⟧ »EurOpenD22/isode/isode-6.tar.Z« 
        └─⟦de7628f85⟧ 
            └─⟦this⟧ »isode-6.0/snmp/snmpd.c« 

TextFile

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

#ifndef	lint
static char *rcsid = "$Header: /f/osi/snmp/RCS/snmpd.c,v 7.8 90/01/11 18:34:33 mrose Exp $";
#endif

/* 
 * $Header: /f/osi/snmp/RCS/snmpd.c,v 7.8 90/01/11 18:34:33 mrose Exp $
 *
 * Contributed by NYSERNet Inc.  This work was partially supported by the
 * U.S. Defense Advanced Research Projects Agency and the Rome Air Development
 * Center of the U.S. Air Force Systems Command under contract number
 * F30602-88-C-0016.
 *
 *
 * $Log:	snmpd.c,v $
 * Revision 7.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"
#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
#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


#define	IDLE_TIME	(3 * 60)

/* \f

   DATA */

int	debug = 0;
static	int	nbits = FD_SETSIZE;
static	int	rflag = 0;

static LLog _pgm_log = {
    "snmpd.log", NULLCP, NULLCP, LLOG_FATAL | LLOG_EXCEPTIONS | LLOG_NOTICE,
    LLOG_FATAL, -1, LLOGCLS | LLOGCRT | LLOGZER, NOTOK
};
static LLog *pgm_log = &_pgm_log;

static char *myname = "snmpd";


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


#define	NTADDRS	FD_SETSIZE

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

static fd_set  ifds;
#ifdef	COTS
static struct TSAPaddr  taddrs[FD_SETSIZE];
static struct timeval   lru[FD_SETSIZE];
#endif


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


extern int  errno;

/* \f

 */

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;	/* global check, someday do per-object... */
#define	COMM_TRAP     (-1)
#define	COMM_NONE	0
#define	COMM_RDONLY	1
#define	COMM_RDWRITE	2

    struct qbuf *c_community;	/* for traps only... */
#ifdef	TCP
    struct sockaddr_in c_sin;	/*   .. */
#endif
};

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


struct community *str2comm ();


static struct type_SNMP_Message *trap = NULL;

int	nd = NOTOK;
int	quantum = 0;
#ifdef	TCP
static	int	udp = NOTOK;
static	int	traport;
#endif
#ifdef	CLTS
static	int	clts = NOTOK;
#endif

struct snmpstat snmpstat;

/* \f

   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_type) {
		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_type);
		    /* 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");

    do_trap (int_SNMP_generic__trap_coldStart, 0,
	     (struct type_SNMP_VarBindList *) 0);

    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++) {
#ifdef	TCP
	    if (fd == udp)
		continue;
#endif
#ifdef	CLTS
	    if (fd == clts)
		continue;
#endif

	    if (FD_ISSET (fd, &ifds)) {
		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);

#ifdef	COTS
	    FD_CLR (udp, &rfds);
#endif
	}
#endif
#ifdef	CLTS
	if (clts != NOTOK && FD_ISSET (clts, &rfds)) {
	    doit_clts (clts);

#ifdef	COTS
	    FD_CLR (clts, &rfds);
#endif
	}
#endif

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

	for (fd = 0; fd < nfds; fd++)
	    if (FD_ISSET (fd, &rfds))
		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, &ifds)) {
#ifdef	TCP
		if (fd == udp)
		    continue;
#endif
#ifdef	CLTS
		if (fd == clts)
		    continue;
#endif
		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);
		}
	    }

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

/* \f

 */

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

/* \f

 */

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

    cp = inet_ntoa (isock -> sin_addr);
    advise (LLOG_NOTICE, NULLCP, "packet from %s/%d",
	    cp, (int) ntohs (isock -> sin_port));

    bzero ((char *) na, sizeof *na);
    na -> na_type = NA_TCP;
    na -> na_subnet = 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);

    (void) close_udp_socket (fd);
}
#endif

/* \f

 */

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

    advise (LLOG_NOTICE, NULLCP, "packet from %s", taddr2str (ta));

    doit_aux (fd, ta -> ta_addrs, read_clts_socket, write_clts_socket);

    (void) close_clts_socket (fd);
}
#endif

/* \f

 */

#ifdef	COTS
static	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_NOTICE, 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 OK;
}

/* \f

 */

static	doit_cots (fd)
int	fd;
{
    doit_aux (fd, &(taddrs[fd].ta_addrs[0]), ts_read, ts_write);

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

/* \f

 */

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

    pe = NULLPE;
    msg = NULL;
    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");
	else
	    advise (LLOG_EXCEPTIONS, NULLCP, "dg_setup: %s",
		    ps_error (ps -> ps_errno));

	goto out;
    }

    if ((pe = ps2pe (ps)) == NULLPE) {
#ifdef	COTS
	if (rfx == ts_read) {
	    FD_CLR (fd, &ifds);

	    if (ps -> ps_errno == PS_ERR_NONE) {
		advise (LLOG_NOTICE, NULLCP,
			"T-DISCONNECT.INDICATION: %d (%s)",
			fd, taddr2str (taddrs + fd));
		goto out;
	    }
	}
#endif
	advise (LLOG_EXCEPTIONS, NULLCP, "ps2pe: %s",
		ps_error (ps -> ps_errno));
	snmpstat.s_inpkts++;
	snmpstat.s_asnparseerrs++;
	goto out;
    }
#ifdef	COTS
    if (rfx == ts_read)
	advise (LLOG_NOTICE, NULLCP, "T-DATA.INDICATION: %d (%s)", fd,
		taddr2str (taddrs + fd));
#endif

    snmpstat.s_inpkts++;

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

	    (void) TDiscRequest (fd, NULLCP, 0, &tds);
	    FD_CLR (fd, &ifds);
	}
#endif
	goto out;
    }

    PLOG (pgm_log, print_SNMP_Message, pe, "Message", 1);

    process (ps, msg, na);

out: ;
    if (msg)
	free_SNMP_Message (msg);
    if (pe)
	pe_free (pe);
    if (ps)
	ps_free (ps);
}

/* \f

 */

static	process (ps, msg, na)
PS	ps;
register struct type_SNMP_Message *msg;
struct NSAPaddr *na;
{
    char   *commname;
    struct community *comm;
    PE	    pe;

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

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

    if ((comm = str2comm (commname, na)) == NULL) {
	advise (LLOG_EXCEPTIONS, NULLCP, "badCommunity: %s", commname);
	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 (do_operation (msg, comm) == NOTOK)
	goto out;

    pe = NULLPE;

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

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

    if (pe)
	pe_free (pe);

out: ;
    free (commname);
}

/* \f

 */

static int  do_operation (msg, comm)
struct type_SNMP_Message *msg;
struct community *comm;
{
    int	    idx,
	    offset,
	    status;
    object_instance ois;
    register struct type_SNMP_PDUs *pdu = msg -> data;
    register struct type_SNMP_VarBindList *vp;
    register struct type_SNMP_GetResponse__PDU *parm = pdu -> un.get__response;

    idx = 0;
    switch (pdu -> offset) {
	case type_SNMP_PDUs_get__request:
	    snmpstat.s_ingetrequests++;
	    goto do_get;
	case type_SNMP_PDUs_get__next__request:
	    snmpstat.s_ingetnexts++;
do_get: ;
	    if (comm -> c_permission < COMM_RDONLY) {
		snmpstat.s_badcommunityuses++;
		parm -> error__status = int_SNMP_error__status_genErr;
		goto out;
	    }
	    break;

	case type_SNMP_PDUs_set__request:
	    snmpstat.s_insetrequests++;
	    if (comm -> c_permission < COMM_RDWRITE) {
read_only: ;
		snmpstat.s_badcommunityuses++;
		parm -> error__status = int_SNMP_error__status_readOnly;
		goto out;
	    }
	    break;

	case type_SNMP_PDUs_get__response:
	case type_SNMP_PDUs_trap:
	    advise (LLOG_EXCEPTIONS, NULLCP,
		    "unexpectedOperation: %d", pdu -> offset);
	    snmpstat.s_badtypes++;
	    return NOTOK;

	default:
	    advise (LLOG_EXCEPTIONS, NULLCP,
		    "badOperation: %d", pdu -> offset);
	    snmpstat.s_badtypes++;
	    return NOTOK;
    }

    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)
		goto get_next;
	}
	else
	    if ((oi = name2inst (v -> name)) == NULLOI
	            || (ot = oi -> oi_type) -> ot_getfnx == NULLIFP) {
no_name: ;
		parm -> error__status = int_SNMP_error__status_noSuchName;
		goto out;
	    }

try_again: ;
	switch (ot -> ot_access) {
	    case OT_NONE:
	        if (offset == type_SNMP_PDUs_get__next__request)
		    goto get_next;
	        goto no_name;

	    case OT_RDONLY:
		if (offset == type_SNMP_PDUs_set__request)
		    goto read_only;
		break;

	    case OT_RDWRITE:
		break;
	}
		
	switch (status = (*ot -> ot_getfnx) (oi, v, offset)) {
	    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)
			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 OK;
}

/* \f

   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 (c -> c_permission == COMM_TRAP)
	    continue;

	if (lexequ (c -> c_name, name) == 0) {
	    if (c -> c_addr.na_type == NA_TCP
		    && strcmp (c -> c_addr.na_domain, "0.0.0.0") == 0) {
		d = c;
		continue;
	    }
	    else {
		if (c -> c_addr.na_type != na -> na_type)
		    continue;
		switch (na -> na_type) {
		    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_type, name);
			/* NOTREACHED */
		}
	    }

	    d = c;
	    break;
	}
    }

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

    return d;
}

/* \f

   TRAPS */

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

    for (c = CHead -> c_forw; c != CHead; c = c -> c_forw)
	if (c -> c_permission == COMM_TRAP)
	    break;
    if (c == CHead)
	return;

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

/* \f

 */

#ifndef	TCP
/* ARGSUSED */
#endif

static	do_trap (generic, specific, bindings)
int	generic,
	specific;
struct type_SNMP_VarBindList *bindings;
{
#ifdef	TCP
    register struct community *c;
    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;

    for (c = CHead -> c_forw; c != CHead; c = c -> c_forw) {
	PE	pe;
	PS	ps;

	if (c -> c_permission != COMM_TRAP)
	    continue;

	msg -> community = c -> c_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;
	}
	PLOG (pgm_log, print_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, (struct sockaddr *) &c -> c_sin)
		    == 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
}

/* \f

   MISCELLANY */

static	arginit (vec)
char	**vec;
{
    register char  *ap;
#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_type = NA_TCP;
    tcp_na -> na_subnet = ts_comm_tcp_default;
    tcp_na -> na_domain[0] = NULL;
    tcp_na -> na_port = sp ? sp -> s_port : htons ((u_short) 161);
    tz -> ta_naddr = 1;

    tz++;

    if ((sp = getservbyname ("snmp-trap", "udp")) == NULL)
	advise (LLOG_EXCEPTIONS, NULLCP, "udp/snmp-trap: unknown service");
    traport = sp ? sp -> s_port : htons ((u_short) 162);
#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_type = NA_X25;
    x25_na -> na_subnet = ts_comm_x25_default;
    if (x25_local_dte && *x25_local_dte) {
	(void) strcpy (x25_na -> na_dte, x25_local_dte);
	x25_na -> na_dtelen = strlen (x25_na -> na_dte);
    }
    x25_na -> na_pidlen = str2sel ("03018200", -1, x25_na -> na_pid, NPSIZE);
    tz -> ta_naddr = 1;

    tz++;
#endif

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

    bcopy ("snmp", tz -> ta_selector, tz -> ta_selectlen = sizeof "snmp" - 1);
    tz -> ta_naddr = 0;

    tz++;
#endif

    for (vec++; ap = *vec; vec++) {
	if (*ap == '-')
	    switch (*++ap) {
		case 'd':
		    debug++;
		    continue;

		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;

		case 'r':
		    rflag = 1;
		    continue;

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

/* \f

 */

static  envinit () {
    int     i,
            sd;
    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	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);

    if (readobjects () == NOTOK)
	adios (NULLCP, "readobjects: %s", PY_pepy);

    readmib ();
    readconfig ();
    checkmib ();
    initrap ();

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

    advise (LLOG_NOTICE, NULLCP, "starting");
}

/* \f

   CONFIG */

struct pair {
    char   *p_name;		/* runcom directive */
    IFP	    p_handler;		/* dispatch */
};


int	f_community (), f_logging (), f_variable ();

static struct pair pairs[] = {
    "community",    f_community,
    "logging",	    f_logging,
    "trap",	    f_community,
    "variable",	    f_variable,

    NULL
};


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

    CHead -> c_forw = CHead -> c_back = CHead;

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

    for (c = CHead -> c_forw; c != CHead; c = c -> c_forw)
	if (c -> c_permission != COMM_TRAP)
	    break;
    if (c == CHead) {
	vec[0] = "community";
	vec[1] = "public";
	vec[2] = "readOnly";
	vec[3] = NULL;

	(void) f_community (vec);
    }
}

/* \f

 */

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

    istrap = lexequ (*vec++, "trap") == 0;

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

    if (istrap
	    && (c -> c_community = str2qb (c -> c_name, strlen (c -> c_name),
					   1)) == NULL)
	adios (NULLCP, "out of memory");

    na = &c -> c_addr;
    if (*vec) {
#ifdef	TCP
	register struct hostent *hp;
#endif
	struct TSAPaddr *ta;

#ifdef	TCP
	if (hp = gethostbystring (*vec)) {
	    struct sockaddr_in    sin;
	    
	    na -> na_type = NA_TCP;
	    na -> na_subnet = 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 (*vec)) && ta -> ta_naddr > 0) {
		*na = ta -> ta_addrs[0];	/* struct copy */
	    }
	    else
		adios (NULLCP, "unknown address \"%s\" for %s \"%s\"",
		       *vec, istrap ? "trap sink" : "community", c -> c_name);

	if (istrap) {
	    if (na -> na_type != NA_TCP) {
		advise (LLOG_EXCEPTIONS, NULLCP,
			"traps are supported only over UDP");
		return NOTOK;
	    }

#ifdef	TCP
	    c -> c_sin.sin_port = na -> na_port ? na -> na_port : traport;

	    if ((hp = gethostbystring (na -> na_domain)) == NULL)
		adios (NULLCP, "unknown host \"%s\" for trap sink \"%s\"",
		       na -> na_domain, c -> c_name);

	    c -> c_sin.sin_family = hp -> h_addrtype;
	    inaddr_copy (hp, &c -> c_sin);
#endif
	}

	vec++;
    }
    else {
	if (istrap)
	    return NOTOK;

	na -> na_type = NA_TCP;
	na -> na_subnet = ts_comm_tcp_default;
	(void) strcpy (na -> na_domain, "0.0.0.0");
    }

    if (*vec) {
	if (istrap)
	    return NOTOK;

	if (lexequ (*vec, "readOnly") == 0)
	    c -> c_permission = COMM_RDONLY;
	else
	    if (lexequ (*vec, "readWrite") == 0)
		c -> c_permission = COMM_RDWRITE;

	vec++;
    }
    else
	if (istrap)
	    c -> c_permission = COMM_TRAP;

    insque (c, CHead -> c_back);

    return OK;
}

/* \f

 */

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

/* \f

 */

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

/* \f

   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