Net2/usr/src/contrib/isode/snmp/gawk-2.11/snmp.c

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

/* snmp.c - SNMP changes for gawk */

#ifndef	lint
static char *rcsid = "$Header: /f/osi/snmp/gawk-2.11/RCS/snmp.c,v 7.10 91/02/22 09:45:08 mrose Interim $";
#endif

/* 
 * $Header: /f/osi/snmp/gawk-2.11/RCS/snmp.c,v 7.10 91/02/22 09:45:08 mrose Interim $
 *
 *
 * $Log:	snmp.c,v $
 * Revision 7.10  91/02/22  09:45:08  mrose
 * Interim 6.8
 * 
 * Revision 7.9  91/01/07  12:42:41  mrose
 * update
 * 
 * Revision 7.8  90/10/23  20:44:36  mrose
 * update
 * 
 * Revision 7.7  90/10/02  15:17:05  mrose
 * robust
 * 
 * Revision 7.6  90/09/07  11:11:50  mrose
 * update
 * 
 * Revision 7.3  90/08/17  15:12:28  mrose
 * for-in
 * 
 * Revision 7.2  90/06/06  22:59:38  mrose
 * update
 * 
 * Revision 7.1  90/03/22  16:44:22  mrose
 * touch-up
 * 
 * Revision 7.0  90/03/05  10:33:19  mrose
 * *** empty log message ***
 * 
 */

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


#ifdef	SNMP
#include "awk.h"
#ifdef	HUGE
#undef	HUGE
#endif
#include <isode/snmp/objects.h>
#include <isode/pepsy/SNMP-types.h>
#include <isode/dgram.h>
#include <isode/internet.h>
#include <isode/isoaddrs.h>
#include <isode/tailor.h>

/*    DATA */

int	debug = 0;

int	snmp_enabled = 1;
int	snmp_scalars_as_arrays = 1;
char   *snmp_file = NULLCP;

static	integer	snmp_id = 0;
static	int	snmp_portno = 0;
static	int	snmp_retries = 3;
static	int	snmp_timeout = 10;

static	char   *snmp_agent = NULL;
static	char   *snmp_community = NULL;

NODE   *AGENT_node,
       *COMMUNITY_node,
       *DIAGNOSTIC_node,
       *ERROR_node,
       *RETRIES_node,
       *TIMEOUT_node;
NODE   *Ndot_string;

static	int	snmp_fd = NOTOK;
static	struct sockaddr_in in_socket;
static	PS	ps = NULLPS;

static	struct type_SNMP_Message	msgs;
static	struct type_SNMP_PDUs		pdus;
static	struct type_SNMP_PDU		parms;
static	struct type_SNMP_VarBindList	vps;
static	struct type_SNMP_VarBind	vs;


struct snmp_search {
    struct search s_search;	/* must be first element in struct */

    OT	    s_parent;

    struct type_SNMP_VarBindList *s_prototype;

#define	NREQ	10
    struct snmp_req {
	struct type_SNMP_VarBindList *r_bindings;
	PE	r_pb;

	integer	r_id;
	PE	r_pe;

	struct type_SNMP_Message *r_msg;
    }	    s_reqs[NREQ];

    struct snmp_search *s_prev;
    struct snmp_search *s_next;
};

static struct snmp_search *head = NULL;
static struct snmp_search *tail = NULL;


char   *snmp_error (), *snmp_variable ();


#ifndef	SYS5
long	random ();
#endif

/*    INIT */

int	snmp_init ()
{
    char   *addr;
    register struct hostent *hp;
    struct sockaddr_in lo_socket;

    snmp_onceonly ();

    if (lookup (variables, "AGENT")
	    || !(hp = gethostbystring (getlocalhost ())))
	return 1;

    inaddr_copy (hp, &lo_socket);
    addr = inet_ntoa (lo_socket.sin_addr);
    AGENT_node = install (variables, "AGENT",
			  node (make_string (addr, strlen (addr)),
				Node_var, (NODE *) NULL));

    return 0;
}

/*  */

int	f_integer (), f_octets (), f_display (), f_objectID (), f_null (),
	f_ulong (), f_ipaddr (), f_clnpaddr ();


static struct pair {
    char   *pp_name;
    IFP	    pp_value;
}	pairs[] = {
    "INTEGER", f_integer,
    "Services", f_integer,
    "Privileges", f_integer,
    "OctetString", f_octets,
    "DisplayString", f_display,
    "ObjectID", f_objectID,
    "NULL", f_null,
    "IpAddress", f_ipaddr,
    "NetworkAddress", f_ipaddr,
    "Counter", f_ulong,
    "Gauge", f_ulong,
    "TimeTicks", f_ulong,
    "ClnpAddress", f_clnpaddr,

    NULL
};


