Coverage Report

Created: 2026-08-13 09:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/net-snmp/snmplib/snmp_client.c
Line
Count
Source
1
/*
2
 * snmp_client.c - a toolkit of common functions for an SNMP client.
3
 *
4
 */
5
/* Portions of this file are subject to the following copyright(s).  See
6
 * the Net-SNMP's COPYING file for more details and other copyrights
7
 * that may apply:
8
 */
9
/**********************************************************************
10
  Copyright 1988, 1989, 1991, 1992 by Carnegie Mellon University
11
12
                      All Rights Reserved
13
14
Permission to use, copy, modify, and distribute this software and its
15
documentation for any purpose and without fee is hereby granted,
16
provided that the above copyright notice appear in all copies and that
17
both that copyright notice and this permission notice appear in
18
supporting documentation, and that the name of CMU not be
19
used in advertising or publicity pertaining to distribution of the
20
software without specific, written prior permission.
21
22
CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
23
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
24
CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
25
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
26
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
27
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
28
SOFTWARE.
29
******************************************************************/
30
/*
31
 * Portions of this file are copyrighted by:
32
 * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
33
 * Use is subject to license terms specified in the COPYING file
34
 * distributed with the Net-SNMP package.
35
 *
36
 * Portions of this file are copyrighted by:
37
 * Copyright (c) 2016 VMware, Inc. All rights reserved.
38
 * Use is subject to license terms specified in the COPYING file
39
 * distributed with the Net-SNMP package.
40
 */
41
42
/** @defgroup snmp_client various PDU processing routines
43
 *  @ingroup library
44
 * 
45
 *  @{
46
 */
47
#include <net-snmp/net-snmp-config.h>
48
#include <net-snmp/net-snmp-features.h>
49
50
#include <stdio.h>
51
#include <errno.h>
52
#ifdef HAVE_INTTYPES_H
53
#include <inttypes.h>
54
#endif
55
#ifdef HAVE_STDLIB_H
56
#include <stdlib.h>
57
#endif
58
#ifdef HAVE_STRING_H
59
#include <string.h>
60
#else
61
#include <strings.h>
62
#endif
63
#ifdef HAVE_UNISTD_H
64
#include <unistd.h>
65
#endif
66
#include <sys/types.h>
67
#ifdef TIME_WITH_SYS_TIME
68
# include <sys/time.h>
69
# include <time.h>
70
#else
71
# ifdef HAVE_SYS_TIME_H
72
#  include <sys/time.h>
73
# else
74
#  include <time.h>
75
# endif
76
#endif
77
#ifdef HAVE_SYS_PARAM_H
78
#include <sys/param.h>
79
#endif
80
#ifdef HAVE_NETINET_IN_H
81
#include <netinet/in.h>
82
#endif
83
#ifdef HAVE_ARPA_INET_H
84
#include <arpa/inet.h>
85
#endif
86
#ifdef HAVE_SYS_SELECT_H
87
#include <sys/select.h>
88
#endif
89
#ifdef HAVE_SYSLOG_H
90
#include <syslog.h>
91
#endif
92
93
#include <net-snmp/types.h>
94
95
#include <net-snmp/agent/ds_agent.h>
96
#include <net-snmp/library/default_store.h>
97
#include <net-snmp/library/snmp.h>
98
#include <net-snmp/library/snmp-tc.h>
99
#include <net-snmp/library/snmp_api.h>
100
#include <net-snmp/library/snmp_client.h>
101
#include <net-snmp/library/snmp_impl.h>
102
#include <net-snmp/library/snmp_secmod.h>
103
#include <net-snmp/library/snmpusm.h>
104
#include <net-snmp/library/mib.h>
105
#include <net-snmp/library/snmp_logging.h>
106
#include <net-snmp/library/snmp_assert.h>
107
#include <net-snmp/library/large_fd_set.h>
108
#include <net-snmp/library/tools.h>
109
#include <net-snmp/pdu_api.h>
110
111
netsnmp_feature_child_of(snmp_client_all, libnetsnmp);
112
113
netsnmp_feature_child_of(snmp_split_pdu, snmp_client_all);
114
netsnmp_feature_child_of(snmp_reset_var_types, snmp_client_all);
115
netsnmp_feature_child_of(query_set_default_session, snmp_client_all);
116
netsnmp_feature_child_of(row_create, snmp_client_all);
117
118
#ifndef BSD4_3
119
#define BSD4_2
120
#endif
121
122
123
/*
124
 * Prototype definitions 
125
 */
126
static int      snmp_synch_input(int op, netsnmp_session * session,
127
                                 int reqid, netsnmp_pdu *pdu, void *magic);
128
129
netsnmp_pdu    *
130
snmp_pdu_create(int command)
131
0
{
132
0
    netsnmp_pdu    *pdu;
133
134
0
    pdu = calloc(1, sizeof(netsnmp_pdu));
135
0
    if (pdu) {
136
0
        pdu->version = SNMP_DEFAULT_VERSION;
137
0
        pdu->command = command;
138
0
        pdu->errstat = SNMP_DEFAULT_ERRSTAT;
139
0
        pdu->errindex = SNMP_DEFAULT_ERRINDEX;
140
0
        pdu->securityModel = SNMP_DEFAULT_SECMODEL;
141
0
        pdu->transport_data = NULL;
142
0
        pdu->transport_data_length = 0;
143
0
        pdu->securityNameLen = 0;
144
0
        pdu->contextNameLen = 0;
145
0
        pdu->time = 0;
146
0
        pdu->reqid = snmp_get_next_reqid();
147
0
        pdu->msgid = snmp_get_next_msgid();
148
0
    }
149
0
    return pdu;
150
151
0
}
152
153
154
/*
155
 * Add a null variable with the requested name to the end of the list of
156
 * variables for this pdu.
157
 */
158
netsnmp_variable_list *
159
snmp_add_null_var(netsnmp_pdu *pdu, const oid * name, size_t name_length)
160
0
{
161
0
    return snmp_pdu_add_variable(pdu, name, name_length, ASN_NULL, NULL, 0);
162
0
}
163
164
165
#include <net-snmp/library/snmp_debug.h>
166
static int
167
snmp_synch_input(int op,
168
                 netsnmp_session * session,
169
                 int reqid, netsnmp_pdu *pdu, void *magic)
170
0
{
171
0
    struct synch_state *state = (struct synch_state *) magic;
172
0
    int             rpt_type;
173
174
0
    if (reqid != state->reqid && pdu && pdu->command != SNMP_MSG_REPORT) {
175
0
        DEBUGMSGTL(("snmp_synch", "Unexpected response (ReqID: %d,%d - Cmd %d)\n",
176
0
                                   reqid, state->reqid, pdu->command ));
177
0
        return 0;
178
0
    }
179
180
0
    state->waiting = 0;
181
0
    DEBUGMSGTL(("snmp_synch", "Response (ReqID: %d - Cmd %d)\n",
182
0
                               reqid, (pdu ? pdu->command : -1)));
183
184
0
    if (op == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE && pdu) {
185
0
        if (pdu->command == SNMP_MSG_REPORT) {
186
0
            rpt_type = snmpv3_get_report_type(pdu);
187
0
            if (SNMPV3_IGNORE_UNAUTH_REPORTS ||
188
0
                rpt_type == SNMPERR_NOT_IN_TIME_WINDOW) {
189
0
                state->waiting = 1;
190
0
            }
191
0
            state->pdu = NULL;
192
0
            state->status = STAT_ERROR;
193
0
            session->s_snmp_errno = rpt_type;
194
0
            SET_SNMP_ERROR(rpt_type);
195
0
        } else if (pdu->command == SNMP_MSG_RESPONSE) {
196
            /*
197
             * clone the pdu to return to snmp_synch_response 
198
             */
199
0
            state->pdu = snmp_clone_pdu(pdu);
200
0
            state->status = STAT_SUCCESS;
201
0
            session->s_snmp_errno = SNMPERR_SUCCESS;
202
0
        }
203
0
        else {
204
0
            char msg_buf[50];
205
0
            state->status = STAT_ERROR;
206
0
            session->s_snmp_errno = SNMPERR_PROTOCOL;
207
0
            SET_SNMP_ERROR(SNMPERR_PROTOCOL);
208
0
            snprintf(msg_buf, sizeof(msg_buf), "Expected RESPONSE-PDU but got %s-PDU",
209
0
                     snmp_pdu_type(pdu->command));
210
0
            snmp_set_detail(msg_buf);
211
0
            return 0;
212
0
        }
213
0
    } else if (op == NETSNMP_CALLBACK_OP_TIMED_OUT) {
214
0
        state->pdu = NULL;
215
0
        state->status = STAT_TIMEOUT;
216
0
        session->s_snmp_errno = SNMPERR_TIMEOUT;
217
0
        SET_SNMP_ERROR(SNMPERR_TIMEOUT);
218
0
    } else if (op == NETSNMP_CALLBACK_OP_SEC_ERROR) {
219
0
        state->pdu = NULL;
220
        /*
221
         * If we already have an error in status, then leave it alone.
222
         */
223
0
        if (state->status == STAT_SUCCESS) {
224
0
            state->status = STAT_ERROR;
225
0
            session->s_snmp_errno = SNMPERR_GENERR;
226
0
            SET_SNMP_ERROR(SNMPERR_GENERR);
227
0
        }
228
0
    } else if (op == NETSNMP_CALLBACK_OP_DISCONNECT) {
229
0
        state->pdu = NULL;
230
0
        state->status = STAT_ERROR;
231
0
        session->s_snmp_errno = SNMPERR_ABORT;
232
0
        SET_SNMP_ERROR(SNMPERR_ABORT);
233
0
    }
234
0
    DEBUGMSGTL(("snmp_synch", "status = %d errno = %d\n",
235
0
                               state->status, session->s_snmp_errno));
236
237
0
    return 1;
238
0
}
239
240
241
/*
242
 * Clone an SNMP variable data structure.
243
 * Sets pointers to structure private storage, or
244
 * allocates larger object identifiers and values as needed.
245
 *
246
 * Caller must make list association for cloned variable.
247
 *
248
 * Returns 0 if successful.
249
 */
