Blob Blame History Raw
/* -*- C -*- */
#if defined(_WIN32) && !defined(_WIN32_WINNT)
#define _WIN32_WINNT 0x501
#endif

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include <netdb.h>
#include <sys/socket.h>

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>

#ifndef sv_undef
#define sv_undef PL_sv_undef
#endif

typedef netsnmp_handler_registration *NetSNMP__agent__netsnmp_handler_registration;

/*
 * needs to be in sync with the definitions in snmplib/snmpUDPDomain.c
 * and snmplib/snmpTCPDomain.c
 */
typedef netsnmp_indexed_addr_pair netsnmp_udp_addr_pair;

typedef struct handler_cb_data_s {
   SV *perl_cb;
} handler_cb_data;

typedef struct netsnmp_oid_s {
    oid                 *name;
    size_t               len;
    oid                  namebuf[ MAX_OID_LEN ];
} netsnmp_oid;

#define TEST_CONSTANT(value, name, C)           \
    if (strEQ(name, #C)) {                      \
        *value = C;                             \
        return 0;                               \
    }

static int constant_MODE_G(double *value, const char *name, const int len)
{
    switch (len >= 8 ? name[8] : -1) {
    case '\0':
        TEST_CONSTANT(value, name, MODE_GET);
        break;
    case 'B':
        TEST_CONSTANT(value, name, MODE_GETBULK);
        break;
    case 'N':
        TEST_CONSTANT(value, name, MODE_GETNEXT);
        break;
    }
    return EINVAL;
}

static int constant_MODE_SET_R(double *value, const char *name, const int len)
{
    switch (len >= 16 ? name[16] : -1) {
#ifndef NETSNMP_NO_WRITE_SUPPORT
    case '1':
	TEST_CONSTANT(value, name, MODE_SET_RESERVE1);
        break;
    case '2':
	TEST_CONSTANT(value, name, MODE_SET_RESERVE2);
        break;
#endif /* NETSNMP_NO_WRITE_SUPPORT */
    }
    return EINVAL;
}

static int constant_SNMP_ERR(double *value, char *name, int len)
{
    switch (len >= 9 ? name[9] : -1) {
    case 'A':
        TEST_CONSTANT(value, name, SNMP_ERR_AUTHORIZATIONERROR);
        break;
    case 'B':
        TEST_CONSTANT(value, name, SNMP_ERR_BADVALUE);
        break;
    case 'C':
        TEST_CONSTANT(value, name, SNMP_ERR_COMMITFAILED);
        break;
    case 'G':
	TEST_CONSTANT(value, name, SNMP_ERR_GENERR);
        break;
    case 'I':
	TEST_CONSTANT(value, name, SNMP_ERR_INCONSISTENTVALUE);
        break;
    case 'N':
        TEST_CONSTANT(value, name, SNMP_ERR_NOACCESS);
        TEST_CONSTANT(value, name, SNMP_ERR_NOCREATION);
        TEST_CONSTANT(value, name, SNMP_ERR_NOERROR);
        TEST_CONSTANT(value, name, SNMP_ERR_NOSUCHNAME);
        TEST_CONSTANT(value, name, SNMP_ERR_NOTWRITABLE);
        break;
    case 'R':
        TEST_CONSTANT(value, name, SNMP_ERR_READONLY);
        TEST_CONSTANT(value, name, SNMP_ERR_RESOURCEUNAVAILABLE);
        break;
    case 'T':
        TEST_CONSTANT(value, name, SNMP_ERR_TOOBIG);
        break;
    case 'U':
        TEST_CONSTANT(value, name, SNMP_ERR_UNDOFAILED);
        break;
    case 'W':
        TEST_CONSTANT(value, name, SNMP_ERR_WRONGENCODING);
        TEST_CONSTANT(value, name, SNMP_ERR_WRONGLENGTH);
        TEST_CONSTANT(value, name, SNMP_ERR_WRONGTYPE);
        TEST_CONSTANT(value, name, SNMP_ERR_WRONGVALUE);
        break;
    }
    return EINVAL;
}
    
static int constant_MODE_S(double *value, char *name, int len)
{
    if (!strnEQ(name + 6, "ET_", 3))
        return EINVAL;

    switch (len >= 9 ? name[9] : -1) {
#ifndef NETSNMP_NO_WRITE_SUPPORT
    case 'A':
        TEST_CONSTANT(value, name, MODE_SET_ACTION);
        break;
    case 'B':
        TEST_CONSTANT(value, name, MODE_SET_BEGIN);
        break;
    case 'C':
        TEST_CONSTANT(value, name, MODE_SET_COMMIT);
        break;
    case 'F':
        TEST_CONSTANT(value, name, MODE_SET_FREE);
        break;
    case 'U':
        TEST_CONSTANT(value, name, MODE_SET_UNDO);
        break;
#endif /* NETSNMP_NO_WRITE_SUPPORT */
    case 'R':
        return constant_MODE_SET_R(value, name, len);
    }
    return EINVAL;
}

static int constant(double *value, char *name, int len)
{
    switch (len >= 5 ? name[5] : -1) {
    case 'G':
	if (strnEQ(name + 0,"MODE_", 5))
            return constant_MODE_G(value, name, len);
        break;
    case 'S':
	if (strnEQ(name + 0,"MODE_", 5))
            return constant_MODE_S(value, name, len);
        break;
    case 'E':
	if (strnEQ(name + 0,"SNMP_ERR_", 9))
            return constant_SNMP_ERR(value, name, len);
        break;
    }
    return EINVAL;
}

int
handler_wrapper(netsnmp_mib_handler          *handler,
                netsnmp_handler_registration *reginfo,
                netsnmp_agent_request_info   *reqinfo,
                netsnmp_request_info         *requests) 
{
    handler_cb_data *cb_data = (handler_cb_data *) handler->myvoid;
    SV *cb;

    if (cb_data && (cb = cb_data->perl_cb)) {
        SV *arg;
        SV *rarg;
        dSP;
        ENTER;
        SAVETMPS;
        PUSHMARK(sp);
        rarg = newSViv(0);
        arg = newSVrv(rarg, "NetSNMP::agent::netsnmp_mib_handler");
        sv_setiv(arg, (IV) handler);
        XPUSHs(sv_2mortal(rarg));
        rarg = newSViv(0);
        arg = newSVrv(rarg, "NetSNMP::agent::netsnmp_handler_registrationPtr");
        sv_setiv(arg, (IV) reginfo);
        XPUSHs(sv_2mortal(rarg));
        rarg = newSViv(0);
        arg = newSVrv(rarg, "NetSNMP::agent::netsnmp_agent_request_info");
        sv_setiv(arg, (IV) reqinfo);
        XPUSHs(sv_2mortal(rarg));
        rarg = newSViv(0);
        arg = newSVrv(rarg, "NetSNMP::agent::netsnmp_request_infoPtr");
        sv_setiv(arg, (IV) requests);
        XPUSHs(sv_2mortal(rarg));
        PUTBACK;
        if (SvTYPE(cb) == SVt_PVCV) {
            perl_call_sv(cb, G_DISCARD);
                                       
        } else if (SvROK(cb) && SvTYPE(SvRV(cb)) == SVt_PVCV) {
            perl_call_sv(SvRV(cb), G_DISCARD); 
        }
        SPAGAIN;
        PUTBACK;
        FREETMPS;
        LEAVE;
    }
    return SNMP_ERR_NOERROR;
}

MODULE = NetSNMP::agent		PACKAGE = NetSNMP::agent		

void
constant(sv)
    PREINIT:
	STRLEN		len;
    INPUT:
	SV *		sv
	char *		s = SvPV(sv, len);
    INIT:
        int status;
        double value;
    PPCODE:
        value = 0;
        status = constant(&value, s, len);
        XPUSHs(sv_2mortal(newSVuv(status)));
        XPUSHs(sv_2mortal(newSVnv(value)));

int
__agent_check_and_process(block = 1)
	int block;
    CODE:
	RETVAL = agent_check_and_process(block);
    OUTPUT:
	RETVAL

int
_uptime()
    CODE:
        RETVAL = netsnmp_get_agent_uptime();
    OUTPUT:
	RETVAL

void
init_mib()
    CODE:
    {
        netsnmp_init_mib();
    }

int
init_agent(name)
        const char *name;
    CODE:
        SOCK_STARTUP;
        RETVAL = init_agent(name);
    OUTPUT:
        RETVAL

void
init_snmp(name)
        const char *name;

int
init_master_agent()

void    
snmp_enable_stderrlog()    

MODULE = NetSNMP::agent  PACKAGE = NetSNMP::agent PREFIX = na_

void
na_shutdown(me)
    SV *me;
    CODE:
    {
        if (0)
            printf("me = %p\n", me);
        snmp_shutdown("perl");
    }

void
na_errlog(me,value)
    SV *me;
    SV *value;
   PREINIT:
        STRLEN stringlen;
        char * stringptr;
    CODE:
    {
        if (0)
            printf("me = %p\n", me);
        stringptr = SvPV(value, stringlen);
        snmp_log(LOG_ERR, "%s", stringptr );
    }



MODULE = NetSNMP::agent  PACKAGE = NetSNMP::agent::netsnmp_handler_registration  PREFIX = nsahr_

NetSNMP::agent::netsnmp_handler_registration
nsahr_new(name, regoid, perlcallback)
        char *name;
	char *regoid;
        SV   *perlcallback;
    PREINIT:
	oid myoid[MAX_OID_LEN];
	size_t myoid_len = MAX_OID_LEN;
        handler_cb_data *cb_data;
        int gotit=1;
    CODE:
	if (!snmp_parse_oid(regoid, myoid, &myoid_len)) {
            if (!read_objid(regoid, myoid, &myoid_len)) {
                snmp_log(LOG_ERR, "couldn't parse %s (reg name: %s)\n",
                        regoid, name);
                RETVAL = NULL;
                gotit = 0;
            }
        }
        if (gotit) {
            cb_data = (handler_cb_data *) malloc(sizeof(handler_cb_data));
            RETVAL = netsnmp_create_handler_registration(name, handler_wrapper,
                                                 myoid, myoid_len,
                                                 HANDLER_CAN_RWRITE);
            cb_data->perl_cb = newSVsv(perlcallback);
            RETVAL->handler->myvoid = cb_data;
        }
    OUTPUT:
        RETVAL

void
nsahr_DESTROY(reginfo)
	netsnmp_handler_registration *reginfo
    PREINIT:
        handler_cb_data *cb_data;
    CODE:
        if (reginfo && reginfo->handler && reginfo->handler->myvoid) {
	    cb_data = (handler_cb_data *) (reginfo->handler->myvoid);
	    SvREFCNT_dec(cb_data->perl_cb);
	    free(cb_data);
        }
	netsnmp_handler_registration_free(reginfo);

int
nsahr_register(me)
        SV *me;
        PREINIT:
        netsnmp_handler_registration *reginfo;
        handler_cb_data *cb_data = NULL;
        CODE:
            {
                reginfo = (netsnmp_handler_registration *) SvIV(SvRV(me));
                if (reginfo && reginfo->handler && reginfo->handler->myvoid)
                    cb_data = (handler_cb_data *) (reginfo->handler->myvoid);
                RETVAL = netsnmp_register_handler(reginfo);
                if (!RETVAL) {
                    /* the agent now has a "reference" to this reg pointer */
                    SvREFCNT_inc(me);
                } else {
                    /*
                     * The reginfo was freed by netsnmp_register_handler,
                     * don't touch it in nsahr_DESTROY!
                     */
                    sv_setiv(SvRV(me), 0);
                    if (cb_data) {
                        /* And just free the callback. */
                        SvREFCNT_dec(cb_data->perl_cb);
                        free(cb_data);
                    }
                }
            }
    OUTPUT:
	RETVAL


MODULE = NetSNMP::agent PACKAGE = NetSNMP::agent::netsnmp_handler_registrationPtr PREFIX = nsahr_

void
nsahr_getRootOID(me)
    SV *me;
    PREINIT:
        int i;
        netsnmp_oid *o;
        netsnmp_handler_registration *reginfo;
        SV *arg, *rarg;
    PPCODE:
    {
        dSP;
        PUSHMARK(SP);
        reginfo = (netsnmp_handler_registration *) SvIV(SvRV(me));

        o = malloc(sizeof(netsnmp_oid));
        o->name = o->namebuf;
        o->len = reginfo->rootoid_len;
        memcpy(o->name, reginfo->rootoid,
               reginfo->rootoid_len * sizeof(oid));

        rarg = newSViv((int) 0);
        arg = newSVrv(rarg, "netsnmp_oidPtr");
        sv_setiv(arg, (IV) o);

        XPUSHs(sv_2mortal(rarg));

        PUTBACK;
        i = perl_call_pv("NetSNMP::OID::newwithptr", G_SCALAR);
        SPAGAIN;
        if (i != 1) {
            snmp_log(LOG_ERR, "unhandled OID error.\n");
            /* ack XXX */
        }
        ST(0) = POPs;
        XSRETURN(1);
    }

MODULE = NetSNMP::agent  PACKAGE = NetSNMP::agent::netsnmp_request_infoPtr PREFIX = nari_

void
getOID(me)
    SV *me;
    PREINIT:
        int i;
        netsnmp_oid *o;
        netsnmp_request_info *request;
        SV *arg, *rarg;
    PPCODE:
    {
        dSP;
        PUSHMARK(SP);
        request = (netsnmp_request_info *) SvIV(SvRV(me));

        o = malloc(sizeof(netsnmp_oid));
        o->name = o->namebuf;
        o->len = request->requestvb->name_length;
        memcpy(o->name, request->requestvb->name,
               request->requestvb->name_length * sizeof(oid));

        rarg = newSViv((int) 0);
        arg = newSVrv(rarg, "netsnmp_oidPtr");
        sv_setiv(arg, (IV) o);

        XPUSHs(sv_2mortal(rarg));

        PUTBACK;
        i = perl_call_pv("NetSNMP::OID::newwithptr", G_SCALAR);
        SPAGAIN;
        if (i != 1) {
            snmp_log(LOG_ERR, "unhandled OID error.\n");
            /* ack XXX */
        }
        ST(0) = POPs;
        XSRETURN(1);
    }
        
netsnmp_oid *
nari_getOIDptr(me)
        SV *me;
        PREINIT:
        netsnmp_request_info *request;
        CODE:
        request = (netsnmp_request_info *) SvIV(SvRV(me));
        RETVAL = malloc(sizeof(netsnmp_oid));
        RETVAL->name = RETVAL->namebuf;
        RETVAL->len = request->requestvb->name_length;
        memcpy(RETVAL->name, request->requestvb->name,
               request->requestvb->name_length * sizeof(oid));
    OUTPUT:
        RETVAL

int
nari_getType(me)
        SV *me;
    PREINIT:
        netsnmp_request_info *request;
    CODE:
        request = (netsnmp_request_info *) SvIV(SvRV(me));

        RETVAL =  request->requestvb->type ;
    OUTPUT:
        RETVAL 

void
nari_setType(me, newvalue)
        SV *me;
        int newvalue;
    PREINIT:
        netsnmp_request_info *request;
    CODE:
        request = (netsnmp_request_info *) SvIV(SvRV(me));
        request->requestvb->type=newvalue;

SV *
nari_getValue(me)
        SV *me;
    PREINIT:
        char *outbuf = NULL;
        size_t ob_len = 0, oo_len = 0;
        netsnmp_request_info *request;
    CODE:
        request = (netsnmp_request_info *) SvIV(SvRV(me));
	sprint_realloc_by_type((u_char **) &outbuf, &ob_len, &oo_len, 1,
                               request->requestvb, 0, 0, 0);
        RETVAL = newSVpv(outbuf, 0);
	netsnmp_free(outbuf);
    OUTPUT:
        RETVAL

int
nari_getDelegated(me)
        SV *me;
    PREINIT:
        netsnmp_request_info *request;
    CODE:
        request = (netsnmp_request_info *) SvIV(SvRV(me));
        RETVAL = request->delegated;
    OUTPUT:
        RETVAL

void
nari_setDelegated(me, newdelegated)
        SV *me;
        int newdelegated;
    PREINIT:
        netsnmp_request_info *request;
    CODE:
        request = (netsnmp_request_info *) SvIV(SvRV(me));
        request->delegated = newdelegated;

int
nari_getProcessed(me)
        SV *me;
    PREINIT:
        netsnmp_request_info *request;
    CODE:
        request = (netsnmp_request_info *) SvIV(SvRV(me));
        RETVAL = request->processed;
    OUTPUT:
        RETVAL

void
nari_setProcessed(me, newprocessed)
        SV *me;
        int newprocessed;
    PREINIT:
        netsnmp_request_info *request;
    CODE:
        request = (netsnmp_request_info *) SvIV(SvRV(me));
        request->processed = newprocessed;

int
nari_getStatus(me)
        SV *me;
    PREINIT:
        netsnmp_request_info *request;
    CODE:
        request = (netsnmp_request_info *) SvIV(SvRV(me));
        RETVAL = request->status;
    OUTPUT:
        RETVAL

void
nari_setStatus(me, newstatus)
        SV *me;
        int newstatus;
    PREINIT:
        netsnmp_request_info *request;
    CODE:
        request = (netsnmp_request_info *) SvIV(SvRV(me));
        request->status = newstatus;

int
nari_getRepeat(me)
        SV *me;
    PREINIT:
        netsnmp_request_info *request;
    CODE:
        request = (netsnmp_request_info *) SvIV(SvRV(me));
        RETVAL = request->repeat;
    OUTPUT:
        RETVAL

void
nari_setRepeat(me, newrepeat)
        SV *me;
        int newrepeat;
    PREINIT:
        netsnmp_request_info *request;
    CODE:
        request = (netsnmp_request_info *) SvIV(SvRV(me));
        request->repeat = newrepeat;

int
nari_setValue(me, type, value)
        SV *me;
        int type;
        SV *value;
    PREINIT:
        netsnmp_request_info *request;
        u_long utmp;
        long ltmp;
        uint64_t ulltmp;
        struct counter64 c64;
	oid myoid[MAX_OID_LEN];
	size_t myoid_len;
        STRLEN stringlen;
        char * stringptr;
    CODE:
        request = (netsnmp_request_info *) SvIV(SvRV(me));
        switch(type) {
          case  SNMP_NOSUCHINSTANCE :
              snmp_set_var_typed_value(request->requestvb,SNMP_NOSUCHINSTANCE,0,0) ;
              RETVAL = 1;
              break ;
          case  SNMP_NOSUCHOBJECT :
              snmp_set_var_typed_value(request->requestvb,SNMP_NOSUCHOBJECT,0,0) ;
              RETVAL = 1;
              break ;
          case  SNMP_ENDOFMIBVIEW :
              snmp_set_var_typed_value(request->requestvb,SNMP_ENDOFMIBVIEW,0,0) ;
              RETVAL = 1;
              break ;
          case ASN_INTEGER:
	      /* We want an integer here */
	      if ((SvTYPE(value) == SVt_IV) || (SvTYPE(value) == SVt_PVMG) ||
                   SvIOK(value)) {
		  /* Good - got a real one (or a blessed object that we hope will turn out OK) */
		  ltmp = SvIV(value);
		  snmp_set_var_typed_value(request->requestvb, (u_char)type,
					   (u_char *) &ltmp, sizeof(ltmp));
		  RETVAL = 1;
		  break;
	      }
	      else if (SvPOKp(value)) {
	          /* Might be OK - got a string, so try to convert it, allowing base 10, octal, and hex forms */
	          stringptr = SvPV(value, stringlen);
		  ltmp = strtol( stringptr, NULL, 0 );
		  if (errno == EINVAL) {
		  	snmp_log(LOG_ERR, "Could not convert string to number in setValue: '%s'", stringptr);
			RETVAL = 0;
			break;
		  }

		  snmp_set_var_typed_value(request->requestvb, (u_char)type,
					   (u_char *) &ltmp, sizeof(ltmp));
		  RETVAL = 1;
		  break;
	      }
	      else {
		snmp_log(LOG_ERR, "Non-integer value passed to setValue with ASN_INTEGER: type was %lu\n",
			(unsigned long)SvTYPE(value));
		RETVAL = 0;
		break;
	      }


          case ASN_UNSIGNED:
          case ASN_COUNTER:
          case ASN_TIMETICKS:
	      /* We want an integer here */
	      if ((SvTYPE(value) == SVt_IV) || (SvTYPE(value) == SVt_PVMG) ||
                   SvIOK(value)) {
		  /* Good - got a real one (or a blessed scalar which we have to hope will turn out OK) */
		  utmp = SvIV(value);
                  snmp_set_var_typed_value(request->requestvb, (u_char)type,
                                       (u_char *) &utmp, sizeof(utmp));
		  RETVAL = 1;
		  break;
	      }
	      else if (SvPOKp(value)) {
	          /* Might be OK - got a string, so try to convert it, allowing base 10, octal, and hex forms */
	          stringptr = SvPV(value, stringlen);
		  utmp = strtoul( stringptr, NULL, 0 );
		  if (errno == EINVAL) {
		  	snmp_log(LOG_ERR, "Could not convert string to number in setValue: '%s'", stringptr);
			RETVAL = 0;
			break;
		  }

                  snmp_set_var_typed_value(request->requestvb, (u_char)type,
                                       (u_char *) &utmp, sizeof(utmp));
		  RETVAL = 1;
		  break;
	      }
	      else {
		snmp_log(LOG_ERR, "Non-unsigned-integer value passed to setValue with ASN_UNSIGNED/ASN_COUNTER/ASN_TIMETICKS: type was %lu\n",
			(unsigned long)SvTYPE(value));
		RETVAL = 0;
		break;
	      }

          case ASN_COUNTER64:
	      /* We want an integer here */
	      if ((SvTYPE(value) == SVt_IV) || (SvTYPE(value) == SVt_PVMG)) {
		  /* Good - got a real one (or a blessed scalar which we have to hope will turn out OK) */
		  ulltmp = SvIV(value);
		  RETVAL = 1;
	      }
	      else if (SvPOKp(value)) {
	          /* Might be OK - got a string, so try to convert it, allowing base 10, octal, and hex forms */
	          stringptr = SvPV(value, stringlen);
	          errno = 0;
		  ulltmp = strtoull( stringptr, NULL, 0 );
		  if (errno != 0) {
		      snmp_log(LOG_ERR, "Could not convert string to number in setValue: '%s'", stringptr);
		      RETVAL = 0;
		  } else

		      RETVAL = 1;
	      }
	      else {
		snmp_log(LOG_ERR, "Non-unsigned-integer value passed to setValue with ASN_COUNTER64: type was %lu\n",
			(unsigned long)SvTYPE(value));
		RETVAL = 0;
	      }
	      if (RETVAL) {
		  c64.high = (uint32_t)(ulltmp >> 32);
		  c64.low  = (uint32_t)(ulltmp >> 0);
		  snmp_set_var_typed_value(request->requestvb, (u_char)type,
				     (u_char *) &c64, sizeof(struct counter64));
	      }
	      break;

          case ASN_OCTET_STR:
          case ASN_BIT_STR:
          case ASN_OPAQUE:
	      /* Check that we have been passed something with a string value (or a blessed scalar) */
	      if (!SvPOKp(value) && (SvTYPE(value) != SVt_PVMG)) {
		snmp_log(LOG_ERR, "Non-string value passed to setValue with ASN_OCTET_STR/ASN_BIT_STR: type was %lu\n",
			(unsigned long)SvTYPE(value));
		RETVAL = 0;
		break;
	      }

	      /* Find length of string (strlen will *not* do, as these are binary strings) */
	      stringptr = SvPV(value, stringlen);

              snmp_set_var_typed_value(request->requestvb, (u_char)type,
                                       (u_char *) stringptr,
                                       stringlen);
              RETVAL = 1;
              break;

          case ASN_IPADDRESS:
	      /* IP addresses are passed as *binary* strings.
	       * In the case of IPv4 addresses, these are 4 bytes long.
	       * NOTE: the use of binary strings rather than dotted-quad or FQDNs was
	       * introduced here by Andrew Findlay's patch of March 17th 2003,
	       * and is effectively a change to the previous implied API which assumed
	       * the value was a (valid) hostname.
	       * Responsibility for decoding and resolving has been passed up to the Perl script.
	       */

	      /* Check that we have been passed something with a string value (or a blessed scalar) */
	      if (!SvPOKp(value) && (SvTYPE(value) != SVt_PVMG)) {
		snmp_log(LOG_ERR, "Non-string value passed to setValue with ASN_IPADDRESS: type was %lu\n",
			(unsigned long)SvTYPE(value));
		RETVAL = 0;
		break;
	      }

	      /* Find length of string (strlen will *not* do, as these are binary strings) */
	      stringptr = SvPV(value, stringlen);

	      # snmp_log(LOG_ERR, "IP address returned with length %d: %u.%u.%u.%u\n", stringlen, stringptr[0],
	      #     stringptr[1], stringptr[2], stringptr[3] );

	      # Sanity check on address length
	      if ((stringlen != 4) && (stringlen != 16)) {
	      		snmp_log(LOG_ERR, "IP address of %" NETSNMP_PRIz "d bytes passed to setValue with ASN_IPADDRESS\n", stringlen);
			RETVAL = 0;
			break;
	      }

              snmp_set_var_typed_value(request->requestvb, (u_char)type,
                                   stringptr, stringlen);
              RETVAL = 1;
              break;

          case ASN_OBJECT_ID:
	      /* Check that we have been passed something with a string value (or a blessed scalar) */
	      if (!SvPOKp(value) && (SvTYPE(value) != SVt_PVMG)) {
		snmp_log(LOG_ERR, "Non-string value passed to setValue with ASN_OBJECT_ID: type was %lu\n",
			(unsigned long)SvTYPE(value));
		RETVAL = 0;
		break;
	      }

	      /* Extract the string */
	      stringptr = SvPV(value, stringlen);

	      /* snmp_log(LOG_ERR, "setValue returning OID '%s'\n", stringptr); */

	      myoid_len = MAX_OID_LEN;
              if (!snmp_parse_oid(stringptr, myoid, &myoid_len)) {
                  snmp_log(LOG_ERR, "couldn't parse %s in setValue\n", stringptr);
		  RETVAL = 0;
		  break;
              } else {
		  /* snmp_log(LOG_ERR, "setValue returning OID length %d\n", myoid_len); */

                  request = (netsnmp_request_info *) SvIV(SvRV(me));
                  snmp_set_var_typed_value(request->requestvb, (u_char)type,
                                           (u_char *) myoid, (myoid_len * sizeof(myoid[0])) );
              }

              RETVAL = 1;
              break;
              
            default:
                snmp_log(LOG_ERR, "unknown var value type: %d\n",
                        type);
                RETVAL = 0;
                break;
        }

    OUTPUT:
        RETVAL
        
void
nari_setOID(me, value)
        SV *me;
        char *value;
    PREINIT:
	oid myoid[MAX_OID_LEN];
	size_t myoid_len = MAX_OID_LEN;
        netsnmp_request_info *request;
    CODE:
	myoid_len = MAX_OID_LEN;
	if (!snmp_parse_oid(value, myoid, &myoid_len)) {
            snmp_log(LOG_ERR, "couldn't parse %s in setOID\n", value);
        } else {
            request = (netsnmp_request_info *) SvIV(SvRV(me));
            snmp_set_var_objid(request->requestvb, myoid, myoid_len);
        }

void
nari_setError(me, rinfo, ecode)
        SV *me;
        SV *rinfo;
        int ecode;
    PREINIT:
        netsnmp_request_info *request;
        netsnmp_agent_request_info *reqinfo;
    CODE:
        request = (netsnmp_request_info *) SvIV(SvRV(me));
        reqinfo = (netsnmp_agent_request_info *) SvIV(SvRV(rinfo));
        netsnmp_set_request_error(reqinfo, request, ecode);

SV *
nari_next(me)
        SV *me;
    PREINIT:
        netsnmp_request_info *request;
        SV *arg, *rarg;
    CODE:
        {
            request = (netsnmp_request_info *) SvIV(SvRV(me));
            if (request && request->next) {
                request = request->next;
                rarg = newSViv(0);
                arg = newSVrv(rarg, "NetSNMP::agent::netsnmp_request_infoPtr");
                sv_setiv(arg, (IV) request);
                RETVAL = rarg;				
            } else {
                RETVAL = &sv_undef;
            }
        }
    OUTPUT:
        RETVAL

MODULE = NetSNMP::agent  PACKAGE = NetSNMP::agent::netsnmp_agent_request_info PREFIX = narqi_


SV *
narqi_getSourceIp(me)
        SV *me;
    PREINIT:
        netsnmp_agent_request_info *reqinfo;
	netsnmp_udp_addr_pair *addr_pair;
	struct sockaddr_in *from;
        SV *rarg;

    CODE:
        reqinfo = (netsnmp_agent_request_info *) SvIV(SvRV(me));

        /* XXX: transport-specific: UDP/IPv4 only! */
	addr_pair = (netsnmp_udp_addr_pair *) (reqinfo->asp->pdu->transport_data);
	from = (struct sockaddr_in *) &(addr_pair->remote_addr);
        rarg = newSVpv((const char *)(&from->sin_addr.s_addr), sizeof(from->sin_addr.s_addr));
        RETVAL = rarg;
    OUTPUT:
        RETVAL


SV *
narqi_getDestIp(me)
        SV *me;
    PREINIT:
        netsnmp_agent_request_info *reqinfo;
	netsnmp_udp_addr_pair *addr_pair;
	struct in_addr *to;
        SV *rarg;

    CODE:
        reqinfo = (netsnmp_agent_request_info *) SvIV(SvRV(me));

        /* XXX: transport-specific: UDP/IPv4 only! */
	addr_pair = (netsnmp_udp_addr_pair *) (reqinfo->asp->pdu->transport_data);
	to = (struct in_addr *) &(addr_pair->local_addr);
        rarg = newSVpv((const char *)(&to->s_addr), sizeof(to->s_addr));
        RETVAL = rarg;
    OUTPUT:
        RETVAL

int
narqi_getMode(me)
        SV *me;
    PREINIT:
        netsnmp_agent_request_info *reqinfo;
    CODE:
        reqinfo = (netsnmp_agent_request_info *) SvIV(SvRV(me));
        RETVAL = reqinfo->mode;
    OUTPUT:
        RETVAL

void
narqi_setMode(me, newvalue)
        SV *me;
        int newvalue;
    PREINIT:
        netsnmp_agent_request_info *reqinfo;
    CODE:
        reqinfo = (netsnmp_agent_request_info *) SvIV(SvRV(me));
        reqinfo->mode = newvalue;
        

MODULE = NetSNMP::agent		PACKAGE = NetSNMP::agent