summaryrefslogtreecommitdiff
path: root/agent/helpers/scalar_group.c
blob: a5d119ed21cd3958cc421094c6afe15b4c478a6a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#include <net-snmp/net-snmp-config.h>

#include <stdlib.h>
#if HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif

#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>

#include <net-snmp/agent/scalar_group.h>
#include <net-snmp/agent/scalar.h>
#include <net-snmp/agent/serialize.h>
#include <net-snmp/agent/read_only.h>

/** @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;
	}
    }
    return ret;
}

int
netsnmp_register_scalar_group(netsnmp_handler_registration *reginfo,
                              oid first, oid last)
{
    netsnmp_inject_handler(reginfo, netsnmp_get_instance_handler());
    netsnmp_inject_handler(reginfo, netsnmp_get_scalar_handler());
    netsnmp_inject_handler(reginfo, netsnmp_get_scalar_group_handler(first, last));
    return netsnmp_register_serialize(reginfo);
}


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;

    ret = SNMP_ERR_NOCREATION;
    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.
     */
    case MODE_GET:
        ret = SNMP_NOSUCHOBJECT;
        /* Fallthrough */

    case MODE_SET_RESERVE1:
    case MODE_SET_RESERVE2:
    case MODE_SET_ACTION:
    case MODE_SET_COMMIT:
    case MODE_SET_UNDO:
    case MODE_SET_FREE:
        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 = root_tmp;
            ret = netsnmp_call_next_handler(handler, reginfo, reqinfo,
                                            requests);
            reginfo->rootoid = root_save;
            reginfo->rootoid_len--;
            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 = 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);
            requests->requestvb->name[reginfo->rootoid_len-1] = ++subid;
            requests->requestvb->type = ASN_PRIV_RETRY;
        }
        reginfo->rootoid = root_save;
        reginfo->rootoid_len--;
        return ret;
    }
    /*
     * got here only if illegal mode found 
     */
    return SNMP_ERR_GENERR;
}

/** @} 
 */