250
int
251
snmp_clone_var(netsnmp_variable_list * var, netsnmp_variable_list * newvar)
252
0
{
253
0
    if (!newvar || !var)
254
0
        return 1;
255
256
0
    memmove(newvar, var, sizeof(netsnmp_variable_list));
257
0
    newvar->next_variable = NULL;
258
0
    newvar->name = NULL;
259
0
    newvar->val.string = NULL;
260
0
    newvar->data = NULL;
261
0
    newvar->dataFreeHook = NULL;
262
0
    newvar->index = 0;
263
264
    /*
265
     * Clone the object identifier and the value.
266
     * Allocate memory iff original will not fit into local storage.
267
     */
268
0
    if (snmp_set_var_objid(newvar, var->name, var->name_length))
269
0
        return 1;
270
271
    /*
272
     * need a pointer to copy a string value. 
273
     */
274
0
    if (var->val.string) {
275
0
        if (var->val.string != &var->buf[0]) {
276
0
            if (var->val_len <= sizeof(var->buf))
277
0
                newvar->val.string = newvar->buf;
278
0
            else {
279
0
                newvar->val.string = (u_char *) malloc(var->val_len);
280
0
                if (!newvar->val.string)
281
0
                    return 1;
282
0
            }
283
0
            memmove(newvar->val.string, var->val.string, var->val_len);
284
0
        } else {                /* fix the pointer to new local store */
285
0
            newvar->val.string = newvar->buf;
286
            /*
287
             * no need for a memmove, since we copied the whole var
288
             * struct (and thus var->buf) at the beginning of this function.
289
             */
290
0
        }
291
0
    } else {
292
0
        newvar->val.string = NULL;
293
0
        newvar->val_len = 0;
294
0
    }
295
296
0
    return 0;
297
0
}
298
299
300
/*
301
 * Possibly make a copy of source memory buffer.
302
 * Will reset destination pointer if source pointer is NULL.
303
 * Returns 0 if successful, 1 if memory allocation fails.
304
 */
305
int
306
snmp_clone_mem(void **dstPtr, const void *srcPtr, unsigned len)
307
0
{
308
0
    *dstPtr = NULL;
309
0
    if (srcPtr) {
310
0
        *dstPtr = malloc(len + 1);
311
0
        if (!*dstPtr) {
312
0
            return 1;
313
0
        }
314
0
        memmove(*dstPtr, srcPtr, len);
315
        /*
316
         * this is for those routines that expect 0-terminated strings!!!
317
         * someone should rather have called strdup
318
         */
319
0
        ((char *) *dstPtr)[len] = 0;
320
0
    }
321
0
    return 0;
322
0
}
323
324
325
/*
326
 * Walks through a list of varbinds and frees and allocated memory,
327
 * restoring pointers to local buffers
328
 */
329
void
330
snmp_reset_var_buffers(netsnmp_variable_list * var)
331
0
{
332
0
    while (var) {
333
0
        if (var->name != var->name_loc) {
334
0
            if(NULL != var->name)
335
0
                free(var->name);
336
0
            var->name = var->name_loc;
337
0
            var->name_length = 0;
338
0
        }
339
0
        if (var->val.string != var->buf) {
340
0
            if (NULL != var->val.string)
341
0
                free(var->val.string);
342
0
            var->val.string = var->buf;
343
0
            var->val_len = 0;
344
0
        }
345
0
        var = var->next_variable;
346
0
    }
347
0
}
348
349
/*
350
 * Creates and allocates a clone of the input PDU,
351
 * but does NOT copy the variables.
352
 * This function should be used with another function,
353
 * such as _copy_pdu_vars.
354
 *
355
 * Returns a pointer to the cloned PDU if successful.
356
 * Returns 0 if failure.
357
 */
358
static
359
netsnmp_pdu    *
360
_clone_pdu_header(netsnmp_pdu *pdu)
361
0
{
362
0
    netsnmp_pdu    *newpdu;
363
0
    struct snmp_secmod_def *sptr;
364
0
    int ret;
365
366
0
    if (!pdu)
367
0
        return NULL;
368
369
0
    newpdu = netsnmp_memdup(pdu, sizeof(netsnmp_pdu));
370
0
    if (!newpdu)
371
0
        return NULL;
372
373
    /*
374
     * reset copied pointers if copy fails 
375
     */
376
0
    newpdu->variables = NULL;
377
0
    newpdu->enterprise = NULL;
378
0
    newpdu->community = NULL;
379
0
    newpdu->securityEngineID = NULL;
380
0
    newpdu->securityName = NULL;
381
0
    newpdu->contextEngineID = NULL;
382
0
    newpdu->contextName = NULL;
383
0
    newpdu->transport_data = NULL;
384
0
    newpdu->securityStateRef = NULL;
385
386
    /*
387
     * copy buffers individually. If any copy fails, all are freed. 
388
     */
389
0
    if (snmp_clone_mem((void **) &newpdu->enterprise, pdu->enterprise,
390
0
                       sizeof(oid) * pdu->enterprise_length) ||
391
0
        snmp_clone_mem((void **) &newpdu->community, pdu->community,
392
0
                       pdu->community_len) ||
393
0
        snmp_clone_mem((void **) &newpdu->contextEngineID,
394
0
                       pdu->contextEngineID, pdu->contextEngineIDLen)
395
0
        || snmp_clone_mem((void **) &newpdu->securityEngineID,
396
0
                          pdu->securityEngineID, pdu->securityEngineIDLen)
397
0
        || snmp_clone_mem((void **) &newpdu->contextName, pdu->contextName,
398
0
                          pdu->contextNameLen)
399
0
        || snmp_clone_mem((void **) &newpdu->securityName,
400
0
                          pdu->securityName, pdu->securityNameLen)
401
0
        || snmp_clone_mem((void **) &newpdu->transport_data,
402
0
                          pdu->transport_data,
403
0
                          pdu->transport_data_length)) {
404
0
        snmp_free_pdu(newpdu);
405
0
        return NULL;
406
0
    }
407
408
0
    sptr = find_sec_mod(newpdu->securityModel);
409
0
    if (sptr && sptr->pdu_clone) {
410
        /* call security model if it needs to know about this */
411
0
        ret = sptr->pdu_clone(pdu, newpdu);
412
0
        if (ret) {
413
0
            snmp_free_pdu(newpdu);
414
0
            return NULL;
415
0
        }
416
0
    }
417
418
0
    return newpdu;
419
0
}
420
421
static
422
netsnmp_variable_list *
423
_copy_varlist(netsnmp_variable_list * var,      /* source varList */
424
              int errindex,     /* index of variable to drop (if any) */
425
              int copy_count)
426
0
{                               /* !=0 number variables to copy */
427
0
    netsnmp_variable_list *newhead, *newvar, *oldvar;
428
0
    int             ii = 0;
429
430
0
    newhead = NULL;
431
0
    oldvar = NULL;
432
433
0
    while (var && (copy_count-- > 0)) {
434
        /*
435
         * Drop the specified variable (if applicable) 
436
         * xxx hmm, is it intentional that dropping the errindex
437
         *     counts towards copy_count?
438
         */
439
0
        if (++ii == errindex) {
440
0
            var = var->next_variable;
441
0
            continue;
442
0
        }
443
444
        /*
445
         * clone the next variable. Cleanup if alloc fails 
446
         */
447
0
        newvar = (netsnmp_variable_list *)
448
0
            malloc(sizeof(netsnmp_variable_list));
449
0
        if (snmp_clone_var(var, newvar)) {
450
0
            if (newvar)
451
0
                free(newvar);
452
0
            snmp_free_varbind(newhead);
453
0
            return NULL;
454
0
        }
455
456
        /*
457
         * add cloned variable to new list  
458
         */
459
0
        if (NULL == newhead)
460
0
            newhead = newvar;
461
0
        if (oldvar)
462
0
            oldvar->next_variable = newvar;
463
0
        oldvar = newvar;
464
465
0
        var = var->next_variable;
466
0
    }
467
0
    return newhead;
468
0
}
469
470
471
/*
472
 * Copy some or all variables from source PDU to target PDU.
473
 * This function consolidates many of the needs of PDU variables:
474
 * Clone PDU : copy all the variables.
475
 * Split PDU : skip over some variables to copy other variables.
476
 * Fix PDU   : remove variable associated with error index.
477
 *
478
 * Designed to work with _clone_pdu_header.
479
 *
480
 * If drop_err is set, drop any variable associated with errindex.
481
 * If skip_count is set, skip the number of variable in pdu's list.
482
 * While copy_count is greater than zero, copy pdu variables to newpdu.
483
 *
484
 * If an error occurs, newpdu is freed and pointer is set to 0.
485
 *
486
 * Returns a pointer to the cloned PDU if successful.
487
 * Returns 0 if failure.
488
 */
489
static
490
netsnmp_pdu    *
491
_copy_pdu_vars(netsnmp_pdu *pdu,        /* source PDU */
492
               netsnmp_pdu *newpdu,     /* target PDU */
493
               int drop_err,    /* !=0 drop errored variable */
494
               int skip_count,  /* !=0 number of variables to skip */
495
               int copy_count)
