SNMP.xs
上传用户:wxp200602
上传日期:2007-10-30
资源大小:4028k
文件大小:171k
- /* -*- C -*-
- SNMP.xs -- Perl 5 interface to the UCD SNMP toolkit
- written by G. S. Marzot (gmarzot@nortelnetworks.com)
- Copyright (c) 1995-1999 G. S. Marzot. All rights reserved.
- This program is free software; you can redistribute it and/or
- modify it under the same terms as Perl itself.
- */
- #define WIN32SCK_IS_STDSCK
- #include "EXTERN.h"
- #include "perl.h"
- #include "XSUB.h"
- #include <net-snmp/net-snmp-config.h>
- #include <net-snmp/net-snmp-includes.h>
- #include <sys/types.h>
- #include <arpa/inet.h>
- #include <errno.h>
- #ifndef MSVC_PERL
- #include <signal.h>
- #endif
- #include <stdio.h>
- #include <ctype.h>
- #ifdef I_SYS_TIME
- #include <sys/time.h>
- #endif
- #include <netdb.h>
- #include <stdlib.h>
- #ifndef MSVC_PERL
- #include <unistd.h>
- #endif
- #ifdef HAVE_REGEX_H
- #include <regex.h>
- #endif
- #ifndef __P
- #define __P(x) x
- #endif
- #ifndef na
- #define na PL_na
- #endif
- #ifndef sv_undef
- #define sv_undef PL_sv_undef
- #endif
- #ifndef stack_base
- #define stack_base PL_stack_base
- #endif
- #ifndef G_VOID
- #define G_VOID G_DISCARD
- #endif
- #ifdef WIN32
- #define SOCK_STARTUP winsock_startup()
- #define SOCK_CLEANUP winsock_cleanup()
- #define strcasecmp _stricmp
- #define strncasecmp _strnicmp
- #else
- #define SOCK_STARTUP
- #define SOCK_CLEANUP
- #endif
- #include "perlsnmp.h"
- #define SUCCESS 1
- #define FAILURE 0
- #define ZERO_BUT_TRUE "0 but true"
- #define VARBIND_TAG_F 0
- #define VARBIND_IID_F 1
- #define VARBIND_VAL_F 2
- #define VARBIND_TYPE_F 3
- #define TYPE_UNKNOWN 0
- #define MAX_TYPE_NAME_LEN 32
- #define STR_BUF_SIZE (MAX_TYPE_NAME_LEN * MAX_OID_LEN)
- #define ENG_ID_BUF_SIZE 32
- #define SYS_UPTIME_OID_LEN 9
- #define SNMP_TRAP_OID_LEN 11
- #define NO_RETRY_NOSUCH 0
- static oid sysUpTime[SYS_UPTIME_OID_LEN] = {1, 3, 6, 1, 2, 1, 1, 3, 0};
- static oid snmpTrapOID[SNMP_TRAP_OID_LEN] = {1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0};
- /* Internal flag to determine snmp_main_loop() should return after callback */
- static int mainloop_finish = 0;
- /* these should be part of transform_oids.h ? */
- #define USM_AUTH_PROTO_MD5_LEN 10
- #define USM_AUTH_PROTO_SHA_LEN 10
- #define USM_PRIV_PROTO_DES_LEN 10
- /* why does ucd-snmp redefine sockaddr_in ??? */
- #define SIN_ADDR(snmp_addr) (((struct sockaddr_in *) &(snmp_addr))->sin_addr)
- typedef netsnmp_session SnmpSession;
- typedef struct tree SnmpMibNode;
- typedef struct snmp_xs_cb_data {
- SV* perl_cb;
- SV* sess_ref;
- } snmp_xs_cb_data;
- static void __recalc_timeout _((struct timeval*,struct timeval*,
- struct timeval*,struct timeval*, int* ));
- static in_addr_t __parse_address _((char*));
- static int __is_numeric_oid _((char*));
- static int __is_leaf _((struct tree*));
- static int __translate_appl_type _((char*));
- static int __translate_asn_type _((int));
- static int __snprint_value _((char *, size_t,
- netsnmp_variable_list*, struct tree *,
- int, int));
- static int __sprint_num_objid _((char *, oid *, int));
- static int __scan_num_objid _((char *, oid *, int *));
- static int __get_type_str _((int, char *));
- static int __get_label_iid _((char *, char **, char **, int));
- static int __oid_cmp _((oid *, int, oid *, int));
- static int __tp_sprint_num_objid _((char*,SnmpMibNode *));
- static SnmpMibNode * __get_next_mib_node _((SnmpMibNode *));
- static struct tree * __oid2tp _((oid*, int, struct tree *, int*));
- static struct tree * __tag2oid _((char *, char *, oid *, int *, int *, int));
- static int __concat_oid_str _((oid *, int *, char *));
- static int __add_var_val_str _((netsnmp_pdu *, oid *, int, char *,
- int, int));
- static int __send_sync_pdu _((netsnmp_session *, netsnmp_pdu *,
- netsnmp_pdu **, int , SV *, SV *, SV *));
- static int __snmp_xs_cb __P((int, netsnmp_session *, int,
- netsnmp_pdu *, void *));
- static int __callback_wrapper __P((int, netsnmp_session *, int,
- netsnmp_pdu *, void *));
- static SV* __push_cb_args2 _((SV * sv, SV * esv, SV * tsv));
- #define __push_cb_args(a,b) __push_cb_args2(a,b,NULL)
- static int __call_callback _((SV * sv, int flags));
- static char* __av_elem_pv _((AV * av, I32 key, char *dflt));
- static u_int compute_match _((const char *, const char *));
- #define USE_NUMERIC_OIDS 0x08
- #define NON_LEAF_NAME 0x04
- #define USE_LONG_NAMES 0x02
- #define FAIL_ON_NULL_IID 0x01
- #define NO_FLAGS 0x00
- /* Structures used by snmp_bulkwalk method to track requested OID's/subtrees. */
- typedef struct bulktbl {
- oid req_oid[MAX_OID_LEN]; /* The OID originally requested. */
- oid last_oid[MAX_OID_LEN]; /* Last-seen OID under this branch. */
- AV *vars; /* Array of Varbinds for this OID. */
- int req_len; /* Length of requested OID. */
- int last_len; /* Length of last-seen OID. */
- char norepeat; /* Is this a non-repeater OID? */
- char complete; /* Non-zero if this tree complete. */
- char ignore; /* Ignore this OID, not requested. */
- } bulktbl;
- /* Context for bulkwalk() sessions. Used to store state across callbacks. */
- typedef struct walk_context {
- SV *sess_ref; /* Reference to Perl SNMP session object. */
- SV *perl_cb; /* Pointer to Perl callback func or array. */
- bulktbl *req_oids; /* Pointer to bulktbl[] for requested OIDs. */
- bulktbl *repbase; /* Pointer to first repeater in req_oids[]. */
- bulktbl *reqbase; /* Pointer to start of requests req_oids[]. */
- int nreq_oids; /* Number of valid bulktbls in req_oids[]. */
- int req_remain; /* Number of outstanding requests remaining */
- int non_reps; /* Number of nonrepeater vars in req_oids[] */
- int repeaters; /* Number of repeater vars in req_oids[]. */
- int max_reps; /* Maximum repetitions of variable per PDU. */
- int exp_reqid; /* Expect a response to this request only. */
- int getlabel_f; /* Flag long/numeric names for get_label(). */
- int sprintval_f; /* Flag enum/sprint values for sprintval(). */
- int pkts_exch; /* Number of packet exchanges with agent. */
- int oid_total; /* Total number of OIDs received this walk. */
- int oid_saved; /* Total number of OIDs saved as results. */
- } walk_context;
- /* Prototypes for bulkwalk support functions. */
- static netsnmp_pdu *_bulkwalk_send_pdu _((walk_context *context));
- static int _bulkwalk_done _((walk_context *context));
- static int _bulkwalk_recv_pdu _((walk_context *context, netsnmp_pdu *pdu));
- static int _bulkwalk_finish _((walk_context *context, int okay));
- static int _bulkwalk_async_cb _((int op, SnmpSession *ss, int reqid,
- netsnmp_pdu *pdu, void *context_ptr));
- /* Structure to hold valid context sessions. */
- struct valid_contexts {
- walk_context **valid; /* Array of valid walk_context pointers. */
- int sz_valid; /* Maximum size of valid contexts array. */
- int num_valid; /* Count of valid contexts in the array. */
- };
- static struct valid_contexts *_valid_contexts = NULL;
- static int _context_add _((walk_context *context));
- static int _context_del _((walk_context *context));
- static int _context_okay _((walk_context *context));
- /* Wrapper around fprintf(stderr, ...) for clean and easy debug output. */
- #ifdef DEBUGGING
- static int _debug_level = 0;
- #define DBOUT PerlIO_stderr(),
- #define DBPRT(severity, otherargs)
- do {
- if (_debug_level && severity <= _debug_level) {
- (void)PerlIO_printf otherargs;
- }
- } while (/*CONSTCOND*/0)
- char _debugx[1024]; /* Space to sprintf() into - used by sprint_objid(). */
- #else /* DEBUGGING */
- #define DBOUT
- #define DBPRT(severity, otherargs) /* Ignore */
- #endif /* DEBUGGING */
- void
- __libraries_init(char *appname)
- {
- static int have_inited = 0;
- if (have_inited)
- return;
- have_inited = 1;
- snmp_set_quick_print(1);
- snmp_enable_stderrlog();
- init_snmp(appname);
-
- netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_BREAKDOWN_OIDS, 1);
- netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_SUFFIX_ONLY, 1);
- netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT,
- NETSNMP_OID_OUTPUT_SUFFIX);
- SOCK_STARTUP;
-
- }
- static void
- __recalc_timeout (tvp, ctvp, ltvp, itvp, block)
- struct timeval* tvp;
- struct timeval* ctvp;
- struct timeval* ltvp;
- struct timeval* itvp;
- int *block;
- {
- struct timeval now;
- if (!timerisset(itvp)) return; /* interval zero means loop forever */
- *block = 0;
- gettimeofday(&now,(struct timezone *)0);
- if (ctvp->tv_sec < 0) { /* first time or callback just fired */
- timersub(&now,ltvp,ctvp);
- timersub(ctvp,itvp,ctvp);
- timersub(itvp,ctvp,ctvp);
- timeradd(ltvp,itvp,ltvp);
- } else {
- timersub(&now,ltvp,ctvp);
- timersub(itvp,ctvp,ctvp);
- }
- /* flag is set for callback but still hasnt fired so set to something
- * small and we will service packets first if there are any ready
- * (also guard against negative timeout - should never happen?)
- */
- if (!timerisset(ctvp) || ctvp->tv_sec < 0 || ctvp->tv_usec < 0) {
- ctvp->tv_sec = 0;
- ctvp->tv_usec = 10;
- }
- /* if snmp timeout > callback timeout or no more requests to process */
- if (timercmp(tvp, ctvp, >) || !timerisset(tvp)) {
- *tvp = *ctvp; /* use the smaller non-zero timeout */
- timerclear(ctvp); /* used as a flag to let callback fire on timeout */
- }
- }
- static in_addr_t
- __parse_address(address)
- char *address;
- {
- in_addr_t addr;
- struct sockaddr_in saddr;
- struct hostent *hp;
- if ((addr = inet_addr(address)) != -1)
- return addr;
- hp = gethostbyname(address);
- if (hp == NULL){
- return (-1); /* error value */
- } else {
- memcpy(&saddr.sin_addr, hp->h_addr, hp->h_length);
- return saddr.sin_addr.s_addr;
- }
- }
- static int
- __is_numeric_oid (oidstr)
- char* oidstr;
- {
- if (!oidstr) return 0;
- for (; *oidstr; oidstr++) {
- if (isalpha((int)*oidstr)) return 0;
- }
- return(1);
- }
- static int
- __is_leaf (tp)
- struct tree* tp;
- {
- char buf[MAX_TYPE_NAME_LEN];
- return (tp && __get_type_str(tp->type,buf));
- }
- static SnmpMibNode*
- __get_next_mib_node (tp)
- SnmpMibNode* tp;
- {
- /* printf("tp = %lX, parent = %lX, peer = %lX, child = %lXn",
- tp, tp->parent, tp->next_peer, tp->child_list); */
- if (tp->child_list) return(tp->child_list);
- if (tp->next_peer) return(tp->next_peer);
- if (!tp->parent) return(NULL);
- for (tp = tp->parent; !tp->next_peer; tp = tp->parent) {
- if (!tp->parent) return(NULL);
- }
- return(tp->next_peer);
- }
- static int
- __translate_appl_type(typestr)
- char* typestr;
- {
- if (typestr == NULL || *typestr == ' ') return TYPE_UNKNOWN;
- if (!strncasecmp(typestr,"INTEGER32",8))
- return(TYPE_INTEGER32);
- if (!strncasecmp(typestr,"INTEGER",3))
- return(TYPE_INTEGER);
- if (!strncasecmp(typestr,"UNSIGNED32",3))
- return(TYPE_UNSIGNED32);
- if (!strcasecmp(typestr,"COUNTER")) /* check all in case counter64 */
- return(TYPE_COUNTER);
- if (!strncasecmp(typestr,"GAUGE",3))
- return(TYPE_GAUGE);
- if (!strncasecmp(typestr,"IPADDR",3))
- return(TYPE_IPADDR);
- if (!strncasecmp(typestr,"OCTETSTR",3))
- return(TYPE_OCTETSTR);
- if (!strncasecmp(typestr,"TICKS",3))
- return(TYPE_TIMETICKS);
- if (!strncasecmp(typestr,"OPAQUE",3))
- return(TYPE_OPAQUE);
- if (!strncasecmp(typestr,"OBJECTID",3))
- return(TYPE_OBJID);
- if (!strncasecmp(typestr,"NETADDR",3))
- return(TYPE_NETADDR);
- if (!strncasecmp(typestr,"COUNTER64",3))
- return(TYPE_COUNTER64);
- if (!strncasecmp(typestr,"NULL",3))
- return(TYPE_NULL);
- if (!strncasecmp(typestr,"BITS",3))
- return(TYPE_BITSTRING);
- if (!strncasecmp(typestr,"ENDOFMIBVIEW",3))
- return(SNMP_ENDOFMIBVIEW);
- if (!strncasecmp(typestr,"NOSUCHOBJECT",7))
- return(SNMP_NOSUCHOBJECT);
- if (!strncasecmp(typestr,"NOSUCHINSTANCE",7))
- return(SNMP_NOSUCHINSTANCE);
- if (!strncasecmp(typestr,"UINTEGER",3))
- return(TYPE_UINTEGER); /* historic - should not show up */
- /* but it does? */
- if (!strncasecmp(typestr, "NOTIF", 3))
- return(TYPE_NOTIFTYPE);
- if (!strncasecmp(typestr, "TRAP", 4))
- return(TYPE_TRAPTYPE);
- return(TYPE_UNKNOWN);
- }
- static int
- __translate_asn_type(type)
- int type;
- {
- switch (type) {
- case ASN_INTEGER:
- return(TYPE_INTEGER);
- break;
- case ASN_OCTET_STR:
- return(TYPE_OCTETSTR);
- break;
- case ASN_OPAQUE:
- return(TYPE_OPAQUE);
- break;
- case ASN_OBJECT_ID:
- return(TYPE_OBJID);
- break;
- case ASN_TIMETICKS:
- return(TYPE_TIMETICKS);
- break;
- case ASN_GAUGE:
- return(TYPE_GAUGE);
- break;
- case ASN_COUNTER:
- return(TYPE_COUNTER);
- break;
- case ASN_IPADDRESS:
- return(TYPE_IPADDR);
- break;
- case ASN_BIT_STR:
- return(TYPE_BITSTRING);
- break;
- case ASN_NULL:
- return(TYPE_NULL);
- break;
- /* no translation for these exception type values */
- case SNMP_ENDOFMIBVIEW:
- case SNMP_NOSUCHOBJECT:
- case SNMP_NOSUCHINSTANCE:
- return(type);
- break;
- case ASN_UINTEGER:
- return(TYPE_UINTEGER);
- break;
- case ASN_COUNTER64:
- return(TYPE_COUNTER64);
- break;
- default:
- warn("translate_asn_type: unhandled asn type (%d)n",type);
- return(TYPE_OTHER);
- break;
- }
- }
- #define USE_BASIC 0
- #define USE_ENUMS 1
- #define USE_SPRINT_VALUE 2
- static int
- __snprint_value (buf, buf_len, var, tp, type, flag)
- char * buf;
- size_t buf_len;
- netsnmp_variable_list * var;
- struct tree * tp;
- int type;
- int flag;
- {
- int len = 0;
- u_char* ip;
- struct enum_list *ep;
- buf[0] = ' ';
- if (flag == USE_SPRINT_VALUE) {
- snprint_value(buf, buf_len, var->name, var->name_length, var);
- len = strlen(buf);
- } else {
- switch (var->type) {
- case ASN_INTEGER:
- if (flag == USE_ENUMS) {
- for(ep = tp->enums; ep; ep = ep->next) {
- if (ep->value == *var->val.integer) {
- strcpy(buf, ep->label);
- len = strlen(buf);
- break;
- }
- }
- }
- if (!len) {
- sprintf(buf,"%ld", *var->val.integer);
- len = strlen(buf);
- }
- break;
- case ASN_GAUGE:
- case ASN_COUNTER:
- case ASN_TIMETICKS:
- case ASN_UINTEGER:
- sprintf(buf,"%lu", (unsigned long) *var->val.integer);
- len = strlen(buf);
- break;
- case ASN_OCTET_STR:
- case ASN_OPAQUE:
- memcpy(buf, (char*)var->val.string, var->val_len);
- len = var->val_len;
- break;
- case ASN_IPADDRESS:
- ip = (u_char*)var->val.string;
- sprintf(buf, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
- len = strlen(buf);
- break;
- case ASN_NULL:
- break;
- case ASN_OBJECT_ID:
- __sprint_num_objid(buf, (oid *)(var->val.objid),
- var->val_len/sizeof(oid));
- len = strlen(buf);
- break;
- case SNMP_ENDOFMIBVIEW:
- sprintf(buf,"%s", "ENDOFMIBVIEW");
- break;
- case SNMP_NOSUCHOBJECT:
- sprintf(buf,"%s", "NOSUCHOBJECT");
- break;
- case SNMP_NOSUCHINSTANCE:
- sprintf(buf,"%s", "NOSUCHINSTANCE");
- break;
- case ASN_COUNTER64:
- printU64(buf,(struct counter64 *)var->val.counter64);
- len = strlen(buf);
- break;
- case ASN_BIT_STR:
- snprint_bitstring(buf, sizeof(buf), var, NULL, NULL, NULL);
- len = strlen(buf);
- break;
- case ASN_NSAP:
- default:
- warn("snprint_value: asn type not handled %dn",var->type);
- }
- }
- return(len);
- }
- static int
- __sprint_num_objid (buf, objid, len)
- char *buf;
- oid *objid;
- int len;
- {
- int i;
- buf[0] = ' ';
- for (i=0; i < len; i++) {
- sprintf(buf,".%lu",*objid++);
- buf += strlen(buf);
- }
- return SUCCESS;
- }
- static int
- __tp_sprint_num_objid (buf, tp)
- char *buf;
- SnmpMibNode *tp;
- {
- oid newname[MAX_OID_LEN], *op;
- /* code taken from get_node in snmp_client.c */
- for (op = newname + MAX_OID_LEN - 1; op >= newname; op--) {
- *op = tp->subid;
- tp = tp->parent;
- if (tp == NULL) break;
- }
- return __sprint_num_objid(buf, op, newname + MAX_OID_LEN - op);
- }
- static int
- __scan_num_objid (buf, objid, len)
- char *buf;
- oid *objid;
- int *len;
- {
- char *cp;
- *len = 0;
- if (*buf == '.') buf++;
- cp = buf;
- while (*buf) {
- if (*buf++ == '.') {
- sscanf(cp, "%lu", objid++);
- /* *objid++ = atoi(cp); */
- (*len)++;
- cp = buf;
- } else {
- if (isalpha((int)*buf)) {
- return FAILURE;
- }
- }
- }
- sscanf(cp, "%lu", objid++);
- /* *objid++ = atoi(cp); */
- (*len)++;
- return SUCCESS;
- }
- static int
- __get_type_str (type, str)
- int type;
- char * str;
- {
- switch (type) {
- case TYPE_OBJID:
- strcpy(str, "OBJECTID");
- break;
- case TYPE_OCTETSTR:
- strcpy(str, "OCTETSTR");
- break;
- case TYPE_INTEGER:
- strcpy(str, "INTEGER");
- break;
- case TYPE_INTEGER32:
- strcpy(str, "INTEGER32");
- break;
- case TYPE_UNSIGNED32:
- strcpy(str, "UNSIGNED32");
- break;
- case TYPE_NETADDR:
- strcpy(str, "NETADDR");
- break;
- case TYPE_IPADDR:
- strcpy(str, "IPADDR");
- break;
- case TYPE_COUNTER:
- strcpy(str, "COUNTER");
- break;
- case TYPE_GAUGE:
- strcpy(str, "GAUGE");
- break;
- case TYPE_TIMETICKS:
- strcpy(str, "TICKS");
- break;
- case TYPE_OPAQUE:
- strcpy(str, "OPAQUE");
- break;
- case TYPE_COUNTER64:
- strcpy(str, "COUNTER64");
- break;
- case TYPE_NULL:
- strcpy(str, "NULL");
- break;
- case SNMP_ENDOFMIBVIEW:
- strcpy(str, "ENDOFMIBVIEW");
- break;
- case SNMP_NOSUCHOBJECT:
- strcpy(str, "NOSUCHOBJECT");
- break;
- case SNMP_NOSUCHINSTANCE:
- strcpy(str, "NOSUCHINSTANCE");
- break;
- case TYPE_UINTEGER:
- strcpy(str, "UINTEGER"); /* historic - should not show up */
- /* but it does? */
- break;
- case TYPE_NOTIFTYPE:
- strcpy(str, "NOTIF");
- break;
- case TYPE_BITSTRING:
- strcpy(str, "BITS");
- break;
- case TYPE_TRAPTYPE:
- strcpy(str, "TRAP");
- break;
- case TYPE_OTHER: /* not sure if this is a valid leaf type?? */
- case TYPE_NSAPADDRESS:
- default: /* unsupported types for now */
- strcpy(str, "");
- return(FAILURE);
- }
- return SUCCESS;
- }
- /* does a destructive disection of <label1>...<labeln>.<iid> returning
- <labeln> and <iid> in seperate strings (note: will destructively
- alter input string, 'name') */
- static int
- __get_label_iid (name, last_label, iid, flag)
- char * name;
- char ** last_label;
- char ** iid;
- int flag;
- {
- char *lcp;
- char *icp;
- int len = strlen(name);
- int found_label = 0;
- *last_label = *iid = NULL;
- if (len == 0) return(FAILURE);
- /* Handle case where numeric oid's have been requested. The input 'name'
- ** in this case should be a numeric OID -- return failure if not.
- */
- if ((flag & USE_NUMERIC_OIDS)) {
- if (!__is_numeric_oid(name))
- return(FAILURE);
- /* Walk backward through the string, looking for first two '.' chars */
- lcp = &(name[len]);
- icp = NULL;
- while (lcp > name) {
- if (*lcp == '.') {
- /* If this is the first occurence of '.', note it in icp.
- ** Otherwise, this must be the second occurrence, so break
- ** out of the loop.
- */
- if (icp == NULL)
- icp = lcp;
- else
- break;
- }
- lcp --;
- }
- /* Make sure we found at least a label and index. */
- if (!icp)
- return(FAILURE);
- /* Push forward past leading '.' chars and separate the strings. */
- lcp ++;
- *icp ++ = ' ';
- *last_label = (flag & USE_LONG_NAMES) ? name : lcp;
- *iid = icp;
- return(SUCCESS);
- }
- lcp = icp = &(name[len]);
- while (lcp > name) {
- if (*lcp == '.') {
- if (found_label) {
- lcp++;
- break;
- } else {
- icp = lcp;
- }
- }
- if (!found_label && isalpha((int)*lcp)) found_label = 1;
- lcp--;
- }
- if (!found_label || (!isdigit((int)*(icp+1)) && (flag & FAIL_ON_NULL_IID)))
- return(FAILURE);
- if (flag & NON_LEAF_NAME) { /* dont know where to start instance id */
- /* put the whole thing in label */
- icp = &(name[len]);
- flag |= USE_LONG_NAMES;
- /* special hack in case no mib loaded - object identifiers will
- * start with .iso.<num>.<num>...., in which case it is preferable
- * to make the label entirely numeric (i.e., convert "iso" => "1")
- */
- if (*lcp == '.' && lcp == name) {
- if (!strncmp(".ccitt.",lcp,7)) {
- name += 2;
- *name = '.';
- *(name+1) = '0';
- } else if (!strncmp(".iso.",lcp,5)) {
- name += 2;
- *name = '.';
- *(name+1) = '1';
- } else if (!strncmp(".joint-iso-ccitt.",lcp,17)) {
- name += 2;
- *name = '.';
- *(name+1) = '2';
- }
- }
- } else if (*icp) {
- *(icp++) = ' ';
- }
- *last_label = (flag & USE_LONG_NAMES ? name : lcp);
- *iid = icp;
- return(SUCCESS);
- }
- static int
- __oid_cmp(oida_arr, oida_arr_len, oidb_arr, oidb_arr_len)
- oid *oida_arr;
- int oida_arr_len;
- oid *oidb_arr;
- int oidb_arr_len;
- {
- for (;oida_arr_len && oidb_arr_len;
- oida_arr++, oida_arr_len--, oidb_arr++, oidb_arr_len--) {
- if (*oida_arr == *oidb_arr) continue;
- return(*oida_arr > *oidb_arr ? 1 : -1);
- }
- if (oida_arr_len == oidb_arr_len) return(0);
- return(oida_arr_len > oidb_arr_len ? 1 : -1);
- }
- #define MAX_BAD 0xffffff
- static u_int
- compute_match(search_base, key)
- const char *search_base;
- const char *key;
- {
- #if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
- int rc;
- regex_t parsetree;
- regmatch_t pmatch;
- rc = regcomp(&parsetree, key, REG_ICASE | REG_EXTENDED);
- if (rc == 0)
- rc = regexec(&parsetree, search_base, 1, &pmatch, 0);
- regfree(&parsetree);
- if (rc == 0) {
- /*
- * found
- */
- return pmatch.rm_so;
- }
- #else /* use our own wildcard matcher */
- /*
- * first find the longest matching substring (ick)
- */
- char *first = NULL, *result = NULL, *entry;
- const char *position;
- char *newkey = strdup(key);
- char *st;
- entry = strtok_r(newkey, "*", &st);
- position = search_base;
- while (entry) {
- result = strcasestr(position, entry);
- if (result == NULL) {
- free(newkey);
- return MAX_BAD;
- }
- if (first == NULL)
- first = result;
- position = result + strlen(entry);
- entry = strtok_r(NULL, "*", &st);
- }
- free(newkey);
- if (result)
- return (first - search_base);
- #endif
- /*
- * not found
- */
- return MAX_BAD;
- }
- /* Convert a tag (string) to an OID array */
- /* Tag can be either a symbolic name, or an OID string */
- static struct tree *
- __tag2oid(tag, iid, oid_arr, oid_arr_len, type, best_guess)
- char * tag;
- char * iid;
- oid * oid_arr;
- int * oid_arr_len;
- int * type;
- int best_guess;
- {
- struct tree *tp = NULL;
- struct tree *rtp = NULL;
- oid newname[MAX_OID_LEN], *op;
- int newname_len = 0;
- const char *cp = NULL;
- char *module = NULL;
- char str_buf[STR_BUF_SIZE];
- str_buf[0] = ' ';
- if (type) *type = TYPE_UNKNOWN;
- if (oid_arr_len) *oid_arr_len = 0;
- if (!tag) goto done;
- /*********************************************************/
- /* best_guess = 0 - same as no switches (read_objid) */
- /* if multiple parts, or uses find_node */
- /* if a single leaf */
- /* best_guess = 1 - same as -Ib (get_wild_node) */
- /* best_guess = 2 - same as -IR (get_node) */
- /*********************************************************/
- /* numeric scalar (1,2) */
- /* single symbolic (1,2) */
- /* single regex (1) */
- /* partial full symbolic (2) */
- /* full symbolic (2) */
- /* module::single symbolic (2) */
- /* module::partial full symbolic (2) */
- if (best_guess == 1 || best_guess == 2) {
- if (!__scan_num_objid(tag, newname, &newname_len)) { /* make sure it's not a numeric tag */
- newname_len = MAX_OID_LEN;
- if (best_guess == 2) { /* Random search -IR */
- if (get_node(tag, newname, &newname_len)) {
- rtp = tp = get_tree(newname, newname_len, get_tree_head());
- }
- }
- else if (best_guess == 1) { /* Regex search -Ib */
- clear_tree_flags(get_tree_head());
- if (get_wild_node(tag, newname, &newname_len)) {
- rtp = tp = get_tree(newname, newname_len, get_tree_head());
- }
- }
- }
- else {
- rtp = tp = get_tree(newname, newname_len, get_tree_head());
- }
- if (type) *type = (tp ? tp->type : TYPE_UNKNOWN);
- if ((oid_arr == NULL) || (oid_arr_len == NULL)) return rtp;
- memcpy(oid_arr,(char*)newname,newname_len*sizeof(oid));
- *oid_arr_len = newname_len;
- }
-
- /* if best_guess is off and multi part tag or module::tag */
- /* numeric scalar */
- /* module::single symbolic */
- /* module::partial full symbolic */
- /* FULL symbolic OID */
- else if (strchr(tag,'.') || strchr(tag,':')) {
- if (!__scan_num_objid(tag, newname, &newname_len)) { /* make sure it's not a numeric tag */
- newname_len = MAX_OID_LEN;
- if (read_objid(tag, newname, &newname_len)) { /* long name */
- rtp = tp = get_tree(newname, newname_len, get_tree_head());
- }
- }
- else {
- rtp = tp = get_tree(newname, newname_len, get_tree_head());
- }
- if (type) *type = (tp ? tp->type : TYPE_UNKNOWN);
- if ((oid_arr == NULL) || (oid_arr_len == NULL)) return rtp;
- memcpy(oid_arr,(char*)newname,newname_len*sizeof(oid));
- *oid_arr_len = newname_len;
- }
-
- /* else best_guess is off and it is a single leaf */
- /* single symbolic */
- else {
- rtp = tp = find_node(tag, get_tree_head());
- if (tp) {
- if (type) *type = tp->type;
- if ((oid_arr == NULL) || (oid_arr_len == NULL)) return rtp;
- /* code taken from get_node in snmp_client.c */
- for(op = newname + MAX_OID_LEN - 1; op >= newname; op--){
- *op = tp->subid;
- tp = tp->parent;
- if (tp == NULL)
- break;
- }
- *oid_arr_len = newname + MAX_OID_LEN - op;
- memcpy(oid_arr, op, *oid_arr_len * sizeof(oid));
- } else {
- return(rtp); /* HACK: otherwise, concat_oid_str confuses things */
- }
- }
- done:
- if (iid && *iid) __concat_oid_str(oid_arr, oid_arr_len, iid);
- return(rtp);
- }
- /* searches down the mib tree for the given oid
- returns the last found tp and its index in lastind
- */
- static struct tree *
- __oid2tp (oidp, len, subtree, lastind)
- oid* oidp;
- int len;
- struct tree * subtree;
- int* lastind;
- {
- struct tree *return_tree = NULL;
- for (; subtree; subtree = subtree->next_peer) {
- if (*oidp == subtree->subid){
- goto found;
- }
- }
- *lastind=0;
- return NULL;
- found:
- if (len > 1){
- return_tree =
- __oid2tp(oidp + 1, len - 1, subtree->child_list, lastind);
- (*lastind)++;
- } else {
- *lastind=1;
- }
- if (return_tree)
- return return_tree;
- else
- return subtree;
- }
- /* function: __concat_oid_str
- *
- * This function converts a dotted-decimal string, soid_str, to an array
- * of oid types and concatenates them on doid_arr begining at the index
- * specified by doid_arr_len.
- *
- * returns : SUCCESS, FAILURE
- */
- static int
- __concat_oid_str(doid_arr, doid_arr_len, soid_str)
- oid *doid_arr;
- int *doid_arr_len;
- char * soid_str;
- {
- char soid_buf[STR_BUF_SIZE];
- char *cp;
- char *st;
- if (!soid_str || !*soid_str) return SUCCESS;/* successfully added nothing */
- if (*soid_str == '.') soid_str++;
- strcpy(soid_buf, soid_str);
- cp = strtok_r(soid_buf,".",&st);
- while (cp) {
- sscanf(cp, "%lu", &(doid_arr[(*doid_arr_len)++]));
- /* doid_arr[(*doid_arr_len)++] = atoi(cp); */
- cp = strtok_r(NULL,".",&st);
- }
- return(SUCCESS);
- }
- /*
- * add a varbind to PDU
- */
- static int
- __add_var_val_str(pdu, name, name_length, val, len, type)
- netsnmp_pdu *pdu;
- oid *name;
- int name_length;
- char * val;
- int len;
- int type;
- {
- netsnmp_variable_list *vars;
- oid oidbuf[MAX_OID_LEN];
- int ret = SUCCESS;
- if (pdu->variables == NULL){
- pdu->variables = vars =
- (netsnmp_variable_list *)calloc(1,sizeof(netsnmp_variable_list));
- } else {
- for(vars = pdu->variables;
- vars->next_variable;
- vars = vars->next_variable)
- /*EXIT*/;
- vars->next_variable =
- (netsnmp_variable_list *)calloc(1,sizeof(netsnmp_variable_list));
- vars = vars->next_variable;
- }
- vars->next_variable = NULL;
- vars->name = (oid *)malloc(name_length * sizeof(oid));
- memcpy((char *)vars->name, (char *)name, name_length * sizeof(oid));
- vars->name_length = name_length;
- switch (type) {
- case TYPE_INTEGER:
- case TYPE_INTEGER32:
- vars->type = ASN_INTEGER;
- vars->val.integer = (long *)malloc(sizeof(long));
- if (val)
- *(vars->val.integer) = strtol(val,NULL,0);
- else {
- ret = FAILURE;
- *(vars->val.integer) = 0;
- }
- vars->val_len = sizeof(long);
- break;
- case TYPE_GAUGE:
- case TYPE_UNSIGNED32:
- vars->type = ASN_GAUGE;
- goto UINT;
- case TYPE_COUNTER:
- vars->type = ASN_COUNTER;
- goto UINT;
- case TYPE_TIMETICKS:
- vars->type = ASN_TIMETICKS;
- goto UINT;
- case TYPE_UINTEGER:
- vars->type = ASN_UINTEGER;
- UINT:
- vars->val.integer = (long *)malloc(sizeof(long));
- if (val)
- sscanf(val,"%lu",vars->val.integer);
- else {
- ret = FAILURE;
- *(vars->val.integer) = 0;
- }
- vars->val_len = sizeof(long);
- break;
- case TYPE_OCTETSTR:
- vars->type = ASN_OCTET_STR;
- goto OCT;
- case TYPE_BITSTRING:
- vars->type = ASN_OCTET_STR;
- goto OCT;
- case TYPE_OPAQUE:
- vars->type = ASN_OCTET_STR;
- OCT:
- vars->val.string = (u_char *)malloc(len);
- vars->val_len = len;
- if (val && len)
- memcpy((char *)vars->val.string, val, len);
- else {
- ret = FAILURE;
- vars->val.string = strdup("");
- vars->val_len = 0;
- }
- break;
- case TYPE_IPADDR:
- vars->type = ASN_IPADDRESS;
- vars->val.integer = (long *)malloc(sizeof(long));
- if (val)
- *(vars->val.integer) = inet_addr(val);
- else {
- ret = FAILURE;
- *(vars->val.integer) = 0;
- }
- vars->val_len = sizeof(long);
- break;
- case TYPE_OBJID:
- vars->type = ASN_OBJECT_ID;
- vars->val_len = MAX_OID_LEN;
- /* if (read_objid(val, oidbuf, &(vars->val_len))) { */
- /* tp = __tag2oid(val,NULL,oidbuf,&(vars->val_len),NULL,0); */
- if (!val || !snmp_parse_oid(val, oidbuf, &vars->val_len)) {
- vars->val.objid = NULL;
- ret = FAILURE;
- } else {
- vars->val_len *= sizeof(oid);
- vars->val.objid = (oid *)malloc(vars->val_len);
- memcpy((char *)vars->val.objid, (char *)oidbuf, vars->val_len);
- }
- break;
- default:
- vars->type = ASN_NULL;
- vars->val_len = 0;
- vars->val.string = NULL;
- ret = FAILURE;
- }
- return ret;
- }
- /* takes ss and pdu as input and updates the 'response' argument */
- /* the input 'pdu' argument will be freed */
- static int
- __send_sync_pdu(ss, pdu, response, retry_nosuch,
- err_str_sv, err_num_sv, err_ind_sv)
- netsnmp_session *ss;
- netsnmp_pdu *pdu;
- netsnmp_pdu **response;
- int retry_nosuch;
- SV * err_str_sv;
- SV * err_num_sv;
- SV * err_ind_sv;
- {
- int status;
- long command = pdu->command;
- *response = NULL;
- retry:
- status = snmp_synch_response(ss, pdu, response);
- if ((*response == NULL) && (status == STAT_SUCCESS)) status = STAT_ERROR;
- switch (status) {
- case STAT_SUCCESS:
- switch ((*response)->errstat) {
- case SNMP_ERR_NOERROR:
- break;
- case SNMP_ERR_NOSUCHNAME:
- if (retry_nosuch && (pdu = snmp_fix_pdu(*response, command))) {
- if (*response) snmp_free_pdu(*response);
- goto retry;
- }
- /* Pv1, SNMPsec, Pv2p, v2c, v2u, v2*, and SNMPv3 PDUs */
- case SNMP_ERR_TOOBIG:
- case SNMP_ERR_BADVALUE:
- case SNMP_ERR_READONLY:
- case SNMP_ERR_GENERR:
- /* in SNMPv2p, SNMPv2c, SNMPv2u, SNMPv2*, and SNMPv3 PDUs */
- case SNMP_ERR_NOACCESS:
- case SNMP_ERR_WRONGTYPE:
- case SNMP_ERR_WRONGLENGTH:
- case SNMP_ERR_WRONGENCODING:
- case SNMP_ERR_WRONGVALUE:
- case SNMP_ERR_NOCREATION:
- case SNMP_ERR_INCONSISTENTVALUE:
- case SNMP_ERR_RESOURCEUNAVAILABLE:
- case SNMP_ERR_COMMITFAILED:
- case SNMP_ERR_UNDOFAILED:
- case SNMP_ERR_AUTHORIZATIONERROR:
- case SNMP_ERR_NOTWRITABLE:
- /* in SNMPv2c, SNMPv2u, SNMPv2*, and SNMPv3 PDUs */
- case SNMP_ERR_INCONSISTENTNAME:
- default:
- sv_catpv(err_str_sv,
- (char*)snmp_errstring((*response)->errstat));
- sv_setiv(err_num_sv, (*response)->errstat);
- sv_setiv(err_ind_sv, (*response)->errindex);
- status = (*response)->errstat;
- break;
- }
- break;
- case STAT_TIMEOUT:
- case STAT_ERROR:
- sv_catpv(err_str_sv, (char*)snmp_api_errstring(ss->s_snmp_errno));
- sv_setiv(err_num_sv, ss->s_snmp_errno);
- break;
- default:
- sv_catpv(err_str_sv, "send_sync_pdu: unknown status");
- sv_setiv(err_num_sv, ss->s_snmp_errno);
- break;
- }
- return(status);
- }
- static int
- __callback_wrapper (op, ss, reqid, pdu, cb_data)
- int op;
- netsnmp_session *ss;
- int reqid;
- netsnmp_pdu *pdu;
- void *cb_data;
- {
- /* we should probably just increment the reference counter... */
- /* sv_inc(cb_data); */
- return __snmp_xs_cb(op, ss, reqid, pdu, newSVsv(cb_data));
- }
- static int
- __snmp_xs_cb (op, ss, reqid, pdu, cb_data)
- int op;
- netsnmp_session *ss;
- int reqid;
- netsnmp_pdu *pdu;
- void *cb_data;
- {
- SV *varlist_ref;
- AV *varlist;
- SV *varbind_ref;
- AV *varbind;
- SV *traplist_ref = NULL;
- AV *traplist = NULL;
- netsnmp_variable_list *vars;
- struct tree *tp;
- int len;
- SV *tmp_sv;
- int type;
- char tmp_type_str[MAX_TYPE_NAME_LEN];
- u_char str_buf[STR_BUF_SIZE], *str_bufp = str_buf;
- size_t str_buf_len = sizeof(str_buf);
- size_t out_len = 0;
- int buf_over = 0;
- char *label;
- char *iid;
- char *cp;
- int getlabel_flag = NO_FLAGS;
- int sprintval_flag = USE_BASIC;
- netsnmp_pdu *reply_pdu;
- int old_numeric, old_printfull;
- netsnmp_transport *transport = NULL;
- SV* cb = ((struct snmp_xs_cb_data*)cb_data)->perl_cb;
- SV* sess_ref = ((struct snmp_xs_cb_data*)cb_data)->sess_ref;
- SV **err_str_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorStr", 8, 1);
- SV **err_num_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorNum", 8, 1);
- SV **err_ind_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorInd", 8, 1);
- dSP;
- ENTER;
- SAVETMPS;
- free(cb_data);
- sv_setpv(*err_str_svp, (char*)snmp_errstring(pdu->errstat));
- sv_setiv(*err_num_svp, pdu->errstat);
- sv_setiv(*err_ind_svp, pdu->errindex);
- varlist_ref = &sv_undef; /* Prevent unintialized use below. */
- switch (op) {
- case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE:
- traplist_ref = NULL;
- switch (pdu->command) {
- case SNMP_MSG_INFORM:
- /*
- * Ideally, we would use the return value from the callback to
- * decide what response, if any, we send, and what the error status
- * and error index should be.
- */
- reply_pdu = snmp_clone_pdu(pdu);
- if (reply_pdu) {
- reply_pdu->command = SNMP_MSG_RESPONSE;
- reply_pdu->reqid = pdu->reqid;
- reply_pdu->errstat = reply_pdu->errindex = 0;
- snmp_send(ss, reply_pdu);
- } else {
- warn("Couldn't clone PDU for inform response");
- }
- /* FALLTHRU */
- case SNMP_MSG_TRAP2:
- traplist = newAV();
- traplist_ref = newRV_noinc((SV*)traplist);
- #if 0
- /* of dubious utility... */
- av_push(traplist, newSViv(pdu->command));
- #endif
- av_push(traplist, newSViv(pdu->reqid));
- if ((transport = snmp_sess_transport(snmp_sess_pointer(ss))) != NULL) {
- cp = transport->f_fmtaddr(transport, pdu->transport_data,
- pdu->transport_data_length);
- av_push(traplist, newSVpv(cp, strlen(cp)));
- free(cp);
- } else {
- /* This shouldn't ever happen; every session has a transport. */
- av_push(traplist, newSVpv("", 0));
- }
- av_push(traplist, newSVpv((char*) pdu->community, pdu->community_len));
- /* FALLTHRU */
- case SNMP_MSG_RESPONSE:
- {
- varlist = newAV();
- varlist_ref = newRV_noinc((SV*)varlist);
- /*
- ** Set up for numeric OID's, if necessary. Save the old values
- ** so that they can be restored when we finish -- these are
- ** library-wide globals, and have to be set/restored for each
- ** session.
- */
- old_numeric = netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_NUMERIC_OIDS);
- old_printfull = netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_FULL_OID);
- if (SvIV(*hv_fetch((HV*)SvRV(sess_ref),"UseLongNames", 12, 1))) {
- getlabel_flag |= USE_LONG_NAMES;
- netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_FULL_OID, 1);
- }
- /* Setting UseNumeric forces UseLongNames on so check for UseNumeric
- after UseLongNames (above) to make sure the final outcome of
- NETSNMP_DS_LIB_OID_OUTPUT_FORMAT is NETSNMP_OID_OUTPUT_NUMERIC */
- if (SvIV(*hv_fetch((HV*)SvRV(sess_ref),"UseNumeric", 10, 1))) {
- getlabel_flag |= USE_NUMERIC_OIDS;
- netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_NUMERIC_OIDS, 1);
- }
- sv_bless(varlist_ref, gv_stashpv("SNMP::VarList",0));
- for(vars = (pdu?pdu->variables:NULL); vars; vars = vars->next_variable) {
- varbind = newAV();
- varbind_ref = newRV_noinc((SV*)varbind);
- sv_bless(varbind_ref, gv_stashpv("SNMP::Varbind",0));
- av_push(varlist, varbind_ref);
- *str_buf = '.';
- *(str_buf+1) = ' ';
- out_len = 0;
- tp = netsnmp_sprint_realloc_objid_tree(&str_bufp, &str_buf_len,
- &out_len, 0, &buf_over,
- vars->name,vars->name_length);
- str_buf[sizeof(str_buf)-1] = ' ';
- if (__is_leaf(tp)) {
- type = tp->type;
- } else {
- getlabel_flag |= NON_LEAF_NAME;
- type = __translate_asn_type(vars->type);
- }
- __get_label_iid(str_buf,&label,&iid,getlabel_flag);
- if (label) {
- av_store(varbind, VARBIND_TAG_F,
- newSVpv(label, strlen(label)));
- } else {
- av_store(varbind, VARBIND_TAG_F,
- newSVpv("", 0));
- }
- if (iid) {
- av_store(varbind, VARBIND_IID_F,
- newSVpv(iid, strlen(iid)));
- } else {
- av_store(varbind, VARBIND_IID_F,
- newSVpv("", 0));
- }
- __get_type_str(type, tmp_type_str);
- tmp_sv = newSVpv(tmp_type_str, strlen(tmp_type_str));
- av_store(varbind, VARBIND_TYPE_F, tmp_sv);
- len = __snprint_value(str_buf, sizeof(str_buf),
- vars, tp, type, sprintval_flag);
- tmp_sv = newSVpv((char*)str_buf, len);
- av_store(varbind, VARBIND_VAL_F, tmp_sv);
- } /* for */
- /* Reset the library's behavior for numeric/symbolic OID's. */
- netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_NUMERIC_OIDS, old_numeric );
- netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_FULL_OID, old_printfull);
- } /* case SNMP_MSG_RESPONSE */
- break;
- default:;
- } /* switch pdu->command */
- break;
- case NETSNMP_CALLBACK_OP_TIMED_OUT:
- varlist_ref = &sv_undef;
- break;
- default:;
- } /* switch op */
- sv_2mortal(cb);
- cb = __push_cb_args2(cb,
- (SvTRUE(varlist_ref) ? sv_2mortal(varlist_ref):varlist_ref),
- (SvTRUE(traplist_ref) ? sv_2mortal(traplist_ref):traplist_ref));
- __call_callback(cb, G_DISCARD);
- FREETMPS;
- LEAVE;
- sv_2mortal(sess_ref);
- return 1;
- }
- static SV *
- __push_cb_args2(sv,esv,tsv)
- SV *sv;
- SV *esv;
- SV *tsv;
- {
- dSP;
- if (SvTYPE(SvRV(sv)) != SVt_PVCV) sv = SvRV(sv);
- PUSHMARK(sp);
- if (SvTYPE(sv) == SVt_PVAV) {
- AV *av = (AV *) sv;
- int n = av_len(av) + 1;
- SV **x = av_fetch(av, 0, 0);
- if (x) {
- int i = 1;
- sv = *x;
- for (i = 1; i < n; i++) {
- x = av_fetch(av, i, 0);
- if (x) {
- SV *arg = *x;
- XPUSHs(sv_mortalcopy(arg));
- } else {
- XPUSHs(&sv_undef);
- }
- }
- } else {
- sv = &sv_undef;
- }
- }
- if (esv) XPUSHs(sv_mortalcopy(esv));
- if (tsv) XPUSHs(sv_mortalcopy(tsv));
- PUTBACK;
- return sv;
- }
- static int
- __call_callback(sv, flags)
- SV *sv;
- int flags;
- {
- dSP;
- I32 myframe = TOPMARK;
- I32 count;
- ENTER;
- if (SvTYPE(sv) == SVt_PVCV)
- {
- count = perl_call_sv(sv, flags);
- }
- else if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV)
- {
- count = perl_call_sv(SvRV(sv), flags);
- }
- else
- {
- SV **top = stack_base + myframe + 1;
- SV *obj = *top;
- if (SvPOK(sv) && SvROK(obj) && SvOBJECT(SvRV(obj)))
- {
- count = perl_call_method(SvPV(sv, na), flags);
- }
- else if (SvPOK(obj) && SvROK(sv) && SvOBJECT(SvRV(sv)))
- {
- /* We have obj method ...
- Used to be used instead of LangMethodCall()
- */
- *top = sv;
- count = perl_call_method(SvPV(obj, na), flags);
- }
- else
- {
- count = perl_call_sv(sv, flags);
- }
- }
- LEAVE;
- return count;
- }
- /* Bulkwalk support routines */
- /* Add a context pointer to the list of valid pointers. Place it in the first
- ** NULL slot in the array.
- */
- static int
- _context_add(walk_context *context)
- {
- int i, j, new_sz;
- if ((i = _context_okay(context)) != 0) /* Already exists? Okay. */
- return i;
- /* Initialize the array if necessary. */
- if (_valid_contexts == NULL) {
- /* Create the _valid_contexts structure. */
- Newz(0, _valid_contexts, 1, struct valid_contexts);
- assert(_valid_contexts != NULL);
- /* Populate the original valid contexts array. */
- Newz(0, _valid_contexts->valid, 4, walk_context *);
- assert(_valid_contexts->valid != NULL);
- /* Computer number of slots in the array. */
- _valid_contexts->sz_valid = sizeof(*_valid_contexts->valid) /
- sizeof(walk_context *);
- for (i = 0; i < _valid_contexts->sz_valid; i++)
- _valid_contexts->valid[i] = NULL;
- DBPRT(3, (DBOUT "Created valid_context array 0x%p (%d slots)n",
- _valid_contexts->valid, _valid_contexts->sz_valid));
- }
- /* Search through the list, looking for NULL's -- unused slots. */
- for (i = 0; i < _valid_contexts->sz_valid; i++)
- if (_valid_contexts->valid[i] == NULL)
- break;
- /* Did we walk off the end of the list? Need to grow the list. Double
- ** it for now.
- */
- if (i == _valid_contexts->sz_valid) {
- new_sz = _valid_contexts->sz_valid * 2;
- Renew(_valid_contexts->valid, new_sz, walk_context *);
- assert(_valid_contexts->valid != NULL);
- DBPRT(3, (DBOUT "Resized valid_context array 0x%p from %d to %d slotsn",
- _valid_contexts->valid, _valid_contexts->sz_valid, new_sz));
- _valid_contexts->sz_valid = new_sz;
- /* Initialize the new half of the resized array. */
- for (j = i; j < new_sz; j++)
- _valid_contexts->valid[j] = NULL;
- }
- /* Store the context pointer in the array and return 0 (success). */
- _valid_contexts->valid[i] = context;
- DBPRT(3,(DBOUT "Add context 0x%p to valid context listn", context));
- return 0;
- }
- /* Remove a context pointer from the valid list. Replace the pointer with
- ** NULL in the valid pointer list.
- */
- static int
- _context_del(walk_context *context)
- {
- int i;
- if (_valid_contexts == NULL) /* Make sure it was initialized. */
- return 1;
- for (i = 0; i < _valid_contexts->sz_valid; i++) {
- if (_valid_contexts->valid[i] == context) {
- DBPRT(3,(DBOUT "Remove context 0x%p from valid context listn", context));
- _valid_contexts->valid[i] = NULL; /* Remove it from the list. */
- return 0; /* Return successful status. */
- }
- }
- return 1;
- }
- /* Check if a specific context pointer is in the valid list. Return true (1)
- ** if the context is still in the valid list, or 0 if not (or context is NULL).
- */
- static int
- _context_okay(walk_context *context)
- {
- int i;
- if (_valid_contexts == NULL) /* Make sure it was initialized. */
- return 0;
- if (context == NULL) /* Asked about a NULL context? Fail. */
- return 0;
- for (i = 0; i < _valid_contexts->sz_valid; i++)
- if (_valid_contexts->valid[i] == context)
- return 1; /* Found it! */
- return 0; /* No match -- return failure. */
- }
- /* Check if the walk is completed, based upon the context. Also set the
- ** ignore flag on any completed variables -- this prevents them from being
- ** being sent in later packets.
- */
- static int
- _bulkwalk_done(walk_context *context)
- {
- int is_done = 1;
- int i;
- bulktbl *bt_entry; /* bulktbl requested OID entry */
- /* Don't consider walk done until at least one packet has been exchanged. */
- if (context->pkts_exch == 0)
- return 0;
- /* Fix up any requests that have completed. If the complete flag is set,
- ** or it is a non-repeater OID, set the ignore flag so that it will not
- ** be considered further. Assume we are done with the walk, and note
- ** otherwise if we aren't. Return 1 if all requests are complete, or 0
- ** if there's more to do.
- */
- for (i = 0; i < context->nreq_oids; i ++) {
- bt_entry = &context->req_oids[i];
- if (bt_entry->complete || bt_entry->norepeat) {
- /* This request is complete. Remove it from list of
- ** walks still in progress.
- */
- DBPRT(1, (DBOUT "Ignoring %s request oid %sn",
- bt_entry->norepeat? "nonrepeater" : "completed",
- snprint_objid(_debugx, sizeof(_debugx), bt_entry->req_oid,
- bt_entry->req_len)));
- /* Ignore this OID in any further packets. */
- bt_entry->ignore = 1;
- }
- /* If any OID is not being ignored, the walk is not done. Must loop
- ** through all requests to do the fixup -- no early return possible.
- */
- if (!bt_entry->ignore)
- is_done = 0;
- }
- return is_done; /* Did the walk complete? */
- }
- /* Callback registered with SNMP. Return 1 from this callback to cause the
- ** current request to be deleted from the retransmit queue.
- */
- static int
- _bulkwalk_async_cb(int op,
- SnmpSession *ss,
- int reqid,
- netsnmp_pdu *pdu,
- void *context_ptr)
- {
- walk_context *context;
- int done = 0;
- int npushed;
- SV **err_str_svp;
- SV **err_num_svp;
- /* Handle callback request for asynchronous bulkwalk. If the bulkwalk has
- ** not completed, and has not timed out, send the next request packet in
- ** the walk.
- **
- ** Return 0 to indicate success (caller ignores return value).
- */
- DBPRT(2, (DBOUT "bulkwalk_async_cb(op %d, reqid 0x%08X, context 0x%p)n",
- op, reqid, context_ptr));
- context = (walk_context *)context_ptr;
- /* Make certain this is a valid context pointer. This pdu may
- ** have been retransmitted after the bulkwalk was completed
- ** (and the context was destroyed). If so, just return.
- */
- if (!_context_okay(context)) {
- DBPRT(2,(DBOUT "Ignoring PDU for dead context 0x%p...n", context));
- return 1;
- }
- /* Is this a retransmission of a request we've already seen or some
- ** unexpected request id? If so, just ignore it.
- */
- if (reqid != context->exp_reqid) {
- DBPRT(2,
- (DBOUT "Got reqid 0x%08X, expected reqid 0x%08X. Ignoring...n", reqid,
- context->exp_reqid));
- return 1;
- }
- /* Ignore any future packets for this reqid. */
- context->exp_reqid = -1;
- err_str_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorStr", 8, 1);
- err_num_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorNum", 8, 1);
- switch (op) {
- case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE:
- {
- DBPRT(1,(DBOUT "Received message for reqid 0x%08X ...n", reqid));
- switch (pdu->command)
- {
- case SNMP_MSG_RESPONSE:
- {
- DBPRT(2, (DBOUT "Calling bulkwalk_recv_pdu(context 0x%p, pdu 0x%p)n",
- context_ptr, pdu));
- /* Handle the response PDU. If an error occurs or there were
- ** no variables in the response, consider the walk done. If
- ** the response was okay, check if we have any more to do after
- ** this response.
- */
- if (_bulkwalk_recv_pdu(context, pdu) <= 0)
- done = 1;
- else
- done = _bulkwalk_done(context); /* Also set req ignore flags */
- break;
- }
- default:
- {
- DBPRT(1,(DBOUT "unexpected pdu->command %dn", pdu->command));
- done = 1; /* "This can't happen!", so bail out when it does. */
- break;
- }
- }
- break;
- }
- case NETSNMP_CALLBACK_OP_TIMED_OUT:
- {
- DBPRT(1,(DBOUT "n*** Timeout for reqid 0x%08Xnn", reqid));
- sv_setpv(*err_str_svp, (char*)snmp_api_errstring(SNMPERR_TIMEOUT));
- sv_setiv(*err_num_svp, SNMPERR_TIMEOUT);
- /* Timeout means something bad has happened. Return a not-okay
- ** result to the async callback.
- */
- npushed = _bulkwalk_finish(context, 0 /* NOT OKAY */);
- return 1;
- }
- default:
- {
- DBPRT(1,(DBOUT "unexpected callback op %dn", op));
- sv_setpv(*err_str_svp, (char*)snmp_api_errstring(SNMPERR_GENERR));
- sv_setiv(*err_num_svp, SNMPERR_GENERR);
- npushed = _bulkwalk_finish(context, 0 /* NOT OKAY */);
- return 1;
- }
- }
- /* We have either timed out, or received and parsed in a response. Now,
- ** if we have more variables to test, call bulkwalk_send_pdu() to enqueue
- ** another async packet, and return.
- **
- ** If, however, the bulkwalk has completed (or an error has occurred that
- ** cuts the walk short), call bulkwalk_finish() to push the results onto
- ** the Perl call stack. Then explicitly call the Perl callback that was
- ** passed in by the user oh-so-long-ago.
- */
- if (!done) {
- DBPRT(1,(DBOUT "bulkwalk not complete -- send next pdu from callbackn"));
- if (_bulkwalk_send_pdu(context) != NULL)
- return 1;
- DBPRT(1,(DBOUT "send_pdu() failed!n"));
- /* Fall through and return what we have so far. */
- }
- /* Call the perl callback with the return values and we're done. */
- npushed = _bulkwalk_finish(context, 1 /* OKAY */);
- return 1;
- }
- static netsnmp_pdu *
- _bulkwalk_send_pdu(walk_context *context)
- {
- netsnmp_pdu *pdu = NULL;
- netsnmp_pdu *response = NULL;
- struct bulktbl *bt_entry;
- int nvars = 0;
- int reqid;
- int status;
- int i;
- /* Send a pdu requesting any remaining variables in the context.
- **
- ** In synchronous mode, returns a pointer to the response packet.
- **
- ** In asynchronous mode, it returns the request ID, cast to a struct snmp *,
- ** not a valid SNMP response packet. The async code should not be trying
- ** to get variables out of this "response".
- **
- ** In either case, return a NULL pointer on error or failure.
- */
- SV **sess_ptr_sv = hv_fetch((HV*)SvRV(context->sess_ref), "SessPtr", 7, 1);
- netsnmp_session *ss = (SnmpSession *)SvIV((SV*)SvRV(*sess_ptr_sv));
- SV **err_str_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorStr", 8, 1);
- SV **err_num_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorNum", 8, 1);
- SV **err_ind_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorInd", 8, 1);
- /* Create a new PDU and send the remaining set of requests to the agent. */
- pdu = snmp_pdu_create(SNMP_MSG_GETBULK);
- if (pdu == NULL) {
- sv_setpv(*err_str_svp, "snmp_pdu_create(GETBULK) failed: ");
- sv_catpv(*err_str_svp, strerror(errno));
- sv_setiv(*err_num_svp, SNMPERR_MALLOC);
- goto err;
- }
- /* Request non-repeater variables only in the first packet exchange. */
- pdu->errstat = (context->pkts_exch == 0) ? context->non_reps : 0;
- pdu->errindex = context->max_reps;
- for (i = 0; i < context->nreq_oids; i++) {
- bt_entry = &context->req_oids[i];
- if (bt_entry->ignore)
- continue;
- assert(bt_entry->complete == 0);
- if (!snmp_add_null_var(pdu, bt_entry->last_oid, bt_entry->last_len)) {
- sv_setpv(*err_str_svp, "snmp_add_null_var() failed");
- sv_setiv(*err_num_svp, SNMPERR_GENERR);
- sv_setiv(*err_ind_svp, i);
- goto err;
- }
- nvars ++;
- DBPRT(1, (DBOUT " Add %srepeater %sn", bt_entry->norepeat ? "non" : "",
- snprint_objid(_debugx, sizeof(_debugx), bt_entry->last_oid, bt_entry->last_len)));
- }
- /* Make sure variables are actually being requested in the packet. */
- assert (nvars != 0);
- context->pkts_exch ++;
- DBPRT(1, (DBOUT "Sending %ssynchronous request %d...n",
- SvTRUE(context->perl_cb) ? "a" : "", context->pkts_exch));
- /* We handle the asynchronous and synchronous requests differently here.
- ** For async, we simply enqueue the packet with a callback to handle the
- ** returned response, then return. Note that this we call the bulkwalk
- ** callback, and hand it the walk_context, not the Perl callback. The
- ** snmp_async_send() function returns the reqid on success, 0 on failure.
- */
- if (SvTRUE(context->perl_cb)) {
- reqid = snmp_async_send(ss, pdu, _bulkwalk_async_cb, (void *)context);
- DBPRT(2,(DBOUT "bulkwalk_send_pdu(): snmp_async_send => 0x%08Xn", reqid));
- if (reqid == 0) {
- sv_setpv(*err_str_svp, (char*)snmp_api_errstring(ss->s_snmp_errno));
- sv_setiv(*err_num_svp, ss->s_snmp_errno);
- goto err;
- }
- /* Make a note of the request we expect to be answered. */
- context->exp_reqid = reqid;
- /* Callbacks take care of the rest. Let the caller know how many vars
- ** we sent in this request. Note that this is not a valid SNMP PDU,
- ** but that's because a response has not yet been received.
- */
- return (netsnmp_pdu *)reqid;
- }
- /* This code is for synchronous mode support.
- **
- ** Send the PDU and block awaiting the response. Return the response
- ** packet back to the caller. Note that snmp_sess_read() frees the pdu.
- */
- status = __send_sync_pdu(ss, pdu, &response, NO_RETRY_NOSUCH,
- *err_str_svp, *err_num_svp, *err_ind_svp);
- pdu = NULL;
- /* Check for a failed request. __send_sync_pdu() will set the appropriate
- ** values in the error string and number SV's.
- */
- if (status != STAT_SUCCESS) {
- DBPRT(1,(DBOUT "__send_sync_pdu() -> %dn",(int)status));
- goto err;
- }
- DBPRT(1, (DBOUT "%d packets exchanged, response 0x%pn", context->pkts_exch,
- response));
- return response;
- err:
- if (pdu)
- snmp_free_pdu(pdu);
- return NULL;
- }
- /* Handle an incoming GETBULK response PDU. This function just pulls the
- ** variables off of the PDU and builds up the arrays of returned values
- ** that are stored in the context.
- **
- ** Returns the number of variables found in this packet, or -1 on error.
- ** Note that the caller is expected to free the pdu.
- */
- static int
- _bulkwalk_recv_pdu(walk_context *context, netsnmp_pdu *pdu)
- {
- netsnmp_variable_list *vars;
- struct tree *tp;
- char type_str[MAX_TYPE_NAME_LEN];
- u_char str_buf[STR_BUF_SIZE], *str_bufp = str_buf;
- size_t str_buf_len = sizeof(str_buf);
- size_t out_len = 0;
- int buf_over = 0;
- char *label;
- char *iid;
- bulktbl *expect = NULL;
- int old_numeric;
- int old_printfull;
- int old_format;
- int getlabel_flag;
- int type;
- int pix;
- int len;
- int i;
- AV *varbind;
- SV *rv;
- SV **sess_ptr_sv = hv_fetch((HV*)SvRV(context->sess_ref), "SessPtr", 7, 1);
- SV **err_str_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorStr", 8, 1);
- SV **err_num_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorNum", 8, 1);
- SV **err_ind_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorInd", 8, 1);
- DBPRT(3, (DBOUT "bulkwalk: sess_ref = 0x%p, sess_ptr_sv = 0x%pn",
- context->sess_ref, sess_ptr_sv));
- /* Set up for numeric OID's, if necessary. Save the old values
- ** so that they can be restored when we finish -- these are
- ** library-wide globals, and have to be set/restored for each
- ** session.
- */
- old_numeric = netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_NUMERIC_OIDS);
- old_printfull = netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_FULL_OID);
- old_format = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT);
- if (context->getlabel_f & USE_NUMERIC_OIDS) {
- DBPRT(2,(DBOUT "Using numeric oid'sn"));
- netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_NUMERIC_OIDS, 1);
- netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_FULL_OID, 1);
- netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, NETSNMP_OID_OUTPUT_NUMERIC);
- }
- /* Parse through the list of variables returned, adding each return to
- ** the appropriate array (as a VarBind). Also keep track of which
- ** repeated OID we're expecting to see, and check if that tree walk has
- ** been completed (i.e. we've walked past the root of our request). If
- ** so, mark the request complete so that we don't send it again in any
- ** subsequent request packets.
- */
- if (context->pkts_exch == 1)
- context->reqbase = context->req_oids; /* Request with non-repeaters */
- else
- context->reqbase = context->repbase; /* Request only repeater vars */
- /* Note the first variable we expect to see. Should be reqbase. */
- expect = context->reqbase;
- for (vars = pdu->variables, pix = 0;
- vars != NULL;
- vars = vars->next_variable, pix ++)
- {
- /* If no outstanding requests remain, we're done. This works, but it
- ** causes the reported total variable count to be wrong (since the
- ** remaining vars on the last packet are not counted). In practice
- ** this is probably worth the win, but for debugging it's not.
- */
- if (context->req_remain == 0) {
- DBPRT(2,(DBOUT "No outstanding requests remain. Terminating processing.n"));
- while (vars) {
- pix ++;
- vars = vars->next_variable;
- }
- break;
- }
- /* Determine which OID we expect to see next. We assert that the OID's
- ** must be returned in the expected order. The first nreq_oids returns
- ** should match the req_oids array, after that, we must cycle through
- ** the repeaters in order. Non-repeaters are not included in later
- ** packets, so cannot have the "ignore" flag set.
- */
- if (context->oid_saved < context->non_reps) {
- assert(context->pkts_exch == 1);
- expect = context->reqbase ++;
- assert(expect->norepeat);
- } else {
- /* Must be a repeater. Look for the first one that is not being
- ** ignored. Make sure we don't loop around to where we started.
- ** If we get here but everything is being ignored, there's a problem.
- **
- ** Note that we *do* accept completed but not ignored OID's -- these
- ** are OID's for trees that have been completed sometime in this
- ** response, but must be looked at to maintain ordering.
- */
- if (pix == 0) {
- /* Special case code for no non-repeater case. This
- ** is necessary because expect normally points to the
- ** last non-repeater upon entry to this code (so the
- ** '++expect' below increments it into the repeaters
- ** section of the req_oids[] array).
- ** If there are no non-repeaters, the expect pointer
- ** is never initialized. This addresses this problem.
- */
- expect = context->reqbase;
- } else {
- /* Find the repeater OID we expect to see. Ignore any
- ** OID's marked 'ignore' -- these have been completed
- ** and were not requested in this iteration.
- */
- for (i = 0; i < context->repeaters; i++) {
- /* Loop around to first repeater if we hit the end. */
- if (++ expect == &context->req_oids[context->nreq_oids])
- expect = context->reqbase = context->repbase;
- /* Stop if this OID is not being ignored. */
- if (!expect->ignore)
- break;
- }
- /* Make sure we did find an expected OID. */
- assert(i <= context->repeaters);
- }
- }
- DBPRT(2, (DBOUT "Var %03d request %sn", pix, snprint_objid(_debugx, sizeof(_debugx),
- expect->req_oid, expect->req_len)));
- /* Did we receive an error condition for this variable?
- ** If it's a repeated variable, mark it as complete and
- ** fall through to the block below.
- */
- if ((vars->type == SNMP_ENDOFMIBVIEW) ||
- (vars->type == SNMP_NOSUCHOBJECT) ||
- (vars->type == SNMP_NOSUCHINSTANCE))
- {
- DBPRT(2,(DBOUT "error type %dn", (int)vars->type));
- /* ENDOFMIBVIEW should be okay for a repeater - just walked off the
- ** end of the tree. Mark the request as complete, and go on to the
- ** next one.
- */
- if ((context->oid_saved >= context->non_reps) &&
- (vars->type == SNMP_ENDOFMIBVIEW))
- {
- expect->complete = 1;
- DBPRT(2, (DBOUT "Ran out of tree for oid %sn",
- snprint_objid(_debugx, sizeof(_debugx), vars->name,vars->name_length)));
- context->req_remain --;
- /* Go on to the next variable. */
- continue;
- }
- sv_setpv(*err_str_svp,
- (char*)snmp_api_errstring(SNMPERR_UNKNOWN_OBJID));
- sv_setiv(*err_num_svp, SNMPERR_UNKNOWN_OBJID);
- sv_setiv(*err_ind_svp, pix);
- goto err;
- }
- /* If this is not the first packet, skip any duplicated OID values, if
- ** present. These should be the seed values copied from the last OID's
- ** of the previous packet. In practice we don't see this, but it is
- ** easy enough to do, and will avoid confusion for the caller from mis-
- ** behaving agents (badly misbehaving... ;^).
- */
- if ((context->pkts_exch > 1) && (pix < context->repeaters)) {
- if (__oid_cmp(vars->name, vars->name_length,
- context->reqbase[pix].last_oid,
- context->reqbase[pix].last_len) == 0)
- {
- DBPRT(2, (DBOUT "Ignoring repeat oid: %sn",
- snprint_objid(_debugx, sizeof(_debugx), vars->name,vars->name_length)));
- continue;
- }
- }
- context->oid_total ++; /* Count each variable received. */
- /* If this is a non-repeater, handle it. Otherwise, if it is a
- ** repeater, has the walk wandered off of the requested tree? If so,
- ** this request is complete, so mark it as such. Ignore any other
- ** variables in a completed request. In order to maintain the correct
- ** ordering of which variables we expect to see in this packet, we must
- ** not set the ignore flags immediately. It is done in bulkwalk_done().
- ** XXX Can we use 'expect' instead of 'context->req_oids[pix]'?
- */
- if (context->oid_saved < context->non_reps) {
- DBPRT(2, (DBOUT " expected var %s (nonrepeater %d/%d)n",
- snprint_objid(_debugx, sizeof(_debugx), context->req_oids[pix].req_oid,
- context->req_oids[pix].req_len),
- pix, context->non_reps));
- DBPRT(2, (DBOUT " received var %sn",
- snprint_objid(_debugx, sizeof(_debugx), vars->name, vars->name_length)));
- /* This non-repeater has now been seen, so mark the sub-tree as
- ** completed. Note that this may not be the same oid as requested,
- ** since non-repeaters act like GETNEXT requests, not GET's. <sigh>
- */
- context->req_oids[pix].complete = 1;
- context->req_remain --;
- } else { /* Must be a repeater variable. */
- DBPRT(2, (DBOUT " received oid %sn",
- snprint_objid(_debugx, sizeof(_debugx), vars->name, vars->name_length)));
- /* Are we already done with this tree? If so, just ignore this
- ** variable and move on to the next expected variable.
- */
- if (expect->complete) {
- DBPRT(2,(DBOUT " this branch is complete - ignoring.n"));
- continue;
- }
- /* If the base oid of this variable doesn't match the expected oid,
- ** assume that we've walked past the end of the subtree. Set this
- ** subtree to be completed, and go on to the next variable.
- */
- if (((int)vars->name_length < expect->req_len) ||
- (memcmp(vars->name, expect->req_oid, expect->req_len*sizeof(oid))))
- {
- DBPRT(2,(DBOUT " walked off branch - marking subtree as complete.n"));
- expect->complete = 1;
- context->req_remain --;
- continue;
- }
- /* Still interested in the tree -- we need to keep track of the
- ** last-seen value in case we need to send an additional request
- ** packet.
- */
- (void)memcpy(expect->last_oid, vars->name,
- vars->name_length * sizeof(oid));
- expect->last_len = vars->name_length;
- }
- /* Create a new Varbind and populate it with the parsed information
- ** returned by the agent. This Varbind is then pushed onto the arrays
- ** maintained for each request OID in the context. These varbinds are
- ** collected into a return array by bulkwalk_finish().
- */
- varbind = (AV*) newAV();
- if (varbind == NULL) {
- sv_setpv(*err_str_svp, "newAV() failed: ");
- sv_catpv(*err_str_svp, (char*)strerror(errno));
- sv_setiv(*err_num_svp, SNMPERR_MALLOC);
- goto err;
- }
- *str_buf = '.';
- *(str_buf+1) = ' ';
- out_len = 0;
- tp = netsnmp_sprint_realloc_objid_tree(&str_bufp, &str_buf_len,
- &out_len, 0, &buf_over,
- vars->name,vars->name_length);
- str_buf[sizeof(str_buf)-1] = ' ';
- getlabel_flag = context->getlabel_f;
- if (__is_leaf(tp)) {
- type = tp->type;
- } else {
- getlabel_flag |= NON_LEAF_NAME;
- type = __translate_asn_type(vars->type);
- }
- if (__get_label_iid(str_buf, &label, &iid, getlabel_flag) == FAILURE) {
- label = str_buf;
- iid = label + strlen(label);
- }
- DBPRT(2,(DBOUT " save var %s.%s = ", label, iid));
- av_store(varbind, VARBIND_TAG_F, newSVpv(label, strlen(label)));
- av_store(varbind, VARBIND_IID_F, newSVpv(iid, strlen(iid)));
- __get_type_str(type, type_str);
- av_store(varbind, VARBIND_TYPE_F, newSVpv(type_str, strlen(type_str)));
- len=__snprint_value(str_buf, sizeof(str_buf),
- vars, tp, type, context->sprintval_f);
- av_store(varbind, VARBIND_VAL_F, newSVpv((char*)str_buf, len));
- str_buf[len] = ' ';
- DBPRT(3,(DBOUT "'%s' (%s)n", str_buf, type_str));
- #if 0
- /* huh? */
- /* If necessary, store a timestamp as the semi-documented 5th element. */
- if (sv_timestamp)
- av_store(varbind, VARBIND_TIME_F, SvREFCNT_inc(sv_timestamp));
- #endif
- /* Push ref to the varbind onto the list of vars for OID. */
- rv = newRV_noinc((SV *)varbind);
- sv_bless(rv, gv_stashpv("SNMP::Varbind", 0));
- av_push(expect->vars, rv);
- context->oid_saved ++; /* Count this as a saved variable. */
- } /* next variable in response packet */
- DBPRT(1, (DBOUT "-- pkt %d saw %d vars, total %d (%d saved)n", context->pkts_exch,
- pix, context->oid_total, context->oid_saved));
- /* We assert that all non-repeaters must be returned in
- ** the initial response (they are not repeated in additional
- ** packets, so would be dropped). If nonrepeaters still
- ** exist, consider it a fatal error.
- */
- if ((context->pkts_exch == 1) && (context->oid_saved < context->non_reps)) {
- /* Re-use space from the value string for error message. */
- sprintf(str_buf, "%d non-repeaters went unanswered", context->non_reps);
- sv_setpv(*err_str_svp, str_buf);
- sv_setiv(*err_num_svp, SNMPERR_GENERR);
- sv_setiv(*err_num_svp, context->oid_saved);
- goto err;
- }
- /* Reset the library's behavior for numeric/symbolic OID's. */
- if (context->getlabel_f & USE_NUMERIC_OIDS) {
- netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_NUMERIC_OIDS, old_numeric);
- netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_FULL_OID, old_printfull);
- netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, old_format);
- }
- return pix;
- err:
- if (pdu)
- snmp_free_pdu(pdu);
- return -1;
- }
- /* Once the bulkwalk has completed, extend the stack and push references to
- ** each of the arrays of SNMP::Varbind's onto the stack. Return the number
- ** of arrays pushed on the stack. The caller should return to Perl, or call
- ** the Perl callback function.
- **
- ** Note that this function free()'s the walk_context and request bulktbl's.
- */
- static int
- _bulkwalk_finish(walk_context *context, int okay)
- {
- int npushed = 0;
- int i;
- int async = 0;
- bulktbl *bt_entry;
- AV *ary = NULL;
- SV *rv;
- SV *perl_cb;
- SV **err_str_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorStr", 8, 1);
- SV **err_num_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorNum", 8, 1);
- dXSARGS;
- async = SvTRUE(context->perl_cb);
- /* Successfully completed the bulkwalk. For synchronous calls, push each
- ** of the request value arrays onto the stack, and return the number of
- ** items pushed onto the stack. For async, create a new array and push
- ** the references onto it. The array is then passed to the Perl callback.
- */
- if (!async)
- SP -= items;
- DBPRT(1, (DBOUT "Bulwalk %s (saved %d/%d), ", okay ? "completed" : "had error",
- context->oid_saved, context->oid_total));
- if (okay) {
- DBPRT(1, (DBOUT "%s %d varbind refs %sn",
- async ? "pass ref to array of" : "return",
- context->nreq_oids,
- async ? "to callback" : "on stack to caller"));
- /* Create the array to hold the responses for the asynchronous callback,
- ** or pre-extend the stack enough to hold responses for synch return.
- */
- if (async) {
- ary = (AV *)newAV();
- if (ary == NULL) {
- sv_setpv(*err_str_svp, "newAV(): ");
- sv_catpv(*err_str_svp, (char *)strerror(errno));
- sv_setiv(*err_num_svp, errno);
- }
- /* NULL ary pointer is okay -- we'll handle it below... */
- } else {
- EXTEND(sp, context->nreq_oids);
- }
- /* Push a reference to each array of varbinds onto the stack, in
- ** the order requested. Note that these arrays may be empty.
- */
- for (i = 0; i < context->nreq_oids; i++) {
- bt_entry = &context->req_oids[i];
- DBPRT(2, (DBOUT " %sreq #%d (%s) => %d var%sn",
- bt_entry->complete ? "" : "incomplete ", i,
- snprint_objid(_debugx, sizeof(_debugx), bt_entry->req_oid, bt_entry->req_len),
- (int)av_len(bt_entry->vars) + 1,
- (int)av_len(bt_entry->vars) > 0 ? "s" : ""));
- if (async && ary == NULL) {
- DBPRT(2,(DBOUT " [dropped due to newAV() failure]n"));
- continue;
- }
- /* Get a reference to the varlist, and push it onto array or stack */
- rv = newRV_noinc((SV *)bt_entry->vars);
- sv_bless(rv, gv_stashpv("SNMP::VarList",0));
- if (async)
- av_push(ary, rv);
- else
- PUSHs(sv_2mortal((SV *)rv));
- npushed ++;
- }
- } else { /* Not okay -- push a single undef on the stack if not async */
- if (!async) {
- XPUSHs(&sv_undef);
- npushed = 1;
- }
- }
- /* XXX Future enhancement -- make statistics (pkts exchanged, vars
- ** saved vs. received, total time, etc) available to caller so they
- ** can adjust their request parameters and/or re-order requests.
- */
- PUTBACK;
- if (async) {
- /* Asynchronous callback. Push the caller's arglist onto the stack,
- ** and follow it with the contents of the array (or undef if newAV()
- ** failed or the session had an error). Then mortalize the Perl
- ** callback pointer, and call the callback.
- */
- if (!okay || ary == NULL)
- rv = &sv_undef;
- else
- rv = newRV_noinc((SV *)ary);
- sv_2mortal(perl_cb = context->perl_cb);
- perl_cb = __push_cb_args(perl_cb, (SvTRUE(rv) ? sv_2mortal(rv) : rv));
- __call_callback(perl_cb, G_DISCARD);
- }
- sv_2mortal(context->sess_ref);
- /* Free the allocated space for the request states and return number of
- ** variables found. Remove the context from the valid context list.
- */
- _context_del(context);
- DBPRT(2,(DBOUT "Free() context->req_oidsn"));
- Safefree(context->req_oids);
- DBPRT(2,(DBOUT "Free() context 0x%pn", context));
- Safefree(context);
- return npushed;
- }
- /* End of bulkwalk support routines */
- static char *
- __av_elem_pv(AV *av, I32 key, char *dflt)
- {
- SV **elem = av_fetch(av, key, 0);
- return (elem && SvOK(*elem)) ? SvPV(*elem, na) : dflt;
- }
- static int
- not_here(s)
- char *s;
- {
- croak("%s not implemented on this architecture", s);
- return -1;
- }
- static double
- constant(name, arg)
- char *name;
- int arg;
- {
- errno = 0;
- switch (*name) {
- case 'R':
- if (strEQ(name, "NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE"))
- #ifdef NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE
- return NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE;
- #else
- goto not_there;
- #endif
- break;
- case 'S':
- if (strEQ(name, "SNMPERR_BAD_ADDRESS"))
- #ifdef SNMPERR_BAD_ADDRESS
- return SNMPERR_BAD_ADDRESS;
- #else
- goto not_there;
- #endif
- if (strEQ(name, "SNMPERR_BAD_LOCPORT"))
- #ifdef SNMPERR_BAD_LOCPORT
- return SNMPERR_BAD_LOCPORT;
- #else
- goto not_there;
- #endif
- if (strEQ(name, "SNMPERR_BAD_SESSION"))
- #ifdef SNMPERR_BAD_SESSION
- return SNMPERR_BAD_SESSION;
- #else
- goto not_there;
- #endif
- if (strEQ(name, "SNMPERR_GENERR"))
- #ifdef SNMPERR_GENERR
- return SNMPERR_GENERR;
- #else
- goto not_there;
- #endif
- if (strEQ(name, "SNMPERR_TOO_LONG"))
- #ifdef SNMPERR_TOO_LONG
- return SNMPERR_TOO_LONG;
- #else
- goto not_there;
- #endif
- if (strEQ(name, "SNMP_DEFAULT_ADDRESS"))
- #ifdef SNMP_DEFAULT_ADDRESS
- return SNMP_DEFAULT_ADDRESS;
- #else
- goto not_there;
- #endif
- if (strEQ(name, "SNMP_DEFAULT_COMMUNITY_LEN"))
- #ifdef SNMP_DEFAULT_COMMUNITY_LEN
- return SNMP_DEFAULT_COMMUNITY_LEN;
- #else
- goto not_there;
- #endif
- if (strEQ(name, "SNMP_DEFAULT_ENTERPRISE_LENGTH"))
- #ifdef SNMP_DEFAULT_ENTERPRISE_LENGTH
- return SNMP_DEFAULT_ENTERPRISE_LENGTH;
- #else
- goto not_there;
- #endif
- if (strEQ(name, "SNMP_DEFAULT_ERRINDEX"))
- #ifdef SNMP_DEFAULT_ERRINDEX
- return SNMP_DEFAULT_ERRINDEX;
- #else
- goto not_there;
- #endif
- if (strEQ(name, "SNMP_DEFAULT_ERRSTAT"))
- #ifdef SNMP_DEFAULT_ERRSTAT
- return SNMP_DEFAULT_ERRSTAT;
- #else
- goto not_there;
- #endif
- if (strEQ(name, "SNMP_DEFAULT_PEERNAME"))
- #ifdef SNMP_DEFAULT_PEERNAME
- return 0;
- #else
- goto not_there;
- #endif
- if (strEQ(name, "SNMP_DEFAULT_REMPORT"))
- #ifdef SNMP_DEFAULT_REMPORT
- return SNMP_DEFAULT_REMPORT;
- #else
- goto not_there;
- #endif
- if (strEQ(name, "SNMP_DEFAULT_REQID"))
- #ifdef SNMP_DEFAULT_REQID
- return SNMP_DEFAULT_REQID;
- #else
- goto not_there;
- #endif
- if (strEQ(name, "SNMP_DEFAULT_RETRIES"))
- #ifdef SNMP_DEFAULT_RETRIES
- return SNMP_DEFAULT_RETRIES;
- #else
- goto not_there;
- #endif
- if (strEQ(name, "SNMP_DEFAULT_TIME"))
- #ifdef SNMP_DEFAULT_TIME
- return SNMP_DEFAULT_TIME;
- #else
- goto not_there;
- #endif
- if (strEQ(name, "SNMP_DEFAULT_TIMEOUT"))
- #ifdef SNMP_DEFAULT_TIMEOUT
- return SNMP_DEFAULT_TIMEOUT;
- #else
- goto not_there;
- #endif
- if (strEQ(name, "SNMP_DEFAULT_VERSION"))
- #ifdef DEFAULT_SNMP_VERSION
- return DEFAULT_SNMP_VERSION;
- #else
- #ifdef SNMP_DEFAULT_VERSION
- return SNMP_DEFAULT_VERSION;
- #else
- goto not_there;
- #endif
- #endif
- break;
- case 'T':
- if (strEQ(name, "NETSNMP_CALLBACK_OP_TIMED_OUT"))
- #ifdef NETSNMP_CALLBACK_OP_TIMED_OUT
- return NETSNMP_CALLBACK_OP_TIMED_OUT;
- #else
- goto not_there;
- #endif
- break;
- default:
- break;
- }
- errno = EINVAL;
- return 0;
- #ifndef NETSNMP_CALLBACK_OP_TIMED_OUT
- not_there:
- #endif
- errno = ENOENT;
- return 0;
- }
- MODULE = SNMP PACKAGE = SNMP PREFIX = snmp
- double
- constant(name,arg)
- char * name
- int arg
- long
- snmp_sys_uptime()
- CODE:
- RETVAL = get_uptime();
- OUTPUT:
- RETVAL
- void
- init_snmp(appname)
- char *appname
- CODE:
- __libraries_init(appname);
- SnmpSession *
- snmp_new_session(version, community, peer, lport, retries, timeout)
- char * version
- char * community
- char * peer
- int lport
- int retries
- int timeout
- CODE:
- {
- SnmpSession session = {0};
- SnmpSession *ss = NULL;
- int verbose = SvIV(perl_get_sv("SNMP::verbose", 0x01 | 0x04));
- __libraries_init("perl");
-
- session.version = -1;
- #ifndef DISABLE_SNMPV1
- if (!strcmp(version, "1")) {
- session.version = SNMP_VERSION_1;
- }
- #endif
- #ifndef DISABLE_SNMPV2C
- if ((!strcmp(version, "2")) || (!strcmp(version, "2c"))) {
- session.version = SNMP_VERSION_2c;
- }
- #endif
- if (!strcmp(version, "3")) {
- session.version = SNMP_VERSION_3;
- }
- if (session.version == -1) {
- if (verbose)
- warn("error:snmp_new_session:Unsupported SNMP version (%s)n", version);
- goto end;
- }
- session.community_len = strlen((char *)community);