/* -*- 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 #include #include #include #include #include #ifdef I_SYS_TIME #include #endif #include #include #include /* XXX This is a problem if regex.h is not on the system. */ #include #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 DLL_IMPORT __declspec( dllimport ) #define strcasecmp _stricmp #define strncasecmp _strnicmp #else #define SOCK_STARTUP #define SOCK_CLEANUP #define DLL_IMPORT #endif DLL_IMPORT extern struct tree *Mib; #include #include #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 16 #define STR_BUF_SIZE 1024 #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. */ char req_len; /* Length of requested OID. */ char 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_fprintf(PerlIO_stderr(), 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); 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); 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 = %lX\n", 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 == '\0') 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] = '\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 %d\n",var->type); } } return(len); } static int __sprint_num_objid (buf, objid, len) char *buf; oid *objid; int len; { int i; buf[0] = '\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 .... returning and 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 ++ = '\0'; *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......, 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++) = '\0'; } *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; { 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) { return pmatch.rm_so; } return MAX_BAD; } 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; DLL_IMPORT extern struct tree *tree_head; oid newname[MAX_OID_LEN], *op; int newname_len = 0; if (type) *type = TYPE_UNKNOWN; if (oid_arr_len) *oid_arr_len = 0; if (!tag) goto done; if (best_guess) { tp = rtp = find_best_tree_node(tag, tree_head, NULL); if (tp) { if (type) *type = tp->type; if ((oid_arr == NULL) || (oid_arr_len == NULL)) return rtp; 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)); } return(rtp); } if (strchr(tag,'.')) { /* if multi part tag */ if (!__scan_num_objid(tag, newname, &newname_len)) { /* numeric tag */ newname_len = MAX_OID_LEN; read_objid(tag, newname, &newname_len); /* long name */ } 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 { /* else it is a leaf */ rtp = tp = find_node(tag, Mib); 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; if (!soid_str || !*soid_str) return SUCCESS;/* successfully added nothing */ if (*soid_str == '.') soid_str++; strcpy(soid_buf, soid_str); cp = strtok(soid_buf,"."); while (cp) { sscanf(cp, "%lu", &(doid_arr[(*doid_arr_len)++])); /* doid_arr[(*doid_arr_len)++] = atoi(cp); */ cp = strtok(NULL,"."); } 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; struct tree *tp; 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),"UseNumeric", 10, 1))) { getlabel_flag |= USE_NUMERIC_OIDS; netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_NUMERIC_OIDS, 1); } 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); } 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) = '\0'; 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] = '\0'; 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); av_store(varbind, VARBIND_TAG_F, newSVpv(label, strlen(label))); av_store(varbind, VARBIND_IID_F, newSVpv(iid, strlen(iid))); __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 slots\n", _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,( "Add context 0x%p to valid context list\n", 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,( "Remove context 0x%p from valid context list\n", 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 %s\n", 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,( "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, ("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,( "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,( "unexpected pdu->command %d\n", pdu->command)); done = 1; /* "This can't happen!", so bail out when it does. */ break; } } break; } case NETSNMP_CALLBACK_OP_TIMED_OUT: { DBPRT(1,( "\n*** Timeout for reqid 0x%08X\n\n", 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,( "unexpected callback op %d\n", 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,( "bulkwalk not complete -- send next pdu from callback\n")); if (_bulkwalk_send_pdu(context) != NULL) return 1; DBPRT(1,( "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 %s\n", 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,( "bulkwalk_send_pdu(): snmp_async_send => 0x%08X\n", 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,( "__send_sync_pdu() -> %d\n",(int)status)); goto err; } DBPRT(1, (DBOUT "%d packets exchanged, response 0x%p\n", 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%p\n", 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,( "Using numeric oid's\n")); 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,( "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 %s\n", 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,( "error type %d\n", (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 %s\n", 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: %s\n", 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 %s\n", 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. */ context->req_oids[pix].complete = 1; context->req_remain --; } else { /* Must be a repeater variable. */ DBPRT(2, (DBOUT " received oid %s\n", 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,( " 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 ((vars->name_length < expect->req_len) || (memcmp(vars->name, expect->req_oid, expect->req_len*sizeof(oid)))) { DBPRT(2,( " 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) = '\0'; 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] = '\0'; 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,( " 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] = '\0'; DBPRT(3,( "'%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 %s\n", 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%s\n", 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,( " [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,( "Free() context->req_oids\n")); Safefree(context->req_oids); DBPRT(2,( "Free() context 0x%p\n", 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 BOOT: # first blank line terminates bootstrap code Mib = 0; 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"); if (!strcmp(version, "1")) { session.version = SNMP_VERSION_1; } else if ((!strcmp(version, "2")) || (!strcmp(version, "2c"))) { session.version = SNMP_VERSION_2c; } else if (!strcmp(version, "3")) { session.version = SNMP_VERSION_3; } else { if (verbose) warn("error:snmp_new_session:Unsupported SNMP version (%s)\n", version); goto end; } session.community_len = strlen((char *)community); session.community = (u_char *)community; session.peername = peer; session.local_port = lport; session.retries = retries; /* 5 */ session.timeout = timeout; /* 1000000L */ session.authenticator = NULL; ss = snmp_open(&session); if (ss == NULL) { if (verbose) warn("error:snmp_new_session: Couldn't open SNMP session"); } end: RETVAL = ss; } OUTPUT: RETVAL SnmpSession * snmp_new_v3_session(version, peer, retries, timeout, sec_name, sec_level, sec_eng_id, context_eng_id, context, auth_proto, auth_pass, priv_proto, priv_pass, eng_boots, eng_time) int version char * peer int retries int timeout char * sec_name int sec_level char * sec_eng_id char * context_eng_id char * context char * auth_proto char * auth_pass char * priv_proto char * priv_pass int eng_boots int eng_time CODE: { /* u_char sec_eng_id_buf[ENG_ID_BUF_SIZE]; */ /* u_char context_eng_id_buf[ENG_ID_BUF_SIZE]; */ SnmpSession session = {0}; SnmpSession *ss = NULL; int verbose = SvIV(perl_get_sv("SNMP::verbose", 0x01 | 0x04)); __libraries_init("perl"); if (version == 3) { session.version = SNMP_VERSION_3; } else { if (verbose) warn("error:snmp_new_v3_session:Unsupported SNMP version (%d)\n", version); goto end; } session.peername = strdup(peer); session.retries = retries; /* 5 */ session.timeout = timeout; /* 1000000L */ session.authenticator = NULL; session.contextNameLen = strlen(context); session.contextName = context; session.securityNameLen = strlen(sec_name); session.securityName = sec_name; session.securityLevel = sec_level; session.securityModel = USM_SEC_MODEL_NUMBER; /* session.securityEngineID = sec_eng_id_buf;*/ session.securityEngineIDLen = hex_to_binary2(sec_eng_id, strlen(sec_eng_id), (char **) &session.securityEngineID); /* session.contextEngineID = context_eng_id_buf; */ session.contextEngineIDLen = hex_to_binary2(sec_eng_id, strlen(sec_eng_id), (char **) &session.contextEngineID); session.engineBoots = eng_boots; session.engineTime = eng_time; if (!strcmp(auth_proto, "MD5")) { session.securityAuthProto = snmp_duplicate_objid(usmHMACMD5AuthProtocol, USM_AUTH_PROTO_MD5_LEN); session.securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN; } else if (!strcmp(auth_proto, "SHA")) { session.securityAuthProto = snmp_duplicate_objid(usmHMACSHA1AuthProtocol, USM_AUTH_PROTO_SHA_LEN); session.securityAuthProtoLen = USM_AUTH_PROTO_SHA_LEN; } else { if (verbose) warn("error:snmp_new_v3_session:Unsupported authentication protocol(%s)\n", auth_proto); goto end; } if (session.securityLevel >= SNMP_SEC_LEVEL_AUTHNOPRIV) { session.securityAuthKeyLen = USM_AUTH_KU_LEN; if (generate_Ku(session.securityAuthProto, session.securityAuthProtoLen, (u_char *)auth_pass, strlen(auth_pass), session.securityAuthKey, &session.securityAuthKeyLen) != SNMPERR_SUCCESS) { if (verbose) warn("error:snmp_new_v3_session:Error generating Ku from authentication password.\n"); goto end; } } if (!strcmp(priv_proto, "DES")) { session.securityPrivProto = snmp_duplicate_objid(usmDESPrivProtocol, USM_PRIV_PROTO_DES_LEN); session.securityPrivProtoLen = USM_PRIV_PROTO_DES_LEN; } else { if (verbose) warn("error:snmp_new_v3_session:Unsupported privacy protocol(%s)\n", priv_proto); goto end; } if (session.securityLevel >= SNMP_SEC_LEVEL_AUTHPRIV) { session.securityPrivKeyLen = USM_PRIV_KU_LEN; if (generate_Ku(session.securityAuthProto, session.securityAuthProtoLen, (u_char *)priv_pass, strlen(priv_pass), session.securityPrivKey, &session.securityPrivKeyLen) != SNMPERR_SUCCESS) { if (verbose) warn("error:snmp_new_v3_session:Error generating Ku from privacy pass phrase.\n"); goto end; } } ss = snmp_open(&session); if (ss == NULL) { if (verbose) warn("error:snmp_new_v3_session:Couldn't open SNMP session"); } end: RETVAL = ss; free (session.contextEngineID); } OUTPUT: RETVAL SnmpSession * snmp_update_session(sess_ref, version, community, peer, lport, retries, timeout) SV * sess_ref char * version char * community char * peer int lport int retries int timeout CODE: { SV **sess_ptr_sv; SnmpSession *ss; int verbose = SvIV(perl_get_sv("SNMP::verbose", 0x01 | 0x04)); sess_ptr_sv = hv_fetch((HV*)SvRV(sess_ref), "SessPtr", 7, 1); ss = (SnmpSession *)SvIV((SV*)SvRV(*sess_ptr_sv)); if (!ss) goto update_end; if (!strcmp(version, "1")) { ss->version = SNMP_VERSION_1; } else if (!strcmp(version, "2") || !strcmp(version, "2c")) { ss->version = SNMP_VERSION_2c; } else if (!strcmp(version, "3")) { ss->version = SNMP_VERSION_3; } else { if (verbose) warn("Unsupported SNMP version (%s)\n", version); goto update_end; } /* WARNING LEAKAGE but I cant free lib memory under win32 */ ss->community_len = strlen((char *)community); ss->community = (u_char *)strdup(community); ss->peername = strdup(peer); ss->local_port = lport; ss->retries = retries; /* 5 */ ss->timeout = timeout; /* 1000000L */ ss->authenticator = NULL; update_end: RETVAL = ss; } OUTPUT: RETVAL int snmp_add_mib_dir(mib_dir,force=0) char * mib_dir int force CODE: { int result = 0; /* Avoid use of uninitialized variable below. */ int verbose = SvIV(perl_get_sv("SNMP::verbose", 0x01 | 0x04)); if (mib_dir && *mib_dir) { result = add_mibdir(mib_dir); } if (result) { if (verbose) warn("Added mib dir %s\n", mib_dir); } else { if (verbose) warn("Failed to add %s\n", mib_dir); } RETVAL = (I32)result; } OUTPUT: RETVAL void snmp_init_mib_internals() CODE: { int verbose = SvIV(perl_get_sv("SNMP::verbose", 0x01 | 0x04)); /* should test better to see if it has been done already */ if (Mib == NULL) { if (verbose) warn("initializing MIB internals (empty)\n"); /* init_mib_internals(); */ } } int snmp_read_mib(mib_file, force=0) char * mib_file int force CODE: { int verbose = SvIV(perl_get_sv("SNMP::verbose", 0x01 | 0x04)); /* if (Mib && force) __free_tree(Mib); needs more work to cleanup */ if ((mib_file == NULL) || (*mib_file == '\0')) { if (Mib == NULL) { if (verbose) warn("initializing MIB\n"); /* init_mib_internals(); */ init_mib(); if (Mib) { if (verbose) warn("done\n"); } else { if (verbose) warn("failed\n"); } } } else { if (verbose) warn("reading MIB: %s [%s:%s]\n", mib_file, DEFAULT_MIBDIRS, DEFAULT_MIBS); /* if (Mib == NULL) init_mib_internals();*/ if (strcmp("ALL",mib_file)) read_mib(mib_file); else read_all_mibs(); if (Mib) { if (verbose) warn("done\n"); } else { if (verbose) warn("failed\n"); } } RETVAL = (I32)Mib; } OUTPUT: RETVAL int snmp_read_module(module) char * module CODE: { int verbose = SvIV(perl_get_sv("SNMP::verbose", 0x01 | 0x04)); if (!strcmp(module,"ALL")) { read_all_mibs(); } else { read_module(module); } if (Mib) { if (verbose) warn("Read %s\n", module); } else { if (verbose) warn("Failed reading %s\n", module); } RETVAL = (I32)Mib; } OUTPUT: RETVAL int snmp_set(sess_ref, varlist_ref, perl_callback) SV * sess_ref SV * varlist_ref SV * perl_callback PPCODE: { AV *varlist; SV **varbind_ref; SV **varbind_val_f; AV *varbind; I32 varlist_len; I32 varlist_ind; SnmpSession *ss; netsnmp_pdu *pdu, *response; struct tree *tp; oid *oid_arr; int oid_arr_len = MAX_OID_LEN; char *tag_pv; snmp_xs_cb_data *xs_cb_data; SV **sess_ptr_sv; SV **err_str_svp; SV **err_num_svp; SV **err_ind_svp; int status = 0; int type; int res; int verbose = SvIV(perl_get_sv("SNMP::verbose", 0x01 | 0x04)); int use_enums; struct enum_list *ep; New (0, oid_arr, MAX_OID_LEN, oid); if (oid_arr && SvROK(sess_ref) && SvROK(varlist_ref)) { use_enums = SvIV(*hv_fetch((HV*)SvRV(sess_ref),"UseEnums",8,1)); sess_ptr_sv = hv_fetch((HV*)SvRV(sess_ref), "SessPtr", 7, 1); ss = (SnmpSession *)SvIV((SV*)SvRV(*sess_ptr_sv)); err_str_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorStr", 8, 1); err_num_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorNum", 8, 1); err_ind_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorInd", 8, 1); sv_setpv(*err_str_svp, ""); sv_setiv(*err_num_svp, 0); sv_setiv(*err_ind_svp, 0); pdu = snmp_pdu_create(SNMP_MSG_SET); varlist = (AV*) SvRV(varlist_ref); varlist_len = av_len(varlist); for(varlist_ind = 0; varlist_ind <= varlist_len; varlist_ind++) { varbind_ref = av_fetch(varlist, varlist_ind, 0); if (SvROK(*varbind_ref)) { varbind = (AV*) SvRV(*varbind_ref); tag_pv = __av_elem_pv(varbind, VARBIND_TAG_F,NULL); tp=__tag2oid(tag_pv, __av_elem_pv(varbind, VARBIND_IID_F,NULL), oid_arr, &oid_arr_len, &type,0); if (oid_arr_len==0) { if (verbose) warn("error: set: unknown object ID (%s)", (tag_pv?tag_pv:"")); sv_catpv(*err_str_svp, (char*)snmp_api_errstring(SNMPERR_UNKNOWN_OBJID)); sv_setiv(*err_num_svp, SNMPERR_UNKNOWN_OBJID); XPUSHs(&sv_undef); /* unknown OID */ snmp_free_pdu(pdu); goto done; } if (type == TYPE_UNKNOWN) { type = __translate_appl_type( __av_elem_pv(varbind, VARBIND_TYPE_F, NULL)); if (type == TYPE_UNKNOWN) { if (verbose) warn("error: set: no type found for object"); sv_catpv(*err_str_svp, (char*)snmp_api_errstring(SNMPERR_VAR_TYPE)); sv_setiv(*err_num_svp, SNMPERR_VAR_TYPE); XPUSHs(&sv_undef); /* unknown OID */ snmp_free_pdu(pdu); goto done; } } varbind_val_f = av_fetch(varbind, VARBIND_VAL_F, 0); if (type==TYPE_INTEGER && use_enums && tp && tp->enums) { for(ep = tp->enums; ep; ep = ep->next) { if (varbind_val_f && SvOK(*varbind_val_f) && !strcmp(ep->label, SvPV(*varbind_val_f,na))) { sv_setiv(*varbind_val_f, ep->value); break; } } } res = __add_var_val_str(pdu, oid_arr, oid_arr_len, (varbind_val_f && SvOK(*varbind_val_f) ? SvPV(*varbind_val_f,na):NULL), (varbind_val_f && SvOK(*varbind_val_f) ? SvCUR(*varbind_val_f):0), type); if (verbose && res == FAILURE) warn("error: adding variable/value to PDU"); } /* if var_ref is ok */ } /* for all the vars */ if (SvTRUE(perl_callback)) { xs_cb_data = (snmp_xs_cb_data*)malloc(sizeof(snmp_xs_cb_data)); xs_cb_data->perl_cb = newSVsv(perl_callback); xs_cb_data->sess_ref = newRV_inc(SvRV(sess_ref)); status = snmp_async_send(ss, pdu, __snmp_xs_cb, (void*)xs_cb_data); if (status != 0) { XPUSHs(sv_2mortal(newSViv(status))); /* push the reqid?? */ } else { snmp_free_pdu(pdu); sv_catpv(*err_str_svp, (char*)snmp_api_errstring(ss->s_snmp_errno)); sv_setiv(*err_num_svp, ss->s_snmp_errno); XPUSHs(&sv_undef); } goto done; } status = __send_sync_pdu(ss, pdu, &response, NO_RETRY_NOSUCH, *err_str_svp, *err_num_svp, *err_ind_svp); if (response) snmp_free_pdu(response); if (status) { XPUSHs(&sv_undef); } else { XPUSHs(sv_2mortal(newSVpv(ZERO_BUT_TRUE,0))); } } else { /* BUG!!! need to return an error value */ XPUSHs(&sv_undef); /* no mem or bad args */ } done: Safefree(oid_arr); } void snmp_catch(sess_ref, perl_callback) SV * sess_ref SV * perl_callback PPCODE: { netsnmp_session *ss; SV **sess_ptr_sv; SV **err_str_svp; SV **err_num_svp; SV **err_ind_svp; if (SvROK(sess_ref)) { sess_ptr_sv = hv_fetch((HV*)SvRV(sess_ref), "SessPtr", 7, 1); ss = (SnmpSession *)SvIV((SV*)SvRV(*sess_ptr_sv)); err_str_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorStr", 8, 1); err_num_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorNum", 8, 1); err_ind_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorInd", 8, 1); sv_setpv(*err_str_svp, ""); sv_setiv(*err_num_svp, 0); sv_setiv(*err_ind_svp, 0); snmp_synch_reset(ss); ss->callback = NULL; ss->callback_magic = NULL; if (SvTRUE(perl_callback)) { perl_callback = newSVsv(perl_callback); # it might be more efficient to pass the varbind_ref to # __snmp_xs_cb as part of perl_callback so it is not freed # and reconstructed for each call ss->callback = __callback_wrapper; ss->callback_magic = perl_callback; sv_2mortal(newSViv(1)); goto done; } } sv_2mortal(newSViv(0)); done: ; } void snmp_get(sess_ref, retry_nosuch, varlist_ref, perl_callback) SV * sess_ref int retry_nosuch SV * varlist_ref SV * perl_callback PPCODE: { AV *varlist; SV **varbind_ref; AV *varbind; I32 varlist_len; I32 varlist_ind; netsnmp_session *ss; netsnmp_pdu *pdu, *response; netsnmp_variable_list *vars; netsnmp_variable_list *last_vars; struct tree *tp; int len; oid *oid_arr = NULL; int oid_arr_len = MAX_OID_LEN; SV *tmp_sv; char *tag_pv; int type; char tmp_type_str[MAX_TYPE_NAME_LEN]; char str_buf[STR_BUF_SIZE]; snmp_xs_cb_data *xs_cb_data; SV **sess_ptr_sv; SV **err_str_svp; SV **err_num_svp; SV **err_ind_svp; int status; int sprintval_flag = USE_BASIC; int verbose = SvIV(perl_get_sv("SNMP::verbose", 0x01 | 0x04)); SV *sv_timestamp = NULL; New (0, oid_arr, MAX_OID_LEN, oid); if (oid_arr && SvROK(sess_ref) && SvROK(varlist_ref)) { sess_ptr_sv = hv_fetch((HV*)SvRV(sess_ref), "SessPtr", 7, 1); ss = (SnmpSession *)SvIV((SV*)SvRV(*sess_ptr_sv)); err_str_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorStr", 8, 1); err_num_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorNum", 8, 1); err_ind_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorInd", 8, 1); sv_setpv(*err_str_svp, ""); sv_setiv(*err_num_svp, 0); sv_setiv(*err_ind_svp, 0); if (SvIV(*hv_fetch((HV*)SvRV(sess_ref),"UseEnums", 8, 1))) sprintval_flag = USE_ENUMS; if (SvIV(*hv_fetch((HV*)SvRV(sess_ref),"UseSprintValue", 14, 1))) sprintval_flag = USE_SPRINT_VALUE; pdu = snmp_pdu_create(SNMP_MSG_GET); varlist = (AV*) SvRV(varlist_ref); varlist_len = av_len(varlist); for(varlist_ind = 0; varlist_ind <= varlist_len; varlist_ind++) { varbind_ref = av_fetch(varlist, varlist_ind, 0); if (SvROK(*varbind_ref)) { varbind = (AV*) SvRV(*varbind_ref); tag_pv = __av_elem_pv(varbind, VARBIND_TAG_F,NULL); tp = __tag2oid(tag_pv, __av_elem_pv(varbind, VARBIND_IID_F,NULL), oid_arr, &oid_arr_len, NULL,0); if (oid_arr_len) { snmp_add_null_var(pdu, oid_arr, oid_arr_len); } else { if (verbose) warn("error: get: unknown object ID (%s)", (tag_pv?tag_pv:"")); sv_catpv(*err_str_svp, (char*)snmp_api_errstring(SNMPERR_UNKNOWN_OBJID)); sv_setiv(*err_num_svp, SNMPERR_UNKNOWN_OBJID); XPUSHs(&sv_undef); /* unknown OID */ snmp_free_pdu(pdu); goto done; } } /* if var_ref is ok */ } /* for all the vars */ if (perl_callback && SvTRUE(perl_callback)) { xs_cb_data = (snmp_xs_cb_data*)malloc(sizeof(snmp_xs_cb_data)); xs_cb_data->perl_cb = newSVsv(perl_callback); xs_cb_data->sess_ref = newSVsv(sess_ref); status = snmp_async_send(ss, pdu, __snmp_xs_cb, (void*)xs_cb_data); if (status != 0) { XPUSHs(sv_2mortal(newSViv(status))); /* push the reqid?? */ } else { snmp_free_pdu(pdu); sv_catpv(*err_str_svp, (char*)snmp_api_errstring(ss->s_snmp_errno)); sv_setiv(*err_num_svp, ss->s_snmp_errno); XPUSHs(&sv_undef); } goto done; } status = __send_sync_pdu(ss, pdu, &response, retry_nosuch, *err_str_svp,*err_num_svp,*err_ind_svp); last_vars = (response ? response->variables : NULL); if (SvIOK(*hv_fetch((HV*)SvRV(sess_ref),"TimeStamp", 9, 1)) && SvIV(*hv_fetch((HV*)SvRV(sess_ref),"TimeStamp", 9, 1))) sv_timestamp = newSViv((IV)time(NULL)); for(varlist_ind = 0; varlist_ind <= varlist_len; varlist_ind++) { varbind_ref = av_fetch(varlist, varlist_ind, 0); if (SvROK(*varbind_ref)) { varbind = (AV*) SvRV(*varbind_ref); tp=__tag2oid(__av_elem_pv(varbind, VARBIND_TAG_F,NULL), __av_elem_pv(varbind, VARBIND_IID_F,NULL), oid_arr, &oid_arr_len, &type,0); for (vars = last_vars; vars; vars=vars->next_variable) { if (__oid_cmp(oid_arr, oid_arr_len, vars->name, vars->name_length) == 0) { if (type == TYPE_UNKNOWN) type = __translate_asn_type(vars->type); last_vars = vars->next_variable; break; } } if (vars) { __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); if (sv_timestamp) av_store(varbind, VARBIND_TYPE_F, sv_timestamp); XPUSHs(sv_mortalcopy(tmp_sv)); } else { av_store(varbind, VARBIND_VAL_F, &sv_undef); av_store(varbind, VARBIND_TYPE_F, &sv_undef); XPUSHs(&sv_undef); } } } if (response) snmp_free_pdu(response); } else { XPUSHs(&sv_undef); /* no mem or bad args */ } done: Safefree(oid_arr); } int snmp_getnext(sess_ref, varlist_ref, perl_callback) SV * sess_ref SV * varlist_ref SV * perl_callback PPCODE: { AV *varlist; SV **varbind_ref; AV *varbind; I32 varlist_len; I32 varlist_ind; netsnmp_session *ss; netsnmp_pdu *pdu, *response; netsnmp_variable_list *vars; struct tree *tp; int len; oid *oid_arr; int oid_arr_len = MAX_OID_LEN; SV *tmp_sv; int type; char tmp_type_str[MAX_TYPE_NAME_LEN]; snmp_xs_cb_data *xs_cb_data; SV **sess_ptr_sv; SV **err_str_svp; SV **err_num_svp; SV **err_ind_svp; int status; 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; int getlabel_flag = NO_FLAGS; int sprintval_flag = USE_BASIC; int verbose = SvIV(perl_get_sv("SNMP::verbose", 0x01 | 0x04)); int old_numeric, old_printfull; /* Old values of globals */ SV *sv_timestamp = NULL; New (0, oid_arr, MAX_OID_LEN, oid); if (oid_arr && SvROK(sess_ref) && SvROK(varlist_ref)) { sess_ptr_sv = hv_fetch((HV*)SvRV(sess_ref), "SessPtr", 7, 1); ss = (SnmpSession *)SvIV((SV*)SvRV(*sess_ptr_sv)); err_str_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorStr", 8, 1); err_num_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorNum", 8, 1); err_ind_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorInd", 8, 1); sv_setpv(*err_str_svp, ""); sv_setiv(*err_num_svp, 0); sv_setiv(*err_ind_svp, 0); if (SvIV(*hv_fetch((HV*)SvRV(sess_ref),"UseLongNames", 12, 1))) getlabel_flag |= USE_LONG_NAMES; if (SvIV(*hv_fetch((HV*)SvRV(sess_ref),"UseEnums", 8, 1))) sprintval_flag = USE_ENUMS; if (SvIV(*hv_fetch((HV*)SvRV(sess_ref),"UseSprintValue", 14, 1))) sprintval_flag = USE_SPRINT_VALUE; pdu = snmp_pdu_create(SNMP_MSG_GETNEXT); varlist = (AV*) SvRV(varlist_ref); varlist_len = av_len(varlist); for(varlist_ind = 0; varlist_ind <= varlist_len; varlist_ind++) { varbind_ref = av_fetch(varlist, varlist_ind, 0); if (SvROK(*varbind_ref)) { varbind = (AV*) SvRV(*varbind_ref); tp = __tag2oid(__av_elem_pv(varbind, VARBIND_TAG_F, ".0"), __av_elem_pv(varbind, VARBIND_IID_F, NULL), oid_arr, &oid_arr_len, NULL,0); if (oid_arr_len) { snmp_add_null_var(pdu, oid_arr, oid_arr_len); } else { if (verbose) warn("error: set: unknown object ID"); sv_catpv(*err_str_svp, (char*)snmp_api_errstring(SNMPERR_UNKNOWN_OBJID)); sv_setiv(*err_num_svp, SNMPERR_UNKNOWN_OBJID); XPUSHs(&sv_undef); /* unknown OID */ snmp_free_pdu(pdu); goto done; } } /* if var_ref is ok */ } /* for all the vars */ if (SvTRUE(perl_callback)) { xs_cb_data = (snmp_xs_cb_data*)malloc(sizeof(snmp_xs_cb_data)); xs_cb_data->perl_cb = newSVsv(perl_callback); xs_cb_data->sess_ref = newSVsv(sess_ref); status = snmp_async_send(ss, pdu, __snmp_xs_cb, (void*)xs_cb_data); if (status != 0) { XPUSHs(sv_2mortal(newSViv(status))); /* push the reqid?? */ } else { snmp_free_pdu(pdu); sv_catpv(*err_str_svp, (char*)snmp_api_errstring(ss->s_snmp_errno)); sv_setiv(*err_num_svp, ss->s_snmp_errno); XPUSHs(&sv_undef); } goto done; } status = __send_sync_pdu(ss, pdu, &response, NO_RETRY_NOSUCH, *err_str_svp, *err_num_svp, *err_ind_svp); /* ** 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),"UseNumeric", 10, 1))) { getlabel_flag |= USE_NUMERIC_OIDS; 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); } if (SvIOK(*hv_fetch((HV*)SvRV(sess_ref),"TimeStamp", 9, 1)) && SvIV(*hv_fetch((HV*)SvRV(sess_ref),"TimeStamp", 9, 1))) sv_timestamp = newSViv((IV)time(NULL)); for(vars = (response?response->variables:NULL), varlist_ind = 0; vars && (varlist_ind <= varlist_len); vars = vars->next_variable, varlist_ind++) { varbind_ref = av_fetch(varlist, varlist_ind, 0); if (SvROK(*varbind_ref)) { varbind = (AV*) SvRV(*varbind_ref); *str_buf = '.'; *(str_buf+1) = '\0'; 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] = '\0'; 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); if (sv_timestamp) av_store(varbind, VARBIND_TYPE_F, sv_timestamp); XPUSHs(sv_mortalcopy(tmp_sv)); } else { /* Return undef for this variable. */ XPUSHs(&sv_undef); } } /* Reset the library's behavior for numeric/symbolic OID's. */ if (getlabel_flag & 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); } if (response) snmp_free_pdu(response); } else { XPUSHs(&sv_undef); /* no mem or bad args */ } done: Safefree(oid_arr); } int snmp_getbulk(sess_ref, nonrepeaters, maxrepetitions, varlist_ref, perl_callback) SV * sess_ref int nonrepeaters int maxrepetitions SV * varlist_ref SV * perl_callback PPCODE: { AV *varlist; SV **varbind_ref; AV *varbind; I32 varlist_len; I32 varlist_ind; netsnmp_session *ss; netsnmp_pdu *pdu, *response; netsnmp_variable_list *vars; struct tree *tp; int len; oid *oid_arr; int oid_arr_len = MAX_OID_LEN; SV *tmp_sv; int type; char tmp_type_str[MAX_TYPE_NAME_LEN]; snmp_xs_cb_data *xs_cb_data; SV **sess_ptr_sv; SV **err_str_svp; SV **err_num_svp; SV **err_ind_svp; int status; 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; int getlabel_flag = NO_FLAGS; int sprintval_flag = USE_BASIC; int verbose = SvIV(perl_get_sv("SNMP::verbose", 0x01 | 0x04)); int old_numeric, old_printfull; /* Old values of globals */ SV *rv; SV *sv_timestamp = NULL; New (0, oid_arr, MAX_OID_LEN, oid); if (oid_arr && SvROK(sess_ref) && SvROK(varlist_ref)) { sess_ptr_sv = hv_fetch((HV*)SvRV(sess_ref), "SessPtr", 7, 1); ss = (SnmpSession *)SvIV((SV*)SvRV(*sess_ptr_sv)); err_str_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorStr", 8, 1); err_num_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorNum", 8, 1); err_ind_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorInd", 8, 1); sv_setpv(*err_str_svp, ""); sv_setiv(*err_num_svp, 0); sv_setiv(*err_ind_svp, 0); if (SvIV(*hv_fetch((HV*)SvRV(sess_ref),"UseLongNames", 12, 1))) getlabel_flag |= USE_LONG_NAMES; if (SvIV(*hv_fetch((HV*)SvRV(sess_ref),"UseNumeric", 10, 1))) getlabel_flag |= USE_NUMERIC_OIDS; if (SvIV(*hv_fetch((HV*)SvRV(sess_ref),"UseEnums", 8, 1))) sprintval_flag = USE_ENUMS; if (SvIV(*hv_fetch((HV*)SvRV(sess_ref),"UseSprintValue", 14, 1))) sprintval_flag = USE_SPRINT_VALUE; pdu = snmp_pdu_create(SNMP_MSG_GETBULK); pdu->errstat = nonrepeaters; pdu->errindex = maxrepetitions; varlist = (AV*) SvRV(varlist_ref); varlist_len = av_len(varlist); for(varlist_ind = 0; varlist_ind <= varlist_len; varlist_ind++) { varbind_ref = av_fetch(varlist, varlist_ind, 0); if (SvROK(*varbind_ref)) { varbind = (AV*) SvRV(*varbind_ref); __tag2oid(__av_elem_pv(varbind, VARBIND_TAG_F, "0"), __av_elem_pv(varbind, VARBIND_IID_F, NULL), oid_arr, &oid_arr_len, NULL,0); if (oid_arr_len) { snmp_add_null_var(pdu, oid_arr, oid_arr_len); } else { if (verbose) warn("error: set: unknown object ID"); sv_catpv(*err_str_svp, (char*)snmp_api_errstring(SNMPERR_UNKNOWN_OBJID)); sv_setiv(*err_num_svp, SNMPERR_UNKNOWN_OBJID); XPUSHs(&sv_undef); /* unknown OID */ snmp_free_pdu(pdu); goto done; } } /* if var_ref is ok */ } /* for all the vars */ if (SvTRUE(perl_callback)) { xs_cb_data = (snmp_xs_cb_data*)malloc(sizeof(snmp_xs_cb_data)); xs_cb_data->perl_cb = newSVsv(perl_callback); xs_cb_data->sess_ref = newSVsv(sess_ref); status = snmp_async_send(ss, pdu, __snmp_xs_cb, (void*)xs_cb_data); if (status != 0) { XPUSHs(sv_2mortal(newSViv(status))); /* push the reqid?? */ } else { snmp_free_pdu(pdu); sv_catpv(*err_str_svp, (char*)snmp_api_errstring(ss->s_snmp_errno)); sv_setiv(*err_num_svp, ss->s_snmp_errno); XPUSHs(&sv_undef); } goto done; } status = __send_sync_pdu(ss, pdu, &response, NO_RETRY_NOSUCH, *err_str_svp, *err_num_svp, *err_ind_svp); if (SvIV(*hv_fetch((HV*)SvRV(sess_ref),"TimeStamp", 9, 1))) sv_timestamp = newSViv((IV)time(NULL)); av_clear(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),"UseNumeric", 10, 1))) { getlabel_flag |= USE_NUMERIC_OIDS; 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); } if(response && response->variables) { for(vars = response->variables; vars; vars = vars->next_variable) { varbind = (AV*) newAV(); *str_buf = '.'; *(str_buf+1) = '\0'; 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] = '\0'; 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); av_store(varbind, VARBIND_TAG_F, newSVpv(label, strlen(label))); av_store(varbind, VARBIND_IID_F, newSVpv(iid, strlen(iid))); __get_type_str(type, tmp_type_str); av_store(varbind, VARBIND_TYPE_F, newSVpv(tmp_type_str, strlen(tmp_type_str))); 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); if (sv_timestamp) av_store(varbind, VARBIND_TYPE_F, SvREFCNT_inc(sv_timestamp)); rv = newRV_noinc((SV *)varbind); sv_bless(rv, gv_stashpv("SNMP::Varbind",0)); av_push(varlist, rv); XPUSHs(sv_mortalcopy(tmp_sv)); } } else { XPUSHs(&sv_undef); } /* Reset the library's behavior for numeric/symbolic OID's. */ if (getlabel_flag & 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); } if (response) snmp_free_pdu(response); } else { XPUSHs(&sv_undef); /* no mem or bad args */ } done: Safefree(oid_arr); } int snmp_bulkwalk(sess_ref, nonrepeaters, maxrepetitions, varlist_ref,perl_callback) SV * sess_ref int nonrepeaters int maxrepetitions SV * varlist_ref SV * perl_callback PPCODE: { AV *varlist; SV **varbind_ref; AV *varbind; I32 varlist_len; I32 varlist_ind; netsnmp_session *ss; netsnmp_pdu *pdu = NULL; oid oid_arr[MAX_OID_LEN]; int oid_arr_len; SV **sess_ptr_sv; SV **err_str_svp; SV **err_num_svp; SV **err_ind_svp; char str_buf[STR_BUF_SIZE]; int verbose = SvIV(perl_get_sv("SNMP::verbose", 0x01 | 0x04)); walk_context *context = NULL; /* Context for this bulkwalk */ bulktbl *bt_entry; /* Current bulktbl/OID entry */ int i; /* General purpose iterator */ int npushed; /* Number of return arrays */ int okay; /* Did bulkwalk complete okay */ if (!SvROK(sess_ref) || !SvROK(varlist_ref)) { if (verbose) warn("Bad session or varlist reference!\n"); XSRETURN_UNDEF; } sess_ptr_sv = hv_fetch((HV*)SvRV(sess_ref), "SessPtr", 7, 1); ss = (SnmpSession *)SvIV((SV*)SvRV(*sess_ptr_sv)); err_str_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorStr", 8, 1); err_num_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorNum", 8, 1); err_ind_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorInd", 8, 1); sv_setpv(*err_str_svp, ""); sv_setiv(*err_num_svp, 0); sv_setiv(*err_ind_svp, 0); /* Create and initialize a new session context for this bulkwalk. ** This will be used to carry state between callbacks. */ Newz(0x57616b6c /* "Walk" */, context, 1, walk_context); if (context == NULL) { sprintf(str_buf, "malloc(context) failed (%s)", strerror(errno)); sv_setpv(*err_str_svp, str_buf); sv_setiv(*err_num_svp, SNMPERR_MALLOC); goto err; } /* Store the Perl callback and session reference in the context. */ context->perl_cb = newSVsv(perl_callback); context->sess_ref = newSVsv(sess_ref); DBPRT(3,("bulkwalk: sess_ref = 0x%p, sess_ptr_sv = 0x%p, ss = 0x%p\n", sess_ref, sess_ptr_sv, ss)); context->getlabel_f = NO_FLAGS; /* long/numeric name flags */ context->sprintval_f = USE_BASIC; /* Don't do fancy printing */ context->req_oids = NULL; /* List of oid's requested */ context->repbase = NULL; /* Repeaters in req_oids[] */ context->reqbase = NULL; /* Ptr to start of requests */ context->nreq_oids = 0; /* Number of oid's in list */ context->repeaters = 0; /* Repeater count (see below) */ context->non_reps = nonrepeaters; /* Non-repeater var count */ context->max_reps = maxrepetitions; /* Max repetition/var count */ context->pkts_exch = 0; /* Packets exchanged in walk */ context->oid_total = 0; /* OID's received during walk */ context->oid_saved = 0; /* OID's saved as results */ if (SvIV(*hv_fetch((HV*)SvRV(sess_ref),"UseLongNames", 12, 1))) context->getlabel_f |= USE_LONG_NAMES; if (SvIV(*hv_fetch((HV*)SvRV(sess_ref),"UseNumeric", 10, 1))) context->getlabel_f |= USE_NUMERIC_OIDS; if (SvIV(*hv_fetch((HV*)SvRV(sess_ref),"UseEnums", 8, 1))) context->sprintval_f = USE_ENUMS; if (SvIV(*hv_fetch((HV*)SvRV(sess_ref),"UseSprintValue", 14, 1))) context->sprintval_f = USE_SPRINT_VALUE; /* Set up an array of bulktbl's to hold the original list of ** requested OID's. This is used to populate the PDU's with ** oid values, to contain/sort the return values, and (through ** last_oid/last_len) to determine when the bulkwalk for each ** variable has completed. */ varlist = (AV*) SvRV(varlist_ref); varlist_len = av_len(varlist) + 1; /* XXX av_len returns index of ** last element not #elements */ Newz(0, context->req_oids, varlist_len, bulktbl); if (context->req_oids == NULL) { sprintf(str_buf, "Newz(req_oids) failed (%s)", strerror(errno)); if (verbose) warn(str_buf); sv_setpv(*err_str_svp, str_buf); sv_setiv(*err_num_svp, SNMPERR_MALLOC); goto err; } /* Walk through the varbind_list, parsing and copying each OID ** into a bulktbl slot in the req_oids array. Bail if there's ** some error. Create the initial packet to send out, which ** includes the non-repeaters. */ DBPRT(1,( "Building request table:\n")); for (varlist_ind = 0; varlist_ind < varlist_len; varlist_ind++) { /* Get a handle on this entry in the request table. */ bt_entry = &context->req_oids[context->nreq_oids]; DBPRT(1,( " request %d: ", (int)varlist_ind)); /* Get the request varbind from the varlist, parse it out to ** tag and index, and copy it to the req_oid[] array slots. */ varbind_ref = av_fetch(varlist, varlist_ind, 0); if (!SvROK(*varbind_ref)) { sv_setpv(*err_str_svp, \ (char*)snmp_api_errstring(SNMPERR_BAD_NAME)); sv_setiv(*err_num_svp, SNMPERR_BAD_NAME); goto err; } varbind = (AV*) SvRV(*varbind_ref); __tag2oid(__av_elem_pv(varbind, VARBIND_TAG_F, "0"), __av_elem_pv(varbind, VARBIND_IID_F, NULL), oid_arr, &oid_arr_len, NULL, 0); if ((oid_arr_len == 0) || (oid_arr_len > MAX_OID_LEN)) { if (verbose) warn("error: bulkwalk(): unknown object ID"); sv_setpv(*err_str_svp, \ (char*)snmp_api_errstring(SNMPERR_UNKNOWN_OBJID)); sv_setiv(*err_num_svp, SNMPERR_UNKNOWN_OBJID); goto err; } /* Copy the now-parsed OID into the first available slot ** in the req_oids[] array. Set both the req_oid (original ** request) and the last_oid (last requested/seen oid) to ** the initial value. We build packets using last_oid (see ** below), so initialize last_oid to the initial request. */ Copy((void *)oid_arr, (void *)bt_entry->req_oid, oid_arr_len, oid); Copy((void *)oid_arr, (void *)bt_entry->last_oid, oid_arr_len, oid); bt_entry->req_len = oid_arr_len; bt_entry->last_len = oid_arr_len; /* Adjust offset to and count of repeaters. Note non-repeater ** OID's in the list, if appropriate. */ if (varlist_ind >= context->non_reps) { /* Store a pointer to the first repeater value. */ if (context->repbase == NULL) context->repbase = bt_entry; context->repeaters ++; } else { bt_entry->norepeat = 1; DBPRT(1,( "(nonrepeater) ")); } /* Initialize the array in which to hold the Varbinds to be ** returned for the OID or subtree. */ if ((bt_entry->vars = (AV*) newAV()) == NULL) { sv_setpv(*err_str_svp, "newAV() failed: "); sv_catpv(*err_str_svp, strerror(errno)); sv_setiv(*err_num_svp, SNMPERR_MALLOC); goto err; } DBPRT(1,( "%s\n", snprint_objid(_debugx, sizeof(_debugx), oid_arr, oid_arr_len))); context->nreq_oids ++; } /* Keep track of the number of outstanding requests. This lets us ** finish processing early if we're done with all requests. */ context->req_remain = context->nreq_oids; DBPRT(1,( "Total %d variable requests added\n", context->nreq_oids)); /* If no good variable requests were found, return an error. */ if (context->nreq_oids == 0) { sv_setpv(*err_str_svp, "No variables found in varlist"); sv_setiv(*err_num_svp, SNMPERR_NO_VARS); goto err; } /* Note that this is a good context. This allows later callbacks ** to ignore re-sent PDU's that correspond to completed (and hence ** destroyed) bulkwalk contexts. */ _context_add(context); /* For asynchronous bulkwalk requests, all we have to do at this ** point is enqueue the asynchronous GETBULK request with our ** bulkwalk-specific callback and return. Remember that the ** bulkwalk_send_pdu() function returns the reqid cast to an ** snmp_pdu pointer, or NULL on failure. Return undef if the ** initial send fails; bulkwalk_send_pdu() takes care of setting ** the various error values. ** ** From here, the callbacks do all the work, including sending ** requests for variables and handling responses. The caller's ** callback will be invoked as soon as the walk completes. */ if (SvTRUE(perl_callback)) { DBPRT(1,( "Starting asynchronous bulkwalk...\n")); pdu = _bulkwalk_send_pdu(context); if (pdu == NULL) { DBPRT(1,( "Initial asynchronous send failed...\n")); XSRETURN_UNDEF; } /* Sent okay... Return the request ID in 'pdu' as an SvIV. */ DBPRT(1,( "Okay, request id is %d\n", (int)pdu)); /* XSRETURN_IV((int)pdu); */ XPUSHs(sv_2mortal(newSViv((int)pdu))); XSRETURN(1); } /* For synchronous bulkwalk, we perform the basic send/receive ** iteration right here. Once the walk has been completed, the ** bulkwalk_finish() function will push the return values onto ** the Perl call stack, and we return. */ DBPRT(1,( "Starting synchronous bulkwalk...\n")); while (!(okay = _bulkwalk_done(context))) { /* Send a request for the next batch of variables. */ DBPRT(1, (DBOUT "Building %s GETBULK bulkwalk PDU (%d)...\n", context->pkts_exch ? "next" : "first", context->pkts_exch)); pdu = _bulkwalk_send_pdu(context); /* If the request failed, consider the walk done. */ if (pdu == NULL) { DBPRT(1,( "bulkwalk_send_pdu() failed!\n")); break; } /* Handle the variables in this response packet. Break out ** of the loop if an error occurs or no variables are found ** in the response. */ if ((i = _bulkwalk_recv_pdu(context, pdu)) <= 0) { DBPRT(2,( "bulkwalk_recv_pdu() returned %d (error/empty)\n", i)); break; } /* Free the returned pdu. Don't bother to do this for the async ** case, since the SNMP callback mechanism itself does the free ** for us. */ snmp_free_pdu(pdu); /* And loop. The call to bulkwalk_done() sets the ignore flags ** for any completed request subtrees. Next time around, they ** won't be added to the request sent to the agent. */ continue; } DBPRT(1, (DBOUT "Bulkwalk done... calling bulkwalk_finish(%s)...\n", okay ? "okay" : "error")); npushed = _bulkwalk_finish(context, okay); DBPRT(2,( "Returning %d values on the stack.\n", npushed)); XSRETURN(npushed); /* Handle error cases and clean up after ourselves. */ err: if (context->req_oids && context->nreq_oids) { bt_entry = context->req_oids; for (i = 0; i < context->nreq_oids; i++, bt_entry++) av_clear(bt_entry->vars); } if (context->req_oids) Safefree(context->req_oids); if (context) Safefree(context); if (pdu) snmp_free_pdu(pdu); XSRETURN_UNDEF; } int snmp_trapV1(sess_ref,enterprise,agent,generic,specific,uptime,varlist_ref) SV * sess_ref char * enterprise char * agent int generic int specific long uptime SV * varlist_ref PPCODE: { AV *varlist; SV **varbind_ref; SV **varbind_val_f; AV *varbind; I32 varlist_len; I32 varlist_ind; SnmpSession *ss; netsnmp_pdu *pdu = NULL; struct tree *tp; oid *oid_arr; int oid_arr_len = MAX_OID_LEN; SV **sess_ptr_sv; SV **err_str_svp; SV **err_num_svp; SV **err_ind_svp; int type; int res; int verbose = SvIV(perl_get_sv("SNMP::verbose", 0x01 | 0x04)); int use_enums = SvIV(*hv_fetch((HV*)SvRV(sess_ref),"UseEnums",8,1)); struct enum_list *ep; New (0, oid_arr, MAX_OID_LEN, oid); if (oid_arr && SvROK(sess_ref)) { sess_ptr_sv = hv_fetch((HV*)SvRV(sess_ref), "SessPtr", 7, 1); ss = (SnmpSession *)SvIV((SV*)SvRV(*sess_ptr_sv)); err_str_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorStr", 8, 1); err_num_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorNum", 8, 1); err_ind_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorInd", 8, 1); sv_setpv(*err_str_svp, ""); sv_setiv(*err_num_svp, 0); sv_setiv(*err_ind_svp, 0); pdu = snmp_pdu_create(SNMP_MSG_TRAP); if (SvROK(varlist_ref)) { varlist = (AV*) SvRV(varlist_ref); varlist_len = av_len(varlist); for(varlist_ind = 0; varlist_ind <= varlist_len; varlist_ind++) { varbind_ref = av_fetch(varlist, varlist_ind, 0); if (SvROK(*varbind_ref)) { varbind = (AV*) SvRV(*varbind_ref); tp=__tag2oid(__av_elem_pv(varbind, VARBIND_TAG_F, NULL), __av_elem_pv(varbind, VARBIND_IID_F, NULL), oid_arr, &oid_arr_len, &type,0); if (oid_arr_len == 0) { if (verbose) warn("error:trap: unable to determine oid for object"); goto err; } if (type == TYPE_UNKNOWN) { type = __translate_appl_type( __av_elem_pv(varbind, VARBIND_TYPE_F, NULL)); if (type == TYPE_UNKNOWN) { if (verbose) warn("error:trap: no type found for object"); goto err; } } varbind_val_f = av_fetch(varbind, VARBIND_VAL_F, 0); if (type==TYPE_INTEGER && use_enums && tp && tp->enums) { for(ep = tp->enums; ep; ep = ep->next) { if (varbind_val_f && SvOK(*varbind_val_f) && !strcmp(ep->label, SvPV(*varbind_val_f,na))) { sv_setiv(*varbind_val_f, ep->value); break; } } } res = __add_var_val_str(pdu, oid_arr, oid_arr_len, (varbind_val_f && SvOK(*varbind_val_f) ? SvPV(*varbind_val_f,na):NULL), (varbind_val_f && SvOK(*varbind_val_f) ? SvCUR(*varbind_val_f):0), type); if(res == FAILURE) { if(verbose) warn("error:trap: adding varbind"); goto err; } } /* if var_ref is ok */ } /* for all the vars */ } pdu->enterprise = (oid *)malloc( MAX_OID_LEN * sizeof(oid)); tp = __tag2oid(enterprise,NULL, pdu->enterprise, &pdu->enterprise_length, NULL,0); if (pdu->enterprise_length == 0) { if (verbose) warn("error:trap:invalid enterprise id: %s", enterprise); goto err; } /* If agent is given then set the v1-TRAP specific agent-address field to that. Otherwise set it to our address. */ if (agent && strlen(agent)) { if (__parse_address(agent) == -1 && verbose) { warn("error:trap:invalid agent address: %s", agent); goto err; } else { *((in_addr_t *)pdu->agent_addr) = __parse_address(agent); } } else { *((in_addr_t *)pdu->agent_addr) = get_myaddr(); } pdu->trap_type = generic; pdu->specific_type = specific; pdu->time = uptime; if (snmp_send(ss, pdu) == 0) { snmp_free_pdu(pdu); } XPUSHs(sv_2mortal(newSVpv(ZERO_BUT_TRUE,0))); } else { err: XPUSHs(&sv_undef); /* no mem or bad args */ if (pdu) snmp_free_pdu(pdu); } Safefree(oid_arr); } int snmp_trapV2(sess_ref,uptime,trap_oid,varlist_ref) SV * sess_ref char * uptime char * trap_oid SV * varlist_ref PPCODE: { AV *varlist; SV **varbind_ref; SV **varbind_val_f; AV *varbind; I32 varlist_len; I32 varlist_ind; SnmpSession *ss; netsnmp_pdu *pdu = NULL; struct tree *tp; oid *oid_arr; int oid_arr_len = MAX_OID_LEN; SV **sess_ptr_sv; SV **err_str_svp; SV **err_num_svp; SV **err_ind_svp; int type; int res; int verbose = SvIV(perl_get_sv("SNMP::verbose", 0x01 | 0x04)); int use_enums = SvIV(*hv_fetch((HV*)SvRV(sess_ref),"UseEnums",8,1)); struct enum_list *ep; New (0, oid_arr, MAX_OID_LEN, oid); if (oid_arr && SvROK(sess_ref) && SvROK(varlist_ref)) { sess_ptr_sv = hv_fetch((HV*)SvRV(sess_ref), "SessPtr", 7, 1); ss = (SnmpSession *)SvIV((SV*)SvRV(*sess_ptr_sv)); err_str_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorStr", 8, 1); err_num_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorNum", 8, 1); err_ind_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorInd", 8, 1); sv_setpv(*err_str_svp, ""); sv_setiv(*err_num_svp, 0); sv_setiv(*err_ind_svp, 0); pdu = snmp_pdu_create(SNMP_MSG_TRAP2); varlist = (AV*) SvRV(varlist_ref); varlist_len = av_len(varlist); /************************************************/ res = __add_var_val_str(pdu, sysUpTime, SYS_UPTIME_OID_LEN, uptime, strlen(uptime), TYPE_TIMETICKS); if(res == FAILURE) { if(verbose) warn("error:trap v2: adding sysUpTime varbind"); goto err; } res = __add_var_val_str(pdu, snmpTrapOID, SNMP_TRAP_OID_LEN, trap_oid ,strlen(trap_oid) ,TYPE_OBJID); if(res == FAILURE) { if(verbose) warn("error:trap v2: adding snmpTrapOID varbind"); goto err; } /******************************************************/ for(varlist_ind = 0; varlist_ind <= varlist_len; varlist_ind++) { varbind_ref = av_fetch(varlist, varlist_ind, 0); if (SvROK(*varbind_ref)) { varbind = (AV*) SvRV(*varbind_ref); tp=__tag2oid(__av_elem_pv(varbind, VARBIND_TAG_F,NULL), __av_elem_pv(varbind, VARBIND_IID_F,NULL), oid_arr, &oid_arr_len, &type,0); if (oid_arr_len == 0) { if (verbose) warn("error:trap v2: unable to determine oid for object"); goto err; } if (type == TYPE_UNKNOWN) { type = __translate_appl_type( __av_elem_pv(varbind, VARBIND_TYPE_F, NULL)); if (type == TYPE_UNKNOWN) { if (verbose) warn("error:trap v2: no type found for object"); goto err; } } varbind_val_f = av_fetch(varbind, VARBIND_VAL_F, 0); if (type==TYPE_INTEGER && use_enums && tp && tp->enums) { for(ep = tp->enums; ep; ep = ep->next) { if (varbind_val_f && SvOK(*varbind_val_f) && !strcmp(ep->label, SvPV(*varbind_val_f,na))) { sv_setiv(*varbind_val_f, ep->value); break; } } } res = __add_var_val_str(pdu, oid_arr, oid_arr_len, (varbind_val_f && SvOK(*varbind_val_f) ? SvPV(*varbind_val_f,na):NULL), (varbind_val_f && SvOK(*varbind_val_f) ? SvCUR(*varbind_val_f):0), type); if(res == FAILURE) { if(verbose) warn("error:trap v2: adding varbind"); goto err; } } /* if var_ref is ok */ } /* for all the vars */ if (snmp_send(ss, pdu) == 0) { snmp_free_pdu(pdu); } XPUSHs(sv_2mortal(newSVpv(ZERO_BUT_TRUE,0))); } else { err: XPUSHs(&sv_undef); /* no mem or bad args */ if (pdu) snmp_free_pdu(pdu); } Safefree(oid_arr); } int snmp_inform(sess_ref,uptime,trap_oid,varlist_ref,perl_callback) SV * sess_ref char * uptime char * trap_oid SV * varlist_ref SV * perl_callback PPCODE: { AV *varlist; SV **varbind_ref; SV **varbind_val_f; AV *varbind; I32 varlist_len; I32 varlist_ind; SnmpSession *ss; netsnmp_pdu *pdu = NULL; netsnmp_pdu *response; struct tree *tp; oid *oid_arr; int oid_arr_len = MAX_OID_LEN; snmp_xs_cb_data *xs_cb_data; SV **sess_ptr_sv; SV **err_str_svp; SV **err_num_svp; SV **err_ind_svp; int status = 0; int type; int res; int verbose = SvIV(perl_get_sv("SNMP::verbose", 0x01 | 0x04)); int use_enums = SvIV(*hv_fetch((HV*)SvRV(sess_ref),"UseEnums",8,1)); struct enum_list *ep; New (0, oid_arr, MAX_OID_LEN, oid); if (oid_arr && SvROK(sess_ref) && SvROK(varlist_ref)) { sess_ptr_sv = hv_fetch((HV*)SvRV(sess_ref), "SessPtr", 7, 1); ss = (SnmpSession *)SvIV((SV*)SvRV(*sess_ptr_sv)); err_str_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorStr", 8, 1); err_num_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorNum", 8, 1); err_ind_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorInd", 8, 1); sv_setpv(*err_str_svp, ""); sv_setiv(*err_num_svp, 0); sv_setiv(*err_ind_svp, 0); pdu = snmp_pdu_create(SNMP_MSG_INFORM); varlist = (AV*) SvRV(varlist_ref); varlist_len = av_len(varlist); /************************************************/ res = __add_var_val_str(pdu, sysUpTime, SYS_UPTIME_OID_LEN, uptime, strlen(uptime), TYPE_TIMETICKS); if(res == FAILURE) { if(verbose) warn("error:inform: adding sysUpTime varbind"); goto err; } res = __add_var_val_str(pdu, snmpTrapOID, SNMP_TRAP_OID_LEN, trap_oid ,strlen(trap_oid) ,TYPE_OBJID); if(res == FAILURE) { if(verbose) warn("error:inform: adding snmpTrapOID varbind"); goto err; } /******************************************************/ for(varlist_ind = 0; varlist_ind <= varlist_len; varlist_ind++) { varbind_ref = av_fetch(varlist, varlist_ind, 0); if (SvROK(*varbind_ref)) { varbind = (AV*) SvRV(*varbind_ref); tp=__tag2oid(__av_elem_pv(varbind, VARBIND_TAG_F,NULL), __av_elem_pv(varbind, VARBIND_IID_F,NULL), oid_arr, &oid_arr_len, &type,0); if (oid_arr_len == 0) { if (verbose) warn("error:inform: unable to determine oid for object"); goto err; } if (type == TYPE_UNKNOWN) { type = __translate_appl_type( __av_elem_pv(varbind, VARBIND_TYPE_F, NULL)); if (type == TYPE_UNKNOWN) { if (verbose) warn("error:inform: no type found for object"); goto err; } } varbind_val_f = av_fetch(varbind, VARBIND_VAL_F, 0); if (type==TYPE_INTEGER && use_enums && tp && tp->enums) { for(ep = tp->enums; ep; ep = ep->next) { if (varbind_val_f && SvOK(*varbind_val_f) && !strcmp(ep->label, SvPV(*varbind_val_f,na))) { sv_setiv(*varbind_val_f, ep->value); break; } } } res = __add_var_val_str(pdu, oid_arr, oid_arr_len, (varbind_val_f && SvOK(*varbind_val_f) ? SvPV(*varbind_val_f,na):NULL), (varbind_val_f && SvOK(*varbind_val_f) ? SvCUR(*varbind_val_f):0), type); if(res == FAILURE) { if(verbose) warn("error:inform: adding varbind"); goto err; } } /* if var_ref is ok */ } /* for all the vars */ if (SvTRUE(perl_callback)) { xs_cb_data = (snmp_xs_cb_data*)malloc(sizeof(snmp_xs_cb_data)); xs_cb_data->perl_cb = newSVsv(perl_callback); xs_cb_data->sess_ref = newRV_inc(SvRV(sess_ref)); status = snmp_async_send(ss, pdu, __snmp_xs_cb, (void*)xs_cb_data); if (status != 0) { XPUSHs(sv_2mortal(newSViv(status))); /* push the reqid?? */ } else { snmp_free_pdu(pdu); sv_catpv(*err_str_svp, (char*)snmp_api_errstring(ss->s_snmp_errno)); sv_setiv(*err_num_svp, ss->s_snmp_errno); XPUSHs(&sv_undef); } goto done; } status = __send_sync_pdu(ss, pdu, &response, NO_RETRY_NOSUCH, *err_str_svp, *err_num_svp, *err_ind_svp); if (response) snmp_free_pdu(response); if (status) { XPUSHs(&sv_undef); } else { XPUSHs(sv_2mortal(newSVpv(ZERO_BUT_TRUE,0))); } } else { err: XPUSHs(&sv_undef); /* no mem or bad args */ if (pdu) snmp_free_pdu(pdu); } done: Safefree(oid_arr); } char * snmp_get_type(tag) char * tag CODE: { struct tree *tp = NULL; static char type_str[MAX_TYPE_NAME_LEN]; char *ret = NULL; if (tag && *tag) tp = __tag2oid(tag, NULL, NULL, NULL, NULL,0); if (tp) __get_type_str(tp->type, ret = type_str); RETVAL = ret; } OUTPUT: RETVAL void snmp_dump_packet(flag) int flag CODE: { snmp_set_dump_packet(flag); } char * snmp_map_enum(tag, val, iflag) char * tag char * val int iflag CODE: { struct tree *tp = NULL; struct enum_list *ep; char str_buf[STR_BUF_SIZE]; int ival; RETVAL = NULL; if (tag && *tag) tp = __tag2oid(tag, NULL, NULL, NULL, NULL,0); if (tp) { if (iflag) { ival = atoi(val); for(ep = tp->enums; ep; ep = ep->next) { if (ep->value == ival) { RETVAL = ep->label; break; } } } else { for(ep = tp->enums; ep; ep = ep->next) { if (strEQ(ep->label, val)) { sprintf(str_buf,"%d", ep->value); RETVAL = str_buf; break; } } } } } OUTPUT: RETVAL #define SNMP_XLATE_MODE_OID2TAG 1 #define SNMP_XLATE_MODE_TAG2OID 0 char * snmp_translate_obj(var,mode,use_long,auto_init,best_guess) char * var int mode int use_long int auto_init int best_guess CODE: { char str_buf[STR_BUF_SIZE]; oid oid_arr[MAX_OID_LEN]; int oid_arr_len = MAX_OID_LEN; char * label; char * iid; int status = FAILURE; int verbose = SvIV(perl_get_sv("SNMP::verbose", 0x01 | 0x04)); str_buf[0] = '\0'; switch (mode) { case SNMP_XLATE_MODE_TAG2OID: if (!__tag2oid(var, NULL, oid_arr, &oid_arr_len, NULL, best_guess)) { if (verbose) warn("error:snmp_translate_obj:Unknown OID %s\n",var); } else { status = __sprint_num_objid(str_buf, oid_arr, oid_arr_len); } break; case SNMP_XLATE_MODE_OID2TAG: oid_arr_len = 0; __concat_oid_str(oid_arr, &oid_arr_len, var); snprint_objid(str_buf, sizeof(str_buf), oid_arr, oid_arr_len); if (!use_long) { label = NULL; iid = NULL; if (((status=__get_label_iid(str_buf, &label, &iid, NO_FLAGS)) == SUCCESS) && label) { strcpy(str_buf, label); if (iid && *iid) { strcat(str_buf, "."); strcat(str_buf, iid); } } } break; default: if (verbose) warn("snmp_translate_obj:unknown translation mode: %s\n", mode); } if (*str_buf) { RETVAL = (char*)str_buf; } else { RETVAL = (char*)NULL; } } OUTPUT: RETVAL void snmp_set_replace_newer(val) int val CODE: { netsnmp_ds_toggle_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_MIB_REPLACE); } void snmp_set_save_descriptions(val) int val CODE: { snmp_set_save_descriptions(val); } void snmp_set_debugging(val) int val CODE: { snmp_set_do_debugging(val); } void snmp_debug_internals(val) int val CODE: { #ifdef DEBUGGING _debug_level = val; #endif /* DEBUGGING */ } void snmp_sock_cleanup() CODE: { SOCK_CLEANUP; } void snmp_mainloop_finish() CODE: { mainloop_finish = 1; } void snmp_main_loop(timeout_sec,timeout_usec,perl_callback) int timeout_sec int timeout_usec SV * perl_callback CODE: { int numfds, fd_count; fd_set fdset; struct timeval time_val, *tvp; struct timeval last_time, *ltvp; struct timeval ctimeout, *ctvp; struct timeval interval, *itvp; int block; SV *cb; mainloop_finish = 0; itvp = &interval; itvp->tv_sec = timeout_sec; itvp->tv_usec = timeout_usec; ctvp = &ctimeout; ctvp->tv_sec = -1; ltvp = &last_time; gettimeofday(ltvp,(struct timezone*)0); timersub(ltvp,itvp,ltvp); while (1) { numfds = 0; FD_ZERO(&fdset); block = 1; tvp = &time_val; timerclear(tvp); snmp_select_info(&numfds, &fdset, tvp, &block); __recalc_timeout(tvp,ctvp,ltvp,itvp,&block); # printf("pre-select: numfds = %ld, block = %ld\n", numfds, block); if (block == 1) tvp = NULL; /* block without timeout */ fd_count = select(numfds, &fdset, 0, 0, tvp); #printf("post-select: fd_count = %ld,block = %ld\n",fd_count,block); if (fd_count > 0) { dSP; ENTER; SAVETMPS; snmp_read(&fdset); FREETMPS; LEAVE; } else switch(fd_count) { case 0: SPAGAIN; ENTER; SAVETMPS; snmp_timeout(); if (!timerisset(ctvp)) { if (SvTRUE(perl_callback)) { /* sv_2mortal(perl_callback); */ cb = __push_cb_args(perl_callback, NULL); __call_callback(cb, G_DISCARD); ctvp->tv_sec = -1; } else { FREETMPS; LEAVE; goto done; } } FREETMPS; LEAVE; break; case -1: if (errno == EINTR) { continue; } else { /* snmp_set_detail(strerror(errno)); */ /* snmp_errno = SNMPERR_GENERR; */ } default:; } /* A call to snmp_mainloop_finish() in the callback sets the ** mainloop_finish flag. Exit the loop after the callback returns. */ if (mainloop_finish) goto done; } done: return; } void snmp_get_select_info() PPCODE: { int numfds; fd_set fdset; struct timeval time_val, *tvp; int block; int i; numfds = 0; block = 1; tvp = &time_val; FD_ZERO(&fdset); snmp_select_info(&numfds, &fdset, tvp, &block); XPUSHs(sv_2mortal(newSViv(block))); if(block){ XPUSHs(sv_2mortal(newSViv(0))); XPUSHs(sv_2mortal(newSViv(0))); } else { XPUSHs(sv_2mortal(newSViv(tvp->tv_sec))); XPUSHs(sv_2mortal(newSViv(tvp->tv_usec))); } if ( numfds ) { for(i=0; iaccess) { case MIB_ACCESS_READONLY: sv_setpv(ST(0),"ReadOnly"); break; case MIB_ACCESS_READWRITE: sv_setpv(ST(0),"ReadWrite"); break; case MIB_ACCESS_WRITEONLY: sv_setpv(ST(0),"WriteOnly"); break; case MIB_ACCESS_NOACCESS: sv_setpv(ST(0),"NoAccess"); break; case MIB_ACCESS_NOTIFY: sv_setpv(ST(0),"Notify"); break; case MIB_ACCESS_CREATE: sv_setpv(ST(0),"Create"); break; default: break; } break; case 'c': /* children */ if (strncmp("children", key, strlen(key))) break; child_list_aref = newRV((SV*)newAV()); for (tp = tp->child_list; tp; tp = tp->next_peer) { mib_hv = perl_get_hv("SNMP::MIB", FALSE); if (SvMAGICAL(mib_hv)) mg = mg_find((SV*)mib_hv, 'P'); if (mg) mib_tied_href = (SV*)mg->mg_obj; next_node_href = newRV((SV*)newHV()); __tp_sprint_num_objid(str_buf, tp); nn_hrefp = hv_fetch((HV*)SvRV(mib_tied_href), str_buf, strlen(str_buf), 1); if (!SvROK(*nn_hrefp)) { sv_setsv(*nn_hrefp, next_node_href); ENTER ; SAVETMPS ; PUSHMARK(sp) ; XPUSHs(SvRV(*nn_hrefp)); XPUSHs(sv_2mortal(newSVpv("SNMP::MIB::NODE",0))); XPUSHs(sv_2mortal(newSVpv(str_buf,0))); XPUSHs(sv_2mortal(newSViv((IV)tp))); PUTBACK ; perl_call_pv("SNMP::_tie",G_VOID); /* pp_tie(ARGS); */ SPAGAIN ; FREETMPS ; LEAVE ; } /* if SvROK */ av_push((AV*)SvRV(child_list_aref), *nn_hrefp); } /* for child_list */ sv_setsv(ST(0), child_list_aref); break; case 'v': if (strncmp("varbinds", key, strlen(key))) break; varbind_av = newAV(); for (vp = tp->varbinds; vp; vp = vp->next) { av_push(varbind_av, newSVpv((vp->vblabel),strlen(vp->vblabel))); } sv_setsv(ST(0), newRV((SV*)varbind_av)); break; case 'd': /* description */ if (strncmp("description", key, strlen(key))) { if(!(strncmp("defaultValue",key,strlen(key)))) { /* We're looking at defaultValue */ sv_setpv(ST(0), tp->defaultValue); break; } /* end if */ } /* end if */ /* we must be looking at description */ sv_setpv(ST(0),tp->description); break; case 'i': /* indexes */ if (strncmp("indexes", key, strlen(key))) break; index_av = newAV(); for(ip=tp->indexes; ip != NULL; ip = ip->next) { av_push(index_av,newSVpv((ip->ilabel),strlen(ip->ilabel))); } sv_setsv(ST(0), newRV((SV*)index_av)); break; case 'l': /* label */ if (strncmp("label", key, strlen(key))) break; sv_setpv(ST(0),tp->label); break; case 'm': /* moduleID */ if (strncmp("moduleID", key, strlen(key))) break; mp = find_module(tp->modid); if (mp) sv_setpv(ST(0), mp->name); break; case 'n': /* nextNode */ if (strncmp("nextNode", key, strlen(key))) break; tp = __get_next_mib_node(tp); if (tp == NULL) { sv_setsv(ST(0), &sv_undef); break; } mib_hv = perl_get_hv("SNMP::MIB", FALSE); if (SvMAGICAL(mib_hv)) mg = mg_find((SV*)mib_hv, 'P'); if (mg) mib_tied_href = (SV*)mg->mg_obj; __tp_sprint_num_objid(str_buf, tp); nn_hrefp = hv_fetch((HV*)SvRV(mib_tied_href), str_buf, strlen(str_buf), 1); /* if (!SvROK(*nn_hrefp)) { */ /* bug in ucd - 2 .0.0 nodes */ next_node_href = newRV((SV*)newHV()); sv_setsv(*nn_hrefp, next_node_href); ENTER ; SAVETMPS ; PUSHMARK(sp) ; XPUSHs(SvRV(*nn_hrefp)); XPUSHs(sv_2mortal(newSVpv("SNMP::MIB::NODE",0))); XPUSHs(sv_2mortal(newSVpv(str_buf,0))); XPUSHs(sv_2mortal(newSViv((IV)tp))); PUTBACK ; perl_call_pv("SNMP::_tie",G_VOID); /* pp_tie(ARGS); */ SPAGAIN ; FREETMPS ; LEAVE ; /* } */ sv_setsv(ST(0), *nn_hrefp); break; case 'o': /* objectID */ if (strncmp("objectID", key, strlen(key))) break; __tp_sprint_num_objid(str_buf, tp); sv_setpv(ST(0),str_buf); break; case 'p': /* parent */ if (strncmp("parent", key, strlen(key))) break; tp = tp->parent; if (tp == NULL) { sv_setsv(ST(0), &sv_undef); break; } mib_hv = perl_get_hv("SNMP::MIB", FALSE); if (SvMAGICAL(mib_hv)) mg = mg_find((SV*)mib_hv, 'P'); if (mg) mib_tied_href = (SV*)mg->mg_obj; next_node_href = newRV((SV*)newHV()); __tp_sprint_num_objid(str_buf, tp); nn_hrefp = hv_fetch((HV*)SvRV(mib_tied_href), str_buf, strlen(str_buf), 1); if (!SvROK(*nn_hrefp)) { sv_setsv(*nn_hrefp, next_node_href); ENTER ; SAVETMPS ; PUSHMARK(sp) ; XPUSHs(SvRV(*nn_hrefp)); XPUSHs(sv_2mortal(newSVpv("SNMP::MIB::NODE",0))); XPUSHs(sv_2mortal(newSVpv(str_buf,0))); XPUSHs(sv_2mortal(newSViv((IV)tp))); PUTBACK ; perl_call_pv("SNMP::_tie",G_VOID); /* pp_tie(ARGS); */ SPAGAIN ; FREETMPS ; LEAVE ; } sv_setsv(ST(0), *nn_hrefp); break; case 'r': /* ranges */ if (strncmp("ranges", key, strlen(key))) break; ranges_av = newAV(); for(rp=tp->ranges; rp ; rp = rp->next) { range_hv = newHV(); hv_store(range_hv, "low", strlen("low"), newSViv(rp->low), 0); hv_store(range_hv, "high", strlen("high"), newSViv(rp->high), 0); av_push(ranges_av, newRV((SV*)range_hv)); } sv_setsv(ST(0), newRV((SV*)ranges_av)); break; case 's': /* subID */ if (strncmp("subID", key, strlen(key))) { if (strncmp("status", key, strlen(key))) { if (strncmp("syntax", key, strlen(key))) break; if (tp->tc_index >= 0) { sv_setpv(ST(0), get_tc_descriptor(tp->tc_index)); } else { __get_type_str(tp->type, str_buf); sv_setpv(ST(0), str_buf); } break; } switch(tp->status) { case MIB_STATUS_MANDATORY: sv_setpv(ST(0),"Mandatory"); break; case MIB_STATUS_OPTIONAL: sv_setpv(ST(0),"Optional"); break; case MIB_STATUS_OBSOLETE: sv_setpv(ST(0),"Obsolete"); break; case MIB_STATUS_DEPRECATED: sv_setpv(ST(0),"Deprecated"); break; case MIB_STATUS_CURRENT: sv_setpv(ST(0),"Current"); break; default: break; } } else { sv_setiv(ST(0),(I32)tp->subid); } break; case 't': /* type */ if (strncmp("type", key, strlen(key))) { if (strncmp("textualConvention", key, strlen(key))) break; sv_setpv(ST(0), get_tc_descriptor(tp->tc_index)); break; } __get_type_str(tp->type, str_buf); sv_setpv(ST(0), str_buf); break; case 'u': /* units */ if (strncmp("units", key, strlen(key))) break; sv_setpv(ST(0),tp->units); break; case 'h': /* hint */ if (strncmp("hint", key, strlen(key))) break; sv_setpv(ST(0),tp->hint); break; case 'e': /* enums */ if (strncmp("enums", key, strlen(key))) break; enum_hv = newHV(); for(ep=tp->enums; ep != NULL; ep = ep->next) { hv_store(enum_hv, ep->label, strlen(ep->label), newSViv(ep->value), 0); } sv_setsv(ST(0), newRV((SV*)enum_hv)); break; default: break; } } MODULE = SNMP PACKAGE = SnmpSessionPtr PREFIX = snmp_session_ void snmp_session_DESTROY(sess_ptr) SnmpSession *sess_ptr CODE: { snmp_close( sess_ptr ); }