static	snmp_onceonly () {
    int	    i;
    register struct pair *pp;
    register struct type_SNMP_Message *msg = &msgs;
    register struct type_SNMP_PDUs *pdu = &pdus;
    register struct type_SNMP_PDU *parm = &parms;
    register struct type_SNMP_VarBindList *vp = &vps;
    register struct type_SNMP_VarBind *v = &vs;
    OS	    os;
    register OT	    ot,
		    ot2;

    Ndot_string = make_string (".", 1);
    Ndot_string -> flags |= PERM;

    if (readobjects (snmp_file) == NOTOK)
	fatal ("readobjects: %s", PY_pepy);

				/* mark entries that are actually columns! */
    for (ot = text2obj ("ccitt"); ot; ot = ot -> ot_next) {
	if (ot -> ot_syntax
		|| (i = strlen (ot -> ot_text)) <= 5
	        || strcmp (ot -> ot_text + i - 5, "Entry"))
	    continue;
	for (ot2 = ot -> ot_children; ot2; ot2 = ot2 -> ot_sibling)
	    if (ot2 -> ot_children)
		break;
	if (ot2)
	    continue;
	for (ot2 = ot -> ot_children; ot2; ot2 = ot2 -> ot_sibling)
	    if (ot2 -> ot_syntax)
		ot2 -> ot_getfnx = (IFP) 1;
	    else
		if (debug > 0)
		    fprintf (stderr, "no syntax for columnar object \"%s\"\n",
			     ot -> ot_text);
    }

    for (pp = pairs; pp -> pp_name; pp++)
	if ((os = text2syn (pp -> pp_name)) == NULL)
	    fatal ("lost syntax for \"%s\"", pp -> pp_name);
        else
	    os -> os_decode = pp -> pp_value;

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

    bzero ((char *) pdu, sizeof *pdu);
    pdu -> offset = type_SNMP_PDUs_get__request;
    pdu -> un.get__request = parm;

    bzero ((char *) parm, sizeof *parm);
    parm -> variable__bindings = vp;

    bzero ((char *) vp, sizeof *vp);
    vp -> VarBind = v;

    bzero ((char *) v, sizeof *v);
    if ((v -> value = pe_alloc (PE_CLASS_UNIV, PE_FORM_PRIM, PE_PRIM_NULL))
	    == NULLPE)
	fatal ("pe_alloc: out of memory");

    ps_len_strategy = PS_LEN_LONG;

#ifndef	SYS5
    srandom (getpid ());
#else
    srand (getpid ());
#endif
}

/*    CHECK */

int	snmp_check (r, name)
NODE   *r;
char   *name;
{
    char    c;
    register char   *cp;
    OT	    ot;

    for (cp = name; is_identchar (*cp); cp++)
	continue;
    if (c = *cp)
	*cp = NULL;
    if ((ot = text2obj (name)) && ot -> ot_syntax) {
	r -> magic = (caddr_t) ot;
	if (ot -> ot_getfnx || snmp_scalars_as_arrays)
	    r -> type = Node_var_array;
    }
    *cp = c;
}

/*    GET */