496
0
{                               /* !=0 number of variables to copy */
497
0
    netsnmp_variable_list *var;
498
#ifdef TEMPORARILY_DISABLED
499
    int             copied;
500
#endif
501
0
    int             drop_idx;
502
503
0
    if (!newpdu)
504
0
        return NULL;            /* where is PDU to copy to ? */
505
506
0
    if (drop_err)
507
0
        drop_idx = pdu->errindex - skip_count;
508
0
    else
509
0
        drop_idx = 0;
510
511
0
    var = pdu->variables;
512
0
    while (var && (skip_count-- > 0))   /* skip over pdu variables */
513
0
        var = var->next_variable;
514
515
#ifdef TEMPORARILY_DISABLED
516
    copied = 0;
517
    if (pdu->flags & UCD_MSG_FLAG_FORCE_PDU_COPY)
518
        copied = 1;             /* We're interested in 'empty' responses too */
519
#endif
520
521
0
    newpdu->variables = _copy_varlist(var, drop_idx, copy_count);
522
#ifdef TEMPORARILY_DISABLED
523
    if (newpdu->variables)
524
        copied = 1;
525
#endif
526
527
#ifdef ALSO_TEMPORARILY_DISABLED
528
    /*
529
     * Error if bad errindex or if target PDU has no variables copied 
530
     */
531
    if ((drop_err && (ii < pdu->errindex))
532
#ifdef TEMPORARILY_DISABLED
533
        /*
534
         * SNMPv3 engineID probes are allowed to be empty.
535
         * See the comment in snmp_api.c for further details 
536
         */
537
        || copied == 0
538
#endif
539
        ) {
540
        snmp_free_pdu(newpdu);
541
        return 0;
542
    }
543
#endif
544
0
    return newpdu;
545
0
}
546
547
548
/*
549
 * Creates (allocates and copies) a clone of the input PDU.
550
 * If drop_err is set, don't copy any variable associated with errindex.
551
 * This function is called by snmp_clone_pdu and snmp_fix_pdu.
552
 *
553
 * Returns a pointer to the cloned PDU if successful.
554
 * Returns 0 if failure.
555
 */
556
static
557
netsnmp_pdu    *
558
_clone_pdu(netsnmp_pdu *pdu, int drop_err)
559
0
{
560
0
    netsnmp_pdu    *newpdu;
561
562
0
    newpdu = _clone_pdu_header(pdu);
563
0
    if (!newpdu)
564
0
        return newpdu;
565
0
    newpdu = _copy_pdu_vars(pdu, newpdu, drop_err, 0, 10000);   /* skip none, copy all */
566
567
0
    return newpdu;
568
0
}
569
570
571
/*
572
 * This function will clone a full varbind list
573
 *
574
 * Returns a pointer to the cloned varbind list if successful.
575
 * Returns 0 if failure
576
 */
577
netsnmp_variable_list *
578
snmp_clone_varbind(netsnmp_variable_list * varlist)
579
0
{
580
0
    return _copy_varlist(varlist, 0, 10000);    /* skip none, copy all */
581
0
}
582
583
/*
584
 * This function will clone a PDU including all of its variables.
585
 *
586
 * Returns a pointer to the cloned PDU if successful.
587
 * Returns 0 if failure
588
 */
589
netsnmp_pdu    *
590
snmp_clone_pdu(netsnmp_pdu *pdu)
591
0
{
592
0
    return _clone_pdu(pdu, 0);  /* copies all variables */
593
0
}
594
595
596
/*
597
 * This function will clone a PDU including some of its variables.
598
 *
599
 * If skip_count is not zero, it defines the number of variables to skip.
600
 * If copy_count is not zero, it defines the number of variables to copy.
601
 *
602
 * Returns a pointer to the cloned PDU if successful.
603
 * Returns 0 if failure.
604
 */
605
#ifndef NETSNMP_FEATURE_REMOVE_SNMP_SPLIT_PDU
606
netsnmp_pdu    *
607
snmp_split_pdu(netsnmp_pdu *pdu, int skip_count, int copy_count)
608
0
{
609
0
    netsnmp_pdu    *newpdu;
610
611
0
    newpdu = _clone_pdu_header(pdu);
612
0
    if (!newpdu)
613
0
        return newpdu;
614
0
    newpdu = _copy_pdu_vars(pdu, newpdu, 0,     /* don't drop any variables */
615
0
                            skip_count, copy_count);
616
617
0
    return newpdu;
618
0
}
619
#endif /* NETSNMP_FEATURE_REMOVE_SNMP_SPLIT_PDU */
620
621
622
/*
623
 * If there was an error in the input pdu, creates a clone of the pdu
624
 * that includes all the variables except the one marked by the errindex.
625
 * The command is set to the input command and the reqid, errstat, and
626
 * errindex are set to default values.
627
 * If the error status didn't indicate an error, the error index didn't
628
 * indicate a variable, the pdu wasn't a get response message, the
629
 * marked variable was not present in the initial request, or there
630
 * would be no remaining variables, this function will return 0.
631
 * If everything was successful, a pointer to the fixed cloned pdu will
632
 * be returned.
633
 */
634
netsnmp_pdu    *
635
snmp_fix_pdu(netsnmp_pdu *pdu, int command)
636
0
{
637
0
    netsnmp_pdu    *newpdu;
638
639
0
    if ((pdu->command != SNMP_MSG_RESPONSE)
640
0
        || (pdu->errstat == SNMP_ERR_NOERROR)
641
0
        || (NULL == pdu->variables)
642
0
        || (pdu->errindex > (int)snmp_varbind_len(pdu))
643
0
        || (pdu->errindex <= 0)) {
644
0
        return NULL;            /* pre-condition tests fail */
645
0
    }
646
647
0
    newpdu = _clone_pdu(pdu, 1);        /* copies all except errored variable */
648
0
    if (!newpdu)
649
0
        return NULL;
650
0
    if (!newpdu->variables) {
651
0
        snmp_free_pdu(newpdu);
652
0
        return NULL;            /* no variables. "should not happen" */
653
0
    }
654
0
    newpdu->command = command;
655
0
    newpdu->reqid = snmp_get_next_reqid();
656
0
    newpdu->msgid = snmp_get_next_msgid();
657
0
    newpdu->errstat = SNMP_DEFAULT_ERRSTAT;
658
0
    newpdu->errindex = SNMP_DEFAULT_ERRINDEX;
659
660
0
    return newpdu;
661
0
}
662
663
664
/*
665
 * Returns the number of variables bound to a PDU structure
666
 */
667
unsigned long
668
snmp_varbind_len(netsnmp_pdu *pdu)
669
0
{
670
0
    register netsnmp_variable_list *vars;
671
0
    unsigned long   retVal = 0;
672
0
    if (pdu)
673
0
        for (vars = pdu->variables; vars; vars = vars->next_variable) {
674
0
            retVal++;
675
0
        }
676
677
0
    return retVal;
678
0
}
679
680
/*
681
 * Add object identifier name to SNMP variable.
682
 * If the name is large, additional memory is allocated.
683
 * Returns 0 if successful.
684
 */
685
686
int
687
snmp_set_var_objid(netsnmp_variable_list * vp,
688
                   const oid * objid, size_t name_length)
689
63
{
690
63
    size_t          len = sizeof(oid) * name_length;
691
692
63
    if (vp->name != vp->name_loc && vp->name != NULL) {
693
        /*
694
         * Probably previously-allocated "big storage".  Better free it
695
         * else memory leaks possible.  
696
         */
697
0
        free(vp->name);
698
0
    }
699
700
    /*
701
     * use built-in storage for smaller values 
702
     */
703
63
    if (len <= sizeof(vp->name_loc)) {
704
63
        vp->name = vp->name_loc;
705
63
    } else {
706
0
        vp->name = (oid *) malloc(len);
707
0
        if (!vp->name)
708
0
            return 1;
709
0
    }
710
63
    if (objid)
711
63
        memmove(vp->name, objid, len);
712
63
    vp->name_length = name_length;
713
63
    return 0;
714
63
}
715
716
/**
717
 * snmp_set_var_typed_value is used to set data into the netsnmp_variable_list
718
 * structure.  Used to return data to the snmp request via the
719
 * netsnmp_request_info structure's requestvb pointer.
720
 *
721
 * @param newvar   the structure gets populated with the given data, type,
722
 *                 val_str, and val_len.
723
 * @param type     is the asn data type to be copied
724
 * @param val_str  is a buffer containing the value to be copied into the
725
 *                 newvar structure. 
726
 * @param val_len  the length of val_str
727
 * 
728
 * @return returns 0 on success and 1 on a malloc error
729
 */
730
731
int
732
snmp_set_var_typed_value(netsnmp_variable_list * newvar, u_char type,
733
                         const void * val_str, size_t val_len)
734
0
{
735
0
    newvar->type = type;
736
0
    return snmp_set_var_value(newvar, val_str, val_len);
737
0
}
738
739
int
740
snmp_set_var_typed_integer(netsnmp_variable_list * newvar,
741
                           u_char type, long val)
