SNMP.xs
上传用户:wxp200602
上传日期:2007-10-30
资源大小:4028k
文件大小:171k
源码类别:

SNMP编程

开发平台:

Unix_Linux

  1. /* -*- C -*-
  2.      SNMP.xs -- Perl 5 interface to the UCD SNMP toolkit
  3.      written by G. S. Marzot (gmarzot@nortelnetworks.com)
  4.      Copyright (c) 1995-1999 G. S. Marzot. All rights reserved.
  5.      This program is free software; you can redistribute it and/or
  6.      modify it under the same terms as Perl itself.
  7. */
  8. #define WIN32SCK_IS_STDSCK
  9. #include "EXTERN.h"
  10. #include "perl.h"
  11. #include "XSUB.h"
  12. #include <net-snmp/net-snmp-config.h>
  13. #include <net-snmp/net-snmp-includes.h>
  14. #include <sys/types.h>
  15. #include <arpa/inet.h>
  16. #include <errno.h>
  17. #ifndef MSVC_PERL
  18. #include <signal.h>
  19. #endif
  20. #include <stdio.h>
  21. #include <ctype.h>
  22. #ifdef I_SYS_TIME
  23. #include <sys/time.h>
  24. #endif
  25. #include <netdb.h>
  26. #include <stdlib.h>
  27. #ifndef MSVC_PERL
  28. #include <unistd.h>
  29. #endif
  30. #ifdef HAVE_REGEX_H
  31. #include <regex.h>
  32. #endif
  33. #ifndef __P
  34. #define __P(x) x
  35. #endif
  36. #ifndef na
  37. #define na PL_na
  38. #endif
  39. #ifndef sv_undef
  40. #define sv_undef PL_sv_undef
  41. #endif
  42. #ifndef stack_base
  43. #define stack_base PL_stack_base
  44. #endif
  45. #ifndef G_VOID
  46. #define G_VOID G_DISCARD
  47. #endif
  48. #ifdef WIN32
  49. #define SOCK_STARTUP winsock_startup()
  50. #define SOCK_CLEANUP winsock_cleanup()
  51. #define strcasecmp _stricmp
  52. #define strncasecmp _strnicmp
  53. #else
  54. #define SOCK_STARTUP
  55. #define SOCK_CLEANUP
  56. #endif
  57. #include "perlsnmp.h"
  58. #define SUCCESS 1
  59. #define FAILURE 0
  60. #define ZERO_BUT_TRUE "0 but true"
  61. #define VARBIND_TAG_F 0
  62. #define VARBIND_IID_F 1
  63. #define VARBIND_VAL_F 2
  64. #define VARBIND_TYPE_F 3
  65. #define TYPE_UNKNOWN 0
  66. #define MAX_TYPE_NAME_LEN 32
  67. #define STR_BUF_SIZE (MAX_TYPE_NAME_LEN * MAX_OID_LEN)
  68. #define ENG_ID_BUF_SIZE 32
  69. #define SYS_UPTIME_OID_LEN 9
  70. #define SNMP_TRAP_OID_LEN 11
  71. #define NO_RETRY_NOSUCH 0
  72. static oid sysUpTime[SYS_UPTIME_OID_LEN] = {1, 3, 6, 1, 2, 1, 1, 3, 0};
  73. static oid snmpTrapOID[SNMP_TRAP_OID_LEN] = {1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0};
  74. /* Internal flag to determine snmp_main_loop() should return after callback */
  75. static int mainloop_finish = 0;
  76. /* these should be part of transform_oids.h ? */
  77. #define USM_AUTH_PROTO_MD5_LEN 10
  78. #define USM_AUTH_PROTO_SHA_LEN 10
  79. #define USM_PRIV_PROTO_DES_LEN 10
  80. /* why does ucd-snmp redefine sockaddr_in ??? */
  81. #define SIN_ADDR(snmp_addr) (((struct sockaddr_in *) &(snmp_addr))->sin_addr)
  82. typedef netsnmp_session SnmpSession;
  83. typedef struct tree SnmpMibNode;
  84. typedef struct snmp_xs_cb_data {
  85.     SV* perl_cb;
  86.     SV* sess_ref;
  87. } snmp_xs_cb_data;
  88. static void __recalc_timeout _((struct timeval*,struct timeval*,
  89.                                 struct timeval*,struct timeval*, int* ));
  90. static in_addr_t __parse_address _((char*));
  91. static int __is_numeric_oid _((char*));
  92. static int __is_leaf _((struct tree*));
  93. static int __translate_appl_type _((char*));
  94. static int __translate_asn_type _((int));
  95. static int __snprint_value _((char *, size_t,
  96.                               netsnmp_variable_list*, struct tree *,
  97.                              int, int));
  98. static int __sprint_num_objid _((char *, oid *, int));
  99. static int __scan_num_objid _((char *, oid *, int *));
  100. static int __get_type_str _((int, char *));
  101. static int __get_label_iid _((char *, char **, char **, int));
  102. static int __oid_cmp _((oid *, int, oid *, int));
  103. static int __tp_sprint_num_objid _((char*,SnmpMibNode *));
  104. static SnmpMibNode * __get_next_mib_node _((SnmpMibNode *));
  105. static struct tree * __oid2tp _((oid*, int, struct tree *, int*));
  106. static struct tree * __tag2oid _((char *, char *, oid  *, int  *, int *, int));
  107. static int __concat_oid_str _((oid *, int *, char *));
  108. static int __add_var_val_str _((netsnmp_pdu *, oid *, int, char *,
  109.                                  int, int));
  110. static int __send_sync_pdu _((netsnmp_session *, netsnmp_pdu *,
  111.                               netsnmp_pdu **, int , SV *, SV *, SV *));
  112. static int __snmp_xs_cb __P((int, netsnmp_session *, int,
  113.                              netsnmp_pdu *, void *));
  114. static int __callback_wrapper __P((int, netsnmp_session *, int,
  115.                              netsnmp_pdu *, void *));
  116. static SV* __push_cb_args2 _((SV * sv, SV * esv, SV * tsv));
  117. #define __push_cb_args(a,b) __push_cb_args2(a,b,NULL)
  118. static int __call_callback _((SV * sv, int flags));
  119. static char* __av_elem_pv _((AV * av, I32 key, char *dflt));
  120. static u_int compute_match _((const char *, const char *));
  121. #define USE_NUMERIC_OIDS 0x08
  122. #define NON_LEAF_NAME 0x04
  123. #define USE_LONG_NAMES 0x02
  124. #define FAIL_ON_NULL_IID 0x01
  125. #define NO_FLAGS 0x00
  126. /* Structures used by snmp_bulkwalk method to track requested OID's/subtrees. */
  127. typedef struct bulktbl {
  128.    oid req_oid[MAX_OID_LEN]; /* The OID originally requested.    */
  129.    oid last_oid[MAX_OID_LEN]; /* Last-seen OID under this branch. */
  130.    AV *vars; /* Array of Varbinds for this OID.  */
  131.    int req_len; /* Length of requested OID.         */
  132.    int last_len; /* Length of last-seen OID.         */
  133.    char norepeat; /* Is this a non-repeater OID?      */
  134.    char complete; /* Non-zero if this tree complete.  */
  135.    char ignore; /* Ignore this OID, not requested.  */
  136. } bulktbl;
  137. /* Context for bulkwalk() sessions.  Used to store state across callbacks. */
  138. typedef struct walk_context {
  139.    SV *sess_ref; /* Reference to Perl SNMP session object.   */
  140.    SV *perl_cb; /* Pointer to Perl callback func or array.  */
  141.    bulktbl *req_oids; /* Pointer to bulktbl[] for requested OIDs. */
  142.    bulktbl *repbase; /* Pointer to first repeater in req_oids[]. */
  143.    bulktbl *reqbase; /* Pointer to start of requests req_oids[]. */
  144.    int    nreq_oids; /* Number of valid bulktbls in req_oids[].  */
  145.    int    req_remain; /* Number of outstanding requests remaining */
  146.    int non_reps; /* Number of nonrepeater vars in req_oids[] */
  147.    int repeaters; /* Number of repeater vars in req_oids[].   */
  148.    int max_reps; /* Maximum repetitions of variable per PDU. */
  149.    int exp_reqid; /* Expect a response to this request only.  */
  150.    int getlabel_f; /* Flag long/numeric names for get_label(). */
  151.    int sprintval_f; /* Flag enum/sprint values for sprintval(). */
  152.    int pkts_exch; /* Number of packet exchanges with agent.   */
  153.    int oid_total; /* Total number of OIDs received this walk. */
  154.    int oid_saved; /* Total number of OIDs saved as results.   */
  155. } walk_context;
  156. /* Prototypes for bulkwalk support functions. */
  157. static netsnmp_pdu *_bulkwalk_send_pdu _((walk_context *context));
  158. static int _bulkwalk_done     _((walk_context *context));
  159. static int _bulkwalk_recv_pdu _((walk_context *context, netsnmp_pdu *pdu));
  160. static int _bulkwalk_finish   _((walk_context *context, int okay));
  161. static int _bulkwalk_async_cb _((int op, SnmpSession *ss, int reqid,
  162.      netsnmp_pdu *pdu, void *context_ptr));
  163. /* Structure to hold valid context sessions. */
  164. struct valid_contexts {
  165.    walk_context **valid; /* Array of valid walk_context pointers.    */
  166.    int sz_valid; /* Maximum size of valid contexts array.    */
  167.    int num_valid; /* Count of valid contexts in the array.    */
  168. };
  169. static struct valid_contexts  *_valid_contexts = NULL;
  170. static int _context_add       _((walk_context *context));
  171. static int _context_del       _((walk_context *context));
  172. static int _context_okay      _((walk_context *context));
  173. /* Wrapper around fprintf(stderr, ...) for clean and easy debug output. */
  174. #ifdef DEBUGGING
  175. static int _debug_level = 0;
  176. #define DBOUT PerlIO_stderr(),
  177. #define DBPRT(severity, otherargs)
  178. do {
  179.     if (_debug_level && severity <= _debug_level) {
  180. (void)PerlIO_printf otherargs;
  181.     }
  182. } while (/*CONSTCOND*/0)
  183. char _debugx[1024]; /* Space to sprintf() into - used by sprint_objid(). */
  184. #else /* DEBUGGING */
  185. #define DBOUT
  186. #define DBPRT(severity, otherargs) /* Ignore */
  187. #endif /* DEBUGGING */
  188. void
  189. __libraries_init(char *appname)
  190.     {
  191.         static int have_inited = 0;
  192.         if (have_inited)
  193.             return;
  194.         have_inited = 1;
  195.         snmp_set_quick_print(1);
  196.         snmp_enable_stderrlog();
  197.         init_snmp(appname);
  198.     
  199.         netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_BREAKDOWN_OIDS, 1);
  200.         netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_SUFFIX_ONLY, 1);
  201. netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT,
  202.                                               NETSNMP_OID_OUTPUT_SUFFIX);
  203.         SOCK_STARTUP;
  204.     
  205.     }
  206. static void
  207. __recalc_timeout (tvp, ctvp, ltvp, itvp, block)
  208. struct timeval* tvp;
  209. struct timeval* ctvp;
  210. struct timeval* ltvp;
  211. struct timeval* itvp;
  212. int *block;
  213. {
  214.    struct timeval now;
  215.    if (!timerisset(itvp)) return;  /* interval zero means loop forever */
  216.    *block = 0;
  217.    gettimeofday(&now,(struct timezone *)0);
  218.    if (ctvp->tv_sec < 0) { /* first time or callback just fired */
  219.       timersub(&now,ltvp,ctvp);
  220.       timersub(ctvp,itvp,ctvp);
  221.       timersub(itvp,ctvp,ctvp);
  222.       timeradd(ltvp,itvp,ltvp);
  223.    } else {
  224.       timersub(&now,ltvp,ctvp);
  225.       timersub(itvp,ctvp,ctvp);
  226.    }
  227.    /* flag is set for callback but still hasnt fired so set to something
  228.     * small and we will service packets first if there are any ready
  229.     * (also guard against negative timeout - should never happen?)
  230.     */
  231.    if (!timerisset(ctvp) || ctvp->tv_sec < 0 || ctvp->tv_usec < 0) {
  232.       ctvp->tv_sec = 0;
  233.       ctvp->tv_usec = 10;
  234.    }
  235.    /* if snmp timeout > callback timeout or no more requests to process */
  236.    if (timercmp(tvp, ctvp, >) || !timerisset(tvp)) {
  237.       *tvp = *ctvp; /* use the smaller non-zero timeout */
  238.       timerclear(ctvp); /* used as a flag to let callback fire on timeout */
  239.    }
  240. }
  241. static in_addr_t
  242. __parse_address(address)
  243. char *address;
  244. {
  245.     in_addr_t addr;
  246.     struct sockaddr_in saddr;
  247.     struct hostent *hp;
  248.     if ((addr = inet_addr(address)) != -1)
  249. return addr;
  250.     hp = gethostbyname(address);
  251.     if (hp == NULL){
  252.         return (-1); /* error value */
  253.     } else {
  254. memcpy(&saddr.sin_addr, hp->h_addr, hp->h_length);
  255. return saddr.sin_addr.s_addr;
  256.     }
  257. }
  258. static int
  259. __is_numeric_oid (oidstr)
  260. char* oidstr;
  261. {
  262.   if (!oidstr) return 0;
  263.   for (; *oidstr; oidstr++) {
  264.      if (isalpha((int)*oidstr)) return 0;
  265.   }
  266.   return(1);
  267. }
  268. static int
  269. __is_leaf (tp)
  270. struct tree* tp;
  271. {
  272.    char buf[MAX_TYPE_NAME_LEN];
  273.    return (tp && __get_type_str(tp->type,buf));
  274. }
  275. static SnmpMibNode*
  276. __get_next_mib_node (tp)
  277. SnmpMibNode* tp;
  278. {
  279.    /* printf("tp = %lX, parent = %lX, peer = %lX, child = %lXn",
  280.               tp, tp->parent, tp->next_peer, tp->child_list); */
  281.    if (tp->child_list) return(tp->child_list);
  282.    if (tp->next_peer) return(tp->next_peer);
  283.    if (!tp->parent) return(NULL);
  284.    for (tp = tp->parent; !tp->next_peer; tp = tp->parent) {
  285.       if (!tp->parent) return(NULL);
  286.    }
  287.    return(tp->next_peer);
  288. }
  289. static int
  290. __translate_appl_type(typestr)
  291. char* typestr;
  292. {
  293. if (typestr == NULL || *typestr == '') return TYPE_UNKNOWN;
  294. if (!strncasecmp(typestr,"INTEGER32",8))
  295.             return(TYPE_INTEGER32);
  296. if (!strncasecmp(typestr,"INTEGER",3))
  297.             return(TYPE_INTEGER);
  298. if (!strncasecmp(typestr,"UNSIGNED32",3))
  299.             return(TYPE_UNSIGNED32);
  300. if (!strcasecmp(typestr,"COUNTER")) /* check all in case counter64 */
  301.             return(TYPE_COUNTER);
  302. if (!strncasecmp(typestr,"GAUGE",3))
  303.             return(TYPE_GAUGE);
  304. if (!strncasecmp(typestr,"IPADDR",3))
  305.             return(TYPE_IPADDR);
  306. if (!strncasecmp(typestr,"OCTETSTR",3))
  307.             return(TYPE_OCTETSTR);
  308. if (!strncasecmp(typestr,"TICKS",3))
  309.             return(TYPE_TIMETICKS);
  310. if (!strncasecmp(typestr,"OPAQUE",3))
  311.             return(TYPE_OPAQUE);
  312. if (!strncasecmp(typestr,"OBJECTID",3))
  313.             return(TYPE_OBJID);
  314. if (!strncasecmp(typestr,"NETADDR",3))
  315.     return(TYPE_NETADDR);
  316. if (!strncasecmp(typestr,"COUNTER64",3))
  317.     return(TYPE_COUNTER64);
  318. if (!strncasecmp(typestr,"NULL",3))
  319.     return(TYPE_NULL);
  320. if (!strncasecmp(typestr,"BITS",3))
  321.     return(TYPE_BITSTRING);
  322. if (!strncasecmp(typestr,"ENDOFMIBVIEW",3))
  323.     return(SNMP_ENDOFMIBVIEW);
  324. if (!strncasecmp(typestr,"NOSUCHOBJECT",7))
  325.     return(SNMP_NOSUCHOBJECT);
  326. if (!strncasecmp(typestr,"NOSUCHINSTANCE",7))
  327.     return(SNMP_NOSUCHINSTANCE);
  328. if (!strncasecmp(typestr,"UINTEGER",3))
  329.     return(TYPE_UINTEGER); /* historic - should not show up */
  330.                                    /* but it does?                  */
  331. if (!strncasecmp(typestr, "NOTIF", 3))
  332. return(TYPE_NOTIFTYPE);
  333. if (!strncasecmp(typestr, "TRAP", 4))
  334. return(TYPE_TRAPTYPE);
  335.         return(TYPE_UNKNOWN);
  336. }
  337. static int
  338. __translate_asn_type(type)
  339. int type;
  340. {
  341.    switch (type) {
  342.         case ASN_INTEGER:
  343.             return(TYPE_INTEGER);
  344.     break;
  345. case ASN_OCTET_STR:
  346.             return(TYPE_OCTETSTR);
  347.     break;
  348. case ASN_OPAQUE:
  349.             return(TYPE_OPAQUE);
  350.     break;
  351. case ASN_OBJECT_ID:
  352.             return(TYPE_OBJID);
  353.     break;
  354. case ASN_TIMETICKS:
  355.             return(TYPE_TIMETICKS);
  356.     break;
  357. case ASN_GAUGE:
  358.             return(TYPE_GAUGE);
  359.     break;
  360. case ASN_COUNTER:
  361.             return(TYPE_COUNTER);
  362.     break;
  363. case ASN_IPADDRESS:
  364.             return(TYPE_IPADDR);
  365.     break;
  366. case ASN_BIT_STR:
  367.             return(TYPE_BITSTRING);
  368.     break;
  369. case ASN_NULL:
  370.             return(TYPE_NULL);
  371.     break;
  372. /* no translation for these exception type values */
  373. case SNMP_ENDOFMIBVIEW:
  374. case SNMP_NOSUCHOBJECT:
  375. case SNMP_NOSUCHINSTANCE:
  376.     return(type);
  377.     break;
  378. case ASN_UINTEGER:
  379.             return(TYPE_UINTEGER);
  380.     break;
  381. case ASN_COUNTER64:
  382.             return(TYPE_COUNTER64);
  383.     break;
  384. default:
  385.             warn("translate_asn_type: unhandled asn type (%d)n",type);
  386.             return(TYPE_OTHER);
  387.             break;
  388.         }
  389. }
  390. #define USE_BASIC 0
  391. #define USE_ENUMS 1
  392. #define USE_SPRINT_VALUE 2
  393. static int
  394. __snprint_value (buf, buf_len, var, tp, type, flag)
  395. char * buf;
  396. size_t buf_len;
  397. netsnmp_variable_list * var;
  398. struct tree * tp;
  399. int type;
  400. int flag;
  401. {
  402.    int len = 0;
  403.    u_char* ip;
  404.    struct enum_list *ep;
  405.    buf[0] = '';
  406.    if (flag == USE_SPRINT_VALUE) {
  407. snprint_value(buf, buf_len, var->name, var->name_length, var);
  408. len = strlen(buf);
  409.    } else {
  410.      switch (var->type) {
  411.         case ASN_INTEGER:
  412.            if (flag == USE_ENUMS) {
  413.               for(ep = tp->enums; ep; ep = ep->next) {
  414.                  if (ep->value == *var->val.integer) {
  415.                     strcpy(buf, ep->label);
  416.                     len = strlen(buf);
  417.                     break;
  418.                  }
  419.               }
  420.            }
  421.            if (!len) {
  422.               sprintf(buf,"%ld", *var->val.integer);
  423.               len = strlen(buf);
  424.            }
  425.            break;
  426.         case ASN_GAUGE:
  427.         case ASN_COUNTER:
  428.         case ASN_TIMETICKS:
  429.         case ASN_UINTEGER:
  430.            sprintf(buf,"%lu", (unsigned long) *var->val.integer);
  431.            len = strlen(buf);
  432.            break;
  433.         case ASN_OCTET_STR:
  434.         case ASN_OPAQUE:
  435.            memcpy(buf, (char*)var->val.string, var->val_len);
  436.            len = var->val_len;
  437.            break;
  438.         case ASN_IPADDRESS:
  439.           ip = (u_char*)var->val.string;
  440.           sprintf(buf, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
  441.           len = strlen(buf);
  442.           break;
  443.         case ASN_NULL:
  444.            break;
  445.         case ASN_OBJECT_ID:
  446.           __sprint_num_objid(buf, (oid *)(var->val.objid),
  447.                              var->val_len/sizeof(oid));
  448.           len = strlen(buf);
  449.           break;
  450. case SNMP_ENDOFMIBVIEW:
  451.           sprintf(buf,"%s", "ENDOFMIBVIEW");
  452.   break;
  453. case SNMP_NOSUCHOBJECT:
  454.   sprintf(buf,"%s", "NOSUCHOBJECT");
  455.   break;
  456. case SNMP_NOSUCHINSTANCE:
  457.   sprintf(buf,"%s", "NOSUCHINSTANCE");
  458.   break;
  459.         case ASN_COUNTER64:
  460.           printU64(buf,(struct counter64 *)var->val.counter64);
  461.           len = strlen(buf);
  462.           break;
  463.         case ASN_BIT_STR:
  464.             snprint_bitstring(buf, sizeof(buf), var, NULL, NULL, NULL);
  465.             len = strlen(buf);
  466.             break;
  467.         case ASN_NSAP:
  468.         default:
  469.            warn("snprint_value: asn type not handled %dn",var->type);
  470.      }
  471.    }
  472.    return(len);
  473. }
  474. static int
  475. __sprint_num_objid (buf, objid, len)
  476. char *buf;
  477. oid *objid;
  478. int len;
  479. {
  480.    int i;
  481.    buf[0] = '';
  482.    for (i=0; i < len; i++) {
  483. sprintf(buf,".%lu",*objid++);
  484. buf += strlen(buf);
  485.    }
  486.    return SUCCESS;
  487. }
  488. static int
  489. __tp_sprint_num_objid (buf, tp)
  490. char *buf;
  491. SnmpMibNode *tp;
  492. {
  493.    oid newname[MAX_OID_LEN], *op;
  494.    /* code taken from get_node in snmp_client.c */
  495.    for (op = newname + MAX_OID_LEN - 1; op >= newname; op--) {
  496.       *op = tp->subid;
  497.       tp = tp->parent;
  498.       if (tp == NULL) break;
  499.    }
  500.    return __sprint_num_objid(buf, op, newname + MAX_OID_LEN - op);
  501. }
  502. static int
  503. __scan_num_objid (buf, objid, len)
  504. char *buf;
  505. oid *objid;
  506. int *len;
  507. {
  508.    char *cp;
  509.    *len = 0;
  510.    if (*buf == '.') buf++;
  511.    cp = buf;
  512.    while (*buf) {
  513.       if (*buf++ == '.') {
  514.          sscanf(cp, "%lu", objid++);
  515.          /* *objid++ = atoi(cp); */
  516.          (*len)++;
  517.          cp = buf;
  518.       } else {
  519.          if (isalpha((int)*buf)) {
  520.     return FAILURE;
  521.          }
  522.       }
  523.    }
  524.    sscanf(cp, "%lu", objid++);
  525.    /* *objid++ = atoi(cp); */
  526.    (*len)++;
  527.    return SUCCESS;
  528. }
  529. static int
  530. __get_type_str (type, str)
  531. int type;
  532. char * str;
  533. {
  534.    switch (type) {
  535. case TYPE_OBJID:
  536.         strcpy(str, "OBJECTID");
  537.         break;
  538. case TYPE_OCTETSTR:
  539.         strcpy(str, "OCTETSTR");
  540.         break;
  541. case TYPE_INTEGER:
  542.         strcpy(str, "INTEGER");
  543.         break;
  544. case TYPE_INTEGER32:
  545.         strcpy(str, "INTEGER32");
  546.         break;
  547. case TYPE_UNSIGNED32:
  548.         strcpy(str, "UNSIGNED32");
  549.         break;
  550. case TYPE_NETADDR:
  551.         strcpy(str, "NETADDR");
  552.         break;
  553. case TYPE_IPADDR:
  554.         strcpy(str, "IPADDR");
  555.         break;
  556. case TYPE_COUNTER:
  557.         strcpy(str, "COUNTER");
  558.         break;
  559. case TYPE_GAUGE:
  560.         strcpy(str, "GAUGE");
  561.         break;
  562. case TYPE_TIMETICKS:
  563.         strcpy(str, "TICKS");
  564.         break;
  565. case TYPE_OPAQUE:
  566.         strcpy(str, "OPAQUE");
  567.         break;
  568. case TYPE_COUNTER64:
  569.         strcpy(str, "COUNTER64");
  570.         break;
  571. case TYPE_NULL:
  572.                 strcpy(str, "NULL");
  573.                 break;
  574. case SNMP_ENDOFMIBVIEW:
  575.                 strcpy(str, "ENDOFMIBVIEW");
  576.                 break;
  577. case SNMP_NOSUCHOBJECT:
  578.                 strcpy(str, "NOSUCHOBJECT");
  579.                 break;
  580. case SNMP_NOSUCHINSTANCE:
  581.                 strcpy(str, "NOSUCHINSTANCE");
  582.                 break;
  583. case TYPE_UINTEGER:
  584.                 strcpy(str, "UINTEGER"); /* historic - should not show up */
  585.                                           /* but it does?                  */
  586.                 break;
  587. case TYPE_NOTIFTYPE:
  588. strcpy(str, "NOTIF");
  589. break;
  590. case TYPE_BITSTRING:
  591. strcpy(str, "BITS");
  592. break;
  593. case TYPE_TRAPTYPE:
  594. strcpy(str, "TRAP");
  595. break;
  596. case TYPE_OTHER: /* not sure if this is a valid leaf type?? */
  597. case TYPE_NSAPADDRESS:
  598.         default: /* unsupported types for now */
  599.            strcpy(str, "");
  600.            return(FAILURE);
  601.    }
  602.    return SUCCESS;
  603. }
  604. /* does a destructive disection of <label1>...<labeln>.<iid> returning
  605.    <labeln> and <iid> in seperate strings (note: will destructively
  606.    alter input string, 'name') */
  607. static int
  608. __get_label_iid (name, last_label, iid, flag)
  609. char * name;
  610. char ** last_label;
  611. char ** iid;
  612. int flag;
  613. {
  614.    char *lcp;
  615.    char *icp;
  616.    int len = strlen(name);
  617.    int found_label = 0;
  618.    *last_label = *iid = NULL;
  619.    if (len == 0) return(FAILURE);
  620.    /* Handle case where numeric oid's have been requested.  The input 'name'
  621.    ** in this case should be a numeric OID -- return failure if not.
  622.    */
  623.    if ((flag & USE_NUMERIC_OIDS)) {
  624.       if (!__is_numeric_oid(name))
  625.        return(FAILURE);
  626.       /* Walk backward through the string, looking for first two '.' chars */
  627.       lcp = &(name[len]);
  628.       icp = NULL;
  629.       while (lcp > name) {
  630.        if (*lcp == '.') {
  631.           /* If this is the first occurence of '.', note it in icp.
  632.           ** Otherwise, this must be the second occurrence, so break
  633.           ** out of the loop.
  634.           */
  635.           if (icp == NULL)
  636.              icp = lcp;
  637.           else
  638.              break;
  639.        }
  640.        lcp --;
  641.       }
  642.       /* Make sure we found at least a label and index. */
  643.       if (!icp)
  644.          return(FAILURE);
  645.       /* Push forward past leading '.' chars and separate the strings. */
  646.       lcp ++;
  647.       *icp ++ = '';
  648.       *last_label = (flag & USE_LONG_NAMES) ? name : lcp;
  649.       *iid        = icp;
  650.       return(SUCCESS);
  651.    }
  652.    lcp = icp = &(name[len]);
  653.    while (lcp > name) {
  654.       if (*lcp == '.') {
  655. if (found_label) {
  656.    lcp++;
  657.            break;
  658.         } else {
  659.            icp = lcp;
  660.         }
  661.       }
  662.       if (!found_label && isalpha((int)*lcp)) found_label = 1;
  663.       lcp--;
  664.    }
  665.    if (!found_label || (!isdigit((int)*(icp+1)) && (flag & FAIL_ON_NULL_IID)))
  666.       return(FAILURE);
  667.    if (flag & NON_LEAF_NAME) { /* dont know where to start instance id */
  668.      /* put the whole thing in label */
  669.      icp = &(name[len]);
  670.      flag |= USE_LONG_NAMES;
  671.      /* special hack in case no mib loaded - object identifiers will
  672.       * start with .iso.<num>.<num>...., in which case it is preferable
  673.       * to make the label entirely numeric (i.e., convert "iso" => "1")
  674.       */
  675.       if (*lcp == '.' && lcp == name) {
  676.          if (!strncmp(".ccitt.",lcp,7)) {
  677.             name += 2;
  678.             *name = '.';
  679.             *(name+1) = '0';
  680.          } else if (!strncmp(".iso.",lcp,5)) {
  681.             name += 2;
  682.             *name = '.';
  683.             *(name+1) = '1';
  684.          } else if (!strncmp(".joint-iso-ccitt.",lcp,17)) {
  685.             name += 2;
  686.             *name = '.';
  687.             *(name+1) = '2';
  688.          }
  689.       }
  690.    } else if (*icp) {
  691.       *(icp++) = '';
  692.    }
  693.    *last_label = (flag & USE_LONG_NAMES ? name : lcp);
  694.    *iid = icp;
  695.    return(SUCCESS);
  696. }
  697. static int
  698. __oid_cmp(oida_arr, oida_arr_len, oidb_arr, oidb_arr_len)
  699. oid *oida_arr;
  700. int oida_arr_len;
  701. oid *oidb_arr;
  702. int oidb_arr_len;
  703. {
  704.    for (;oida_arr_len && oidb_arr_len;
  705. oida_arr++, oida_arr_len--, oidb_arr++, oidb_arr_len--) {
  706. if (*oida_arr == *oidb_arr) continue;
  707. return(*oida_arr > *oidb_arr ? 1 : -1);
  708.    }
  709.    if (oida_arr_len == oidb_arr_len) return(0);
  710.    return(oida_arr_len > oidb_arr_len ? 1 : -1);
  711. }
  712. #define MAX_BAD 0xffffff
  713. static u_int
  714. compute_match(search_base, key)
  715. const char *search_base;
  716. const char *key;
  717. {
  718. #if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
  719.     int             rc;
  720.     regex_t         parsetree;
  721.     regmatch_t      pmatch;
  722.     rc = regcomp(&parsetree, key, REG_ICASE | REG_EXTENDED);
  723.     if (rc == 0)
  724.         rc = regexec(&parsetree, search_base, 1, &pmatch, 0);
  725.     regfree(&parsetree);
  726.     if (rc == 0) {
  727.         /*
  728.          * found 
  729.          */
  730.         return pmatch.rm_so;
  731.     }
  732. #else                           /* use our own wildcard matcher */
  733.     /*
  734.      * first find the longest matching substring (ick) 
  735.      */
  736.     char           *first = NULL, *result = NULL, *entry;
  737.     const char     *position;
  738.     char           *newkey = strdup(key);
  739.     char           *st;
  740.     entry = strtok_r(newkey, "*", &st);
  741.     position = search_base;
  742.     while (entry) {
  743.         result = strcasestr(position, entry);
  744.         if (result == NULL) {
  745.             free(newkey);
  746.             return MAX_BAD;
  747.         }
  748.         if (first == NULL)
  749.             first = result;
  750.         position = result + strlen(entry);
  751.         entry = strtok_r(NULL, "*", &st);
  752.     }
  753.     free(newkey);
  754.     if (result)
  755.         return (first - search_base);
  756. #endif
  757.     /*
  758.      * not found 
  759.      */
  760.     return MAX_BAD;
  761. }
  762. /* Convert a tag (string) to an OID array              */
  763. /* Tag can be either a symbolic name, or an OID string */
  764. static struct tree *
  765. __tag2oid(tag, iid, oid_arr, oid_arr_len, type, best_guess)
  766. char * tag;
  767. char * iid;
  768. oid  * oid_arr;
  769. int  * oid_arr_len;
  770. int  * type;
  771. int    best_guess;
  772. {
  773.    struct tree *tp = NULL;
  774.    struct tree *rtp = NULL;
  775.    oid newname[MAX_OID_LEN], *op;
  776.    int newname_len = 0;
  777.    const char *cp = NULL;
  778.    char *module = NULL;
  779.    char str_buf[STR_BUF_SIZE];
  780.    str_buf[0] = '';
  781.    if (type) *type = TYPE_UNKNOWN;
  782.    if (oid_arr_len) *oid_arr_len = 0;
  783.    if (!tag) goto done;
  784.    /*********************************************************/
  785.    /* best_guess = 0 - same as no switches (read_objid)     */
  786.    /*                  if multiple parts, or uses find_node */
  787.    /*                  if a single leaf                     */
  788.    /* best_guess = 1 - same as -Ib (get_wild_node)          */
  789.    /* best_guess = 2 - same as -IR (get_node)               */
  790.    /*********************************************************/
  791.    /* numeric scalar                (1,2) */
  792.    /* single symbolic               (1,2) */
  793.    /* single regex                  (1)   */
  794.    /* partial full symbolic         (2)   */
  795.    /* full symbolic                 (2)   */
  796.    /* module::single symbolic       (2)   */
  797.    /* module::partial full symbolic (2)   */
  798.    if (best_guess == 1 || best_guess == 2) { 
  799.      if (!__scan_num_objid(tag, newname, &newname_len)) { /* make sure it's not a numeric tag */
  800.        newname_len = MAX_OID_LEN;
  801.        if (best_guess == 2) { /* Random search -IR */
  802.          if (get_node(tag, newname, &newname_len)) {
  803.    rtp = tp = get_tree(newname, newname_len, get_tree_head());
  804.          }
  805.        }
  806.        else if (best_guess == 1) { /* Regex search -Ib */
  807.  clear_tree_flags(get_tree_head()); 
  808.          if (get_wild_node(tag, newname, &newname_len)) {
  809.    rtp = tp = get_tree(newname, newname_len, get_tree_head());
  810.          }
  811.        }
  812.      }
  813.      else {
  814.        rtp = tp = get_tree(newname, newname_len, get_tree_head());
  815.      }
  816.      if (type) *type = (tp ? tp->type : TYPE_UNKNOWN);
  817.      if ((oid_arr == NULL) || (oid_arr_len == NULL)) return rtp;
  818.      memcpy(oid_arr,(char*)newname,newname_len*sizeof(oid));
  819.      *oid_arr_len = newname_len;
  820.    }
  821.    
  822.    /* if best_guess is off and multi part tag or module::tag */
  823.    /* numeric scalar                                         */
  824.    /* module::single symbolic                                */
  825.    /* module::partial full symbolic                          */
  826.    /* FULL symbolic OID                                      */
  827.    else if (strchr(tag,'.') || strchr(tag,':')) { 
  828.      if (!__scan_num_objid(tag, newname, &newname_len)) { /* make sure it's not a numeric tag */
  829. newname_len = MAX_OID_LEN;
  830. if (read_objid(tag, newname, &newname_len)) { /* long name */
  831.   rtp = tp = get_tree(newname, newname_len, get_tree_head());
  832. }
  833.       }
  834.       else {
  835. rtp = tp = get_tree(newname, newname_len, get_tree_head());
  836.       }
  837.       if (type) *type = (tp ? tp->type : TYPE_UNKNOWN);
  838.       if ((oid_arr == NULL) || (oid_arr_len == NULL)) return rtp;
  839.       memcpy(oid_arr,(char*)newname,newname_len*sizeof(oid));
  840.       *oid_arr_len = newname_len;
  841.    }
  842.    
  843.    /* else best_guess is off and it is a single leaf */
  844.    /* single symbolic                                */
  845.    else { 
  846.       rtp = tp = find_node(tag, get_tree_head());
  847.       if (tp) {
  848.          if (type) *type = tp->type;
  849.          if ((oid_arr == NULL) || (oid_arr_len == NULL)) return rtp;
  850.          /* code taken from get_node in snmp_client.c */
  851.          for(op = newname + MAX_OID_LEN - 1; op >= newname; op--){
  852.            *op = tp->subid;
  853.    tp = tp->parent;
  854.    if (tp == NULL)
  855.       break;
  856.          }
  857.          *oid_arr_len = newname + MAX_OID_LEN - op;
  858.          memcpy(oid_arr, op, *oid_arr_len * sizeof(oid));
  859.       } else {
  860.          return(rtp);   /* HACK: otherwise, concat_oid_str confuses things */
  861.       }
  862.    }
  863.  done:
  864.    if (iid && *iid) __concat_oid_str(oid_arr, oid_arr_len, iid);
  865.    return(rtp);
  866. }
  867. /* searches down the mib tree for the given oid
  868.    returns the last found tp and its index in lastind
  869.  */
  870. static struct tree *
  871. __oid2tp (oidp, len, subtree, lastind)
  872. oid* oidp;
  873. int len;
  874. struct tree * subtree;
  875. int* lastind;
  876. {
  877.     struct tree    *return_tree = NULL;
  878.     for (; subtree; subtree = subtree->next_peer) {
  879. if (*oidp == subtree->subid){
  880.     goto found;
  881. }
  882.     }
  883.     *lastind=0;
  884.     return NULL;
  885. found:
  886.     if (len > 1){
  887.        return_tree =
  888.           __oid2tp(oidp + 1, len - 1, subtree->child_list, lastind);
  889.        (*lastind)++;
  890.     } else {
  891.        *lastind=1;
  892.     }
  893.     if (return_tree)
  894. return return_tree;
  895.     else
  896. return subtree;
  897. }
  898. /* function: __concat_oid_str
  899.  *
  900.  * This function converts a dotted-decimal string, soid_str, to an array
  901.  * of oid types and concatenates them on doid_arr begining at the index
  902.  * specified by doid_arr_len.
  903.  *
  904.  * returns : SUCCESS, FAILURE
  905.  */
  906. static int
  907. __concat_oid_str(doid_arr, doid_arr_len, soid_str)
  908. oid *doid_arr;
  909. int *doid_arr_len;
  910. char * soid_str;
  911. {
  912.    char soid_buf[STR_BUF_SIZE];
  913.    char *cp;
  914.    char *st;
  915.    if (!soid_str || !*soid_str) return SUCCESS;/* successfully added nothing */
  916.    if (*soid_str == '.') soid_str++;
  917.    strcpy(soid_buf, soid_str);
  918.    cp = strtok_r(soid_buf,".",&st);
  919.    while (cp) {
  920.      sscanf(cp, "%lu", &(doid_arr[(*doid_arr_len)++]));
  921.      /* doid_arr[(*doid_arr_len)++] =  atoi(cp); */
  922.      cp = strtok_r(NULL,".",&st);
  923.    }
  924.    return(SUCCESS);
  925. }
  926. /*
  927.  * add a varbind to PDU
  928.  */
  929. static int
  930. __add_var_val_str(pdu, name, name_length, val, len, type)
  931.     netsnmp_pdu *pdu;
  932.     oid *name;
  933.     int name_length;
  934.     char * val;
  935.     int len;
  936.     int type;
  937. {
  938.     netsnmp_variable_list *vars;
  939.     oid oidbuf[MAX_OID_LEN];
  940.     int ret = SUCCESS;
  941.     if (pdu->variables == NULL){
  942. pdu->variables = vars =
  943.            (netsnmp_variable_list *)calloc(1,sizeof(netsnmp_variable_list));
  944.     } else {
  945. for(vars = pdu->variables;
  946.             vars->next_variable;
  947.             vars = vars->next_variable)
  948.     /*EXIT*/;
  949. vars->next_variable =
  950.            (netsnmp_variable_list *)calloc(1,sizeof(netsnmp_variable_list));
  951. vars = vars->next_variable;
  952.     }
  953.     vars->next_variable = NULL;
  954.     vars->name = (oid *)malloc(name_length * sizeof(oid));
  955.     memcpy((char *)vars->name, (char *)name, name_length * sizeof(oid));
  956.     vars->name_length = name_length;
  957.     switch (type) {
  958.       case TYPE_INTEGER:
  959.       case TYPE_INTEGER32:
  960.         vars->type = ASN_INTEGER;
  961.         vars->val.integer = (long *)malloc(sizeof(long));
  962.         if (val)
  963.             *(vars->val.integer) = strtol(val,NULL,0);
  964.         else {
  965.             ret = FAILURE;
  966.             *(vars->val.integer) = 0;
  967.         }
  968.         vars->val_len = sizeof(long);
  969.         break;
  970.       case TYPE_GAUGE:
  971.       case TYPE_UNSIGNED32:
  972.         vars->type = ASN_GAUGE;
  973.         goto UINT;
  974.       case TYPE_COUNTER:
  975.         vars->type = ASN_COUNTER;
  976.         goto UINT;
  977.       case TYPE_TIMETICKS:
  978.         vars->type = ASN_TIMETICKS;
  979.         goto UINT;
  980.       case TYPE_UINTEGER:
  981.         vars->type = ASN_UINTEGER;
  982. UINT:
  983.         vars->val.integer = (long *)malloc(sizeof(long));
  984.         if (val)
  985.             sscanf(val,"%lu",vars->val.integer);
  986.         else {
  987.             ret = FAILURE;
  988.             *(vars->val.integer) = 0;
  989.         }
  990.         vars->val_len = sizeof(long);
  991.         break;
  992.       case TYPE_OCTETSTR:
  993. vars->type = ASN_OCTET_STR;
  994. goto OCT;
  995.       case TYPE_BITSTRING:
  996. vars->type = ASN_OCTET_STR;
  997. goto OCT;
  998.       case TYPE_OPAQUE:
  999.         vars->type = ASN_OCTET_STR;
  1000. OCT:
  1001.         vars->val.string = (u_char *)malloc(len);
  1002.         vars->val_len = len;
  1003.         if (val && len)
  1004.             memcpy((char *)vars->val.string, val, len);
  1005.         else {
  1006.             ret = FAILURE;
  1007.             vars->val.string = strdup("");
  1008.             vars->val_len = 0;
  1009.         }
  1010.         break;
  1011.       case TYPE_IPADDR:
  1012.         vars->type = ASN_IPADDRESS;
  1013.         vars->val.integer = (long *)malloc(sizeof(long));
  1014.         if (val)
  1015.             *(vars->val.integer) = inet_addr(val);
  1016.         else {
  1017.             ret = FAILURE;
  1018.             *(vars->val.integer) = 0;
  1019.         }
  1020.         vars->val_len = sizeof(long);
  1021.         break;
  1022.       case TYPE_OBJID:
  1023.         vars->type = ASN_OBJECT_ID;
  1024. vars->val_len = MAX_OID_LEN;
  1025.         /* if (read_objid(val, oidbuf, &(vars->val_len))) { */
  1026. /* tp = __tag2oid(val,NULL,oidbuf,&(vars->val_len),NULL,0); */
  1027.         if (!val || !snmp_parse_oid(val, oidbuf, &vars->val_len)) {
  1028.             vars->val.objid = NULL;
  1029.     ret = FAILURE;
  1030.         } else {
  1031.             vars->val_len *= sizeof(oid);
  1032.             vars->val.objid = (oid *)malloc(vars->val_len);
  1033.             memcpy((char *)vars->val.objid, (char *)oidbuf, vars->val_len);
  1034.         }
  1035.         break;
  1036.       default:
  1037.         vars->type = ASN_NULL;
  1038. vars->val_len = 0;
  1039. vars->val.string = NULL;
  1040. ret = FAILURE;
  1041.     }
  1042.      return ret;
  1043. }
  1044. /* takes ss and pdu as input and updates the 'response' argument */
  1045. /* the input 'pdu' argument will be freed */
  1046. static int
  1047. __send_sync_pdu(ss, pdu, response, retry_nosuch,
  1048.         err_str_sv, err_num_sv, err_ind_sv)
  1049. netsnmp_session *ss;
  1050. netsnmp_pdu *pdu;
  1051. netsnmp_pdu **response;
  1052. int retry_nosuch;
  1053. SV * err_str_sv;
  1054. SV * err_num_sv;
  1055. SV * err_ind_sv;
  1056. {
  1057.    int status;
  1058.    long command = pdu->command;
  1059.    *response = NULL;
  1060. retry:
  1061.    status = snmp_synch_response(ss, pdu, response);
  1062.    if ((*response == NULL) && (status == STAT_SUCCESS)) status = STAT_ERROR;
  1063.    switch (status) {
  1064.       case STAT_SUCCESS:
  1065.  switch ((*response)->errstat) {
  1066.     case SNMP_ERR_NOERROR:
  1067.        break;
  1068.             case SNMP_ERR_NOSUCHNAME:
  1069.                if (retry_nosuch && (pdu = snmp_fix_pdu(*response, command))) {
  1070.                   if (*response) snmp_free_pdu(*response);
  1071.                   goto retry;
  1072.                }
  1073.             /* Pv1, SNMPsec, Pv2p, v2c, v2u, v2*, and SNMPv3 PDUs */
  1074.             case SNMP_ERR_TOOBIG:
  1075.             case SNMP_ERR_BADVALUE:
  1076.             case SNMP_ERR_READONLY:
  1077.             case SNMP_ERR_GENERR:
  1078.             /* in SNMPv2p, SNMPv2c, SNMPv2u, SNMPv2*, and SNMPv3 PDUs */
  1079.             case SNMP_ERR_NOACCESS:
  1080.             case SNMP_ERR_WRONGTYPE:
  1081.             case SNMP_ERR_WRONGLENGTH:
  1082.             case SNMP_ERR_WRONGENCODING:
  1083.             case SNMP_ERR_WRONGVALUE:
  1084.             case SNMP_ERR_NOCREATION:
  1085.             case SNMP_ERR_INCONSISTENTVALUE:
  1086.             case SNMP_ERR_RESOURCEUNAVAILABLE:
  1087.             case SNMP_ERR_COMMITFAILED:
  1088.             case SNMP_ERR_UNDOFAILED:
  1089.             case SNMP_ERR_AUTHORIZATIONERROR:
  1090.             case SNMP_ERR_NOTWRITABLE:
  1091.             /* in SNMPv2c, SNMPv2u, SNMPv2*, and SNMPv3 PDUs */
  1092.             case SNMP_ERR_INCONSISTENTNAME:
  1093.             default:
  1094.                sv_catpv(err_str_sv,
  1095.                         (char*)snmp_errstring((*response)->errstat));
  1096.                sv_setiv(err_num_sv, (*response)->errstat);
  1097.        sv_setiv(err_ind_sv, (*response)->errindex);
  1098.                status = (*response)->errstat;
  1099.                break;
  1100.  }
  1101.          break;
  1102.       case STAT_TIMEOUT:
  1103.       case STAT_ERROR:
  1104.           sv_catpv(err_str_sv, (char*)snmp_api_errstring(ss->s_snmp_errno));
  1105.           sv_setiv(err_num_sv, ss->s_snmp_errno);
  1106.          break;
  1107.       default:
  1108.          sv_catpv(err_str_sv, "send_sync_pdu: unknown status");
  1109.          sv_setiv(err_num_sv, ss->s_snmp_errno);
  1110.          break;
  1111.    }
  1112.    return(status);
  1113. }
  1114. static int
  1115. __callback_wrapper (op, ss, reqid, pdu, cb_data)
  1116. int op;
  1117. netsnmp_session *ss;
  1118. int reqid;
  1119. netsnmp_pdu *pdu;
  1120. void *cb_data;
  1121. {
  1122.   /* we should probably just increment the reference counter... */
  1123.   /*  sv_inc(cb_data); */
  1124.   return __snmp_xs_cb(op, ss, reqid, pdu, newSVsv(cb_data));
  1125. }
  1126. static int
  1127. __snmp_xs_cb (op, ss, reqid, pdu, cb_data)
  1128. int op;
  1129. netsnmp_session *ss;
  1130. int reqid;
  1131. netsnmp_pdu *pdu;
  1132. void *cb_data;
  1133. {
  1134.   SV *varlist_ref;
  1135.   AV *varlist;
  1136.   SV *varbind_ref;
  1137.   AV *varbind;
  1138.   SV *traplist_ref = NULL;
  1139.   AV *traplist = NULL;
  1140.   netsnmp_variable_list *vars;
  1141.   struct tree *tp;
  1142.   int len;
  1143.   SV *tmp_sv;
  1144.   int type;
  1145.   char tmp_type_str[MAX_TYPE_NAME_LEN];
  1146.   u_char str_buf[STR_BUF_SIZE], *str_bufp = str_buf;
  1147.   size_t str_buf_len = sizeof(str_buf);
  1148.   size_t out_len = 0;
  1149.   int buf_over = 0;
  1150.   char *label;
  1151.   char *iid;
  1152.   char *cp;
  1153.   int getlabel_flag = NO_FLAGS;
  1154.   int sprintval_flag = USE_BASIC;
  1155.   netsnmp_pdu *reply_pdu;
  1156.   int old_numeric, old_printfull;
  1157.   netsnmp_transport *transport = NULL;
  1158.   SV* cb = ((struct snmp_xs_cb_data*)cb_data)->perl_cb;
  1159.   SV* sess_ref = ((struct snmp_xs_cb_data*)cb_data)->sess_ref;
  1160.   SV **err_str_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorStr", 8, 1);
  1161.   SV **err_num_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorNum", 8, 1);
  1162.   SV **err_ind_svp = hv_fetch((HV*)SvRV(sess_ref), "ErrorInd", 8, 1);
  1163.   dSP;
  1164.   ENTER;
  1165.   SAVETMPS;
  1166.   free(cb_data);
  1167.   sv_setpv(*err_str_svp, (char*)snmp_errstring(pdu->errstat));
  1168.   sv_setiv(*err_num_svp, pdu->errstat);
  1169.   sv_setiv(*err_ind_svp, pdu->errindex);
  1170.   varlist_ref = &sv_undef; /* Prevent unintialized use below. */
  1171.   switch (op) {
  1172.   case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE:
  1173.     traplist_ref = NULL;
  1174.     switch (pdu->command) {
  1175.     case SNMP_MSG_INFORM:
  1176.       /*
  1177.        * Ideally, we would use the return value from the callback to
  1178.        * decide what response, if any, we send, and what the error status
  1179.        * and error index should be.
  1180.        */
  1181.       reply_pdu = snmp_clone_pdu(pdu);
  1182.       if (reply_pdu) {
  1183.         reply_pdu->command = SNMP_MSG_RESPONSE;
  1184.         reply_pdu->reqid = pdu->reqid;
  1185.         reply_pdu->errstat = reply_pdu->errindex = 0;
  1186.         snmp_send(ss, reply_pdu);
  1187.       } else {
  1188.         warn("Couldn't clone PDU for inform response");
  1189.       }
  1190.       /* FALLTHRU */
  1191.     case SNMP_MSG_TRAP2:
  1192.       traplist = newAV();
  1193.       traplist_ref = newRV_noinc((SV*)traplist);
  1194. #if 0
  1195.       /* of dubious utility... */
  1196.       av_push(traplist, newSViv(pdu->command));
  1197. #endif
  1198.       av_push(traplist, newSViv(pdu->reqid));
  1199.       if ((transport = snmp_sess_transport(snmp_sess_pointer(ss))) != NULL) {
  1200. cp = transport->f_fmtaddr(transport, pdu->transport_data,
  1201.   pdu->transport_data_length);
  1202. av_push(traplist, newSVpv(cp, strlen(cp)));
  1203. free(cp);
  1204.       } else {
  1205. /*  This shouldn't ever happen; every session has a transport.  */
  1206. av_push(traplist, newSVpv("", 0));
  1207.       }
  1208.       av_push(traplist, newSVpv((char*) pdu->community, pdu->community_len));
  1209.       /* FALLTHRU */
  1210.     case SNMP_MSG_RESPONSE:
  1211.       {
  1212.       varlist = newAV();
  1213.       varlist_ref = newRV_noinc((SV*)varlist);
  1214.       /*
  1215.       ** Set up for numeric OID's, if necessary.  Save the old values
  1216.       ** so that they can be restored when we finish -- these are
  1217.       ** library-wide globals, and have to be set/restored for each
  1218.       ** session.
  1219.       */
  1220.       old_numeric = netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_NUMERIC_OIDS);
  1221.       old_printfull = netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_FULL_OID);
  1222.       if (SvIV(*hv_fetch((HV*)SvRV(sess_ref),"UseLongNames", 12, 1))) {
  1223.          getlabel_flag |= USE_LONG_NAMES;
  1224.          netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_FULL_OID, 1);
  1225.       }
  1226.       /* Setting UseNumeric forces UseLongNames on so check for UseNumeric
  1227.          after UseLongNames (above) to make sure the final outcome of 
  1228.          NETSNMP_DS_LIB_OID_OUTPUT_FORMAT is NETSNMP_OID_OUTPUT_NUMERIC */
  1229.       if (SvIV(*hv_fetch((HV*)SvRV(sess_ref),"UseNumeric", 10, 1))) {
  1230.          getlabel_flag |= USE_NUMERIC_OIDS;
  1231.          netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_NUMERIC_OIDS, 1);
  1232.       }
  1233.       sv_bless(varlist_ref, gv_stashpv("SNMP::VarList",0));
  1234.       for(vars = (pdu?pdu->variables:NULL); vars; vars = vars->next_variable) {
  1235.          varbind = newAV();
  1236.          varbind_ref = newRV_noinc((SV*)varbind);
  1237.          sv_bless(varbind_ref, gv_stashpv("SNMP::Varbind",0));
  1238.          av_push(varlist, varbind_ref);
  1239.          *str_buf = '.';
  1240.          *(str_buf+1) = '';
  1241.          out_len = 0;
  1242.          tp = netsnmp_sprint_realloc_objid_tree(&str_bufp, &str_buf_len,
  1243.                                                 &out_len, 0, &buf_over,
  1244.                                                 vars->name,vars->name_length);
  1245.          str_buf[sizeof(str_buf)-1] = '';
  1246.          if (__is_leaf(tp)) {
  1247.             type = tp->type;
  1248.          } else {
  1249.             getlabel_flag |= NON_LEAF_NAME;
  1250.             type = __translate_asn_type(vars->type);
  1251.          }
  1252.          __get_label_iid(str_buf,&label,&iid,getlabel_flag);
  1253.          if (label) {
  1254.              av_store(varbind, VARBIND_TAG_F,
  1255.                       newSVpv(label, strlen(label)));
  1256.          } else {
  1257.              av_store(varbind, VARBIND_TAG_F,
  1258.                       newSVpv("", 0));
  1259.          }
  1260.          if (iid) {
  1261.              av_store(varbind, VARBIND_IID_F,
  1262.                       newSVpv(iid, strlen(iid)));
  1263.          } else {
  1264.              av_store(varbind, VARBIND_IID_F,
  1265.                       newSVpv("", 0));
  1266.          }
  1267.          __get_type_str(type, tmp_type_str);
  1268.          tmp_sv = newSVpv(tmp_type_str, strlen(tmp_type_str));
  1269.          av_store(varbind, VARBIND_TYPE_F, tmp_sv);
  1270.          len = __snprint_value(str_buf, sizeof(str_buf),
  1271.                               vars, tp, type, sprintval_flag);
  1272.          tmp_sv = newSVpv((char*)str_buf, len);
  1273.          av_store(varbind, VARBIND_VAL_F, tmp_sv);
  1274.       } /* for */
  1275.       /* Reset the library's behavior for numeric/symbolic OID's. */
  1276.       netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_NUMERIC_OIDS, old_numeric );
  1277.       netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_FULL_OID, old_printfull);
  1278.       } /* case SNMP_MSG_RESPONSE */
  1279.       break;
  1280.     default:;
  1281.     } /* switch pdu->command */
  1282.     break;
  1283.   case NETSNMP_CALLBACK_OP_TIMED_OUT:
  1284.     varlist_ref = &sv_undef;
  1285.     break;
  1286.   default:;
  1287.   } /* switch op */
  1288.   sv_2mortal(cb);
  1289.   cb = __push_cb_args2(cb,
  1290.                  (SvTRUE(varlist_ref) ? sv_2mortal(varlist_ref):varlist_ref),
  1291.          (SvTRUE(traplist_ref) ? sv_2mortal(traplist_ref):traplist_ref));
  1292.   __call_callback(cb, G_DISCARD);
  1293.   FREETMPS;
  1294.   LEAVE;
  1295.   sv_2mortal(sess_ref);
  1296.   return 1;
  1297. }
  1298. static SV *
  1299. __push_cb_args2(sv,esv,tsv)
  1300. SV *sv;
  1301. SV *esv;
  1302. SV *tsv;
  1303. {
  1304.    dSP;
  1305.    if (SvTYPE(SvRV(sv)) != SVt_PVCV) sv = SvRV(sv);
  1306.    PUSHMARK(sp);
  1307.    if (SvTYPE(sv) == SVt_PVAV) {
  1308.       AV *av = (AV *) sv;
  1309.       int n = av_len(av) + 1;
  1310.       SV **x = av_fetch(av, 0, 0);
  1311.       if (x) {
  1312.          int i = 1;
  1313.          sv = *x;
  1314.          for (i = 1; i < n; i++) {
  1315.             x = av_fetch(av, i, 0);
  1316.             if (x) {
  1317.                SV *arg = *x;
  1318.                XPUSHs(sv_mortalcopy(arg));
  1319.             } else {
  1320.                XPUSHs(&sv_undef);
  1321.             }
  1322.          }
  1323.       } else {
  1324.          sv = &sv_undef;
  1325.       }
  1326.    }
  1327.    if (esv) XPUSHs(sv_mortalcopy(esv));
  1328.    if (tsv) XPUSHs(sv_mortalcopy(tsv));
  1329.    PUTBACK;
  1330.    return sv;
  1331. }
  1332. static int
  1333. __call_callback(sv, flags)
  1334. SV *sv;
  1335. int flags;
  1336. {
  1337.  dSP;
  1338.  I32 myframe = TOPMARK;
  1339.  I32 count;
  1340.  ENTER;
  1341.  if (SvTYPE(sv) == SVt_PVCV)
  1342.   {
  1343.    count = perl_call_sv(sv, flags);
  1344.   }
  1345.  else if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV)
  1346.   {
  1347.    count = perl_call_sv(SvRV(sv), flags);
  1348.   }
  1349.  else
  1350.   {
  1351.    SV **top = stack_base + myframe + 1;
  1352.    SV *obj = *top;
  1353.    if (SvPOK(sv) && SvROK(obj) && SvOBJECT(SvRV(obj)))
  1354.     {
  1355.      count = perl_call_method(SvPV(sv, na), flags);
  1356.     }
  1357.    else if (SvPOK(obj) && SvROK(sv) && SvOBJECT(SvRV(sv)))
  1358.     {
  1359.      /* We have obj method ...
  1360.         Used to be used instead of LangMethodCall()
  1361.       */
  1362.      *top = sv;
  1363.      count = perl_call_method(SvPV(obj, na), flags);
  1364.     }
  1365.    else
  1366.     {
  1367.      count = perl_call_sv(sv, flags);
  1368.     }
  1369.  }
  1370.  LEAVE;
  1371.  return count;
  1372. }
  1373. /* Bulkwalk support routines */
  1374. /* Add a context pointer to the list of valid pointers.  Place it in the first
  1375. ** NULL slot in the array.
  1376. */
  1377. static int
  1378. _context_add(walk_context *context)
  1379. {
  1380.     int i, j, new_sz;
  1381.     if ((i = _context_okay(context)) != 0) /* Already exists?  Okay. */
  1382. return i;
  1383.     /* Initialize the array if necessary. */
  1384.     if (_valid_contexts == NULL) {
  1385. /* Create the _valid_contexts structure. */
  1386. Newz(0, _valid_contexts, 1, struct valid_contexts);
  1387. assert(_valid_contexts != NULL);
  1388. /* Populate the original valid contexts array. */
  1389. Newz(0, _valid_contexts->valid, 4, walk_context *);
  1390. assert(_valid_contexts->valid != NULL);
  1391. /* Computer number of slots in the array. */
  1392. _valid_contexts->sz_valid = sizeof(*_valid_contexts->valid) /
  1393. sizeof(walk_context *);
  1394. for (i = 0; i < _valid_contexts->sz_valid; i++)
  1395.     _valid_contexts->valid[i] = NULL;
  1396. DBPRT(3, (DBOUT "Created valid_context array 0x%p (%d slots)n",
  1397.     _valid_contexts->valid, _valid_contexts->sz_valid));
  1398.     }
  1399.     /* Search through the list, looking for NULL's -- unused slots. */
  1400.     for (i = 0; i < _valid_contexts->sz_valid; i++)
  1401. if (_valid_contexts->valid[i] == NULL)
  1402.     break;
  1403.     /* Did we walk off the end of the list?  Need to grow the list.  Double
  1404.     ** it for now.
  1405.     */
  1406.     if (i == _valid_contexts->sz_valid) {
  1407. new_sz = _valid_contexts->sz_valid * 2;
  1408. Renew(_valid_contexts->valid, new_sz, walk_context *);
  1409. assert(_valid_contexts->valid != NULL);
  1410. DBPRT(3, (DBOUT "Resized valid_context array 0x%p from %d to %d slotsn",
  1411.     _valid_contexts->valid, _valid_contexts->sz_valid, new_sz));
  1412. _valid_contexts->sz_valid = new_sz;
  1413. /* Initialize the new half of the resized array. */
  1414. for (j = i; j < new_sz; j++)
  1415.     _valid_contexts->valid[j] = NULL;
  1416.     }
  1417.     /* Store the context pointer in the array and return 0 (success). */
  1418.     _valid_contexts->valid[i] = context;
  1419.     DBPRT(3,(DBOUT "Add context 0x%p to valid context listn", context));
  1420.     return 0;
  1421. }
  1422. /* Remove a context pointer from the valid list.  Replace the pointer with
  1423. ** NULL in the valid pointer list.
  1424. */
  1425. static int
  1426. _context_del(walk_context *context)
  1427. {
  1428.     int i;
  1429.     if (_valid_contexts == NULL) /* Make sure it was initialized. */
  1430. return 1;
  1431.     for (i = 0; i < _valid_contexts->sz_valid; i++) {
  1432. if (_valid_contexts->valid[i] == context) {
  1433.     DBPRT(3,(DBOUT "Remove context 0x%p from valid context listn", context));
  1434.     _valid_contexts->valid[i] = NULL; /* Remove it from the list.  */
  1435.     return 0; /* Return successful status. */
  1436. }
  1437.     }
  1438.     return 1;
  1439. }
  1440. /* Check if a specific context pointer is in the valid list.  Return true (1)
  1441. ** if the context is still in the valid list, or 0 if not (or context is NULL).
  1442. */
  1443. static int
  1444. _context_okay(walk_context *context)
  1445. {
  1446.     int i;
  1447.     if (_valid_contexts == NULL) /* Make sure it was initialized. */
  1448. return 0;
  1449.     if (context == NULL) /* Asked about a NULL context? Fail. */
  1450. return 0;
  1451.     for (i = 0; i < _valid_contexts->sz_valid; i++)
  1452. if (_valid_contexts->valid[i] == context)
  1453.     return 1; /* Found it! */
  1454.     return 0; /* No match -- return failure. */
  1455. }
  1456. /* Check if the walk is completed, based upon the context.  Also set the
  1457. ** ignore flag on any completed variables -- this prevents them from being
  1458. ** being sent in later packets.
  1459. */
  1460. static int
  1461. _bulkwalk_done(walk_context *context)
  1462. {
  1463.    int is_done = 1;
  1464.    int i;
  1465.    bulktbl *bt_entry; /* bulktbl requested OID entry */
  1466.    /* Don't consider walk done until at least one packet has been exchanged. */
  1467.    if (context->pkts_exch == 0)
  1468.       return 0;
  1469.    /* Fix up any requests that have completed.  If the complete flag is set,
  1470.    ** or it is a non-repeater OID, set the ignore flag so that it will not
  1471.    ** be considered further.  Assume we are done with the walk, and note
  1472.    ** otherwise if we aren't.  Return 1 if all requests are complete, or 0
  1473.    ** if there's more to do.
  1474.    */
  1475.    for (i = 0; i < context->nreq_oids; i ++) {
  1476.       bt_entry = &context->req_oids[i];
  1477.       if (bt_entry->complete || bt_entry->norepeat) {
  1478.   /* This request is complete.  Remove it from list of
  1479.   ** walks still in progress.
  1480.   */
  1481.   DBPRT(1, (DBOUT "Ignoring %s request oid %sn",
  1482.         bt_entry->norepeat? "nonrepeater" : "completed",
  1483.         snprint_objid(_debugx, sizeof(_debugx), bt_entry->req_oid,
  1484.       bt_entry->req_len)));
  1485.   /* Ignore this OID in any further packets. */
  1486.   bt_entry->ignore = 1;
  1487.       }
  1488.       /* If any OID is not being ignored, the walk is not done.  Must loop
  1489.       ** through all requests to do the fixup -- no early return possible.
  1490.       */
  1491.       if (!bt_entry->ignore)
  1492.    is_done = 0;
  1493.    }
  1494.    return is_done; /* Did the walk complete? */
  1495. }
  1496. /* Callback registered with SNMP.  Return 1 from this callback to cause the
  1497. ** current request to be deleted from the retransmit queue.
  1498. */
  1499. static int
  1500. _bulkwalk_async_cb(int op,
  1501.   SnmpSession *ss,
  1502.   int  reqid,
  1503.   netsnmp_pdu *pdu,
  1504.   void *context_ptr)
  1505. {
  1506.    walk_context *context;
  1507.    int done = 0;
  1508.    int npushed;
  1509.    SV **err_str_svp;
  1510.    SV **err_num_svp;
  1511.    /* Handle callback request for asynchronous bulkwalk.  If the bulkwalk has
  1512.    ** not completed, and has not timed out, send the next request packet in
  1513.    ** the walk.
  1514.    **
  1515.    ** Return 0 to indicate success (caller ignores return value).
  1516.    */
  1517.    DBPRT(2, (DBOUT "bulkwalk_async_cb(op %d, reqid 0x%08X, context 0x%p)n",
  1518. op, reqid, context_ptr));
  1519.    context = (walk_context *)context_ptr;
  1520.    /* Make certain this is a valid context pointer.  This pdu may
  1521.    ** have been retransmitted after the bulkwalk was completed
  1522.    ** (and the context was destroyed).  If so, just return.
  1523.    */
  1524.    if (!_context_okay(context)) {
  1525.       DBPRT(2,(DBOUT "Ignoring PDU for dead context 0x%p...n", context));
  1526.       return 1;
  1527.    }
  1528.    /* Is this a retransmission of a request we've already seen or some
  1529.    ** unexpected request id?  If so, just ignore it.
  1530.    */
  1531.    if (reqid != context->exp_reqid) {
  1532.        DBPRT(2,
  1533.              (DBOUT "Got reqid 0x%08X, expected reqid 0x%08X.  Ignoring...n", reqid,
  1534.               context->exp_reqid));
  1535.       return 1;
  1536.    }
  1537.    /* Ignore any future packets for this reqid. */
  1538.    context->exp_reqid = -1;
  1539.    err_str_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorStr", 8, 1);
  1540.    err_num_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorNum", 8, 1);
  1541.    switch (op) {
  1542.       case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE:
  1543.       {
  1544.  DBPRT(1,(DBOUT "Received message for reqid 0x%08X ...n", reqid));
  1545.  switch (pdu->command)
  1546.  {
  1547.     case SNMP_MSG_RESPONSE:
  1548.     {
  1549.        DBPRT(2, (DBOUT "Calling bulkwalk_recv_pdu(context 0x%p, pdu 0x%p)n",
  1550.    context_ptr, pdu));
  1551.        /* Handle the response PDU.  If an error occurs or there were
  1552.        ** no variables in the response, consider the walk done.  If
  1553.        ** the response was okay, check if we have any more to do after
  1554.        ** this response.
  1555.        */
  1556.        if (_bulkwalk_recv_pdu(context, pdu) <= 0)
  1557.   done = 1;
  1558.        else
  1559.   done = _bulkwalk_done(context); /* Also set req ignore flags */
  1560.        break;
  1561.     }
  1562.     default:
  1563.     {
  1564.        DBPRT(1,(DBOUT "unexpected pdu->command %dn", pdu->command));
  1565.        done = 1;   /* "This can't happen!", so bail out when it does. */
  1566.        break;
  1567.     }
  1568.  }
  1569.  break;
  1570.       }
  1571.       case NETSNMP_CALLBACK_OP_TIMED_OUT:
  1572.       {
  1573.  DBPRT(1,(DBOUT "n*** Timeout for reqid 0x%08Xnn", reqid));
  1574.          sv_setpv(*err_str_svp, (char*)snmp_api_errstring(SNMPERR_TIMEOUT));
  1575.          sv_setiv(*err_num_svp, SNMPERR_TIMEOUT);
  1576.  /* Timeout means something bad has happened.  Return a not-okay
  1577.  ** result to the async callback.
  1578.  */
  1579.  npushed = _bulkwalk_finish(context, 0 /* NOT OKAY */);
  1580.  return 1;
  1581.       }
  1582.       default:
  1583.       {
  1584.  DBPRT(1,(DBOUT "unexpected callback op %dn", op));
  1585.          sv_setpv(*err_str_svp, (char*)snmp_api_errstring(SNMPERR_GENERR));
  1586.          sv_setiv(*err_num_svp, SNMPERR_GENERR);
  1587.  npushed = _bulkwalk_finish(context, 0 /* NOT OKAY */);
  1588.  return 1;
  1589.       }
  1590.    }
  1591.    /* We have either timed out, or received and parsed in a response.  Now,
  1592.    ** if we have more variables to test, call bulkwalk_send_pdu() to enqueue
  1593.    ** another async packet, and return.
  1594.    **
  1595.    ** If, however, the bulkwalk has completed (or an error has occurred that
  1596.    ** cuts the walk short), call bulkwalk_finish() to push the results onto
  1597.    ** the Perl call stack.  Then explicitly call the Perl callback that was
  1598.    ** passed in by the user oh-so-long-ago.
  1599.    */
  1600.    if (!done) {
  1601.       DBPRT(1,(DBOUT "bulkwalk not complete -- send next pdu from callbackn"));
  1602.       if (_bulkwalk_send_pdu(context) != NULL)
  1603.  return 1;
  1604.       DBPRT(1,(DBOUT "send_pdu() failed!n"));
  1605.       /* Fall through and return what we have so far. */
  1606.    }
  1607.    /* Call the perl callback with the return values and we're done. */
  1608.    npushed = _bulkwalk_finish(context, 1 /* OKAY */);
  1609.    return 1;
  1610. }
  1611. static netsnmp_pdu *
  1612. _bulkwalk_send_pdu(walk_context *context)
  1613. {
  1614.    netsnmp_pdu *pdu = NULL;
  1615.    netsnmp_pdu *response = NULL;
  1616.    struct bulktbl  *bt_entry;
  1617.    int nvars = 0;
  1618.    int reqid;
  1619.    int status;
  1620.    int i;
  1621.    /* Send a pdu requesting any remaining variables in the context.
  1622.    **
  1623.    ** In synchronous mode, returns a pointer to the response packet.
  1624.    **
  1625.    ** In asynchronous mode, it returns the request ID, cast to a struct snmp *,
  1626.    **   not a valid SNMP response packet.  The async code should not be trying
  1627.    **   to get variables out of this "response".
  1628.    **
  1629.    ** In either case, return a NULL pointer on error or failure.
  1630.    */
  1631.    SV **sess_ptr_sv = hv_fetch((HV*)SvRV(context->sess_ref), "SessPtr", 7, 1);
  1632.    netsnmp_session *ss = (SnmpSession *)SvIV((SV*)SvRV(*sess_ptr_sv));
  1633.    SV **err_str_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorStr", 8, 1);
  1634.    SV **err_num_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorNum", 8, 1);
  1635.    SV **err_ind_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorInd", 8, 1);
  1636.    /* Create a new PDU and send the remaining set of requests to the agent. */
  1637.    pdu = snmp_pdu_create(SNMP_MSG_GETBULK);
  1638.    if (pdu == NULL) {
  1639.       sv_setpv(*err_str_svp, "snmp_pdu_create(GETBULK) failed: ");
  1640.       sv_catpv(*err_str_svp, strerror(errno));
  1641.       sv_setiv(*err_num_svp, SNMPERR_MALLOC);
  1642.       goto err;
  1643.    }
  1644.    /* Request non-repeater variables only in the first packet exchange. */
  1645.    pdu->errstat  = (context->pkts_exch == 0) ? context->non_reps : 0;
  1646.    pdu->errindex = context->max_reps;
  1647.    for (i = 0; i < context->nreq_oids; i++) {
  1648.       bt_entry = &context->req_oids[i];
  1649.       if (bt_entry->ignore)
  1650.  continue;
  1651.       assert(bt_entry->complete == 0);
  1652.       if (!snmp_add_null_var(pdu, bt_entry->last_oid, bt_entry->last_len)) {
  1653.  sv_setpv(*err_str_svp, "snmp_add_null_var() failed");
  1654.  sv_setiv(*err_num_svp, SNMPERR_GENERR);
  1655.  sv_setiv(*err_ind_svp, i);
  1656.  goto err;
  1657.       }
  1658.       nvars ++;
  1659.       DBPRT(1, (DBOUT "   Add %srepeater %sn", bt_entry->norepeat ? "non" : "",
  1660.          snprint_objid(_debugx, sizeof(_debugx), bt_entry->last_oid, bt_entry->last_len)));
  1661.    }
  1662.    /* Make sure variables are actually being requested in the packet. */
  1663.    assert (nvars != 0);
  1664.    context->pkts_exch ++;
  1665.    DBPRT(1, (DBOUT "Sending %ssynchronous request %d...n",
  1666.      SvTRUE(context->perl_cb) ? "a" : "", context->pkts_exch));
  1667.    /* We handle the asynchronous and synchronous requests differently here.
  1668.    ** For async, we simply enqueue the packet with a callback to handle the
  1669.    ** returned response, then return.  Note that this we call the bulkwalk
  1670.    ** callback, and hand it the walk_context, not the Perl callback.  The
  1671.    ** snmp_async_send() function returns the reqid on success, 0 on failure.
  1672.    */
  1673.    if (SvTRUE(context->perl_cb)) {
  1674.       reqid = snmp_async_send(ss, pdu, _bulkwalk_async_cb, (void *)context);
  1675.       DBPRT(2,(DBOUT "bulkwalk_send_pdu(): snmp_async_send => 0x%08Xn", reqid));
  1676.       if (reqid == 0) {
  1677.  sv_setpv(*err_str_svp, (char*)snmp_api_errstring(ss->s_snmp_errno));
  1678.  sv_setiv(*err_num_svp, ss->s_snmp_errno);
  1679.  goto err;
  1680.       }
  1681.       /* Make a note of the request we expect to be answered. */
  1682.       context->exp_reqid = reqid;
  1683.       /* Callbacks take care of the rest.  Let the caller know how many vars
  1684.       ** we sent in this request.  Note that this is not a valid SNMP PDU,
  1685.       ** but that's because a response has not yet been received.
  1686.       */
  1687.       return (netsnmp_pdu *)reqid;
  1688.    }
  1689.    /* This code is for synchronous mode support.
  1690.    **
  1691.    ** Send the PDU and block awaiting the response.  Return the response
  1692.    ** packet back to the caller.  Note that snmp_sess_read() frees the pdu.
  1693.    */
  1694.    status = __send_sync_pdu(ss, pdu, &response, NO_RETRY_NOSUCH,
  1695.     *err_str_svp, *err_num_svp, *err_ind_svp);
  1696.    pdu = NULL;
  1697.    /* Check for a failed request.  __send_sync_pdu() will set the appropriate
  1698.    ** values in the error string and number SV's.
  1699.    */
  1700.    if (status != STAT_SUCCESS) {
  1701.       DBPRT(1,(DBOUT "__send_sync_pdu() -> %dn",(int)status));
  1702.       goto err;
  1703.    }
  1704.    DBPRT(1, (DBOUT "%d packets exchanged, response 0x%pn", context->pkts_exch,
  1705.     response));
  1706.    return response;
  1707.    err:
  1708.    if (pdu)
  1709.       snmp_free_pdu(pdu);
  1710.    return NULL;
  1711. }
  1712. /* Handle an incoming GETBULK response PDU.  This function just pulls the
  1713. ** variables off of the PDU and builds up the arrays of returned values
  1714. ** that are stored in the context.
  1715. **
  1716. ** Returns the number of variables found in this packet, or -1 on error.
  1717. ** Note that the caller is expected to free the pdu.
  1718. */
  1719. static int
  1720. _bulkwalk_recv_pdu(walk_context *context, netsnmp_pdu *pdu)
  1721. {
  1722.    netsnmp_variable_list *vars;
  1723.    struct tree *tp;
  1724.    char type_str[MAX_TYPE_NAME_LEN];
  1725.    u_char str_buf[STR_BUF_SIZE], *str_bufp = str_buf;
  1726.    size_t str_buf_len = sizeof(str_buf);
  1727.    size_t out_len = 0;
  1728.    int buf_over = 0;
  1729.    char *label;
  1730.    char *iid;
  1731.    bulktbl *expect = NULL;
  1732.    int old_numeric;
  1733.    int old_printfull;
  1734.    int old_format;
  1735.    int getlabel_flag;
  1736.    int type;
  1737.    int pix;
  1738.    int len;
  1739.    int i;
  1740.    AV *varbind;
  1741.    SV *rv;
  1742.    SV **sess_ptr_sv = hv_fetch((HV*)SvRV(context->sess_ref), "SessPtr", 7, 1);
  1743.    SV **err_str_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorStr", 8, 1);
  1744.    SV **err_num_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorNum", 8, 1);
  1745.    SV **err_ind_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorInd", 8, 1);
  1746.    DBPRT(3, (DBOUT "bulkwalk: sess_ref = 0x%p, sess_ptr_sv = 0x%pn",
  1747.              context->sess_ref, sess_ptr_sv));
  1748.    /* Set up for numeric OID's, if necessary.  Save the old values
  1749.    ** so that they can be restored when we finish -- these are
  1750.    ** library-wide globals, and have to be set/restored for each
  1751.    ** session.
  1752.    */
  1753.    old_numeric   = netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_NUMERIC_OIDS);
  1754.    old_printfull = netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_FULL_OID);
  1755.    old_format = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT);
  1756.    if (context->getlabel_f & USE_NUMERIC_OIDS) {
  1757.       DBPRT(2,(DBOUT "Using numeric oid'sn"));
  1758.       netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_NUMERIC_OIDS, 1);
  1759.       netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_FULL_OID, 1);
  1760.       netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, NETSNMP_OID_OUTPUT_NUMERIC);
  1761.    }
  1762.    /* Parse through the list of variables returned, adding each return to
  1763.    ** the appropriate array (as a VarBind).  Also keep track of which
  1764.    ** repeated OID we're expecting to see, and check if that tree walk has
  1765.    ** been completed (i.e. we've walked past the root of our request).  If
  1766.    ** so, mark the request complete so that we don't send it again in any
  1767.    ** subsequent request packets.
  1768.    */
  1769.    if (context->pkts_exch == 1)
  1770.       context->reqbase = context->req_oids; /* Request with non-repeaters */
  1771.    else
  1772.       context->reqbase = context->repbase; /* Request only repeater vars */
  1773.    /* Note the first variable we expect to see.  Should be reqbase. */
  1774.    expect = context->reqbase;
  1775.    for (vars = pdu->variables, pix = 0;
  1776. vars != NULL;
  1777. vars = vars->next_variable, pix ++)
  1778.    {
  1779.       /* If no outstanding requests remain, we're done.  This works, but it
  1780.       ** causes the reported total variable count to be wrong (since the
  1781.       ** remaining vars on the last packet are not counted).  In practice
  1782.       ** this is probably worth the win, but for debugging it's not.
  1783.       */
  1784.       if (context->req_remain == 0) {
  1785.  DBPRT(2,(DBOUT "No outstanding requests remain.  Terminating processing.n"));
  1786.  while (vars) {
  1787.     pix ++;
  1788.     vars = vars->next_variable;
  1789.  }
  1790.  break;
  1791.       }
  1792.       /* Determine which OID we expect to see next.  We assert that the OID's
  1793.       ** must be returned in the expected order.  The first nreq_oids returns
  1794.       ** should match the req_oids array, after that, we must cycle through
  1795.       ** the repeaters in order.  Non-repeaters are not included in later
  1796.       ** packets, so cannot have the "ignore" flag set.
  1797.       */
  1798.       if (context->oid_saved < context->non_reps) {
  1799.  assert(context->pkts_exch == 1);
  1800.  expect = context->reqbase ++;
  1801.  assert(expect->norepeat);
  1802.       } else {
  1803.  /* Must be a repeater.  Look for the first one that is not being
  1804.  ** ignored.  Make sure we don't loop around to where we started.
  1805.  ** If we get here but everything is being ignored, there's a problem.
  1806.  **
  1807.  ** Note that we *do* accept completed but not ignored OID's -- these
  1808.  ** are OID's for trees that have been completed sometime in this
  1809.  ** response, but must be looked at to maintain ordering.
  1810.  */
  1811.  if (pix == 0) {
  1812.     /* Special case code for no non-repeater case.  This
  1813.     ** is necessary because expect normally points to the
  1814.     ** last non-repeater upon entry to this code (so the
  1815.     ** '++expect' below increments it into the repeaters
  1816.     ** section of the req_oids[] array).
  1817.     ** If there are no non-repeaters, the expect pointer
  1818.     ** is never initialized.  This addresses this problem.
  1819.     */
  1820.     expect = context->reqbase;
  1821.  } else {
  1822.     /* Find the repeater OID we expect to see.  Ignore any
  1823.     ** OID's marked 'ignore' -- these have been completed
  1824.     ** and were not requested in this iteration.
  1825.     */
  1826.     for (i = 0; i < context->repeaters; i++) {
  1827.        /* Loop around to first repeater if we hit the end. */
  1828.        if (++ expect == &context->req_oids[context->nreq_oids])
  1829.   expect = context->reqbase = context->repbase;
  1830.        /* Stop if this OID is not being ignored. */
  1831.        if (!expect->ignore)
  1832.   break;
  1833.     }
  1834.     /* Make sure we did find an expected OID. */
  1835.     assert(i <= context->repeaters);
  1836.  }
  1837.       }
  1838.       DBPRT(2, (DBOUT "Var %03d request %sn", pix, snprint_objid(_debugx, sizeof(_debugx), 
  1839.      expect->req_oid, expect->req_len)));
  1840.       /* Did we receive an error condition for this variable?
  1841.       ** If it's a repeated variable, mark it as complete and
  1842.       ** fall through to the block below.
  1843.       */
  1844.       if ((vars->type == SNMP_ENDOFMIBVIEW) ||
  1845.   (vars->type == SNMP_NOSUCHOBJECT) ||
  1846.   (vars->type == SNMP_NOSUCHINSTANCE))
  1847.       {
  1848.  DBPRT(2,(DBOUT "error type %dn", (int)vars->type));
  1849.  /* ENDOFMIBVIEW should be okay for a repeater - just walked off the
  1850.  ** end of the tree.  Mark the request as complete, and go on to the
  1851.  ** next one.
  1852.  */
  1853.  if ((context->oid_saved >= context->non_reps) &&
  1854.      (vars->type == SNMP_ENDOFMIBVIEW))
  1855.  {
  1856.     expect->complete = 1;
  1857.     DBPRT(2, (DBOUT "Ran out of tree for oid %sn",
  1858.    snprint_objid(_debugx, sizeof(_debugx), vars->name,vars->name_length)));
  1859.     context->req_remain --;
  1860.     /* Go on to the next variable. */
  1861.     continue;
  1862.  }
  1863.  sv_setpv(*err_str_svp,
  1864.       (char*)snmp_api_errstring(SNMPERR_UNKNOWN_OBJID));
  1865.  sv_setiv(*err_num_svp, SNMPERR_UNKNOWN_OBJID);
  1866.  sv_setiv(*err_ind_svp, pix);
  1867.  goto err;
  1868.       }
  1869.       /* If this is not the first packet, skip any duplicated OID values, if
  1870.       ** present.  These should be the seed values copied from the last OID's
  1871.       ** of the previous packet.  In practice we don't see this, but it is
  1872.       ** easy enough to do, and will avoid confusion for the caller from mis-
  1873.       ** behaving agents (badly misbehaving... ;^).
  1874.       */
  1875.       if ((context->pkts_exch > 1) && (pix < context->repeaters)) {
  1876.  if (__oid_cmp(vars->name, vars->name_length,
  1877.    context->reqbase[pix].last_oid,
  1878.    context->reqbase[pix].last_len) == 0)
  1879.  {
  1880.     DBPRT(2, (DBOUT "Ignoring repeat oid: %sn",
  1881. snprint_objid(_debugx, sizeof(_debugx), vars->name,vars->name_length)));
  1882.     continue;
  1883.  }
  1884.       }
  1885.       context->oid_total ++; /* Count each variable received. */
  1886.       /* If this is a non-repeater, handle it.  Otherwise, if it is a
  1887.       ** repeater, has the walk wandered off of the requested tree?  If so,
  1888.       ** this request is complete, so mark it as such.  Ignore any other
  1889.       ** variables in a completed request.  In order to maintain the correct
  1890.       ** ordering of which variables we expect to see in this packet, we must
  1891.       ** not set the ignore flags immediately.  It is done in bulkwalk_done().
  1892.       ** XXX Can we use 'expect' instead of 'context->req_oids[pix]'?
  1893.       */
  1894.       if (context->oid_saved < context->non_reps) {
  1895.  DBPRT(2, (DBOUT "   expected var %s (nonrepeater %d/%d)n",
  1896.      snprint_objid(_debugx, sizeof(_debugx), context->req_oids[pix].req_oid,
  1897.    context->req_oids[pix].req_len),
  1898.      pix, context->non_reps));
  1899.  DBPRT(2, (DBOUT "   received var %sn",
  1900.      snprint_objid(_debugx, sizeof(_debugx), vars->name, vars->name_length)));
  1901.  /* This non-repeater has now been seen, so mark the sub-tree as
  1902.  ** completed.  Note that this may not be the same oid as requested,
  1903.  ** since non-repeaters act like GETNEXT requests, not GET's. <sigh>
  1904.  */
  1905.  context->req_oids[pix].complete = 1;
  1906.  context->req_remain --;
  1907.       } else { /* Must be a repeater variable. */
  1908.  DBPRT(2, (DBOUT "   received oid %sn",
  1909.        snprint_objid(_debugx, sizeof(_debugx), vars->name, vars->name_length)));
  1910.  /* Are we already done with this tree?  If so, just ignore this
  1911.  ** variable and move on to the next expected variable.
  1912.  */
  1913.  if (expect->complete) {
  1914.     DBPRT(2,(DBOUT "      this branch is complete - ignoring.n"));
  1915.     continue;
  1916.  }
  1917.  /* If the base oid of this variable doesn't match the expected oid,
  1918.  ** assume that we've walked past the end of the subtree.  Set this
  1919.  ** subtree to be completed, and go on to the next variable.
  1920.  */
  1921.  if (((int)vars->name_length < expect->req_len) ||
  1922.      (memcmp(vars->name, expect->req_oid, expect->req_len*sizeof(oid))))
  1923.  {
  1924.     DBPRT(2,(DBOUT "      walked off branch - marking subtree as complete.n"));
  1925.     expect->complete = 1;
  1926.     context->req_remain --;
  1927.     continue;
  1928.  }
  1929.  /* Still interested in the tree -- we need to keep track of the
  1930.  ** last-seen value in case we need to send an additional request
  1931.  ** packet.
  1932.  */
  1933.  (void)memcpy(expect->last_oid, vars->name,
  1934.      vars->name_length * sizeof(oid));
  1935.  expect->last_len = vars->name_length;
  1936.       }
  1937.       /* Create a new Varbind and populate it with the parsed information
  1938.       ** returned by the agent.  This Varbind is then pushed onto the arrays
  1939.       ** maintained for each request OID in the context.  These varbinds are
  1940.       ** collected into a return array by bulkwalk_finish().
  1941.       */
  1942.       varbind = (AV*) newAV();
  1943.       if (varbind == NULL) {
  1944.  sv_setpv(*err_str_svp, "newAV() failed: ");
  1945.  sv_catpv(*err_str_svp, (char*)strerror(errno));
  1946.  sv_setiv(*err_num_svp, SNMPERR_MALLOC);
  1947.  goto err;
  1948.       }
  1949.       *str_buf = '.';
  1950.       *(str_buf+1) = '';
  1951.       out_len = 0;
  1952.       tp = netsnmp_sprint_realloc_objid_tree(&str_bufp, &str_buf_len,
  1953.                                              &out_len, 0, &buf_over,
  1954.                                              vars->name,vars->name_length);
  1955.       str_buf[sizeof(str_buf)-1] = '';
  1956.       getlabel_flag = context->getlabel_f;
  1957.       if (__is_leaf(tp)) {
  1958.  type = tp->type;
  1959.       } else {
  1960.  getlabel_flag |= NON_LEAF_NAME;
  1961.  type = __translate_asn_type(vars->type);
  1962.       }
  1963.       if (__get_label_iid(str_buf, &label, &iid, getlabel_flag) == FAILURE) {
  1964.           label = str_buf;
  1965.           iid = label + strlen(label);
  1966.       }
  1967.       DBPRT(2,(DBOUT "       save var %s.%s = ", label, iid));
  1968.       av_store(varbind, VARBIND_TAG_F, newSVpv(label, strlen(label)));
  1969.       av_store(varbind, VARBIND_IID_F, newSVpv(iid, strlen(iid)));
  1970.       __get_type_str(type, type_str);
  1971.       av_store(varbind, VARBIND_TYPE_F, newSVpv(type_str, strlen(type_str)));
  1972.       len=__snprint_value(str_buf, sizeof(str_buf),
  1973.                          vars, tp, type, context->sprintval_f);
  1974.       av_store(varbind, VARBIND_VAL_F, newSVpv((char*)str_buf, len));
  1975.       str_buf[len] = '';
  1976.       DBPRT(3,(DBOUT "'%s' (%s)n", str_buf, type_str));
  1977. #if 0
  1978.     /* huh? */
  1979.       /* If necessary, store a timestamp as the semi-documented 5th element. */
  1980.       if (sv_timestamp)
  1981.   av_store(varbind, VARBIND_TIME_F, SvREFCNT_inc(sv_timestamp));
  1982. #endif
  1983.       /* Push ref to the varbind onto the list of vars for OID. */
  1984.       rv = newRV_noinc((SV *)varbind);
  1985.       sv_bless(rv, gv_stashpv("SNMP::Varbind", 0));
  1986.       av_push(expect->vars, rv);
  1987.       context->oid_saved ++; /* Count this as a saved variable. */
  1988.    } /* next variable in response packet */
  1989.    DBPRT(1, (DBOUT "-- pkt %d saw %d vars, total %d (%d saved)n", context->pkts_exch,
  1990.    pix, context->oid_total, context->oid_saved));
  1991.    /* We assert that all non-repeaters must be returned in
  1992.    ** the initial response (they are not repeated in additional
  1993.    ** packets, so would be dropped).  If nonrepeaters still
  1994.    ** exist, consider it a fatal error.
  1995.    */
  1996.    if ((context->pkts_exch == 1) && (context->oid_saved < context->non_reps)) {
  1997.       /* Re-use space from the value string for error message. */
  1998.       sprintf(str_buf, "%d non-repeaters went unanswered", context->non_reps);
  1999.       sv_setpv(*err_str_svp, str_buf);
  2000.       sv_setiv(*err_num_svp, SNMPERR_GENERR);
  2001.       sv_setiv(*err_num_svp, context->oid_saved);
  2002.       goto err;
  2003.    }
  2004.    /* Reset the library's behavior for numeric/symbolic OID's. */
  2005.    if (context->getlabel_f & USE_NUMERIC_OIDS) {
  2006.       netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_NUMERIC_OIDS, old_numeric);
  2007.       netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_FULL_OID, old_printfull);
  2008.       netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, old_format);
  2009.    }
  2010.    return pix;
  2011.    err:
  2012.    if (pdu)
  2013.       snmp_free_pdu(pdu);
  2014.    return -1;
  2015. }
  2016. /* Once the bulkwalk has completed, extend the stack and push references to
  2017. ** each of the arrays of SNMP::Varbind's onto the stack.  Return the number
  2018. ** of arrays pushed on the stack.  The caller should return to Perl, or call
  2019. ** the Perl callback function.
  2020. **
  2021. ** Note that this function free()'s the walk_context and request bulktbl's.
  2022. */
  2023. static int
  2024. _bulkwalk_finish(walk_context *context, int okay)
  2025. {
  2026.    int npushed = 0;
  2027.    int i;
  2028.    int async = 0;
  2029.    bulktbl *bt_entry;
  2030.    AV *ary = NULL;
  2031.    SV *rv;
  2032.    SV *perl_cb;
  2033.    SV **err_str_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorStr", 8, 1);
  2034.    SV **err_num_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorNum", 8, 1);
  2035.    dXSARGS;
  2036.    async = SvTRUE(context->perl_cb);
  2037.    /* Successfully completed the bulkwalk.  For synchronous calls, push each
  2038.    ** of the request value arrays onto the stack, and return the number of
  2039.    ** items pushed onto the stack.  For async, create a new array and push
  2040.    ** the references onto it.  The array is then passed to the Perl callback.
  2041.    */
  2042.    if (!async)
  2043.       SP -= items;
  2044.    DBPRT(1, (DBOUT "Bulwalk %s (saved %d/%d), ", okay ? "completed" : "had error",
  2045. context->oid_saved, context->oid_total));
  2046.    if (okay) {
  2047.        DBPRT(1, (DBOUT "%s %d varbind refs %sn",
  2048. async ? "pass ref to array of" : "return",
  2049. context->nreq_oids,
  2050. async ? "to callback" : "on stack to caller"));
  2051.        /* Create the array to hold the responses for the asynchronous callback,
  2052.        ** or pre-extend the stack enough to hold responses for synch return.
  2053.        */
  2054.        if (async) {
  2055.    ary = (AV *)newAV();
  2056.   if (ary == NULL) {
  2057.      sv_setpv(*err_str_svp, "newAV(): ");
  2058.      sv_catpv(*err_str_svp, (char *)strerror(errno));
  2059.      sv_setiv(*err_num_svp, errno);
  2060.   }
  2061.   /* NULL ary pointer is okay -- we'll handle it below... */
  2062.        } else {
  2063.    EXTEND(sp, context->nreq_oids);
  2064.        }
  2065.        /* Push a reference to each array of varbinds onto the stack, in
  2066.        ** the order requested.  Note that these arrays may be empty.
  2067.        */
  2068.        for (i = 0; i < context->nreq_oids; i++) {
  2069.   bt_entry = &context->req_oids[i];
  2070.   DBPRT(2, (DBOUT "  %sreq #%d (%s) => %d var%sn",
  2071.  bt_entry->complete ? "" : "incomplete ", i,
  2072.  snprint_objid(_debugx, sizeof(_debugx), bt_entry->req_oid, bt_entry->req_len),
  2073.  (int)av_len(bt_entry->vars) + 1,
  2074.  (int)av_len(bt_entry->vars) > 0 ? "s" : ""));
  2075.   if (async && ary == NULL) {
  2076.      DBPRT(2,(DBOUT "    [dropped due to newAV() failure]n"));
  2077.      continue;
  2078.   }
  2079.   /* Get a reference to the varlist, and push it onto array or stack */
  2080.   rv = newRV_noinc((SV *)bt_entry->vars);
  2081.   sv_bless(rv, gv_stashpv("SNMP::VarList",0));
  2082.   if (async)
  2083.      av_push(ary, rv);
  2084.   else
  2085.      PUSHs(sv_2mortal((SV *)rv));
  2086.   npushed ++;
  2087.        }
  2088.    } else { /* Not okay -- push a single undef on the stack if not async */
  2089.       if (!async) {
  2090.  XPUSHs(&sv_undef);
  2091.  npushed = 1;
  2092.       }
  2093.    }
  2094.    /* XXX Future enhancement -- make statistics (pkts exchanged, vars
  2095.    ** saved vs. received, total time, etc) available to caller so they
  2096.    ** can adjust their request parameters and/or re-order requests.
  2097.    */
  2098.    PUTBACK;
  2099.    if (async) {
  2100.        /* Asynchronous callback.  Push the caller's arglist onto the stack,
  2101.        ** and follow it with the contents of the array (or undef if newAV()
  2102.        ** failed or the session had an error).  Then mortalize the Perl
  2103.        ** callback pointer, and call the callback.
  2104.        */
  2105.        if (!okay || ary == NULL)
  2106.           rv = &sv_undef;
  2107.        else
  2108.   rv = newRV_noinc((SV *)ary);
  2109.        sv_2mortal(perl_cb = context->perl_cb);
  2110.        perl_cb = __push_cb_args(perl_cb, (SvTRUE(rv) ? sv_2mortal(rv) : rv));
  2111.        __call_callback(perl_cb, G_DISCARD);
  2112.    }
  2113.    sv_2mortal(context->sess_ref);
  2114.    /* Free the allocated space for the request states and return number of
  2115.    ** variables found.  Remove the context from the valid context list.
  2116.    */
  2117.    _context_del(context);
  2118.    DBPRT(2,(DBOUT "Free() context->req_oidsn"));
  2119.    Safefree(context->req_oids);
  2120.    DBPRT(2,(DBOUT "Free() context 0x%pn", context));
  2121.    Safefree(context);
  2122.    return npushed;
  2123. }
  2124. /* End of bulkwalk support routines */
  2125. static char *
  2126. __av_elem_pv(AV *av, I32 key, char *dflt)
  2127. {
  2128.    SV **elem = av_fetch(av, key, 0);
  2129.    return (elem && SvOK(*elem)) ? SvPV(*elem, na) : dflt;
  2130. }
  2131. static int
  2132. not_here(s)
  2133. char *s;
  2134. {
  2135.     croak("%s not implemented on this architecture", s);
  2136.     return -1;
  2137. }
  2138. static double
  2139. constant(name, arg)
  2140. char *name;
  2141. int arg;
  2142. {
  2143.     errno = 0;
  2144.     switch (*name) {
  2145.     case 'R':
  2146. if (strEQ(name, "NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE"))
  2147. #ifdef NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE
  2148.     return NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE;
  2149. #else
  2150.     goto not_there;
  2151. #endif
  2152. break;
  2153.     case 'S':
  2154. if (strEQ(name, "SNMPERR_BAD_ADDRESS"))
  2155. #ifdef SNMPERR_BAD_ADDRESS
  2156.     return SNMPERR_BAD_ADDRESS;
  2157. #else
  2158.     goto not_there;
  2159. #endif
  2160. if (strEQ(name, "SNMPERR_BAD_LOCPORT"))
  2161. #ifdef SNMPERR_BAD_LOCPORT
  2162.     return SNMPERR_BAD_LOCPORT;
  2163. #else
  2164.     goto not_there;
  2165. #endif
  2166. if (strEQ(name, "SNMPERR_BAD_SESSION"))
  2167. #ifdef SNMPERR_BAD_SESSION
  2168.     return SNMPERR_BAD_SESSION;
  2169. #else
  2170.     goto not_there;
  2171. #endif
  2172. if (strEQ(name, "SNMPERR_GENERR"))
  2173. #ifdef SNMPERR_GENERR
  2174.     return SNMPERR_GENERR;
  2175. #else
  2176.     goto not_there;
  2177. #endif
  2178. if (strEQ(name, "SNMPERR_TOO_LONG"))
  2179. #ifdef SNMPERR_TOO_LONG
  2180.     return SNMPERR_TOO_LONG;
  2181. #else
  2182.     goto not_there;
  2183. #endif
  2184. if (strEQ(name, "SNMP_DEFAULT_ADDRESS"))
  2185. #ifdef SNMP_DEFAULT_ADDRESS
  2186.     return SNMP_DEFAULT_ADDRESS;
  2187. #else
  2188.     goto not_there;
  2189. #endif
  2190. if (strEQ(name, "SNMP_DEFAULT_COMMUNITY_LEN"))
  2191. #ifdef SNMP_DEFAULT_COMMUNITY_LEN
  2192.     return SNMP_DEFAULT_COMMUNITY_LEN;
  2193. #else
  2194.     goto not_there;
  2195. #endif
  2196. if (strEQ(name, "SNMP_DEFAULT_ENTERPRISE_LENGTH"))
  2197. #ifdef SNMP_DEFAULT_ENTERPRISE_LENGTH
  2198.     return SNMP_DEFAULT_ENTERPRISE_LENGTH;
  2199. #else
  2200.     goto not_there;
  2201. #endif
  2202. if (strEQ(name, "SNMP_DEFAULT_ERRINDEX"))
  2203. #ifdef SNMP_DEFAULT_ERRINDEX
  2204.     return SNMP_DEFAULT_ERRINDEX;
  2205. #else
  2206.     goto not_there;
  2207. #endif
  2208. if (strEQ(name, "SNMP_DEFAULT_ERRSTAT"))
  2209. #ifdef SNMP_DEFAULT_ERRSTAT
  2210.     return SNMP_DEFAULT_ERRSTAT;
  2211. #else
  2212.     goto not_there;
  2213. #endif
  2214. if (strEQ(name, "SNMP_DEFAULT_PEERNAME"))
  2215. #ifdef SNMP_DEFAULT_PEERNAME
  2216.     return 0;
  2217. #else
  2218.     goto not_there;
  2219. #endif
  2220. if (strEQ(name, "SNMP_DEFAULT_REMPORT"))
  2221. #ifdef SNMP_DEFAULT_REMPORT
  2222.     return SNMP_DEFAULT_REMPORT;
  2223. #else
  2224.     goto not_there;
  2225. #endif
  2226. if (strEQ(name, "SNMP_DEFAULT_REQID"))
  2227. #ifdef SNMP_DEFAULT_REQID
  2228.     return SNMP_DEFAULT_REQID;
  2229. #else
  2230.     goto not_there;
  2231. #endif
  2232. if (strEQ(name, "SNMP_DEFAULT_RETRIES"))
  2233. #ifdef SNMP_DEFAULT_RETRIES
  2234.     return SNMP_DEFAULT_RETRIES;
  2235. #else
  2236.     goto not_there;
  2237. #endif
  2238. if (strEQ(name, "SNMP_DEFAULT_TIME"))
  2239. #ifdef SNMP_DEFAULT_TIME
  2240.     return SNMP_DEFAULT_TIME;
  2241. #else
  2242.     goto not_there;
  2243. #endif
  2244. if (strEQ(name, "SNMP_DEFAULT_TIMEOUT"))
  2245. #ifdef SNMP_DEFAULT_TIMEOUT
  2246.     return SNMP_DEFAULT_TIMEOUT;
  2247. #else
  2248.     goto not_there;
  2249. #endif
  2250. if (strEQ(name, "SNMP_DEFAULT_VERSION"))
  2251. #ifdef DEFAULT_SNMP_VERSION
  2252.     return DEFAULT_SNMP_VERSION;
  2253. #else
  2254. #ifdef SNMP_DEFAULT_VERSION
  2255.     return SNMP_DEFAULT_VERSION;
  2256. #else
  2257.     goto not_there;
  2258. #endif
  2259. #endif
  2260. break;
  2261.     case 'T':
  2262. if (strEQ(name, "NETSNMP_CALLBACK_OP_TIMED_OUT"))
  2263. #ifdef NETSNMP_CALLBACK_OP_TIMED_OUT
  2264.     return NETSNMP_CALLBACK_OP_TIMED_OUT;
  2265. #else
  2266.     goto not_there;
  2267. #endif
  2268. break;
  2269.     default:
  2270. break;
  2271.     }
  2272.     errno = EINVAL;
  2273.     return 0;
  2274. #ifndef NETSNMP_CALLBACK_OP_TIMED_OUT
  2275. not_there:
  2276. #endif
  2277.     errno = ENOENT;
  2278.     return 0;
  2279. }
  2280. MODULE = SNMP PACKAGE = SNMP PREFIX = snmp
  2281. double
  2282. constant(name,arg)
  2283. char * name
  2284. int arg
  2285. long
  2286. snmp_sys_uptime()
  2287. CODE:
  2288. RETVAL = get_uptime();
  2289. OUTPUT:
  2290. RETVAL
  2291. void
  2292. init_snmp(appname)
  2293.         char *appname
  2294.     CODE:
  2295.         __libraries_init(appname);
  2296. SnmpSession *
  2297. snmp_new_session(version, community, peer, lport, retries, timeout)
  2298.         char * version
  2299.         char * community
  2300.         char * peer
  2301.         int lport
  2302.         int retries
  2303.         int timeout
  2304. CODE:
  2305. {
  2306.    SnmpSession session = {0};
  2307.    SnmpSession *ss = NULL;
  2308.            int verbose = SvIV(perl_get_sv("SNMP::verbose", 0x01 | 0x04));
  2309.            __libraries_init("perl");
  2310.            
  2311.            session.version = -1;
  2312. #ifndef DISABLE_SNMPV1
  2313.    if (!strcmp(version, "1")) {
  2314. session.version = SNMP_VERSION_1;
  2315.            }
  2316. #endif
  2317. #ifndef DISABLE_SNMPV2C
  2318.            if ((!strcmp(version, "2")) || (!strcmp(version, "2c"))) {
  2319. session.version = SNMP_VERSION_2c;
  2320.            }
  2321. #endif
  2322.            if (!strcmp(version, "3")) {
  2323.         session.version = SNMP_VERSION_3;
  2324.    }
  2325.            if (session.version == -1) {
  2326. if (verbose)
  2327.                    warn("error:snmp_new_session:Unsupported SNMP version (%s)n", version);
  2328.                 goto end;
  2329.    }
  2330.            session.community_len = strlen((char *)community);