int	snmp_get (ptr, instname)
NODE   *ptr;
char   *instname;
{
    int	    gotone,
	    retries,
	    status = -1;
    struct type_SNMP_Message *msg = &msgs;
    register struct type_SNMP_PDU *parm = msg -> data -> un.get__request;
    register struct type_SNMP_VarBind *v =
					parm -> variable__bindings -> VarBind;
    PE	    pe = NULLPE,
	    p = NULLPE;
    OID	    oid;
    OT	    ot = (OT) ptr -> magic;
    NODE   *value = NULL;

    if (snmp_ready (1) == NOTOK)
	goto out;

    parm -> request__id = snmp_id;
    if (v -> name)
	free_SNMP_ObjectName (v -> name), v -> name = NULL;

    if (instname == NULL) {
	if (ot -> ot_getfnx || snmp_scalars_as_arrays) {
	    register struct snmp_search *s;

	    for (s = tail; s; s = s -> s_prev) {
		register struct snmp_req *sr;

		if (ot -> ot_name -> oid_nelem
			   != (oid = s -> s_parent -> ot_name) -> oid_nelem + 1
		        || bcmp ((char *) ot -> ot_name -> oid_elements,
				 (char *) oid -> oid_elements,
				 oid -> oid_nelem
				         * sizeof ot -> ot_name -> oid_elements[0]))
		    continue;
		for (sr = s -> s_reqs; sr -> r_bindings; sr++) {
		    register struct type_SNMP_VarBindList *vp;

		    for (vp = sr -> r_bindings; vp; vp = vp -> next) {
			if (ot -> ot_name -> oid_nelem
			        >= (v = vp -> VarBind) -> name -> oid_nelem)
			    fatal ("snmp_get: internal error");
			if (bcmp ((char *) v -> name -> oid_elements,
				  (char *) ot -> ot_name -> oid_elements,
				  ot -> ot_name -> oid_nelem
				      * sizeof ot -> ot_name 
							-> oid_elements[0]))
			    continue;

			goto get_value;
		    }
		}
		status = int_SNMP_error__status_noSuchName;
		goto out;
	    }
	    if (ot -> ot_getfnx) {
		snmp_diag (NULLCP,
			   "can't use SNMP array variable as scalar unless within for-in construct");
		goto out;
	    }
	}

	if ((oid = v -> name = oid_extend (ot -> ot_name, 1)) == NULL) {
no_mem_for_inst: ;
	    snmp_diag (NULLCP, "oid_extend: out of memory");
	    goto out;
	}
	v -> name -> oid_elements[v -> name -> oid_nelem - 1] = 0;
    }
    else {
	register int	i;
	register unsigned int *ip,
			      *jp;
	OID	inst = str2oid (instname);

	if (inst == NULL) {
	    snmp_diag (NULLCP, "str2oid: bad instance identifier \"%s\"",
		       instname);
	    goto out;
	}
	if ((oid = v -> name = oid_extend (ot -> ot_name, inst -> oid_nelem))
	        == NULL)
	    goto no_mem_for_inst;
	ip = oid -> oid_elements + oid -> oid_nelem - inst -> oid_nelem;
	jp = inst -> oid_elements;
	for (i = inst -> oid_nelem; i > 0; i--)
	    *ip++ = *jp++;
    }

    if (encode_SNMP_Message (&pe, 1, 0, NULLCP, msg) == NOTOK) {
	snmp_diag (NULLCP, "encode_SNMP_Message: %s", PY_pepy);
	goto out;
    }

    msg = NULL, gotone = 0;
    for (retries = snmp_retries; retries > 0; ) {
	int	len;
	fd_set	rfds;

	if (debug > 1)
	    print_SNMP_Message (pe, 1, NULLIP, NULLVP, NULLCP);
	len = ps -> ps_byteno;
	if (pe2ps (ps, pe) == NOTOK) {
	    snmp_diag (NULLCP, "pe2ps: %s", ps_error (ps -> ps_errno));
	    goto error_x;
	}
	if (debug > 0 && (len = ps -> ps_byteno - len) > 484)
	    fprintf (stderr, "sent message of %d octets\n", len);

	FD_ZERO (&rfds);
	FD_SET (snmp_fd, &rfds);

	switch (xselect (snmp_fd + 1, &rfds, NULLFD, NULLFD, snmp_timeout)) {
	    case NOTOK:
	        snmp_diag ("failed", "xselect");
		goto error_x;

	    default:
		if (FD_ISSET (snmp_fd, &rfds))
		    break;
		/* else fall... */
	    case OK:
		if (debug > 0)
		    fprintf (stderr, "timeout...\n");
		retries--;
		continue;
	}

	if ((p = ps2pe (ps)) == NULLPE) {
	    snmp_diag (NULLCP, "ps2pe: %s", ps_error (ps -> ps_errno));
	    goto error_x;
	}
	if (decode_SNMP_Message (p, 1, NULLIP, NULLVP, &msg) == NOTOK) {
	    snmp_diag (NULLCP, "decode_SNMP_Message: %s", PY_pepy);
	    goto out;
	}
	if (debug > 1)
	    print_SNMP_Message (p, 1, NULLIP, NULLVP, NULLCP);

	if (msg -> data -> offset != type_SNMP_PDUs_get__response) {
	    snmp_diag (NULLCP, "unexpected message type %d",
		       msg -> data -> offset);
	    goto out;
	}

	if ((parm = msg -> data -> un.get__response) -> request__id == snmp_id)
	    break;

	if (debug > 0)
	    fprintf (stderr, "bad ID (got %ld, wanted %ld)\n",
		     (long) parm -> request__id, (long) snmp_id);

	if (msg)
	    free_SNMP_Message (msg), msg = NULL;
	if (p)
	    pe_free (p), p = NULLPE;

	gotone++;
    }
    if (retries <= 0) {
	snmp_diag (NULLCP,
		   "no %sresponse within %d retries of %s%d second%s each",
		   gotone ? "acceptable " : "", snmp_retries + gotone,
		   gotone ? "upto " : "",
		   snmp_timeout, snmp_timeout != 1 ? "s" : "");
	goto out;
    }

    if ((status = parm -> error__status) != int_SNMP_error__status_noError) {
	char   *cp = snmp_variable (parm, parm -> error__index);

	snmp_diag (NULLCP, cp ? "%s at position %d (%s)" : "%s at position %d",
		   snmp_error (status), parm -> error__index, cp);
	goto out;
    }

    if (parm -> variable__bindings == NULL
	    || (v = parm -> variable__bindings -> VarBind) == NULL) {
	snmp_diag (NULLCP, "missing variable in response");
	goto out;
    }
    if (debug > 0 && parm -> variable__bindings -> next)
	fprintf (stderr, "too many responses starting with: %s\n",
		 oid2ode (parm -> variable__bindings -> next -> VarBind -> name));

    if (oid_cmp (oid, v -> name)) {
	char    buffer[BUFSIZ];

	(void) strcpy (buffer, oid2ode (v -> name));
	snmp_diag (NULLCP, "wrong variable returned (got %s, wanted %s)",
		 buffer, oid2ode (oid));
	goto out;
    }
		 
get_value: ;
    if ((*ot -> ot_syntax -> os_decode) (&value, v -> value) == NOTOK) {
	snmp_diag (NULLCP, "decode error for variable \"%s\": %s",
		 oid2ode (v -> name), PY_pepy);
	goto out;
    }

    goto out;

error_x: ;
    if (ps)
	ps_free (ps), ps = NULLPS;
    if (snmp_fd != NOTOK)
	(void) close_udp_socket (snmp_fd), snmp_fd = NOTOK;

out: ;
    if (msg && msg != &msgs)
	free_SNMP_Message (msg);
    if (p)
	pe_free (p);
    if (pe)
	pe_free (pe);

    deref = ptr -> var_value;
    do_deref ();

    ptr -> var_value = value ? value : Nnull_string;

    assign_number (&ERROR_node -> var_value, (AWKNUM) status);
}

/*    SCAN */

