📄 snmp.xs
字号:
char *label; char *iid; bulktbl *expect = NULL; int old_numeric; int old_printfull; int getlabel_flag; int type; int pix; int len; int i; AV *varbind; SV *rv; SV *sv_timestamp = NULL; SV **sess_ptr_sv = hv_fetch((HV*)SvRV(context->sess_ref), "SessPtr", 7, 1); struct snmp_session *ss = (SnmpSession *)SvIV((SV*)SvRV(*sess_ptr_sv)); SV **err_str_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorStr", 8, 1); SV **err_num_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorNum", 8, 1); SV **err_ind_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorInd", 8, 1); DBPRT(3, (DBOUT "bulkwalk: sess_ref = 0x%p, sess_ptr_sv = 0x%p, ss = 0x%p\n", context->sess_ref, sess_ptr_sv, ss)); if (SvIV(*hv_fetch((HV*)SvRV(context->sess_ref),"TimeStamp", 9, 1))) sv_timestamp = newSViv((IV)time(NULL)); /* Set up for numeric OID's, if necessary. Save the old values ** so that they can be restored when we finish -- these are ** library-wide globals, and have to be set/restored for each ** session. */ old_numeric = ds_get_boolean(DS_LIBRARY_ID, DS_LIB_PRINT_NUMERIC_OIDS); old_printfull = ds_get_boolean(DS_LIBRARY_ID, DS_LIB_PRINT_FULL_OID); if (context->getlabel_f & USE_NUMERIC_OIDS) { DBPRT(2,( "Using numeric oid's\n")); ds_set_boolean(DS_LIBRARY_ID, DS_LIB_PRINT_NUMERIC_OIDS, 1); ds_set_boolean(DS_LIBRARY_ID, DS_LIB_PRINT_FULL_OID, 1); } /* Parse through the list of variables returned, adding each return to ** the appropriate array (as a VarBind). Also keep track of which ** repeated OID we're expecting to see, and check if that tree walk has ** been completed (i.e. we've walked past the root of our request). If ** so, mark the request complete so that we don't send it again in any ** subsequent request packets. */ if (context->pkts_exch == 1) context->reqbase = context->req_oids; /* Request with non-repeaters */ else context->reqbase = context->repbase; /* Request only repeater vars */ /* Note the first variable we expect to see. Should be reqbase. */ expect = context->reqbase; for (vars = pdu->variables, pix = 0; vars != NULL; vars = vars->next_variable, pix ++) { /* If no outstanding requests remain, we're done. This works, but it ** causes the reported total variable count to be wrong (since the ** remaining vars on the last packet are not counted). In practice ** this is probably worth the win, but for debugging it's not. */ if (context->req_remain == 0) { DBPRT(2,( "No outstanding requests remain. Terminating processing.\n")); while (vars) { pix ++; vars = vars->next_variable; } break; } /* Determine which OID we expect to see next. We assert that the OID's ** must be returned in the expected order. The first nreq_oids returns ** should match the req_oids array, after that, we must cycle through ** the repeaters in order. Non-repeaters are not included in later ** packets, so cannot have the "ignore" flag set. */ if (context->oid_saved < context->non_reps) { assert(context->pkts_exch == 1); expect = context->reqbase ++; assert(expect->norepeat); } else { /* Must be a repeater. Look for the first one that is not being ** ignored. Make sure we don't loop around to where we started. ** If we get here but everything is being ignored, there's a problem. ** ** Note that we *do* accept completed but not ignored OID's -- these ** are OID's for trees that have been completed sometime in this ** response, but must be looked at to maintain ordering. */ if (pix == 0) { /* Special case code for no non-repeater case. This ** is necessary because expect normally points to the ** last non-repeater upon entry to this code (so the ** '++expect' below increments it into the repeaters ** section of the req_oids[] array). ** If there are no non-repeaters, the expect pointer ** is never initialized. This addresses this problem. */ expect = context->reqbase; } else { /* Find the repeater OID we expect to see. Ignore any ** OID's marked 'ignore' -- these have been completed ** and were not requested in this iteration. */ for (i = 0; i < context->repeaters; i++) { /* Loop around to first repeater if we hit the end. */ if (++ expect == &context->req_oids[context->nreq_oids]) expect = context->reqbase = context->repbase; /* Stop if this OID is not being ignored. */ if (!expect->ignore) break; } /* Make sure we did find an expected OID. */ assert(i <= context->repeaters); } } DBPRT(2, (DBOUT "Var %03d request %s\n", pix, sprint_objid(_debugx, expect->req_oid, expect->req_len))); /* Did we receive an error condition for this variable? ** If it's a repeated variable, mark it as complete and ** fall through to the block below. */ if ((vars->type == SNMP_ENDOFMIBVIEW) || (vars->type == SNMP_NOSUCHOBJECT) || (vars->type == SNMP_NOSUCHINSTANCE)) { DBPRT(2,( "error type %d\n", (int)vars->type)); /* ENDOFMIBVIEW should be okay for a repeater - just walked off the ** end of the tree. Mark the request as complete, and go on to the ** next one. */ if ((context->oid_saved >= context->non_reps) && (vars->type == SNMP_ENDOFMIBVIEW)) { expect->complete = 1; DBPRT(2, (DBOUT "Ran out of tree for oid %s\n", sprint_objid(_debugx, vars->name,vars->name_length))); context->req_remain --; /* Go on to the next variable. */ continue; } sv_setpv(*err_str_svp, (char*)snmp_api_errstring(SNMPERR_UNKNOWN_OBJID)); sv_setiv(*err_num_svp, SNMPERR_UNKNOWN_OBJID); sv_setiv(*err_ind_svp, pix); goto err; } /* If this is not the first packet, skip any duplicated OID values, if ** present. These should be the seed values copied from the last OID's ** of the previous packet. In practice we don't see this, but it is ** easy enough to do, and will avoid confusion for the caller from mis- ** behaving agents (badly misbehaving... ;^). */ if ((context->pkts_exch > 1) && (pix < context->repeaters)) { if (__oid_cmp(vars->name, vars->name_length, context->reqbase[pix].last_oid, context->reqbase[pix].last_len) == 0) { DBPRT(2, (DBOUT "Ignoring repeat oid: %s\n", sprint_objid(_debugx, vars->name,vars->name_length))); continue; } } context->oid_total ++; /* Count each variable received. */ /* If this is a non-repeater, handle it. Otherwise, if it is a ** repeater, has the walk wandered off of the requested tree? If so, ** this request is complete, so mark it as such. Ignore any other ** variables in a completed request. In order to maintain the correct ** ordering of which variables we expect to see in this packet, we must ** not set the ignore flags immediately. It is done in bulkwalk_done(). ** XXX Can we use 'expect' instead of 'context->req_oids[pix]'? */ if (context->oid_saved < context->non_reps) { DBPRT(2, (DBOUT " expected var %s (nonrepeater %d/%d)\n", sprint_objid(_debugx, context->req_oids[pix].req_oid, context->req_oids[pix].req_len), pix, context->non_reps)); DBPRT(2, (DBOUT " received var %s\n", sprint_objid(_debugx, vars->name, vars->name_length))); /* This non-repeater has now been seen, so mark the sub-tree as ** completed. Note that this may not be the same oid as requested, ** since non-repeaters act like GETNEXT requests, not GET's. <sigh> */ context->req_oids[pix].complete = 1; context->req_remain --; } else { /* Must be a repeater variable. */ DBPRT(2, (DBOUT " received oid %s\n", sprint_objid(_debugx, vars->name, vars->name_length))); /* Are we already done with this tree? If so, just ignore this ** variable and move on to the next expected variable. */ if (expect->complete) { DBPRT(2,( " this branch is complete - ignoring.\n")); continue; } /* If the base oid of this variable doesn't match the expected oid, ** assume that we've walked past the end of the subtree. Set this ** subtree to be completed, and go on to the next variable. */ if ((vars->name_length < expect->req_len) || (memcmp(vars->name, expect->req_oid, expect->req_len*sizeof(oid)))) { DBPRT(2,( " walked off branch - marking subtree as complete.\n")); expect->complete = 1; context->req_remain --; continue; } /* Still interested in the tree -- we need to keep track of the ** last-seen value in case we need to send an additional request ** packet. */ (void)memcpy(expect->last_oid, vars->name, vars->name_length * sizeof(oid)); expect->last_len = vars->name_length; } /* Create a new Varbind and populate it with the parsed information ** returned by the agent. This Varbind is then pushed onto the arrays ** maintained for each request OID in the context. These varbinds are ** collected into a return array by bulkwalk_finish(). */ varbind = (AV*) newAV(); if (varbind == NULL) { sv_setpv(*err_str_svp, "newAV() failed: "); sv_catpv(*err_str_svp, (char*)strerror(errno)); sv_setiv(*err_num_svp, SNMPERR_MALLOC); goto err; } *str_buf = '.'; *(str_buf+1) = '\0'; tp = get_symbol(vars->name,vars->name_length, get_tree_head(), str_buf+1); getlabel_flag = context->getlabel_f; if (__is_leaf(tp)) { type = tp->type; } else { getlabel_flag |= NON_LEAF_NAME; type = __translate_asn_type(vars->type); } __get_label_iid(str_buf, &label, &iid, getlabel_flag); DBPRT(2,( " save var %s.%s = ", label, iid)); av_store(varbind, VARBIND_TAG_F, newSVpv(label, strlen(label))); av_store(varbind, VARBIND_IID_F, newSVpv(iid, strlen(iid))); __get_type_str(type, type_str); av_store(varbind, VARBIND_TYPE_F, newSVpv(type_str, strlen(type_str))); len=__sprint_value(str_buf, vars, tp, type, context->sprintval_f); av_store(varbind, VARBIND_VAL_F, newSVpv((char*)str_buf, len)); str_buf[len] = '\0'; DBPRT(3,( "'%s' (%s)\n", str_buf, type_str)); /* If necessary, store a timestamp as the semi-documented 5th element. */ if (sv_timestamp) av_store(varbind, VARBIND_TIME_F, SvREFCNT_inc(sv_timestamp)); /* Push ref to the varbind onto the list of vars for OID. */ rv = newRV_noinc((SV *)varbind); sv_bless(rv, gv_stashpv("SNMP::Varbind", 0)); av_push(expect->vars, rv); context->oid_saved ++; /* Count this as a saved variable. */ } /* next variable in response packet */ DBPRT(1, (DBOUT "-- pkt %d saw %d vars, total %d (%d saved)\n", context->pkts_exch, pix, context->oid_total, context->oid_saved)); /* We assert that all non-repeaters must be returned in ** the initial response (they are not repeated in additional ** packets, so would be dropped). If nonrepeaters still ** exist, consider it a fatal error. */ if ((context->pkts_exch == 1) && (context->oid_saved < context->non_reps)) { /* Re-use space from the value string for error message. */ sprintf(str_buf, "%d non-repeaters went unanswered", context->non_reps); sv_setpv(*err_str_svp, str_buf); sv_setiv(*err_num_svp, SNMPERR_GENERR); sv_setiv(*err_num_svp, context->oid_saved); goto err; } /* Reset the library's behavior for numeric/symbolic OID's. */ if (context->getlabel_f & USE_NUMERIC_OIDS) { ds_set_boolean(DS_LIBRARY_ID, DS_LIB_PRINT_NUMERIC_OIDS, old_numeric); ds_set_boolean(DS_LIBRARY_ID, DS_LIB_PRINT_FULL_OID, old_printfull); } return pix; err: if (pdu) snmp_free_pdu(pdu); return -1;}/* Once the bulkwalk has completed, extend the stack and push references to** each of the arrays of SNMP::Varbind's onto the stack. Return the number** of arrays pushed on the stack. The caller should return to Perl, or call** the Perl callback function.**** Note that this function free()'s the walk_context and request bulktbl's.*/static int_bulkwalk_finish(walk_context *context, int okay){ int npushed = 0; int i; int async = 0; bulktbl *bt_entry; AV *ary = NULL; SV *rv; SV *perl_cb; SV **err_str_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorStr", 8, 1); SV **err_num_svp = hv_fetch((HV*)SvRV(context->sess_ref), "ErrorNum", 8, 1); dXSARGS; async = SvTRUE(context->perl_cb); /* Successfully completed the bulkwalk. For synchronous calls, push each ** of the request value arrays onto the stack, and return the number of ** items pushed onto the stack. For async, create a new array and push ** the references onto it. The array is then passed to the Perl callback. */ if (!async) SP -= items; DBPRT(1, (DBOUT "Bulwalk %s (saved %d/%d), ", okay ? "completed" : "had error", context->oid_saved, context->oid_total)); if (okay) { DBPRT(1, (DBOUT "%s %d varbind refs %s\n", async ? "pass ref to array of" : "return", context->nreq_oids, async ? "to callback" : "on stack to caller")); /* Create the array to hold the responses for the asynchronous callback, ** or pre-extend the stack enough to hold responses for synch return. */ if (async) { ary = (AV *)newAV(); if (ary == NULL) { sv_setpv(*err_str_svp, "newAV(): "); sv_catpv(*err_str_svp, (char *)strerror(errno)); sv_setiv(*err_num_svp, errno); } /* NULL ary pointer is okay -- we'll handle it below... */ } else { EXTEND(sp, context->nreq_oids); } /* Push a reference to each array of varbinds onto the stack, in ** the order requested. Note that these arrays may be empty. */ for (i = 0; i < context->nreq_oids; i++) { bt_entry = &context->req_oids[i]; DBPRT(2, (DBOUT " %sreq #%d (%s) => %d var%s\n", bt_entry->complete ? "" : "incomplete ", i, sprint_objid(_debugx, bt_entry->req_oid, bt_entry->req_len), (int)av_len(bt_entry->vars) + 1, (int)av_len(bt_entry->vars) > 0 ? "s" : "")); if (async && ary == NULL) { DBPRT(2,( " [dropped due to newAV() failure]\n")); continue; } /* Get a reference to the varlist, and push it onto array or stack */ rv = newRV_noinc((SV *)bt_entry->vars); sv_bless(rv, gv_stashpv("SNMP::VarList",0)); if (async) av_push(ary, rv); else PUSHs(sv_2mortal((SV *)rv)); npushed ++; } } else { /* Not okay -- push a single undef on the stack if not async */ if (!async) { XPUSHs(&sv_undef); npushed = 1; } } /* XXX Future enhancement -- make statistics (pkts exchanged, vars ** saved vs. received, total time, etc) available to caller so they ** can adjust their request parameters and/or re-order requests. */ PUTBACK; if (async) { /* Asynchronous callback. Push the caller's arglist onto the stack, ** and follow it with the contents of the array (or undef if newAV() ** failed
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -