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

#ifndef	lint
static char *rcsid = "$Header: /var/lib/cvsd/repos/CSRG/43BSDReno/contrib/isode-beta/snmp/gawk-2.11/snmp.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/gawk-2.11/snmp.c,v 1.1.1.1 2018/04/24 16:12:56 root Exp $
 *
 *
 * $Log: snmp.c,v $
 * Revision 1.1.1.1  2018/04/24 16:12:56  root
 * BSD 4.3reno
 *
 * 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/pepy/SNMP-types.h>
#include <isode/dgram.h>
#include <isode/internet.h>
#include <isode/tailor.h>

/*    DATA */

int	debug = 0;

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

static	int	snmp_id = 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 entry */

    OT	    s_parent;

    PE	    s_pe;
    struct type_SNMP_VarBindList *s_bindings;

    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	check_snmp (r, name)
NODE   *r;
char   *name;
{
    char    c;
    register char   *cp;
    OT	    ot;
    static int inited = 0;

    if (inited == 0) {
	inited = 1;

	snmp_onceonly ();
    }

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

/*  */

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


static struct pair {
    char   *pp_name;
    IFP	    pp_value;
}	pairs[] = {
    "INTEGER", f_integer,
    "Services", f_integer,
    "OctetString", f_octets,
    "DisplayString", f_display,
    "ObjectID", f_objectID,
    "NULL", f_null,
    "IpAddress", f_ipaddr,
    "NetworkAddress", f_ipaddr,
    "Counter", f_integer,
    "Gauge", f_integer,
    "TimeTicks", f_integer,
    "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 arrays! */
    for (ot = text2obj ("iso"); 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 || !ot2 -> ot_syntax)
		break;
	if (ot2)
	    continue;
	for (ot2 = ot -> ot_children; ot2; ot2 = ot2 -> ot_sibling)
	    ot2 -> ot_getfnx = (IFP) 1;
    }

    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
}

/*    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 () == 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 type_SNMP_VarBindList *vp;

		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 (vp = s -> s_bindings; vp; vp = vp -> next) {
		    v = vp -> VarBind;

		    if (ot -> ot_name -> oid_nelem >= v -> 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 %d, wanted %d)\n",
		     parm -> request__id, 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: %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);
}

/*  */

static	snmp_ready () {
    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 (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_init1");
	(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_init2");
	(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;
    }

    bzero ((char *) isock, sizeof *isock);
    if ((hp = gethostbystring (snmp_agent)) == NULL) {
	snmp_diag (NULLCP, "%s: unknown host", snmp_agent);
	return NOTOK;
    }
    isock -> sin_family = hp -> h_addrtype;
    isock -> sin_port = (sp = getservbyname ("snmp", "udp"))
					? sp -> s_port
					: htons ((u_short) 161);
    inaddr_copy (hp, isock);

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

/*  */

#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

/*    SCAN */

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

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

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

    vp = &s -> s_bindings;
    for (ot = ot -> ot_children; ot; ot = ot -> ot_sibling) {
	register struct type_SNMP_VarBindList *bind;
	register struct type_SNMP_VarBind *v;

	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 ((v -> name = oid_cpy (ot -> ot_name)) == NULL)
	    fatal ("oid_cpy: out of memory");
	if ((v -> value = pe_alloc (PE_CLASS_UNIV, PE_FORM_PRIM, PE_PRIM_NULL))
	        == NULLPE)
	    fatal ("pe_alloc: out of memory");
    }

    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_bindings == NULL) {
	if (s -> s_bindings)
	    free_SNMP_VarBind (s -> s_bindings);
	if (s -> s_pe)
	    pe_free (s -> s_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_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;
{
    int	    gotone,
	    result = NOTOK,
	    retries,
	    status = -1;
    struct type_SNMP_Message *msg = &msgs;
    register struct type_SNMP_PDU *parm = msg -> data -> un.get__request;
    register struct type_SNMP_VarBindList  *vp = parm -> variable__bindings,
					   *vp2,
					  **vpp;
    PE	    pe = NULLPE,
	    p = NULLPE;

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

    msg -> data -> offset = type_SNMP_PDUs_get__next__request;
    parm -> request__id = snmp_id;
    parm -> variable__bindings = s -> s_bindings;

    result = encode_SNMP_Message (&pe, 1, 0, NULLCP, msg);

    parm -> variable__bindings = vp;
    msg -> data -> offset = type_SNMP_PDUs_get__request;

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

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

    for (vp = s -> s_bindings, vpp = &parm -> variable__bindings;
	     vp && (vp2 = *vpp);
	     vp = vp -> next) {
	if (name2obj (vp -> VarBind -> name)
	        != name2obj (vp2 -> VarBind -> name)) {
	    *vpp = vp2 -> next;
	    vp2 -> next = NULL;
	    free_SNMP_VarBindList (vp2);
	}
	else
	    vpp = &vp2 -> next;
    }
    if (vp) {
	snmp_diag (NULLCP, "missing variable in response");
	goto out;
    }
    else
	if (vp2 = *vpp) {
	    if (debug > 0)
		fprintf (stderr, "too many responses starting with: %s\n",
			 oid2ode (vp2 -> VarBind -> name));

	    *vpp = NULL;
	    free_SNMP_VarBindList (vp2);
	}

    if (s -> s_bindings)
	free_SNMP_VarBindList (s -> s_bindings);
    if (s -> s_pe)
	pe_free (s -> s_pe);

    s -> s_bindings = parm -> variable__bindings;
    parm -> variable__bindings = NULL;
    s -> s_pe = p;
    p = NULLPE;

    result = OK;
    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);

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

    return result;
}

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


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

    free_SNMP_ClnpAddress (clnp);

    return OK;
}

/*    MISC */

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, index)
register struct type_SNMP_PDU *parm;
int	index;
{
    register struct type_SNMP_VarBindList *vp;

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

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