/* * Portions of this file are subject to the following copyright(s). See * the Net-SNMP's COPYING file for more details and other copyrights * that may apply: * * Portions of this file are copyrighted by: * Copyright (c) 2016 VMware, Inc. All rights reserved. * Use is subject to license terms specified in the COPYING file * distributed with the Net-SNMP package. */ #include #include #include #include #include #if HAVE_STRING_H #include #else #include #endif #include #include static netsnmp_scalar_group* clone_scalar_group(netsnmp_scalar_group* src) { netsnmp_scalar_group *t = SNMP_MALLOC_TYPEDEF(netsnmp_scalar_group); if(t != NULL) { t->lbound = src->lbound; t->ubound = src->ubound; } return t; } /** @defgroup scalar_group_group scalar_group * Process groups of scalars. * @ingroup leaf * @{ */ netsnmp_mib_handler * netsnmp_get_scalar_group_handler(oid first, oid last) { netsnmp_mib_handler *ret = NULL; netsnmp_scalar_group *sgroup = NULL; ret = netsnmp_create_handler("scalar_group", netsnmp_scalar_group_helper_handler); if (ret) { sgroup = SNMP_MALLOC_TYPEDEF(netsnmp_scalar_group); if (NULL == sgroup) { netsnmp_handler_free(ret); ret = NULL; } else { sgroup->lbound = first; sgroup->ubound = last; ret->myvoid = (void *)sgroup; ret->data_free = free; ret->data_clone = (void *(*)(void *))clone_scalar_group; } } return ret; } int netsnmp_register_scalar_group(netsnmp_handler_registration *reginfo, oid first, oid last) { netsnmp_mib_handler *h1, *h2; h1 = netsnmp_get_instance_handler(); h2 = netsnmp_get_scalar_group_handler(first, last); if (h1 && h2) { if (netsnmp_inject_handler(reginfo, h1) == SNMPERR_SUCCESS) { h1 = NULL; if (netsnmp_inject_handler(reginfo, h2) == SNMPERR_SUCCESS) return netsnmp_register_serialize(reginfo); } } snmp_log(LOG_ERR, "register read only scalar group failed\n"); netsnmp_handler_free(h1); netsnmp_handler_free(h2); netsnmp_handler_registration_free(reginfo); return MIB_REGISTRATION_FAILED; } int netsnmp_scalar_group_helper_handler(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { netsnmp_variable_list *var = requests->requestvb; netsnmp_scalar_group *sgroup = (netsnmp_scalar_group *)handler->myvoid; int ret, cmp; int namelen; oid subid, root_tmp[MAX_OID_LEN], *root_save; DEBUGMSGTL(("helper:scalar_group", "Got request:\n")); namelen = SNMP_MIN(requests->requestvb->name_length, reginfo->rootoid_len); cmp = snmp_oid_compare(requests->requestvb->name, namelen, reginfo->rootoid, reginfo->rootoid_len); DEBUGMSGTL(( "helper:scalar_group", " cmp=%d, oid:", cmp)); DEBUGMSGOID(("helper:scalar_group", var->name, var->name_length)); DEBUGMSG(( "helper:scalar_group", "\n")); /* * copy root oid to root_tmp, set instance to 0. (subid set later on) * save rootoid, since we'll replace it before calling next handler, * and need to restore it afterwards. */ memcpy(root_tmp, reginfo->rootoid, reginfo->rootoid_len * sizeof(oid)); root_tmp[reginfo->rootoid_len + 1] = 0; root_save = reginfo->rootoid; switch (reqinfo->mode) { /* * The handling of "exact" requests is basically the same. * The only difference between GET and SET requests is the * error/exception to return on failure. */ #ifndef NETSNMP_NO_WRITE_SUPPORT case MODE_SET_RESERVE1: case MODE_SET_RESERVE2: case MODE_SET_ACTION: case MODE_SET_COMMIT: case MODE_SET_UNDO: case MODE_SET_FREE: #endif /* NETSNMP_NO_WRITE_SUPPORT */ case MODE_GET: ret = reqinfo->mode == MODE_GET ? SNMP_NOSUCHOBJECT : SNMP_ERR_NOCREATION; if (cmp != 0 || requests->requestvb->name_length <= reginfo->rootoid_len) { /* * Common prefix doesn't match, or only *just* matches * the registered root (so can't possibly match a scalar) */ netsnmp_set_request_error(reqinfo, requests, ret); return SNMP_ERR_NOERROR; } else { /* * Otherwise, * extract the object subidentifier from the request, * check this is (probably) valid, and then fudge the * registered 'rootoid' to match, before passing the * request off to the next handler ('scalar'). * * Note that we don't bother checking instance subidentifiers * here. That's left to the scalar helper. */ subid = requests->requestvb->name[reginfo->rootoid_len]; if (subid < sgroup->lbound || subid > sgroup->ubound) { netsnmp_set_request_error(reqinfo, requests, ret); return SNMP_ERR_NOERROR; } root_tmp[reginfo->rootoid_len] = subid; reginfo->rootoid_len += 2; reginfo->rootoid = root_tmp; ret = netsnmp_call_next_handler(handler, reginfo, reqinfo, requests); reginfo->rootoid = root_save; reginfo->rootoid_len -= 2; return ret; } break; case MODE_GETNEXT: /* * If we're being asked for something before (or exactly matches) * the registered root OID, then start with the first object. * If we're being asked for something that exactly matches an object * OID, then that's what we pass down. * Otherwise, we pass down the OID of the *next* object.... */ if (cmp < 0 || requests->requestvb->name_length <= reginfo->rootoid_len) { subid = sgroup->lbound; } else if (requests->requestvb->name_length == reginfo->rootoid_len+1) subid = requests->requestvb->name[reginfo->rootoid_len]; else subid = requests->requestvb->name[reginfo->rootoid_len]+1; /* * ... always assuming this is (potentially) valid, of course. */ if (subid < sgroup->lbound) subid = sgroup->lbound; else if (subid > sgroup->ubound) return SNMP_ERR_NOERROR; root_tmp[reginfo->rootoid_len] = subid; reginfo->rootoid_len += 2; reginfo->rootoid = root_tmp; ret = netsnmp_call_next_handler(handler, reginfo, reqinfo, requests); /* * If we didn't get an answer (due to holes in the group) * set things up to retry again. */ if (!requests->delegated && (requests->requestvb->type == ASN_NULL || requests->requestvb->type == SNMP_NOSUCHOBJECT || requests->requestvb->type == SNMP_NOSUCHINSTANCE)) { snmp_set_var_objid(requests->requestvb, reginfo->rootoid, reginfo->rootoid_len - 1); requests->requestvb->name[reginfo->rootoid_len - 2] = ++subid; requests->requestvb->type = ASN_PRIV_RETRY; } reginfo->rootoid = root_save; reginfo->rootoid_len -= 2; return ret; } /* * got here only if illegal mode found */ return SNMP_ERR_GENERR; } /** @} */