742
0
{
743
0
    newvar->type = type;
744
0
    return snmp_set_var_value(newvar, &val, sizeof(long));
745
0
}
746
747
int
748
count_varbinds(netsnmp_variable_list * var_ptr)
749
0
{
750
0
    int             count = 0;
751
752
0
    for (; var_ptr != NULL; var_ptr = var_ptr->next_variable)
753
0
        count++;
754
755
0
    return count;
756
0
}
757
758
netsnmp_feature_child_of(count_varbinds_of_type, netsnmp_unused);
759
#ifndef NETSNMP_FEATURE_REMOVE_COUNT_VARBINDS_OF_TYPE
760
int
761
count_varbinds_of_type(netsnmp_variable_list * var_ptr, u_char type)
762
0
{
763
0
    int             count = 0;
764
765
0
    for (; var_ptr != NULL; var_ptr = var_ptr->next_variable)
766
0
        if (var_ptr->type == type)
767
0
            count++;
768
769
0
    return count;
770
0
}
771
#endif /* NETSNMP_FEATURE_REMOVE_COUNT_VARBINDS_OF_TYPE */
772
773
netsnmp_feature_child_of(find_varind_of_type, netsnmp_unused);
774
#ifndef NETSNMP_FEATURE_REMOVE_FIND_VARIND_OF_TYPE
775
netsnmp_variable_list *
776
find_varbind_of_type(netsnmp_variable_list * var_ptr, u_char type)
777
0
{
778
0
    for (; var_ptr != NULL && var_ptr->type != type;
779
0
         var_ptr = var_ptr->next_variable);
780
781
0
    return var_ptr;
782
0
}
783
#endif /* NETSNMP_FEATURE_REMOVE_FIND_VARIND_OF_TYPE */
784
785
netsnmp_variable_list*
786
find_varbind_in_list( netsnmp_variable_list *vblist,
787
                      const oid *name, size_t len)
788
0
{
789
0
    for (; vblist != NULL; vblist = vblist->next_variable)
790
0
        if (!snmp_oid_compare(vblist->name, vblist->name_length, name, len))
791
0
            return vblist;
792
793
0
    return NULL;
794
0
}
795
796
/*
797
 * Add some value to SNMP variable.
798
 * If the value is large, additional memory is allocated.
799
 * Returns 0 if successful.
800
 */
801
802
int
803
snmp_set_var_value(netsnmp_variable_list * vars,
804
                   const void * value, size_t len)
805
0
{
806
0
    int             largeval = 1;
807
808
    /*
809
     * xxx-rks: why the unconditional free? why not use existing
810
     * memory, if len < vars->val_len ?
811
     */
812
0
    if (vars->val.string && vars->val.string != vars->buf) {
813
0
        free(vars->val.string);
814
0
    }
815
0
    vars->val.string = NULL;
816
0
    vars->val_len = 0;
817
818
0
    if (value == NULL && len > 0) {
819
0
        snmp_log(LOG_ERR, "bad size for NULL value\n");
820
0
        return 1;
821
0
    }
822
823
    /*
824
     * use built-in storage for smaller values 
825
     */
826
0
    if (len <= sizeof(vars->buf)) {
827
0
        vars->val.string = (u_char *) vars->buf;
828
0
        largeval = 0;
829
0
    }
830
831
0
    if ((0 == len) || (NULL == value)) {
832
0
        vars->val.string[0] = 0;
833
0
        return 0;
834
0
    }
835
836
0
    vars->val_len = len;
837
0
    switch (vars->type) {
838
0
    case ASN_INTEGER:
839
0
    case ASN_UNSIGNED:
840
0
    case ASN_TIMETICKS:
841
0
    case ASN_COUNTER:
842
0
    case ASN_UINTEGER:
843
0
        if (vars->val_len == sizeof(int)) {
844
0
            if (ASN_INTEGER == vars->type) {
845
0
                const int      *val_int 
846
0
                    = (const int *) value;
847
0
                *(vars->val.integer) = (long) *val_int;
848
0
            } else {
849
0
                const u_int    *val_uint
850
0
                    = (const u_int *) value;
851
0
                *(vars->val.integer) = (unsigned long) *val_uint;
852
0
            }
853
0
        }
854
0
        else if (vars->val_len == sizeof(long)){
855
0
            const u_long   *val_ulong
856
0
                = (const u_long *) value;
857
0
            *(vars->val.integer) = *val_ulong;
858
0
            if (*(vars->val.integer) > 0xffffffff) {
859
0
                NETSNMP_LOGONCE((LOG_INFO,
860
0
                                 "truncating integer value > 32 bits\n"));
861
0
                *(vars->val.integer) &= 0xffffffff;
862
0
            }
863
0
        }
864
0
        else if (vars->val_len == sizeof(long long)){
865
0
            const unsigned long long   *val_ullong
866
0
                = (const unsigned long long *) value;
867
0
            *(vars->val.integer) = (long) *val_ullong;
868
0
            if (*(vars->val.integer) > 0xffffffff) {
869
0
                NETSNMP_LOGONCE((LOG_INFO,
870
0
                                 "truncating integer value > 32 bits\n"));
871
0
                *(vars->val.integer) &= 0xffffffff;
872
0
            }
873
0
        }
874
0
        else if (vars->val_len == sizeof(intmax_t)){
875
0
            const uintmax_t *val_uintmax_t
876
0
                = (const uintmax_t *) value;
877
0
            *(vars->val.integer) = (long) *val_uintmax_t;
878
0
            if (*(vars->val.integer) > 0xffffffff) {
879
0
                NETSNMP_LOGONCE((LOG_INFO,
880
0
                                 "truncating integer value > 32 bits\n"));
881
0
                *(vars->val.integer) &= 0xffffffff;
882
0
            }
883
0
        }
884
0
        else if (vars->val_len == sizeof(short)) {
885
0
            if (ASN_INTEGER == vars->type) {
886
0
                const short      *val_short 
887
0
                    = (const short *) value;
888
0
                *(vars->val.integer) = (long) *val_short;
889
0
            } else {
890
0
                const u_short    *val_ushort
891
0
                    = (const u_short *) value;
892
0
                *(vars->val.integer) = (unsigned long) *val_ushort;
893
0
            }
894
0
        }
895
0
        else if (vars->val_len == sizeof(char)) {
896
0
            if (ASN_INTEGER == vars->type) {
897
0
                const signed char   *val_char
898
0
                    = (const signed char *) value;
899
0
                *(vars->val.integer) = (long) *val_char;
900
0
            } else {
901
0
                    const u_char    *val_uchar
902
0
                    = (const u_char *) value;
903
0
                *(vars->val.integer) = (unsigned long) *val_uchar;
904
0
            }
905
0
        }
906
0
        else {
907
0
            snmp_log(LOG_ERR,"bad size for integer-like type (%d)\n",
908
0
                     (int)vars->val_len);
909
0
            return (1);
910
0
        }
911
0
        vars->val_len = sizeof(long);
912
0
        break;
913
914
0
    case ASN_OBJECT_ID:
915
0
    case ASN_PRIV_IMPLIED_OBJECT_ID:
916
0
    case ASN_PRIV_INCL_RANGE:
917
0
    case ASN_PRIV_EXCL_RANGE:
918
0
        if (largeval) {
919
0
            vars->val.objid = (oid *) malloc(vars->val_len);
920
0
        }
921
0
        if (vars->val.objid == NULL) {
922
0
            snmp_log(LOG_ERR,"no storage for OID\n");
923
0
            return 1;
924
0
        }
925
0
        memmove(vars->val.objid, value, vars->val_len);
926
0
        break;
927
928
0
    case ASN_IPADDRESS: /* snmp_build_var_op treats IPADDR like a string */
929
0
        if (4 != vars->val_len) {
930
0
            netsnmp_assert("ipaddress length == 4");
931
0
        }
932
0
        NETSNMP_FALLTHROUGH;
933
0
    case ASN_PRIV_IMPLIED_OCTET_STR:
934
0
    case ASN_OCTET_STR:
935
0
    case ASN_BIT_STR:
936
0
    case ASN_OPAQUE:
937
0
    case ASN_NSAP:
938
0
        if (vars->val_len >= sizeof(vars->buf)) {
939
0
            vars->val.string = (u_char *) malloc(vars->val_len + 1);
940
0
        }
941
0
        if (vars->val.string == NULL) {
942
0
            snmp_log(LOG_ERR,"no storage for string\n");
943
0
            return 1;
944
0
        }
945
0
        memmove(vars->val.string, value, vars->val_len);
946
        /*
947
         * Make sure the string is zero-terminated; some bits of code make
948
         * this assumption.  Easier to do this here than fix all these wrong
949
         * assumptions.  
950
         */
951
0
        vars->val.string[vars->val_len] = '\0';
952
0
        break;
953
954
0
    case SNMP_NOSUCHOBJECT:
955
0
    case SNMP_NOSUCHINSTANCE:
956
0
    case SNMP_ENDOFMIBVIEW:
957
0
    case ASN_NULL:
958
0
        vars->val_len = 0;
959
0
        vars->val.string = NULL;
960
0
        break;
961
962
0
#ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES
963
0
    case ASN_OPAQUE_U64:
964
0
    case ASN_OPAQUE_I64:
965
0
#endif                          /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */
966
0
    case ASN_COUNTER64:
967
0
        if (largeval || vars->val_len != sizeof(struct counter64)) {
968
0
            snmp_log(LOG_ERR,"bad size for counter 64 (%d)\n",
969
0
                     (int)vars->val_len);
970
0
            return (1);
971
0
        }
972
0
        vars->val_len = sizeof(struct counter64);
973
0
        memmove(vars->val.counter64, value, vars->val_len);
974
0
        break;
975
976
0
#ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES
977
0
    case ASN_OPAQUE_FLOAT:
978
0
        if (largeval) {
979
0
            snmp_log(LOG_ERR,"bad size for opaque float (%d)\n",
980
0
                     (int)vars->val_len);
981
0
            return (1);
982
0
        }
983
0
        vars->val_len = sizeof(float);
984
0
        memmove(vars->val.floatVal, value, vars->val_len);
985
0
        break;
986
987
0
    case ASN_OPAQUE_DOUBLE:
988
0
        if (largeval) {
989
0
            snmp_log(LOG_ERR,"bad size for opaque double (%d)\n",
990
0
                     (int)vars->val_len);
991
0
            return (1);
992
0
        }
993
0
        vars->val_len = sizeof(double);
994
0
        memmove(vars->val.doubleVal, value, vars->val_len);
995
0
        break;
996
997
0
#endif                          /* NETSNMP_WITH_OPAQUE_SPECIAL_TYPES */
998
999
0
    default:
1000
0
        snmp_log(LOG_ERR,"Internal error in type switching\n");
1001
0
        snmp_set_detail("Internal error in type switching\n");
1002
0
        return (1);
1003
0
    }
1004
1005
0
    return 0;
1006
0
}
1007
1008
void
1009
snmp_replace_var_types(netsnmp_variable_list * vbl, u_char old_type,
1010
                       u_char new_type)