struct search *snmp_assoc_scan (symbol, instance)
NODE   *symbol,
       *instance;
{
    register struct snmp_search *s;
    OID	    inst;
    register OT	    ot = (OT) symbol -> magic;
    register struct type_SNMP_VarBindList **vp,
					  **vp2;

    if (!ot -> ot_getfnx && (!snmp_scalars_as_arrays || instance))
	fatal ("can't use SNMP scalar variable as control for for-in");

    if (instance) {
	char   *instname;
	NODE   *r;

	if ((r = instance) -> type != Node_val)
	    r = r_tree_eval (instance);

	if ((inst = str2oid (instname = force_string (r) -> stptr)) == NULL) {
	    snmp_diag (NULLCP, "str2oid: bad instance identifier \"%s\"",
		       instname);
	    free_temp (r);
	    return NULL;
	}

	if (r != instance)
	    free_temp (r);
    }
    else
	inst = NULL;
    emalloc (s, struct snmp_search *, sizeof *s, "snmp_assoc_scan1");
    bzero ((char *) s, sizeof *s);

    ot -> ot_name -> oid_nelem--;
    s -> s_parent = name2obj (ot -> ot_name);
    ot -> ot_name -> oid_nelem++;

    if ((ot = s -> s_parent) == NULL)
	fatal ("unable to find parent for \"%s\"", snmp_name (symbol));

    s -> s_prototype = NULL;

    vp = &s -> s_reqs[0].r_bindings, vp2 = &s -> s_prototype;
    for (ot = ot -> ot_children; ot; ot = ot -> ot_sibling) {
	register int    i;
	register unsigned int *ip,
			      *jp;
	register struct type_SNMP_VarBindList *bind;
	register struct type_SNMP_VarBind *v,
					  *v2;

	if (!ot -> ot_syntax)
	    continue;
	emalloc (bind, struct type_SNMP_VarBindList *, sizeof *bind,
		 "snmp_assoc_scan2");
	*vp = bind, vp = &bind -> next;
	bind -> next = NULL;

	emalloc (v, struct type_SNMP_VarBind *, sizeof *v, "snmp_assoc_scan3");
	bind -> VarBind = v;
	if (inst) {
	    if ((v -> name = oid_extend (ot -> ot_name, inst -> oid_nelem))
		    == NULL)
		fatal ("oid_extend: out of memory");
	    ip = v -> name -> oid_elements + v -> name -> oid_nelem 
					   - inst -> oid_nelem;
	    jp = inst -> oid_elements;
	    for (i = inst -> oid_nelem; i > 0; i--)
		*ip++ = *jp++;
	}
	else
	    if ((v -> name = oid_cpy (ot -> ot_name)) == NULL)
		fatal ("oid_cpy: out of memory");
	v -> value = NULL;

	emalloc (bind, struct type_SNMP_VarBindList *, sizeof *bind,
		 "snmp_assoc_scan4");
	*vp2 = bind, vp2 = &bind -> next;
	bind -> next = NULL;

	emalloc (v2, struct type_SNMP_VarBind *, sizeof *v2,
		 "snmp_assoc_scan5");
	bind -> VarBind = v2;
	if ((v2 -> name = oid_cpy (v -> name)) == NULL)
	    fatal ("oid_cpy: out of memory");
	v2 -> value = NULL;
    }

    if (head == NULL)
	head = tail = s;
    else {
	tail -> s_next = s;
	s -> s_prev = tail;
	tail = s;
    }

    return snmp_assoc_next (&s -> s_search, 0);
}

/*  */

struct search *snmp_assoc_next (lookat, done)
struct search *lookat;
int	done;
{
    int	    i;
    char   *cp;
    register struct snmp_search *s = (struct snmp_search *) lookat;
    register struct search *l = &s -> s_search;
    struct OIDentifier	oids;
    OID	    oid;
    OT	    ot = s -> s_parent;
    register struct type_SNMP_VarBind *v;

    deref = l -> retval, l -> retval = NULL;
    do_deref ();

    if (done
	    || snmp_get_next (s) == NOTOK
	    || s -> s_reqs[0].r_bindings == NULL) {
	register struct snmp_req *sr;

	if (s -> s_prototype)
	    free_SNMP_VarBindList (s -> s_prototype);
	for (sr = s -> s_reqs; sr -> r_bindings; sr++) {
	    free_SNMP_VarBindList (sr -> r_bindings);
	    if (sr -> r_pb)
		pe_free (sr -> r_pb);
	    if (sr -> r_msg)
		free_SNMP_Message (sr -> r_msg);
	    if (sr -> r_pe)
		pe_free (sr -> r_pe);
	}

	if (tail != s)
	    fatal ("snmp_assoc_next: internal error1");
	if (tail = s -> s_prev)
	    tail -> s_next = NULL;
	else
	    head = NULL;

	free ((char *) s);
	return NULL;
    }

    if ((v = s -> s_reqs[0].r_bindings -> VarBind) == NULL
	     || (oid = v -> name) == NULL)
	fatal ("snmp_assoc_next: internal error2");
    if (ot -> ot_name -> oid_nelem >= oid -> oid_nelem
	    || bcmp ((char *) ot -> ot_name -> oid_elements,
		     (char *) oid -> oid_elements,
		     ot -> ot_name -> oid_nelem
		             * sizeof oid -> oid_elements[0]))
	fatal ("snmp_assoc_next: internal error3");

    oids.oid_nelem = oid -> oid_nelem - (i = ot -> ot_name -> oid_nelem + 1);
    oids.oid_elements = oid -> oid_elements + i;

    cp = sprintoid (&oids);
    l -> retval = make_string (cp, strlen (cp));

    return l;
}

/*  */

