Exemple #1
0
netsnmp_variable_list *
register_index(netsnmp_variable_list * varbind, int flags,
               netsnmp_session * ss)
{
    netsnmp_variable_list *rv = NULL;
    struct snmp_index *new_index, *idxptr, *idxptr2;
    struct snmp_index *prev_oid_ptr, *prev_idx_ptr;
    int             res, res2, i;

    DEBUGMSGTL(("register_index", "register "));
    DEBUGMSGVAR(("register_index", varbind));
    DEBUGMSG(("register_index", "for session %8p\n", ss));

#if defined(USING_AGENTX_SUBAGENT_MODULE) && !defined(TESTING)
    if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, 
			       NETSNMP_DS_AGENT_ROLE) == SUB_AGENT) {
        return (agentx_register_index(ss, varbind, flags));
    }
#endif
    /*
     * Look for the requested OID entry 
     */
    prev_oid_ptr = NULL;
    prev_idx_ptr = NULL;
    res = 1;
    res2 = 1;
    for (idxptr = snmp_index_head; idxptr != NULL;
         prev_oid_ptr = idxptr, idxptr = idxptr->next_oid) {
        if ((res = snmp_oid_compare(varbind->name, varbind->name_length,
                                    idxptr->varbind->name,
                                    idxptr->varbind->name_length)) <= 0)
            break;
    }

    /*
     * Found the OID - now look at the registered indices 
     */
    if (res == 0 && idxptr) {
        if (varbind->type != idxptr->varbind->type)
            return NULL;        /* wrong type */

        /*
         * If we've been asked for an arbitrary new value,
         *      then find the end of the list.
         * If we've been asked for any arbitrary value,
         *      then look for an unused entry, and use that.
         *      If there aren't any, continue as for new.
         * Otherwise, locate the given value in the (sorted)
         *      list of already allocated values
         */
        if (flags & ALLOCATE_ANY_INDEX) {
            for (idxptr2 = idxptr; idxptr2 != NULL;
                 prev_idx_ptr = idxptr2, idxptr2 = idxptr2->next_idx) {

                if (flags == ALLOCATE_ANY_INDEX && !(idxptr2->allocated)) {
                    if ((rv =
                         snmp_clone_varbind(idxptr2->varbind)) != NULL) {
                        idxptr2->session = ss;
                        idxptr2->allocated = 1;
                    }
                    return rv;
                }
            }
        } else {
            for (idxptr2 = idxptr; idxptr2 != NULL;
                 prev_idx_ptr = idxptr2, idxptr2 = idxptr2->next_idx) {
                switch (varbind->type) {
                case ASN_INTEGER:
                    res2 =
                        (*varbind->val.integer -
                         *idxptr2->varbind->val.integer);
                    break;
                case ASN_OCTET_STR:
                    i = SNMP_MIN(varbind->val_len,
                                 idxptr2->varbind->val_len);
                    res2 =
                        memcmp(varbind->val.string,
                               idxptr2->varbind->val.string, i);
                    break;
                case ASN_OBJECT_ID:
                    res2 =
                        snmp_oid_compare(varbind->val.objid,
                                         varbind->val_len / sizeof(oid),
                                         idxptr2->varbind->val.objid,
                                         idxptr2->varbind->val_len /
                                         sizeof(oid));
                    break;
                default:
                    return NULL;        /* wrong type */
                }
                if (res2 <= 0)
                    break;
            }
            if (res2 == 0) {
                if (idxptr2->allocated) {
                    /*
                     * No good: the index is in use.  
                     */
                    return NULL;
                } else {
                    /*
                     * Okay, it's unallocated, we can just claim ownership
                     * here.  
                     */
                    if ((rv =
                         snmp_clone_varbind(idxptr2->varbind)) != NULL) {
                        idxptr2->session = ss;
                        idxptr2->allocated = 1;
                    }
                    return rv;
                }
            }
        }
    }

    /*
     * OK - we've now located where the new entry needs to
     *      be fitted into the index registry tree          
     * To recap:
     *      'prev_oid_ptr' points to the head of the OID index
     *          list prior to this one.  If this is null, then
     *          it means that this is the first OID in the list.
     *      'idxptr' points either to the head of this OID list,
     *          or the next OID (if this is a new OID request)
     *          These can be distinguished by the value of 'res'.
     *
     *      'prev_idx_ptr' points to the index entry that sorts
     *          immediately prior to the requested value (if any).
     *          If an arbitrary value is required, then this will
     *          point to the last allocated index.
     *          If this pointer is null, then either this is a new
     *          OID request, or the requested value is the first
     *          in the list.
     *      'idxptr2' points to the next sorted index (if any)
     *          but is not actually needed any more.
     *
     *  Clear?  Good!
     *      I hope you've been paying attention.
     *          There'll be a test later :-)
     */

    /*
     *      We proceed by creating the new entry
     *         (by copying the entry provided)
     */
    new_index = (struct snmp_index *) calloc(1, sizeof(struct snmp_index));
    if (new_index == NULL)
        return NULL;

    if (NULL == snmp_varlist_add_variable(&new_index->varbind,
                                          varbind->name,
                                          varbind->name_length,
                                          varbind->type,
                                          varbind->val.string,
                                          varbind->val_len)) {
        /*
         * if (snmp_clone_var( varbind, new_index->varbind ) != 0 ) 
         */
        free(new_index);
        return NULL;
    }
    new_index->session = ss;
    new_index->allocated = 1;

    if (varbind->type == ASN_OCTET_STR && flags == ALLOCATE_THIS_INDEX)
        new_index->varbind->val.string[new_index->varbind->val_len] = 0;

    /*
     * If we've been given a value, then we can use that, but
     *    otherwise, we need to create a new value for this entry.
     * Note that ANY_INDEX and NEW_INDEX are both covered by this
     *   test (since NEW_INDEX & ANY_INDEX = ANY_INDEX, remember?)
     */
    if (flags & ALLOCATE_ANY_INDEX) {
        if (prev_idx_ptr) {
            if (snmp_clone_var(prev_idx_ptr->varbind, new_index->varbind)
                != 0) {
                free(new_index);
                return NULL;
            }
        } else
            new_index->varbind->val.string = new_index->varbind->buf;

        switch (varbind->type) {
        case ASN_INTEGER:
            if (prev_idx_ptr) {
                (*new_index->varbind->val.integer)++;
            } else
                *(new_index->varbind->val.integer) = 1;
            new_index->varbind->val_len = sizeof(long);
            break;
        case ASN_OCTET_STR:
            if (prev_idx_ptr) {
                i = new_index->varbind->val_len - 1;
                while (new_index->varbind->buf[i] == 'z') {
                    new_index->varbind->buf[i] = 'a';
                    i--;
                    if (i < 0) {
                        i = new_index->varbind->val_len;
                        new_index->varbind->buf[i] = 'a';
                        new_index->varbind->buf[i + 1] = 0;
                    }
                }
                new_index->varbind->buf[i]++;
            } else
                strcpy((char *) new_index->varbind->buf, "aaaa");
            new_index->varbind->val_len =
                strlen((char *) new_index->varbind->buf);
            break;
        case ASN_OBJECT_ID:
            if (prev_idx_ptr) {
                i = prev_idx_ptr->varbind->val_len / sizeof(oid) - 1;
                while (new_index->varbind->val.objid[i] == 255) {
                    new_index->varbind->val.objid[i] = 1;
                    i--;
                    if (i == 0 && new_index->varbind->val.objid[0] == 2) {
                        new_index->varbind->val.objid[0] = 1;
                        i = new_index->varbind->val_len / sizeof(oid);
                        new_index->varbind->val.objid[i] = 0;
                        new_index->varbind->val_len += sizeof(oid);
                    }
                }
                new_index->varbind->val.objid[i]++;
            } else {
                /*
                 * If the requested OID name is small enough,
                 * *   append another OID (1) and use this as the
                 * *   default starting value for new indexes.
                 */
                if ((varbind->name_length + 1) * sizeof(oid) <= 40) {
                    for (i = 0; i < (int) varbind->name_length; i++)
                        new_index->varbind->val.objid[i] =
                            varbind->name[i];
                    new_index->varbind->val.objid[varbind->name_length] =
                        1;
                    new_index->varbind->val_len =
                        (varbind->name_length + 1) * sizeof(oid);
                } else {
                    /*
                     * Otherwise use '.1.1.1.1...' 
                     */
                    i = 40 / sizeof(oid);
                    if (i > 4)
                        i = 4;
                    new_index->varbind->val_len = i * (sizeof(oid));
                    for (i--; i >= 0; i--)
                        new_index->varbind->val.objid[i] = 1;
                }
            }
            break;
        default:
            snmp_free_var(new_index->varbind);
            free(new_index);
            return NULL;        /* Index type not supported */
        }
    }

    /*
     * Try to duplicate the new varbind for return.  
     */

    if ((rv = snmp_clone_varbind(new_index->varbind)) == NULL) {
        snmp_free_var(new_index->varbind);
        free(new_index);
        return NULL;
    }

    /*
     * Right - we've set up the new entry.
     * All that remains is to link it into the tree.
     * There are a number of possible cases here,
     *   so watch carefully.
     */
    if (prev_idx_ptr) {
        new_index->next_idx = prev_idx_ptr->next_idx;
        new_index->next_oid = prev_idx_ptr->next_oid;
        prev_idx_ptr->next_idx = new_index;
    } else {
        if (res == 0 && idxptr) {
            new_index->next_idx = idxptr;
            new_index->next_oid = idxptr->next_oid;
        } else {
            new_index->next_idx = NULL;
            new_index->next_oid = idxptr;
        }

        if (prev_oid_ptr) {
            while (prev_oid_ptr) {
                prev_oid_ptr->next_oid = new_index;
                prev_oid_ptr = prev_oid_ptr->next_idx;
            }
        } else
            snmp_index_head = new_index;
    }
    return rv;
}
        /*
         * Handle the response from an AgentX subagent,
         *   merging the answers back into the original query
         */