1011
0
{
1012
0
    while (vbl) {
1013
0
        if (vbl->type == old_type) {
1014
0
            snmp_set_var_typed_value(vbl, new_type, NULL, 0);
1015
0
        }
1016
0
        vbl = vbl->next_variable;
1017
0
    }
1018
0
}
1019
1020
#ifndef NETSNMP_FEATURE_REMOVE_SNMP_RESET_VAR_TYPES
1021
void
1022
snmp_reset_var_types(netsnmp_variable_list * vbl, u_char new_type)
1023
0
{
1024
0
    while (vbl) {
1025
0
        snmp_set_var_typed_value(vbl, new_type, NULL, 0);
1026
0
        vbl = vbl->next_variable;
1027
0
    }
1028
0
}
1029
#endif /* NETSNMP_FEATURE_REMOVE_SNMP_RESET_VAR_TYPES */
1030
1031
int
1032
snmp_synch_response_cb(netsnmp_session * ss,
1033
                       netsnmp_pdu *pdu,
1034
                       netsnmp_pdu **response, snmp_callback pcb)
1035
0
{
1036
0
    struct synch_state    lstate, *state;
1037
0
    snmp_callback         cbsav;
1038
0
    void                 *cbmagsav;
1039
0
    int                   numfds, count;
1040
0
    netsnmp_large_fd_set  fdset;
1041
0
    struct timeval        timeout, *tvp;
1042
0
    int                   block;
1043
1044
0
    memset((void *) &lstate, 0, sizeof(lstate));
1045
0
    state = &lstate;
1046
0
    cbsav = ss->callback;
1047
0
    cbmagsav = ss->callback_magic;
1048
0
    ss->callback = pcb;
1049
0
    ss->callback_magic = (void *) state;
1050
0
    netsnmp_large_fd_set_init(&fdset, FD_SETSIZE);
1051
1052
0
    if (snmp_send(ss, pdu) == 0) {
1053
0
        snmp_free_pdu(pdu);
1054
0
        state->status = STAT_ERROR;
1055
0
    } else {
1056
0
        state->reqid = pdu->reqid;
1057
0
        state->waiting = 1;
1058
0
    }
1059
1060
0
    while (state->waiting) {
1061
0
        snmp_sess_poll(NULL);
1062
1063
0
        numfds = 0;
1064
0
        NETSNMP_LARGE_FD_ZERO(&fdset);
1065
0
        block = NETSNMP_SNMPBLOCK;
1066
0
        tvp = &timeout;
1067
0
        timerclear(tvp);
1068
0
        snmp_sess_select_info2_flags(NULL, &numfds, &fdset, tvp, &block,
1069
0
                                     NETSNMP_SELECT_NOALARMS);
1070
0
        if (block == 1)
1071
0
            tvp = NULL;         /* block without timeout */
1072
0
        count = netsnmp_large_fd_set_select(numfds, &fdset, NULL, NULL, tvp);
1073
0
        if (count > 0) {
1074
0
            snmp_read2(&fdset);
1075
0
        } else {
1076
0
            switch (count) {
1077
0
            case 0:
1078
0
                snmp_timeout();
1079
0
                break;
1080
0
            case -1:
1081
0
                if (errno == EINTR) {
1082
0
                    continue;
1083
0
                } else {
1084
0
                    snmp_errno = SNMPERR_GENERR;    /*MTCRITICAL_RESOURCE */
1085
                    /*
1086
                     * CAUTION! if another thread closed the socket(s)
1087
                     * waited on here, the session structure was freed.
1088
                     * It would be nice, but we can't rely on the pointer.
1089
                     * ss->s_snmp_errno = SNMPERR_GENERR;
1090
                     * ss->s_errno = errno;
1091
                     */
1092
0
                    snmp_set_detail(strerror(errno));
1093
0
                }
1094
0
                NETSNMP_FALLTHROUGH;
1095
0
            default:
1096
0
                state->status = STAT_ERROR;
1097
0
                state->waiting = 0;
1098
0
            }
1099
0
        }
1100
1101
0
        if ( ss->flags & SNMP_FLAGS_RESP_CALLBACK ) {
1102
0
            void (*cb)(void);
1103
0
            cb = (void (*)(void))(ss->myvoid);
1104
0
            cb();        /* Used to invoke 'netsnmp_check_outstanding_agent_requests();'
1105
                            on internal AgentX queries.  */
1106
0
        }
1107
0
    }
1108
0
    *response = state->pdu;
1109
0
    ss->callback = cbsav;
1110
0
    ss->callback_magic = cbmagsav;
1111
0
    netsnmp_large_fd_set_cleanup(&fdset);
1112
0
    return state->status;
1113
0
}
1114
1115
int
1116
snmp_synch_response(netsnmp_session * ss,
1117
                    netsnmp_pdu *pdu, netsnmp_pdu **response)
1118
0
{
1119
0
    return snmp_synch_response_cb(ss, pdu, response, snmp_synch_input);
1120
0
}
1121
1122
int
1123
snmp_sess_synch_response(struct session_list *slp,
1124
                         netsnmp_pdu *pdu, netsnmp_pdu **response)
1125
0
{
1126
0
    netsnmp_session      *ss;
1127
0
    struct synch_state    lstate, *state;
1128
0
    snmp_callback         cbsav;
1129
0
    void                 *cbmagsav;
1130
0
    int                   numfds, count;
1131
0
    netsnmp_large_fd_set  fdset;
1132
0
    struct timeval        timeout, *tvp;
1133
0
    int                   block;
1134
1135
0
    ss = snmp_sess_session(slp);
1136
0
    if (ss == NULL) {
1137
0
        return STAT_ERROR;
1138
0
    }
1139
1140
0
    memset((void *) &lstate, 0, sizeof(lstate));
1141
0
    state = &lstate;
1142
0
    cbsav = ss->callback;
1143
0
    cbmagsav = ss->callback_magic;
1144
0
    ss->callback = snmp_synch_input;
1145
0
    ss->callback_magic = (void *) state;
1146
0
    netsnmp_large_fd_set_init(&fdset, FD_SETSIZE);
1147
1148
0
    if (snmp_sess_send(slp, pdu) == 0) {
1149
0
        snmp_free_pdu(pdu);
1150
0
        state->status = STAT_ERROR;
1151
0
    } else {
1152
0
        state->waiting = 1;
1153
0
        state->reqid = pdu->reqid;
1154
0
    }
1155
1156
0
    while (state->waiting) {
1157
0
        snmp_sess_poll(NULL);
1158
1159
0
        numfds = 0;
1160
0
        NETSNMP_LARGE_FD_ZERO(&fdset);
1161
0
        block = NETSNMP_SNMPBLOCK;
1162
0
        tvp = &timeout;
1163
0
        timerclear(tvp);
1164
0
        snmp_sess_select_info2_flags(slp, &numfds, &fdset, tvp, &block,
1165
0
                                     NETSNMP_SELECT_NOALARMS);
1166
0
        if (block == 1)
1167
0
            tvp = NULL;         /* block without timeout */
1168
0
        count = netsnmp_large_fd_set_select(numfds, &fdset, NULL, NULL, tvp);
1169
0
        if (count > 0) {
1170
0
            snmp_sess_read2(slp, &fdset);
1171
0
        } else
1172
0
            switch (count) {
1173
0
            case 0:
1174
0
                snmp_sess_timeout(slp);
1175
0
                break;
1176
0
            case -1:
1177
0
                if (errno == EINTR) {
1178
0
                    continue;
1179
0
                } else {
1180
0
                    snmp_errno = SNMPERR_GENERR;    /*MTCRITICAL_RESOURCE */
1181
                    /*
1182
                     * CAUTION! if another thread closed the socket(s)
1183
                     * waited on here, the session structure was freed.
1184
                     * It would be nice, but we can't rely on the pointer.
1185
                     * ss->s_snmp_errno = SNMPERR_GENERR;
1186
                     * ss->s_errno = errno;
1187
                     */
1188
0
                    snmp_set_detail(strerror(errno));
1189
0
                }
1190
0
                NETSNMP_FALLTHROUGH;
1191
0
            default:
1192
0
                state->status = STAT_ERROR;
1193
0
                state->waiting = 0;
1194
0
            }
1195
0
    }
1196
0
    *response = state->pdu;
1197
0
    ss->callback = cbsav;
1198
0
    ss->callback_magic = cbmagsav;
1199
0
    netsnmp_large_fd_set_cleanup(&fdset);
1200
0
    return state->status;
1201
0
}
1202
1203
1204
const char     *
1205
snmp_errstring(int errstat)
1206
0
{
1207
0
    const char * const error_string[19] = {
1208
0
        "(noError) No Error",
1209
0
        "(tooBig) Response message would have been too large.",
1210
0
        "(noSuchName) There is no such variable name in this MIB.",
1211
0
        "(badValue) The value given has the wrong type or length.",
1212
0
        "(readOnly) The two parties used do not have access to use the specified SNMP PDU.",
1213
0
        "(genError) A general failure occurred",
1214
0
        "noAccess",
1215
0
        "wrongType (The set datatype does not match the data type the agent expects)",
1216
0
        "wrongLength (The set value has an illegal length from what the agent expects)",
1217
0
        "wrongEncoding",
1218
0
        "wrongValue (The set value is illegal or unsupported in some way)",
1219
0
        "noCreation (That table does not support row creation or that object can not ever be created)",
1220
0
        "inconsistentValue (The set value is illegal or unsupported in some way)",
1221
0
        "resourceUnavailable (This is likely a out-of-memory failure within the agent)",
1222
0
        "commitFailed",
1223
0
        "undoFailed",
1224
0
        "authorizationError (access denied to that object)",
1225
0
        "notWritable (That object does not support modification)",
1226
0
        "inconsistentName (That object can not currently be created)"
1227
0
    };
1228
1229
0
    if (errstat <= MAX_SNMP_ERR && errstat >= SNMP_ERR_NOERROR) {
1230
0
        return error_string[errstat];
1231
0
    } else {
1232
0
        return "Unknown Error";
1233
0
    }
1234
0
}
1235
1236
1237
1238
/*
1239
 *
1240
 *  Convenience routines to make various requests
1241
 *  over the specified SNMP session.
1242
 *
1243
 */