static int  snmp_get_next (s)
register struct snmp_search *s;
{
    register struct type_SNMP_VarBindList  *vp,
					   *vp2,
					  **vpp,
					  **vpp2;
    register struct snmp_req *sr,
			     *sp;

    if (snmp_ready (0) == NOTOK || snmp_get_next_aux (s) == NOTOK)
	return NOTOK;

    vpp = &s -> s_prototype, vp = NULL;
    for (sr = s -> s_reqs; sr -> r_bindings; sr++) {
	vpp2 = &sr -> r_msg -> data -> un.get__request -> variable__bindings;

	while ((vp = *vpp) && (vp2 = *vpp2)) {
	    OID	    v = vp -> VarBind -> name,
		    v2 = vp2 -> VarBind -> name;

	    if (v -> oid_nelem > v2 -> oid_nelem
		    || bcmp ((char *) v -> oid_elements,
			     (char *) v2 -> oid_elements,
			     v -> oid_nelem
			         * sizeof v -> oid_elements[0])) {
		*vpp = vp -> next;
		vp -> next = NULL;
		free_SNMP_VarBindList (vp);

		*vpp2 = vp2 -> next;
		vp2 -> next = NULL;
		free_SNMP_VarBindList (vp2);
	    }
	    else
		vpp = &vp -> next, vpp2 = &vp2 -> next;
	}

	if (vp == NULL && (vp2 = *vpp)) {
	    snmp_diag (NULLCP, "too many responses starting with: %s\n",
		       oid2ode (vp2 -> VarBind -> name));
	    return NOTOK;
	}
    }
    if (vp) {
	snmp_diag (NULLCP, "missing variable in response");
	return NOTOK;
    }

    for (sp = s -> s_reqs; sp < sr; sp++) {
	if (sp -> r_bindings)
	    free_SNMP_VarBindList (sp -> r_bindings);
	if (sp -> r_pb)
	    pe_free (sp -> r_pb);

	if (sp -> r_bindings
	        && sp -> r_msg -> data -> un.get__request
						    -> variable__bindings) {
	    sp -> r_bindings = sp -> r_msg -> data -> un.get__request 
							-> variable__bindings;
	    sp -> r_msg -> data -> un.get__request -> variable__bindings =NULL;

	    sp -> r_pb = sp -> r_pe, sp -> r_pe = NULL;

	    free_SNMP_Message (sp -> r_msg), sp -> r_msg = NULL;
	    continue;
	}

	if (sp -> r_msg)
	    free_SNMP_Message (sp -> r_msg);
	if (sp -> r_pe)
	    pe_free (sp -> r_pe);

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

    for (sr--; sr >= s -> s_reqs; sr--)
	if (sr -> r_bindings)
	    for (sp = s -> s_reqs; sp < sr; sp++)
		if (!sp -> r_bindings) {
		    *sp = *sr;		/* struct copy */
		    bzero ((char *) sr, sizeof *sr);
		    break;
		}

    return OK;
}

/*  */

static int  snmp_get_next_aux (s)
register struct snmp_search *s;
{
    int	    gotone,
	    result,
	    retries,
	    status = -1;
    struct type_SNMP_Message *msg;
    register struct type_SNMP_PDU *parm;
    struct type_SNMP_VarBindList  *vp;
    register struct snmp_req *sr;
    PE	    p = NULLPE;

    vp = msgs.data -> un.get__request -> variable__bindings;
    msgs.data -> offset = type_SNMP_PDUs_get__next__request;
    for (sr = s -> s_reqs; sr -> r_bindings; sr++)
	if ((result = req_ready (sr, 1)) == NOTOK)
	    break;
    msgs.data -> un.get__request -> variable__bindings = vp;
    msgs.data -> offset = type_SNMP_PDUs_get__request;

    if (result == NOTOK) {
	snmp_diag (NULLCP, "encode_SNMP_Message: %s", PY_pepy);
	return NOTOK;
    }

    msg = NULL, gotone = 0;
    for (retries = snmp_retries; retries > 0; ) {
	int	len;
	fd_set	rfds;

	for (sr = s -> s_reqs; sr -> r_bindings; sr++) {
	    if (sr -> r_msg)
		continue;

	    if (debug > 1)
		print_SNMP_Message (sr -> r_pe, 1, NULLIP, NULLVP, NULLCP);
	    len = ps -> ps_byteno;
	    if (pe2ps (ps, sr -> r_pe) == NOTOK) {
		snmp_diag (NULLCP, "pe2ps: %s", ps_error (ps -> ps_errno));
		goto error_x;
	    }
	    if (debug > 0 && (len = ps -> ps_byteno - len) > 484)
		fprintf (stderr, "sent message of %d octets\n", len);
	}

	FD_ZERO (&rfds);
	FD_SET (snmp_fd, &rfds);

	switch (xselect (snmp_fd + 1, &rfds, NULLFD, NULLFD, snmp_timeout)) {
	    case NOTOK:
	        snmp_diag ("failed", "xselect");
		goto error_x;

	    default:
		if (FD_ISSET (snmp_fd, &rfds))
		    break;
		/* else fall... */
	    case OK:
		retries--;
		continue;
	}

again: ;
	if ((p = ps2pe (ps)) == NULLPE) {
	    snmp_diag (NULLCP, "ps2pe: %s", ps_error (ps -> ps_errno));
	    goto error_x;
	}
	if (decode_SNMP_Message (p, 1, NULLIP, NULLVP, &msg) == NOTOK) {
	    snmp_diag (NULLCP, "decode_SNMP_Message: %s", PY_pepy);
	    goto out;
	}
	if (debug > 1)
	    print_SNMP_Message (p, 1, NULLIP, NULLVP, NULLCP);

	if (msg -> data -> offset != type_SNMP_PDUs_get__response) {
	    snmp_diag (NULLCP, "unexpected message type %d",
		       msg -> data -> offset);
	    goto out;
	}
	gotone++;

	parm = msg -> data -> un.get__response;
	for (sr = s -> s_reqs; sr -> r_bindings; sr++)
	    if (parm -> request__id == sr -> r_id)
		break;
	if (!sr -> r_bindings || sr -> r_msg) {
	    if (debug > 0)
		fprintf (stderr, "%s response ID (got %ld)\n",
			 sr -> r_bindings ? "duplicate" : "unexpected",
			 (long) parm -> request__id);
	    if (msg)
		free_SNMP_Message (msg), msg = NULL;
	    if (p)
		pe_free (p), p = NULLPE;
	    goto check;
	}

	switch (status = parm -> error__status) {
	    case int_SNMP_error__status_noError:
	        break;

	    case int_SNMP_error__status_tooBig:
		{
		    register int    i;
		    register struct type_SNMP_VarBindList **vpp;
		    register struct snmp_req *sp;
		    struct snmp_req *sz = s -> s_reqs + NREQ;

		    i = 0;
		    for (vp = sr -> r_bindings; vp; vp = vp -> next)
			i++;
		    if ((i >>= 1) < 1) {
			snmp_diag (NULLCP,
				   "%s for request with single variable",
				   snmp_error (status));
			goto out;
		    }
		    for (sp = sr + 1; sp -> r_bindings; sp++)
			if (sp >= sz) {
			    snmp_diag (NULLCP,
				       "too many operations needed (%d max)",
				       NREQ - 1);
			    goto out;
			}
		    for (sz = sr + 1; sp > sz; sp--)
			*sp = *(sp - 1);	/* struct copy */
		    bzero ((char *) sp, sizeof *sp);

		    for (vpp = &sr -> r_bindings;
			     i-- > 0;
			     vpp = &((*vpp) -> next))
			continue;
		    sp -> r_bindings = *vpp, *vpp = NULL;
		    
		    vp = msgs.data -> un.get__request -> variable__bindings;
		    msgs.data -> offset = type_SNMP_PDUs_get__next__request;
		    if ((result = req_ready (sr, 0)) != NOTOK)
			result = req_ready (sp, 0);
		    msgs.data -> un.get__request -> variable__bindings = vp;
		    msgs.data -> offset = type_SNMP_PDUs_get__request;
		    if (result == NOTOK) {
			snmp_diag (NULLCP, "encode_SNMP_Message: %s", PY_pepy);
			goto out;
		    }

		    retries = snmp_retries;
		    goto check;
		}
		break;

	    default:
	    {
		char   *cp = snmp_variable (parm, parm -> error__index);

		snmp_diag (NULLCP, cp ? "%s at position %d (%s)"
				      : "%s at position %d",
			   snmp_error (status), parm -> error__index, cp);
	    }
		goto out;
	}

	pe_free (sr -> r_pe);
	sr -> r_pe = p, p = NULLPE;

	sr -> r_msg = msg, msg = NULL;

	for (sr = s -> s_reqs; sr -> r_bindings; sr++)
	    if (!sr -> r_msg)
		break;
	if (!sr -> r_bindings) {
	    assign_number (&ERROR_node -> var_value, (AWKNUM) status);

	    return OK;
	}

check: ;
	FD_ZERO (&rfds);
	FD_SET (snmp_fd, &rfds);

	if (select_dgram_socket (snmp_fd + 1, &rfds, NULLFD, NULLFD, 1) > OK)
	    goto again;
    }
    snmp_diag (NULLCP,
	       "no %sresponse within %d retries of %s%d second%s each",
	       gotone ? "acceptable " : "", snmp_retries + gotone,
	       gotone ? "up to " : "", snmp_timeout,
	       snmp_timeout != 1 ? "s" : "");
    return NOTOK;

error_x: ;
    if (ps)
	ps_free (ps), ps = NULLPS;
    if (snmp_fd != NOTOK)
	(void) close_udp_socket (snmp_fd), snmp_fd = NOTOK;

out: ;
    if (p)
	pe_free (p);

    return NOTOK;
}

/*  */

static int  req_ready (sr, do_val)
register struct snmp_req *sr;
int	do_val;
{
    register struct type_SNMP_Message *msg = &msgs;
    register struct type_SNMP_PDU *parm = msg -> data -> un.get__request;

    if (do_val) {
	register struct type_SNMP_VarBindList *vp;

	for (vp = sr -> r_bindings; vp; vp = vp -> next) {
	    register struct type_SNMP_VarBind *v = vp -> VarBind;

	    if (v -> value)
		pe_free (v -> value);
	    if ((v -> value = pe_alloc (PE_CLASS_UNIV, PE_FORM_PRIM,
					PE_PRIM_NULL)) == NULLPE)
		fatal ("pe_alloc: out of memory");
	}
    }

    if (snmp_id >= 0x7fffffff)
	snmp_id = 0;
    snmp_id++;

    parm -> request__id = sr -> r_id = snmp_id;
    parm -> variable__bindings = sr -> r_bindings;

    if (sr -> r_msg)
	free_SNMP_Message (sr -> r_msg), sr -> r_msg = NULL;
    if (sr -> r_pe)
	pe_free (sr -> r_pe), sr -> r_pe = NULL;

    return encode_SNMP_Message (&sr -> r_pe, 1, 0, NULLCP, &msgs);
}

/*    DECODE */

static NODE *make_octet_node (base, len)
char   *base;
int	len;
{
    register char *bp,
		  *cp,
		  *ep;
    char   *s = "";
    register NODE *r;

    r = newnode (Node_val);
    emalloc (r -> stptr, char *, len * 3 + 1, "make_octet_node");
    bp = r -> stptr;
    for (ep = (cp = base) + len; cp < ep; cp++, s = ":") {
	(void) sprintf (bp, "%s%02x", s, *cp & 0xff);
	bp += strlen (bp);
    }
    *bp = NULL;		/* in case len == 0 */
    r -> stlen = bp - r -> stptr;
    r -> stref = 1;
    r -> flags |= STR | MALLOC;

    return r;
}


static int  f_integer (x, pe)
NODE  **x;
PE	pe;
{
    integer	i = prim2num (pe);

    if (i == NOTOK && pe -> pe_errno != PE_ERR_NONE) {
	(void) strcpy (PY_pepy, pe_error (pe -> pe_errno));
	return NOTOK;
    }

    *x = make_number ((AWKNUM) i);

    return OK;
}


static int  f_octets (x, pe)
NODE  **x;
PE	pe;
{
    struct qbuf *qb = prim2qb (pe);

    if (qb == NULL || qb_pullup (qb) == NOTOK) {
	(void) strcpy (PY_pepy, qb ? "qb_pullup: out of memory"
				   : pe_error (pe -> pe_errno));
	return NOTOK;
    }

    *x = make_octet_node (qb -> qb_forw -> qb_data, qb -> qb_forw -> qb_len);

    qb_free (qb);

    return OK;
}


static int  f_display (x, pe)
NODE  **x;
PE	pe;
{
    struct qbuf *qb = prim2qb (pe);

    if (qb == NULL || qb_pullup (qb) == NOTOK) {
	(void) strcpy (PY_pepy, qb ? "qb_pullup: out of memory"
				   : pe_error (pe -> pe_errno));
	return NOTOK;
    }

    *x = make_string (qb -> qb_forw -> qb_data, qb -> qb_forw -> qb_len);

    qb_free (qb);

    return OK;
}


static int  f_objectID (x, pe)
NODE  **x;
PE	pe;
{
    char   *cp;
    OID	    oid = prim2oid (pe);

    if (oid == NULLOID) {
	(void) strcpy (PY_pepy, pe_error (pe -> pe_errno));
	return NOTOK;
    }
    cp = sprintoid (oid);

    *x = make_string (cp, strlen (cp));

    return OK;
}


/* ARGSUSED */

static int  f_null (x, pe)
NODE  **x;
PE	pe;
{
    *x = make_str_node ("NULL", 4, 0);

    return OK;
}


static int  f_ipaddr (x, pe)
NODE  **x;
PE	pe;
{
    char    ipaddr[16];
    struct type_SNMP_IpAddress *ip;
    struct qbuf *qb;

    if (decode_SNMP_IpAddress (pe, 1, NULLIP, NULLVP, &ip) == NOTOK)
	return NOTOK;
    if (qb_pullup (ip) == NOTOK) {
	(void) strcpy (PY_pepy, "qb_pullup: out of memory");
	free_SNMP_IpAddress (ip);
	return NOTOK;
    }
    if ((qb = ip -> qb_forw) -> qb_len != 4) {
	(void) sprintf (PY_pepy,
			"IpAddress is wrong length (got %d, wanted 4)",
			qb -> qb_len);
	free_SNMP_IpAddress (ip);
	return NOTOK;
    }
    (void) sprintf (ipaddr, "%d.%d.%d.%d",
		    qb -> qb_data[0] & 0xff, qb -> qb_data[1] & 0xff,
		    qb -> qb_data[2] & 0xff, qb -> qb_data[3] & 0xff);

    *x = make_str_node (ipaddr, strlen (ipaddr), 0);

    free_SNMP_IpAddress (ip);

    return OK;
}


extern	u_long prim2ulong ();

static int  f_ulong (x, pe)
NODE  **x;
PE	pe;
{
    u_long    i = prim2ulong (pe);

    if (i == 0 && pe -> pe_errno != PE_ERR_NONE) {
	(void) strcpy (PY_pepy, pe_error (pe -> pe_errno));
	return NOTOK;
    }

    *x = make_number ((AWKNUM) i);

    return OK;
}


static int  f_clnpaddr (x, pe)
NODE  **x;
PE	pe;
{
    int	    len;
    struct type_SNMP_ClnpAddress *clnp;
    struct qbuf *qb;

    if (decode_SNMP_ClnpAddress (pe, 1, NULLIP, NULLVP, &clnp) == NOTOK)
	return NOTOK;
    if (qb_pullup (clnp) == NOTOK) {
	(void) strcpy (PY_pepy, "qb_pullup: out of memory");
	free_SNMP_ClnpAddress (clnp);
	return NOTOK;
    }
    qb = clnp -> qb_forw;
    if ((len = qb -> qb_data[0] & 0xff) >= qb -> qb_len)
	len = qb -> qb_len - 1;

    *x = make_octet_node (qb -> qb_data + 1, len);

    free_SNMP_ClnpAddress (clnp);

    return OK;
}

/*    MISC */

static	snmp_ready (do_id)
int	do_id;
{
    int	    changed = 0;
    char   *pp;
    struct sockaddr_in lo_socket;
    register struct sockaddr_in *lsock = &lo_socket;
    register struct sockaddr_in *isock = &in_socket;
    register struct hostent *hp;
    register struct servent *sp;
    register NODE   *tmp;

    deref = DIAGNOSTIC_node -> var_value;
    do_deref ();

    DIAGNOSTIC_node -> var_value = Nnull_string;

    if ((snmp_retries = (int) RETRIES_node -> var_value -> numbr) <= 0)
	snmp_retries = 1;
    if ((snmp_timeout = (int) TIMEOUT_node -> var_value -> numbr) <= 0)
	snmp_timeout = 1;

    if (do_id) {
	if (snmp_id >= 0x7fffffff)
	    snmp_id = 0;
	snmp_id++;
    }

    if (snmp_fd == NOTOK || ps == NULLPS)
	changed++;

    tmp = force_string (AGENT_node -> var_value);
    if (snmp_agent == NULL || strcmp (snmp_agent, tmp -> stptr)) {
	if (snmp_agent)
	    free (snmp_agent), snmp_agent = NULL;

	emalloc (snmp_agent, char *, strlen (tmp -> stptr) + 1, "snmp_ready1");
	(void) strcpy (snmp_agent, tmp -> stptr);

	changed++;
    }

    tmp = force_string (COMMUNITY_node -> var_value);
    if (snmp_community == NULL || strcmp (snmp_community, tmp -> stptr)) {
	register struct type_SNMP_Message *msg = &msgs;

	if (snmp_community)
	    free (snmp_community), snmp_community = NULL;

	emalloc (snmp_community, char *, strlen (tmp -> stptr) + 1,
		 "snmp_ready2");
	(void) strcpy (snmp_community, tmp -> stptr);
	if ((msg -> community = str2qb (snmp_community,
					strlen (snmp_community), 1)) == NULL) {
	    snmp_diag (NULLCP, "str2qb: out of memory");
	    free (snmp_community), snmp_community = NULL;
	    return NOTOK;
	}
    }

    if (changed) {
	if (ps)
	    ps_free (ps), ps = NULLPS;
	if (snmp_fd != NOTOK)
	    (void) close_udp_socket (snmp_fd), snmp_fd = NOTOK;
    }
    else
	return OK;

    bzero ((char *) lsock, sizeof *lsock);
    if ((hp = gethostbystring (pp = strcmp (snmp_agent, "localhost")
					? getlocalhost () : "localhost"))
	    == NULL) {
	snmp_diag (NULLCP, "%s: unknown host", pp);
	return NOTOK;
    }
    lsock -> sin_family = hp -> h_addrtype;
    inaddr_copy (hp, lsock);
    if ((snmp_fd = start_udp_client (lsock, 0, 0, 0)) == NOTOK) {
	snmp_diag ("failed", "start_udp_server");
	return NOTOK;
    }

    if ((hp = gethostbystring (pp = snmp_agent)) == NULL) {
	struct TSAPaddr *ta;
	struct NSAPaddr *na;

	if ((ta = str2taddr (snmp_agent))
	        && ta -> ta_naddr > 0
	        && (na = ta -> ta_addrs) -> na_stack == NA_TCP) {
	    snmp_portno = na -> na_port;

	    if (hp = gethostbystring (pp = na -> na_domain))
		goto got_host;
	}

	snmp_diag (NULLCP, "%s: unknown host", pp);
	return NOTOK;
    }
got_host: ;
    if (snmp_portno == 0)
	snmp_portno = (sp = getservbyname ("snmp", "udp"))
			    ? sp -> s_port
			    : htons ((u_short) 161);

    bzero ((char *) isock, sizeof *isock);
    isock -> sin_family = hp -> h_addrtype;
    inaddr_copy (hp, isock);	
    isock -> sin_port = snmp_portno;

    if (*snmp_community == '/' && snmp_map (isock) == NOTOK)
	return NOTOK;

    if (join_udp_server (snmp_fd, isock) == NOTOK) {
	snmp_diag ("failed", "join_udp_server");
	return NOTOK;
    }

    if ((ps = ps_alloc (dg_open)) == NULLPS
	    || dg_setup (ps, snmp_fd, MAXDGRAM, read_udp_socket,
			 write_udp_socket, check_udp_socket) == NOTOK) {
	if (ps == NULLPS)
	    snmp_diag (NULLCP, "ps_alloc: out of memory");
	else
	    snmp_diag (NULLCP, "dg_setup: %s", ps_error (ps -> ps_errno));

	return NOTOK;
    }

#ifndef	SYS5
    snmp_id = ((int) random ()) & 0x7fffffff;
#else
    snmp_id = ((int) rand ()) & 0x7fffffff;
#endif

    return OK;
}

/*  */

/* Reads an IP address to community mapping file.  Use UNIX modes for
   protection of information therein...

   Syntax:

	<netaddr>	[<netmask>]	<community>

	Each token is seperated by LWSP, though double-quotes may be used to
	prevent separation.

 */

static	snmp_map (isock)
struct sockaddr_in *isock;
{
    int	    result = NOTOK;
    u_long	hostaddr,
		netmask,
		netaddr;
    register char *cp;
    char    buffer[BUFSIZ + 1],
	   *vec[NVEC + 1];
    FILE   *fp;

    if ((fp = fopen (snmp_community, "r")) == NULL) {
	snmp_diag (snmp_community, "unable to read");
	return NOTOK;
    }

    hostaddr = isock -> sin_addr.s_addr;

    while (fgets (buffer, sizeof buffer, fp)) {
	if (*buffer == '#')
	    continue;
	if (cp = index (buffer, '\n'))
	    *cp = NULL;
	bzero ((char *) vec, sizeof vec);
	switch (str2vec (buffer, vec)) {
	    case 3:
		netmask = inet_addr (vec[1]);
	        cp = vec[2];
		break;

	    case 2:
		netmask = 0xffffffff;
	        cp = vec[1];
	        break;

	    default:
		continue;
	}
	if ((netaddr = inet_network (vec[0])) == NOTOK)
	    continue;
	netaddr = ntohl (netaddr);
	if (!(netaddr & 0xff000000))
	    netaddr <<=   (netaddr & 0x00ff0000) ? 8
		        : (netaddr & 0x0000ff00) ? 16
		        :			   24;
	netaddr = htonl (netaddr);
	if ((hostaddr & netmask) != netaddr)
	    continue;

	if ((msgs.community = str2qb (cp, strlen (cp), 1)) == NULL) {
	    snmp_diag (NULLCP, "str2qb: out of memory");
	    goto done;
	}

	result = OK;
	break;
    }
    if (result == NOTOK)
	snmp_diag (NULLCP, "no match for IP-address in %s", snmp_community);

done: ;
    (void) fclose (fp);

    return result;
}

/*  */

#ifndef	lint
static	snmp_diag (va_alist)
va_dcl
{
    char   *what,
	    buffer[BUFSIZ];
    va_list ap;

    va_start (ap);

    what = va_arg (ap, char *);

    _asprintf (buffer, what, ap);

    va_end (ap);

    if (debug > 0)
	fprintf (stderr, "%s\n", buffer);

    deref = DIAGNOSTIC_node -> var_value;
    do_deref ();

    DIAGNOSTIC_node -> var_value = make_string (buffer, strlen (buffer));
}
#else
/* VARARGS */

static	snmp_diag (what, fmt)
char   *what,
       *fmt;
{
    snmp_diag (what, fmt);
}
#endif

/*  */

char   *snmp_name (ptr)
NODE   *ptr;
{
    return ((OT) (ptr -> magic)) -> ot_text;
}

/*  */

static char *errors[] = {
    "noError", "tooBig", "noSuchName", "badValue", "readOnly", "genErr"
};


static char *snmp_error (i)
int	i;
{
    static char buffer[BUFSIZ];

    if (0 < i && i < sizeof errors / sizeof errors[0])
	return errors[i];
    (void) sprintf (buffer, "error %d", i);

    return buffer;
}


static char *snmp_variable (parm, idx)
register struct type_SNMP_PDU *parm;
int	idx;
{
    register struct type_SNMP_VarBindList *vp;

    if (idx <= 0 || (vp = parm -> variable__bindings) == NULL)
	return NULL;
    for (idx--; idx > 0; idx--)
	if ((vp = vp -> next) == NULL)
	    return NULL;

    return oid2ode (vp -> VarBind -> name);
}
#endif	/* SNMP */