int
agentx_got_response(int operation,
                    netsnmp_session * session,
                    int reqid, netsnmp_pdu *pdu, void *magic)
{
    netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *) magic;
    int             i, ret;
    netsnmp_request_info *requests, *request;
    netsnmp_variable_list *var;
    netsnmp_session *ax_session;

    cache = netsnmp_handler_check_cache(cache);
    if (!cache) {
        DEBUGMSGTL(("agentx/master", "response too late on session %8p\n",
                    session));
        return 0;
    }
    requests = cache->requests;

    switch (operation) {
    case NETSNMP_CALLBACK_OP_TIMED_OUT:{
            void           *s = snmp_sess_pointer(session);
            DEBUGMSGTL(("agentx/master", "timeout on session %8p\n",
                        session));

            netsnmp_handler_mark_requests_as_delegated(requests,
                                       REQUEST_IS_NOT_DELEGATED);
            netsnmp_set_request_error(cache->reqinfo, requests,
                                      /* XXXWWW: should be index=0 */
                                      SNMP_ERR_GENERR);

            /*
             * This is a bit sledgehammer because the other sessions on this
             * transport may be okay (e.g. some thread in the subagent has
             * wedged, but the others are alright).  OTOH the overwhelming
             * probability is that the whole agent has died somehow.  
             */

            if (s != NULL) {
                netsnmp_transport *t = snmp_sess_transport(s);
                close_agentx_session(session, -1);

                if (t != NULL) {
                    DEBUGMSGTL(("agentx/master", "close transport\n"));
                    t->f_close(t);
                } else {
                    DEBUGMSGTL(("agentx/master", "NULL transport??\n"));
                }
            } else {
                DEBUGMSGTL(("agentx/master", "NULL sess_pointer??\n"));
            }
            ax_session = (netsnmp_session *) cache->localinfo;
            netsnmp_free_agent_snmp_session_by_session(ax_session, NULL);
            netsnmp_free_delegated_cache(cache);
            return 0;
        }

    case NETSNMP_CALLBACK_OP_DISCONNECT:
    case NETSNMP_CALLBACK_OP_SEND_FAILED:
        if (operation == NETSNMP_CALLBACK_OP_DISCONNECT) {
            DEBUGMSGTL(("agentx/master", "disconnect on session %8p\n",
                        session));
        } else {
            DEBUGMSGTL(("agentx/master", "send failed on session %8p\n",
                        session));
        }
        close_agentx_session(session, -1);
        netsnmp_handler_mark_requests_as_delegated(requests,
                                                   REQUEST_IS_NOT_DELEGATED);
        netsnmp_set_request_error(cache->reqinfo, requests,     /* XXXWWW: should be index=0 */
                                  SNMP_ERR_GENERR);
        netsnmp_free_delegated_cache(cache);
        return 0;

    case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE:
        /*
         * This session is alive 
         */
        CLEAR_SNMP_STRIKE_FLAGS(session->flags);
        break;
    default:
        snmp_log(LOG_ERR, "Unknown operation %d in agentx_got_response\n",
                 operation);
        netsnmp_free_delegated_cache(cache);
        return 0;
    }

    DEBUGMSGTL(("agentx/master", "got response errstat=%ld, (req=0x%x,trans="
                "0x%x,sess=0x%x)\n",
                pdu->errstat, (unsigned)pdu->reqid, (unsigned)pdu->transid,
		(unsigned)pdu->sessid));

    if (pdu->errstat != AGENTX_ERR_NOERROR) {
        /* [RFC 2471 - 7.2.5.2.]
         *
         *   1) For any received AgentX response PDU, if res.error is
         *      not `noError', the SNMP response PDU's error code is
         *      set to this value.  If res.error contains an AgentX
         *      specific value (e.g.  `parseError'), the SNMP response
         *      PDU's error code is set to a value of genErr instead.
         *      Also, the SNMP response PDU's error index is set to
         *      the index of the variable binding corresponding to the
         *      failed VarBind in the subagent's AgentX response PDU.
         *
         *      All other AgentX response PDUs received due to
         *      processing this SNMP request are ignored.  Processing
         *      is complete; the SNMP Response PDU is ready to be sent
         *      (see section 7.2.6, "Sending the SNMP Response-PDU").
         */
        int err;

        DEBUGMSGTL(("agentx/master",
                    "agentx_got_response() error branch\n"));

        switch (pdu->errstat) {
        case AGENTX_ERR_PARSE_FAILED:
        case AGENTX_ERR_REQUEST_DENIED:
        case AGENTX_ERR_PROCESSING_ERROR:
            err = SNMP_ERR_GENERR;
            break;
        default:
            err = pdu->errstat;
        }

        ret = 0;
        for (request = requests, i = 1; request;
             request = request->next, i++) {
            if (i == pdu->errindex) {
                /*
                 * Mark this varbind as the one generating the error.
                 * Note that the AgentX errindex may not match the
                 * position in the original SNMP PDU (request->index)
                 */
                netsnmp_set_request_error(cache->reqinfo, request,
                                          err);
                ret = 1;
            }
            request->delegated = REQUEST_IS_NOT_DELEGATED;
        }
        if (!ret) {
            /*
             * ack, unknown, mark the first one
             */
            netsnmp_set_request_error(cache->reqinfo, requests,
                                      SNMP_ERR_GENERR);
        }
        netsnmp_free_delegated_cache(cache);
        DEBUGMSGTL(("agentx/master", "end error branch\n"));
        return 1;
    } else if (cache->reqinfo->mode == MODE_GET ||
               cache->reqinfo->mode == MODE_GETNEXT ||
               cache->reqinfo->mode == MODE_GETBULK) {
        /*
         * Replace varbinds for data request types, but not SETs.  
         */
        DEBUGMSGTL(("agentx/master",
                    "agentx_got_response() beginning...\n"));
        for (var = pdu->variables, request = requests; request && var;
             request = request->next, var = var->next_variable) {
            /*
             * Otherwise, process successful requests
             */
            DEBUGMSGTL(("agentx/master",
                        "  handle_agentx_response: processing: "));
            DEBUGMSGOID(("agentx/master", var->name, var->name_length));
            DEBUGMSG(("agentx/master", "\n"));
            if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_VERBOSE)) {
                DEBUGMSGTL(("snmp_agent", "    >> "));
                DEBUGMSGVAR(("snmp_agent", var));
                DEBUGMSG(("snmp_agent", "\n"));
            }

            /*
             * update the oid in the original request 
             */
            if (var->type != SNMP_ENDOFMIBVIEW) {
                snmp_set_var_typed_value(request->requestvb, var->type,
                                         var->val.string, var->val_len);
                snmp_set_var_objid(request->requestvb, var->name,
                                   var->name_length);
            }
            request->delegated = REQUEST_IS_NOT_DELEGATED;
        }

        if (request || var) {
            /*
             * ack, this is bad.  The # of varbinds don't match and
             * there is no way to fix the problem 
             */
            snmp_log(LOG_ERR,
                     "response to agentx request illegal.  bailing out.\n");
            netsnmp_set_request_error(cache->reqinfo, requests,
                                      SNMP_ERR_GENERR);
        }

        if (cache->reqinfo->mode == MODE_GETBULK)
            netsnmp_bulk_to_next_fix_requests(requests);
    } else {
        /*
         * mark set requests as handled 
         */
        for (request = requests; request; request = request->next) {
            request->delegated = REQUEST_IS_NOT_DELEGATED;
        }
    }
    DEBUGMSGTL(("agentx/master",
                "handle_agentx_response() finishing...\n"));
    netsnmp_free_delegated_cache(cache);
    return 1;
}