1244
#include <net-snmp/library/snmp_debug.h>
1245
1246
static netsnmp_session *_def_query_session = NULL;
1247
1248
#ifndef NETSNMP_FEATURE_REMOVE_QUERY_SET_DEFAULT_SESSION
1249
void
1250
0
netsnmp_query_set_default_session( netsnmp_session *sess) {
1251
0
    DEBUGMSGTL(("iquery", "set default session %p\n", sess));
1252
0
    _def_query_session = sess;
1253
0
}
1254
#endif /* NETSNMP_FEATURE_REMOVE_QUERY_SET_DEFAULT_SESSION */
1255
1256
void
1257
0
netsnmp_query_shutdown(void) {
1258
0
    if (_def_query_session) {
1259
0
        snmp_close(_def_query_session);
1260
0
        _def_query_session = NULL;
1261
0
    }
1262
0
}
1263
1264
/**
1265
 * Return a pointer to the default internal query session.
1266
 */
1267
netsnmp_session *
1268
0
netsnmp_query_get_default_session_unchecked( void ) {
1269
0
    DEBUGMSGTL(("iquery", "get default session %p\n", _def_query_session));
1270
0
    return _def_query_session;
1271
0
}
1272
1273
/**
1274
 * Return a pointer to the default internal query session and log a
1275
 * warning message once if this session does not exist.
1276
 */
1277
netsnmp_session *
1278
0
netsnmp_query_get_default_session( void ) {
1279
0
    static int warning_logged = 0;
1280
1281
0
    if (! _def_query_session && ! warning_logged) {
1282
0
        if (! netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID,
1283
0
                                    NETSNMP_DS_AGENT_INTERNAL_SECNAME)) {
1284
0
            snmp_log(LOG_WARNING,
1285
0
                     "iquerySecName has not been configured - internal queries will fail\n");
1286
0
        } else {
1287
0
            snmp_log(LOG_WARNING,
1288
0
                     "default session is not available - internal queries will fail\n");
1289
0
        }
1290
0
        warning_logged = 1;
1291
0
    }
1292
1293
0
    return netsnmp_query_get_default_session_unchecked();
1294
0
}
1295
1296
1297
/*
1298
 * Internal utility routine to actually send the query
1299
 */
1300
static int _query(netsnmp_variable_list *list,
1301
                  int                    request,
1302
0
                  netsnmp_session       *session) {
1303
1304
0
    netsnmp_pdu *pdu;
1305
0
    netsnmp_pdu *response = NULL;
1306
0
    netsnmp_variable_list *vb1, *vb2, *vtmp;
1307
0
    int ret, count;
1308
1309
0
    DEBUGMSGTL(("iquery", "query on session %p\n", session));
1310
1311
0
    if (NULL == list) {
1312
0
        snmp_log(LOG_ERR, "empty variable list in _query\n");
1313
0
        return SNMP_ERR_GENERR;
1314
0
    }
1315
1316
0
    pdu = snmp_pdu_create( request );
1317
0
    if (NULL == pdu) {
1318
0
        snmp_log(LOG_ERR, "could not allocate pdu\n");
1319
0
        return SNMP_ERR_GENERR;
1320
0
    }
1321
1322
    /*
1323
     * Clone the varbind list into the request PDU...
1324
     */
1325
0
    pdu->variables = snmp_clone_varbind( list );
1326
0
    if (NULL == pdu->variables) {
1327
0
        snmp_log(LOG_ERR, "could not clone variable list\n");
1328
0
        snmp_free_pdu(pdu);
1329
0
        return SNMP_ERR_GENERR;
1330
0
    }
1331
1332
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
1333
0
retry:
1334
0
#endif
1335
0
    if ( session )
1336
0
        ret = snmp_synch_response(            session, pdu, &response );
1337
0
    else if (_def_query_session)
1338
0
        ret = snmp_synch_response( _def_query_session, pdu, &response );
1339
0
    else {
1340
        /* No session specified */
1341
0
        snmp_free_pdu(pdu);
1342
0
        return SNMP_ERR_GENERR;
1343
0
    }
1344
0
    DEBUGMSGTL(("iquery", "query returned %d\n", ret));
1345
1346
    /*
1347
     * ....then copy the results back into the
1348
     * list (assuming the request succeeded!).
1349
     * This avoids having to worry about how this
1350
     * list was originally allocated.
1351
     */
1352
0
    if ( ret == SNMP_ERR_NOERROR ) {
1353
0
        if ( response->errstat != SNMP_ERR_NOERROR ) {
1354
0
            DEBUGMSGT(("iquery", "Error in packet: %s\n",
1355
0
                       snmp_errstring(response->errstat)));
1356
            /*
1357
             * If the request failed, then remove the
1358
             *  offending varbind and try again.
1359
             *  (all except SET requests)
1360
             *
1361
             * XXX - implement a library version of
1362
             *       NETSNMP_DS_APP_DONT_FIX_PDUS ??
1363
             */
1364
0
            ret = response->errstat;
1365
0
            if (response->errindex != 0) {
1366
0
                DEBUGMSGT(("iquery:result", "Failed object:\n"));
1367
0
                for (count = 1, vtmp = response->variables;
1368
0
                     vtmp && count != response->errindex;
1369
0
                     vtmp = vtmp->next_variable, count++)
1370
0
                    /*EMPTY*/;
1371
0
                if (vtmp)
1372
0
                    DEBUGMSGVAR(("iquery:result", vtmp));
1373
0
                DEBUGMSG(("iquery:result", "\n"));
1374
0
            }
1375
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
1376
0
            if (request != SNMP_MSG_SET &&
1377
0
                response->errindex != 0) {
1378
0
                DEBUGMSGTL(("iquery", "retrying query (%d, %ld)\n", ret, response->errindex));
1379
0
                pdu = snmp_fix_pdu( response, request );
1380
0
                snmp_free_pdu( response );
1381
0
                response = NULL;
1382
0
                if ( pdu != NULL )
1383
0
                    goto retry;
1384
0
            }
1385
0
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
1386
0
        } else {
1387
0
            for (vb1 = response->variables, vb2 = list;
1388
0
                 vb1;
1389
0
                 vb1 = vb1->next_variable,  vb2 = vb2->next_variable) {
1390
0
                DEBUGMSGVAR(("iquery:result", vb1));
1391
0
                DEBUGMSG(("iquery:results", "\n"));
1392
0
                if ( !vb2 ) {
1393
0
                    ret = SNMP_ERR_GENERR;
1394
0
                    break;
1395
0
                }
1396
0
                vtmp = vb2->next_variable;
1397
0
                snmp_free_var_internals( vb2 );
1398
0
                snmp_clone_var( vb1, vb2 ); /* xxx: check return? */
1399
0
                vb2->next_variable = vtmp;
1400
0
            }
1401
0
        }
1402
0
    } else {
1403
        /* Distinguish snmp_send errors from SNMP errStat errors */
1404
0
        ret = -ret;
1405
0
    }
1406
0
    snmp_free_pdu( response );
1407
0
    return ret;
1408
0
}
1409
1410
/*
1411
 * These are simple wrappers round the internal utility routine
1412
 */
1413
int netsnmp_query_get(netsnmp_variable_list *list,
1414
0
                      netsnmp_session       *session){
1415
0
    return _query( list, SNMP_MSG_GET, session );
1416
0
}
1417
1418
1419
int netsnmp_query_getnext(netsnmp_variable_list *list,
1420
0
                          netsnmp_session       *session){
1421
0
    return _query( list, SNMP_MSG_GETNEXT, session );
1422
0
}
1423
1424
1425
#ifndef NETSNMP_NO_WRITE_SUPPORT
1426
int netsnmp_query_set(netsnmp_variable_list *list,
1427
0
                      netsnmp_session       *session){
1428
0
    return _query( list, SNMP_MSG_SET, session );
1429
0
}
1430
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
1431
1432
/*
1433
 * A walk needs a bit more work.
1434
 */
1435
int netsnmp_query_walk(netsnmp_variable_list *list,
1436
0
                       netsnmp_session       *session) {
1437
    /*
1438
     * Create a working copy of the original (single)
1439
     * varbind, so we can use this varbind parameter
1440
     * to check when we've finished walking this subtree.
1441
     */
1442
0
    netsnmp_variable_list *vb = snmp_clone_varbind( list );
1443
0
    netsnmp_variable_list *res_list = NULL;
1444
0
    netsnmp_variable_list *res_last = NULL;
1445
0
    int ret;
1446
1447
    /*
1448
     * Now walk the tree as usual
1449
     */
1450
0
    ret = _query( vb, SNMP_MSG_GETNEXT, session );
1451
0
    while ( ret == SNMP_ERR_NOERROR &&
1452
0
        snmp_oidtree_compare( list->name, list->name_length,
1453
0
                                vb->name,   vb->name_length ) == 0) {
1454
1455
0
  if (vb->type == SNMP_ENDOFMIBVIEW ||
1456
0
      vb->type == SNMP_NOSUCHOBJECT ||
1457
0
      vb->type == SNMP_NOSUCHINSTANCE)
1458
0
      break;
1459
1460
        /*
1461
         * Copy each response varbind to the end of the result list
1462
         * and then re-use this to ask for the next entry.
1463
         */
1464
0
        if ( res_last ) {
1465
0
            res_last->next_variable = snmp_clone_varbind( vb );
1466
0
            res_last = res_last->next_variable;
1467
0
        } else {
1468
0
            res_list = snmp_clone_varbind( vb );
1469
0
            res_last = res_list;
1470
0
        }
1471
0
        ret = _query( vb, SNMP_MSG_GETNEXT, session );
1472
0
    }
1473
    /*
1474
     * Copy the first result back into the original varbind parameter,
1475
     * add the rest of the results (if any), and clean up.
1476
     */
1477
0
    if ( res_list ) {
1478
0
        snmp_clone_var( res_list, list );
1479
0
        list->next_variable = res_list->next_variable;
1480
0
        res_list->next_variable = NULL;
1481
0
        snmp_free_varbind( res_list );
1482
0
    }
1483
0
    snmp_free_varbind( vb );
1484
0
    return ret;
1485
0
}
1486
1487
/** **************************************************************************
1488
 *
1489
 * state machine
1490
 *
1491
 */
1492
int
1493
netsnmp_state_machine_run( netsnmp_state_machine_input *input)
1494
0
{
1495
0
    netsnmp_state_machine_step *current, *last;
1496
1497
0
    netsnmp_require_ptr_LRV( input, SNMPERR_GENERR );
1498
0
    netsnmp_require_ptr_LRV( input->steps, SNMPERR_GENERR );
1499
0
    last = current = input->steps;
1500
1501
0
    DEBUGMSGT(("state_machine:run", "starting step: %s\n", current->name));
1502
1503
0
    while (current) {
1504
1505
        /*
1506
         * log step and check for required data
1507
         */
1508
0
        DEBUGMSGT(("state_machine:run", "at step: %s\n", current->name));
1509
0
        if (NULL == current->run) {
1510
0
            DEBUGMSGT(("state_machine:run", "no run step\n"));
1511
0
            current->result = last->result;
1512
0
            break;
1513
0
        }
1514
1515
        /*
1516
         * run step
1517
         */
1518
0
        DEBUGMSGT(("state_machine:run", "running step: %s\n", current->name));
1519
0
        current->result = (*current->run)( input, current );
1520
0
        ++input->steps_so_far;
1521
        
1522
        /*
1523
         * log result and move to next step
1524
         */
1525
0
        input->last_run = current;
1526
0
        DEBUGMSGT(("state_machine:run:result", "step %s returned %d\n",
1527
0
                   current->name, current->result));
1528
0
        if (SNMPERR_SUCCESS == current->result)
1529
0
            current = current->on_success;
1530
0
        else if (SNMPERR_ABORT == current->result) {
1531
0
            DEBUGMSGT(("state_machine:run:result", "ABORT from %s\n",
1532
0
                       current->name));
1533
0
            break;
1534
0
        }
1535
0
        else
1536
0
            current = current->on_error;
1537
0
    }
1538
1539
    /*
1540
     * run cleanup
1541
     */
1542
0
    if ((input->cleanup) && (input->cleanup->run))
1543
0
        (*input->cleanup->run)( input, input->last_run );
1544
1545
0
    return input->last_run->result;
1546
0
}
1547
1548
#ifndef NETSNMP_NO_WRITE_SUPPORT
1549
#ifndef NETSNMP_FEATURE_REMOVE_ROW_CREATE
1550
/** **************************************************************************
1551
 *
1552
 * row create state machine steps
1553
 *
1554
 */
1555
typedef struct rowcreate_state_s {
1556
1557
    netsnmp_session        *session;
1558
    netsnmp_variable_list  *vars;
1559
    int                     row_status_index;
1560
} rowcreate_state;
1561
1562
static netsnmp_variable_list *
1563
_get_vb_num(netsnmp_variable_list *vars, int index)
1564
0
{
1565
0
    for (; vars && index > 0; --index)
1566
0
        vars = vars->next_variable;
1567
1568
0
    if (!vars || index > 0)
1569
0
        return NULL;
1570
    
1571
0
    return vars;
1572
0
}
1573
1574
1575
/*
1576
 * cleanup
1577
 */
1578
static int 
1579
_row_status_state_cleanup(netsnmp_state_machine_input *input,
1580
                 netsnmp_state_machine_step *step)
1581
0
{
1582
0
    rowcreate_state       *ctx;
1583
1584
0
    netsnmp_require_ptr_LRV( input, SNMPERR_ABORT );
1585
0
    netsnmp_require_ptr_LRV( step, SNMPERR_ABORT );
1586
1587
0
    DEBUGMSGT(("row_create:called", "_row_status_state_cleanup, last run step was %s rc %d\n",
1588
0
               step->name, step->result));
1589
1590
0
    ctx = (rowcreate_state *)input->input_context;
1591
0
    if (ctx && ctx->vars)
1592
0
        snmp_free_varbind( ctx->vars );
1593
1594
0
    return SNMPERR_SUCCESS;
1595
0
}
1596
1597
/*
1598
 * send a request to activate the row
1599
 */
1600
static int 
1601
_row_status_state_activate(netsnmp_state_machine_input *input,
1602
                  netsnmp_state_machine_step *step)
1603
0
{
1604
0
    rowcreate_state       *ctx;
1605
0
    netsnmp_variable_list *rs_var, *var = NULL;
1606
0
    int32_t                rc, val = RS_ACTIVE;
1607
1608
0
    netsnmp_require_ptr_LRV( input, SNMPERR_GENERR );
1609
0
    netsnmp_require_ptr_LRV( step, SNMPERR_GENERR );
1610
0
    netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR );
1611
1612
0
    ctx = (rowcreate_state *)input->input_context;
1613
1614
0
    DEBUGMSGT(("row_create:called", "called %s\n", step->name));
1615
1616
    /*
1617
     * just send the rowstatus varbind
1618
     */
1619
0
    rs_var = _get_vb_num(ctx->vars, ctx->row_status_index);
1620
0
    netsnmp_require_ptr_LRV(rs_var, SNMPERR_GENERR);
1621
1622
0
    var = snmp_varlist_add_variable(&var, rs_var->name, rs_var->name_length,
1623
0
                                    rs_var->type, &val, sizeof(val));
1624
0
    netsnmp_require_ptr_LRV( var, SNMPERR_GENERR );
1625
1626
    /*
1627
     * send set
1628
     */
1629
0
    rc = netsnmp_query_set( var, ctx->session );
1630
0
    if (-2 == rc)
1631
0
        rc = SNMPERR_ABORT;
1632
1633
0
    snmp_free_varbind(var);
1634
1635
0
    return rc;
1636
0
}
1637
1638
/*
1639
 * send each non-row status column, one at a time
1640
 */
1641
static int 
1642
_row_status_state_single_value_cols(netsnmp_state_machine_input *input,
1643
                                    netsnmp_state_machine_step *step)
1644
0
{
1645
0
    rowcreate_state       *ctx;
1646
0
    netsnmp_variable_list *var, *tmp_next, *row_status;
1647
0
    int                    rc = SNMPERR_GENERR;
1648
1649
0
    netsnmp_require_ptr_LRV( input, SNMPERR_GENERR );
1650
0
    netsnmp_require_ptr_LRV( step, SNMPERR_GENERR );
1651
0
    netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR );
1652
1653
0
    ctx = (rowcreate_state *)input->input_context;
1654
1655
0
    DEBUGMSGT(("row_create:called", "called %s\n", step->name));
1656
1657
0
    row_status = _get_vb_num(ctx->vars, ctx->row_status_index);
1658
0
    netsnmp_require_ptr_LRV(row_status, SNMPERR_GENERR);
1659
1660
    /*
1661
     * try one varbind at a time
1662
     */
1663
0
    for (var = ctx->vars; var; var = var->next_variable) {
1664
0
        if (var == row_status)
1665
0
            continue;
1666
1667
0
        tmp_next = var->next_variable;
1668
0
        var->next_variable = NULL;
1669
1670
        /*
1671
         * send set
1672
         */
1673
0
        rc = netsnmp_query_set( var, ctx->session );
1674
0
        var->next_variable = tmp_next;
1675
0
        if (-2 == rc)
1676
0
            rc = SNMPERR_ABORT;
1677
0
        if (rc != SNMPERR_SUCCESS)
1678
0
            break;
1679
0
    }
1680
1681
0
    return rc;
1682
0
}
1683
1684
/*
1685
 * send all values except row status
1686
 */
1687
static int 
1688
_row_status_state_multiple_values_cols(netsnmp_state_machine_input *input,
1689
                                       netsnmp_state_machine_step *step)
1690
0
{
1691
0
    rowcreate_state       *ctx;
1692
0
    netsnmp_variable_list *vars, *var, *last, *row_status;
1693
0
    int                    rc;
1694
1695
0
    netsnmp_require_ptr_LRV( input, SNMPERR_GENERR );
1696
0
    netsnmp_require_ptr_LRV( step, SNMPERR_GENERR );
1697
0
    netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR );
1698
1699
0
    ctx = (rowcreate_state *)input->input_context;
1700
1701
0
    DEBUGMSGT(("row_create:called", "called %s\n", step->name));
1702
1703
0
    vars = snmp_clone_varbind(ctx->vars);
1704
0
    netsnmp_require_ptr_LRV(vars, SNMPERR_GENERR);
1705
1706
0
    row_status = _get_vb_num(vars, ctx->row_status_index);
1707
0
    if (NULL == row_status) {
1708
0
        snmp_free_varbind(vars);
1709
0
        return SNMPERR_GENERR;
1710
0
    }
1711
1712
    /*
1713
     * remove row status varbind
1714
     */
1715
0
    if (row_status == vars) {
1716
0
        vars = row_status->next_variable;
1717
0
        row_status->next_variable = NULL;
1718
0
    }
1719
0
    else {
1720
0
        for (last=vars, var=last->next_variable;
1721
0
             var;
1722
0
             last=var, var = var->next_variable) {
1723
0
            if (var == row_status) {
1724
0
                last->next_variable = var->next_variable;
1725
0
                break;
1726
0
            }
1727
0
        }
1728
0
    }
1729
0
    snmp_free_var(row_status);
1730
1731
    /*
1732
     * send set
1733
     */
1734
0
    rc = netsnmp_query_set( vars, ctx->session );
1735
0
    if (-2 == rc)
1736
0
        rc = SNMPERR_ABORT;
1737
1738
0
    snmp_free_varbind(vars);
1739
1740
0
    return rc;
1741
0
}
1742
1743
/*
1744
 * send a createAndWait request with no other values
1745
 */
1746
static int 
1747
_row_status_state_single_value_createAndWait(netsnmp_state_machine_input *input,
1748
                                             netsnmp_state_machine_step *step)
1749
0
{
1750
0
    rowcreate_state       *ctx;
1751
0
    netsnmp_variable_list *var = NULL, *rs_var;
1752
0
    int32_t                rc, val = RS_CREATEANDWAIT;
1753
1754
0
    netsnmp_require_ptr_LRV( input, SNMPERR_GENERR );
1755
0
    netsnmp_require_ptr_LRV( step, SNMPERR_GENERR );
1756
0
    netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR );
1757
1758
0
    ctx = (rowcreate_state *)input->input_context;
1759
1760
0
    DEBUGMSGT(("row_create:called", "called %s\n", step->name));
1761
1762
0
    rs_var = _get_vb_num(ctx->vars, ctx->row_status_index);
1763
0
    netsnmp_require_ptr_LRV(rs_var, SNMPERR_GENERR);
1764
1765
0
    var = snmp_varlist_add_variable(&var, rs_var->name, rs_var->name_length,
1766
0
                                    rs_var->type, &val, sizeof(val));
1767
0
    netsnmp_require_ptr_LRV(var, SNMPERR_GENERR);
1768
1769
    /*
1770
     * send set
1771
     */
1772
0
    rc = netsnmp_query_set( var, ctx->session );
1773
0
    if (-2 == rc)
1774
0
        rc = SNMPERR_ABORT;
1775
1776
0
    snmp_free_varbind(var);
1777
1778
0
    return rc;
1779
0
}
1780
1781
/*
1782
 * send a creatAndWait request with all values
1783
 */
1784
static int 
1785
_row_status_state_all_values_createAndWait(netsnmp_state_machine_input *input,
1786
                                           netsnmp_state_machine_step *step)
1787
0
{
1788
0
    rowcreate_state       *ctx;
1789
0
    netsnmp_variable_list *vars, *rs_var;
1790
0
    int                    rc;
1791
1792
0
    netsnmp_require_ptr_LRV( input, SNMPERR_GENERR );
1793
0
    netsnmp_require_ptr_LRV( step, SNMPERR_GENERR );
1794
0
    netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR );
1795
1796
0
    ctx = (rowcreate_state *)input->input_context;
1797
1798
0
    DEBUGMSGT(("row_create:called", "called %s\n", step->name));
1799
1800
0
    vars = snmp_clone_varbind(ctx->vars);
1801
0
    netsnmp_require_ptr_LRV(vars, SNMPERR_GENERR);
1802
1803
    /*
1804
     * make sure row stats is createAndWait
1805
     */
1806
0
    rs_var = _get_vb_num(vars, ctx->row_status_index);
1807
0
    if (NULL == rs_var) {
1808
0
        snmp_free_varbind(vars);
1809
0
        return SNMPERR_GENERR;
1810
0
    }
1811
1812
0
    if (*rs_var->val.integer != RS_CREATEANDWAIT)
1813
0
        *rs_var->val.integer = RS_CREATEANDWAIT;
1814
1815
    /*
1816
     * send set
1817
     */
1818
0
    rc = netsnmp_query_set( vars, ctx->session );
1819
0
    if (-2 == rc)
1820
0
        rc = SNMPERR_ABORT;
1821
1822
0
    snmp_free_varbind(vars);
1823
1824
0
    return rc;
1825
0
}
1826
1827
1828
/**
1829
 * send createAndGo request with all values
1830
 */
1831
static int 
1832
_row_status_state_all_values_createAndGo(netsnmp_state_machine_input *input,
1833
                                         netsnmp_state_machine_step *step)
1834
0
{
1835
0
    rowcreate_state       *ctx;
1836
0
    netsnmp_variable_list *vars, *rs_var;
1837
0
    int                    rc;
1838
1839
0
    netsnmp_require_ptr_LRV( input, SNMPERR_GENERR );
1840
0
    netsnmp_require_ptr_LRV( step, SNMPERR_GENERR );
1841
0
    netsnmp_require_ptr_LRV( input->input_context, SNMPERR_GENERR );
1842
1843
0
    ctx = (rowcreate_state *)input->input_context;
1844
1845
0
    DEBUGMSGT(("row_create:called", "called %s\n", step->name));
1846
1847
0
    vars = snmp_clone_varbind(ctx->vars);
1848
0
    netsnmp_require_ptr_LRV(vars, SNMPERR_GENERR);
1849
1850
    /*
1851
     * make sure row stats is createAndGo
1852
     */
1853
0
    rs_var = _get_vb_num(vars, ctx->row_status_index + 1);
1854
0
    if (NULL == rs_var) {
1855
0
        snmp_free_varbind(vars);
1856
0
        return SNMPERR_GENERR;
1857
0
    }
1858
1859
0
    if (*rs_var->val.integer != RS_CREATEANDGO)
1860
0
        *rs_var->val.integer = RS_CREATEANDGO;
1861
1862
    /*
1863
     * send set
1864
     */
1865
0
    rc = netsnmp_query_set( vars, ctx->session );
1866
0
    if (-2 == rc)
1867
0
        rc = SNMPERR_ABORT;
1868
1869
0
    snmp_free_varbind(vars);
1870
1871
0
    return rc;
1872
0
}
1873
1874
/** **************************************************************************
1875
 *
1876
 * row api
1877
 *
1878
 */
1879
int
1880
netsnmp_row_create(netsnmp_session *sess, netsnmp_variable_list *vars,
1881
                   int row_status_index)
1882
0
{
1883
0
    netsnmp_state_machine_step rc_cleanup =
1884
0
        { "row_create_cleanup", 0, _row_status_state_cleanup,
1885
0
          0, NULL, NULL, 0, NULL };
1886
0
    netsnmp_state_machine_step rc_activate =
1887
0
        { "row_create_activate", 0, _row_status_state_activate,
1888
0
          0, NULL, NULL, 0, NULL };
1889
0
    netsnmp_state_machine_step rc_sv_cols =
1890
0
        { "row_create_single_value_cols", 0,
1891
0
          _row_status_state_single_value_cols, 0, &rc_activate,NULL, 0, NULL };
1892
0
    netsnmp_state_machine_step rc_mv_cols =
1893
0
        { "row_create_multiple_values_cols", 0,
1894
0
          _row_status_state_multiple_values_cols, 0, &rc_activate, &rc_sv_cols,
1895
0
          0, NULL };
1896
0
    netsnmp_state_machine_step rc_sv_caw =
1897
0
        { "row_create_single_value_createAndWait", 0,
1898
0
          _row_status_state_single_value_createAndWait, 0, &rc_mv_cols, NULL,
1899
0
          0, NULL };
1900
0
    netsnmp_state_machine_step rc_av_caw =
1901
0
        { "row_create_all_values_createAndWait", 0,
1902
0
          _row_status_state_all_values_createAndWait, 0, &rc_activate,
1903
0
          &rc_sv_caw, 0, NULL };
1904
0
    netsnmp_state_machine_step rc_av_cag =
1905
0
        { "row_create_all_values_createAndGo", 0,
1906
0
          _row_status_state_all_values_createAndGo, 0, NULL, &rc_av_caw, 0,
1907
0
          NULL };
1908
1909
0
    netsnmp_state_machine_input sm_input = { "row_create_machine", 0,
1910
0
                                             &rc_av_cag, &rc_cleanup };
1911
0
    rowcreate_state state;
1912
1913
0
    netsnmp_require_ptr_LRV( sess, SNMPERR_GENERR);
1914
0
    netsnmp_require_ptr_LRV( vars, SNMPERR_GENERR);
1915
1916
0
    state.session = sess;
1917
0
    state.vars = vars;
1918
1919
0
    state.row_status_index = row_status_index;
1920
0
    sm_input.input_context = &state;
1921
1922
0
    netsnmp_state_machine_run( &sm_input);
1923
1924
0
    return SNMPERR_SUCCESS;
1925
0
}
1926
#endif /* NETSNMP_FEATURE_REMOVE_ROW_CREATE */
1927
#endif /* NETSNMP_NO_WRITE_SUPPORT */
1928
1929
1930
/** @} */