Coverage Report

Created: 2026-09-04 09:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/net-snmp/agent/mibgroup/agentx/master.c
Line
Count
Source
1
/*
2
 *  AgentX master agent
3
 */
4
/* Portions of this file are subject to the following copyright(s).  See
5
 * the Net-SNMP's COPYING file for more details and other copyrights
6
 * that may apply:
7
 */
8
/*
9
 * Portions of this file are copyrighted by:
10
 * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
11
 * Use is subject to license terms specified in the COPYING file
12
 * distributed with the Net-SNMP package.
13
 */
14
15
16
#include <net-snmp/net-snmp-config.h>
17
#include <net-snmp/net-snmp-features.h>
18
#ifdef HAVE_IO_H
19
#include <io.h>
20
#endif
21
22
#include <stdio.h>
23
#include <sys/types.h>
24
#ifdef HAVE_STDLIB_H
25
#include <stdlib.h>
26
#endif
27
#ifdef HAVE_STRING_H
28
#include <string.h>
29
#else
30
#include <strings.h>
31
#endif
32
#ifdef HAVE_NETINET_IN_H
33
#include <netinet/in.h>
34
#endif
35
#ifdef HAVE_SYS_SOCKET_H
36
#include <sys/socket.h>
37
#endif
38
#ifdef HAVE_NETDB_H
39
#include <netdb.h>
40
#endif
41
#ifdef HAVE_SYS_UN_H
42
#include <sys/un.h>
43
#endif
44
#include <errno.h>
45
46
#ifdef HAVE_UNISTD_H
47
#include <unistd.h>
48
#endif
49
#ifdef HAVE_SYS_STAT_H
50
#include <sys/stat.h>
51
#endif
52
53
#include <net-snmp/net-snmp-includes.h>
54
#include <net-snmp/agent/net-snmp-agent-includes.h>
55
#include <net-snmp/library/snmpUnixDomain.h>
56
#include "snmpd.h"
57
#include "agentx/protocol.h"
58
#include "agentx/master_admin.h"
59
60
netsnmp_feature_require(handler_mark_requests_as_delegated);
61
netsnmp_feature_require(unix_socket_paths);
62
netsnmp_feature_require(free_agent_snmp_session_by_session);
63
64
struct agentx_master_session_el {
65
    netsnmp_session *session;
66
    struct agentx_master_session_el *next;
67
};
68
static struct agentx_master_session_el *master_sessions = NULL;
69
70
void agentx_register_session(netsnmp_session *session)
71
0
{
72
0
    struct agentx_master_session_el *el = master_sessions;
73
0
    while (el) {
74
0
        if (el->session == session)
75
0
            return;
76
0
        el = el->next;
77
0
    }
78
0
    el = malloc(sizeof(*el));
79
0
    if (el) {
80
0
        el->session = session;
81
0
        el->next = master_sessions;
82
0
        master_sessions = el;
83
0
    }
84
0
}
85
86
void agentx_unregister_session(netsnmp_session *session)
87
0
{
88
0
    struct agentx_master_session_el *el = master_sessions, *prev = NULL;
89
0
    while (el) {
90
0
        if (el->session == session) {
91
0
            if (prev)
92
0
                prev->next = el->next;
93
0
            else
94
0
                master_sessions = el->next;
95
0
            free(el);
96
0
            return;
97
0
        }
98
0
        prev = el;
99
0
        el = el->next;
100
0
    }
101
0
}
102
103
static int
104
shutdown_master_callback(int majorID, int minorID, void *serve, void *client)
105
0
{
106
0
    struct agentx_master_session_el *el = master_sessions, *next;
107
0
    while (el) {
108
0
        next = el->next;
109
0
        close_agentx_session(el->session, -1);
110
0
        snmp_close(el->session);
111
0
        free(el);
112
0
        el = next;
113
0
    }
114
0
    master_sessions = NULL;
115
0
    return SNMPERR_SUCCESS;
116
0
}
117
118
static void
119
log_agentx_listening_address(netsnmp_transport *t)
120
0
{
121
0
    struct sockaddr_storage sa;
122
0
    socklen_t len = sizeof(sa);
123
0
    char name[256], serv[16];
124
125
0
    if (getsockname(t->sock, (void *)&sa, &len) < 0)
126
0
        return;
127
128
0
#if defined(AF_UNIX) && defined(HAVE_SYS_UN_H)
129
0
    if (sa.ss_family == AF_UNIX) {
130
0
        struct sockaddr_un *sun_addr = (struct sockaddr_un *)&sa;
131
0
        snmp_log(LOG_INFO, "AgentX master listening on %s\n",
132
0
                 sun_addr->sun_path[0] ? sun_addr->sun_path : "abstract");
133
0
        return;
134
0
    }
135
0
#endif
136
137
0
    if (getnameinfo((void *)&sa, len, name, sizeof(name), serv,
138
0
                    sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV) < 0)
139
140
0
        return;
141
142
0
    snmp_log(LOG_INFO, "AgentX master listening on %s:%s\n", name, serv);
143
0
}
144
145
void
146
real_init_master(void)
147
0
{
148
0
    netsnmp_session sess, *session = NULL;
149
0
    char *agentx_sockets;
150
0
    char *cp1;
151
0
    static int once = 0;
152
153
0
    if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE) != MASTER_AGENT)
154
0
        return;
155
156
0
    if (once == 0) {
157
0
        once = 1;
158
0
        snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_SHUTDOWN,
159
0
                               shutdown_master_callback, NULL);
160
0
    }
161
162
0
    if (netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID,
163
0
                              NETSNMP_DS_AGENT_X_SOCKET)) {
164
0
       agentx_sockets = strdup(netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID,
165
0
                                                     NETSNMP_DS_AGENT_X_SOCKET));
166
#ifdef NETSNMP_AGENTX_DOM_SOCK_ONLY
167
       if (agentx_sockets[0] != '/') {
168
           /* unix:/path */
169
           if (agentx_sockets[5] != '/') {
170
               snmp_log(LOG_ERR,
171
                    "Error: %s transport is not supported, disabling agentx/master.\n", agentx_sockets);
172
               SNMP_FREE(agentx_sockets);
173
               return;
174
           }
175
       }
176
#endif
177
0
    } else {
178
0
        agentx_sockets = strdup("");
179
0
    }
180
181
182
0
    DEBUGMSGTL(("agentx/master", "initializing...\n"));
183
0
    snmp_sess_init(&sess);
184
0
    sess.version = AGENTX_VERSION_1;
185
0
    sess.flags |= SNMP_FLAGS_STREAM_SOCKET;
186
0
    sess.timeout = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
187
0
                                      NETSNMP_DS_AGENT_AGENTX_TIMEOUT);
188
0
    sess.retries = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
189
0
                                      NETSNMP_DS_AGENT_AGENTX_RETRIES);
190
191
0
#ifdef NETSNMP_TRANSPORT_UNIX_DOMAIN
192
0
    {
193
0
  int agentx_dir_perm =
194
0
      netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
195
0
             NETSNMP_DS_AGENT_X_DIR_PERM);
196
0
  if (agentx_dir_perm == 0)
197
0
      agentx_dir_perm = NETSNMP_AGENT_DIRECTORY_MODE;
198
0
  netsnmp_unix_create_path_with_mode(agentx_dir_perm);
199
0
    }
200
0
#endif
201
202
0
    cp1 = agentx_sockets;
203
0
    while (cp1) {
204
0
        netsnmp_transport *t;
205
        /*
206
         *  If the AgentX socket string contains multiple descriptors,
207
         *  then pick this apart and handle them one by one.
208
         *
209
         */
210
0
        sess.peername = cp1;
211
0
        cp1 = strchr(sess.peername, ',');
212
0
        if (cp1 != NULL) {
213
0
            *cp1++ = '\0';
214
0
  }
215
216
        /*
217
         *  Let 'snmp_open' interpret the descriptor.
218
         */
219
0
        sess.local_port = AGENTX_PORT;      /* Indicate server & set default port */
220
0
        sess.callback = handle_master_agentx_packet;
221
0
        errno = 0;
222
0
        t = netsnmp_transport_open_server("agentx", sess.peername);
223
0
        if (t == NULL) {
224
            /*
225
             * diagnose snmp_open errors with the input netsnmp_session
226
             * pointer.
227
             */
228
0
            char buf[1024];
229
0
            if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
230
0
                                        NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) {
231
0
                snprintf(buf, sizeof(buf),
232
0
                         "Error: Couldn't open a master agentx socket to "
233
0
                         "listen on (%s)", sess.peername);
234
0
                snmp_sess_perror(buf, &sess);
235
0
                exit(1);
236
0
            } else {
237
0
                snprintf(buf, sizeof(buf),
238
0
                         "Warning: Couldn't open a master agentx socket to "
239
0
                         "listen on (%s)", sess.peername);
240
0
                netsnmp_sess_log_error(LOG_WARNING, buf, &sess);
241
0
            }
242
0
        } else {
243
0
            log_agentx_listening_address(t);
244
0
#ifdef NETSNMP_TRANSPORT_UNIX_DOMAIN
245
0
            if (t->domain == netsnmp_UnixDomain && t->local != NULL) {
246
                /*
247
                 * Apply any settings to the ownership/permissions of the
248
                 * AgentX socket
249
                 */
250
0
                int agentx_sock_perm =
251
0
                    netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
252
0
                                       NETSNMP_DS_AGENT_X_SOCK_PERM);
253
0
                int agentx_sock_user =
254
0
                    netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
255
0
                                       NETSNMP_DS_AGENT_X_SOCK_USER);
256
0
                int agentx_sock_group =
257
0
                    netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
258
0
                                       NETSNMP_DS_AGENT_X_SOCK_GROUP);
259
260
0
                char name[sizeof(struct sockaddr_un) + 1];
261
0
                memcpy(name, t->local, t->local_length);
262
0
                name[t->local_length] = '\0';
263
264
0
                if (agentx_sock_perm != 0)
265
0
                    chmod(name, agentx_sock_perm);
266
267
0
                if (agentx_sock_user || agentx_sock_group) {
268
                    /*
269
                     * If either of user or group haven't been set,
270
                     *  then leave them unchanged.
271
                     */
272
0
                    if (agentx_sock_user == 0 )
273
0
                        agentx_sock_user = -1;
274
0
                    if (agentx_sock_group == 0 )
275
0
                        agentx_sock_group = -1;
276
0
                    NETSNMP_IGNORE_RESULT(chown(name, agentx_sock_user,
277
0
                                                agentx_sock_group));
278
0
                }
279
0
            }
280
0
#endif
281
0
            session =
282
0
                snmp_add_full(&sess, t, NULL, agentx_parse, NULL, NULL,
283
0
                              agentx_realloc_build, agentx_check_packet, NULL);
284
            /* snmp_add_full() frees 't' upon failure. */
285
0
            if (!session) {
286
0
                t = NULL;
287
0
            } else {
288
0
                agentx_register_session(session);
289
0
            }
290
0
        }
291
0
        if (session == NULL) {
292
0
            netsnmp_transport_free(t);
293
0
        }
294
0
    }
295
296
0
#ifdef NETSNMP_TRANSPORT_UNIX_DOMAIN
297
0
    netsnmp_unix_dont_create_path();
298
0
#endif
299
300
0
    SNMP_FREE(agentx_sockets);
301
0
    DEBUGMSGTL(("agentx/master", "initializing...   DONE\n"));
302
0
}
303
304
305
static netsnmp_refcnt_void *
306
netsnmp_create_delegated_box(void *val_ptr)
307
0
{
308
0
    netsnmp_refcnt_void * box = NULL;
309
310
0
    box = SNMP_MALLOC_TYPEDEF(netsnmp_refcnt_void);
311
0
    if (box) {
312
0
        box->refcnt = 1;
313
0
        box->val = val_ptr;
314
0
    }
315
      
316
0
    return box;
317
0
}
318
319
static void
320
netsnmp_delete_delegated_box(netsnmp_refcnt_void * box)
321
0
{
322
0
    if (box) {
323
0
        box->refcnt--;
324
0
        box->val = 0;
325
0
        if (box->refcnt <= 0)
326
0
      SNMP_FREE(box);
327
0
    }
328
0
}
329
330
331
        /*
332
         * Handle the response from an AgentX subagent,
333
         *   merging the answers back into the original query
334
         */
335
int
336
agentx_got_response(int operation,
337
                    netsnmp_session * session,
338
                    int reqid, netsnmp_pdu *pdu, void *magic)
339
0
{
340
0
    netsnmp_refcnt_void *cache_box = magic;
341
0
    netsnmp_delegated_cache *cache = cache_box ? cache_box->val : NULL;
342
0
    int             i, ret;
343
0
    netsnmp_request_info *requests, *request;
344
0
    netsnmp_variable_list *var;
345
346
0
    cache = netsnmp_handler_check_cache(cache);
347
0
    if (!cache) {
348
0
        DEBUGMSGTL(("agentx/master", "response too late on session %8p\n",
349
0
                    session));
350
        /* response is too late, free the cache */
351
0
        if (magic) {
352
0
            netsnmp_free_delegated_cache(cache);
353
0
            netsnmp_delete_delegated_box(cache_box);
354
0
  }
355
0
        return 1;
356
0
    }
357
0
    requests = cache->requests;
358
359
0
    switch (operation) {
360
0
    case NETSNMP_CALLBACK_OP_TIMED_OUT:{
361
0
            struct session_list *s = snmp_sess_pointer(session);
362
0
            DEBUGMSGTL(("agentx/master", "timeout on session %8p req=0x%x\n",
363
0
                        session, (unsigned)reqid));
364
365
0
            netsnmp_handler_mark_requests_as_delegated(requests,
366
0
                                       REQUEST_IS_NOT_DELEGATED);
367
0
            netsnmp_set_request_error(cache->reqinfo, requests,
368
                                      /* XXXWWW: should be index=0 */
369
0
                                      SNMP_ERR_GENERR);
370
371
            /*
372
             * This is a bit sledgehammer because the other sessions on this
373
             * transport may be okay (e.g. some thread in the subagent has
374
             * wedged, but the others are alright).  OTOH the overwhelming
375
             * probability is that the whole agent has died somehow.  
376
             */
377
378
0
            if (s != NULL) {
379
0
                netsnmp_transport *t = snmp_sess_transport(s);
380
0
                close_agentx_session(session, -1);
381
382
0
                if (t != NULL) {
383
0
                    DEBUGMSGTL(("agentx/master", "close transport\n"));
384
0
                    t->f_close(t);
385
0
                } else {
386
0
                    DEBUGMSGTL(("agentx/master", "NULL transport??\n"));
387
0
                }
388
0
            } else {
389
0
                DEBUGMSGTL(("agentx/master", "NULL sess_pointer??\n"));
390
0
            }
391
0
            netsnmp_free_delegated_cache(cache);
392
0
            netsnmp_delete_delegated_box(cache_box);
393
0
            return 0;
394
0
        }
395
396
0
    case NETSNMP_CALLBACK_OP_DISCONNECT:
397
0
    case NETSNMP_CALLBACK_OP_SEND_FAILED:
398
0
        if (operation == NETSNMP_CALLBACK_OP_DISCONNECT) {
399
0
            DEBUGMSGTL(("agentx/master", "disconnect on session %8p\n",
400
0
                        session));
401
0
        } else {
402
0
            DEBUGMSGTL(("agentx/master", "send failed on session %8p\n",
403
0
                        session));
404
0
        }
405
0
        netsnmp_handler_mark_requests_as_delegated(requests,
406
0
                                                   REQUEST_IS_NOT_DELEGATED);
407
0
        netsnmp_set_request_error(cache->reqinfo, requests,     /* XXXWWW: should be index=0 */
408
0
                                  SNMP_ERR_GENERR);
409
0
        close_agentx_session(session, -1);
410
0
        netsnmp_free_delegated_cache(cache);
411
0
        netsnmp_delete_delegated_box(cache_box);
412
0
        return 0;
413
414
0
    case NETSNMP_CALLBACK_OP_RESEND:
415
0
        DEBUGMSGTL(("agentx/master", "resend on session %8p req=0x%x\n",
416
0
                    session, (unsigned)reqid));
417
0
        return 0;
418
419
0
    case NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE:
420
        /*
421
         * This session is alive 
422
         */
423
0
        CLEAR_SNMP_STRIKE_FLAGS(session->flags);
424
0
        break;
425
0
    default:
426
0
        snmp_log(LOG_ERR, "Unknown operation %d in agentx_got_response\n",
427
0
                 operation);
428
0
        netsnmp_free_delegated_cache(cache);
429
0
        netsnmp_delete_delegated_box(cache_box);
430
0
        return 0;
431
0
    }
432
433
0
    DEBUGMSGTL(("agentx/master", "got response errstat=%ld, (req=0x%x,trans="
434
0
                "0x%x,sess=0x%x)\n",
435
0
                pdu->errstat, (unsigned)pdu->reqid, (unsigned)pdu->transid,
436
0
    (unsigned)pdu->sessid));
437
438
0
    if (pdu->errstat != AGENTX_ERR_NOERROR) {
439
        /* [RFC 2471 - 7.2.5.2.]
440
         *
441
         *   1) For any received AgentX response PDU, if res.error is
442
         *      not `noError', the SNMP response PDU's error code is
443
         *      set to this value.  If res.error contains an AgentX
444
         *      specific value (e.g.  `parseError'), the SNMP response
445
         *      PDU's error code is set to a value of genErr instead.
446
         *      Also, the SNMP response PDU's error index is set to
447
         *      the index of the variable binding corresponding to the
448
         *      failed VarBind in the subagent's AgentX response PDU.
449
         *
450
         *      All other AgentX response PDUs received due to
451
         *      processing this SNMP request are ignored.  Processing
452
         *      is complete; the SNMP Response PDU is ready to be sent
453
         *      (see section 7.2.6, "Sending the SNMP Response-PDU").
454
         */
455
0
        int err;
456
457
0
        DEBUGMSGTL(("agentx/master",
458
0
                    "agentx_got_response() error branch\n"));
459
460
0
        switch (pdu->errstat) {
461
0
        case AGENTX_ERR_PARSE_FAILED:
462
0
        case AGENTX_ERR_REQUEST_DENIED:
463
0
        case AGENTX_ERR_PROCESSING_ERROR:
464
0
            err = SNMP_ERR_GENERR;
465
0
            break;
466
0
        default:
467
0
            err = pdu->errstat;
468
0
        }
469
470
0
        ret = 0;
471
0
        for (request = requests, i = 1; request;
472
0
             request = request->next, i++) {
473
0
            if (i == pdu->errindex) {
474
                /*
475
                 * Mark this varbind as the one generating the error.
476
                 * Note that the AgentX errindex may not match the
477
                 * position in the original SNMP PDU (request->index)
478
                 */
479
0
                netsnmp_set_request_error(cache->reqinfo, request,
480
0
                                          err);
481
0
                ret = 1;
482
0
            }
483
0
            request->delegated = REQUEST_IS_NOT_DELEGATED;
484
0
        }
485
0
        if (!ret) {
486
            /*
487
             * ack, unknown, mark the first one
488
             */
489
0
            netsnmp_set_request_error(cache->reqinfo, requests,
490
0
                                      SNMP_ERR_GENERR);
491
0
        }
492
0
        netsnmp_free_delegated_cache(cache);
493
0
        netsnmp_delete_delegated_box(cache_box);
494
0
        DEBUGMSGTL(("agentx/master", "end error branch\n"));
495
0
        return 1;
496
0
    } else if (cache->reqinfo->mode == MODE_GET ||
497
0
               cache->reqinfo->mode == MODE_GETNEXT ||
498
0
               cache->reqinfo->mode == MODE_GETBULK) {
499
        /*
500
         * Replace varbinds for data request types, but not SETs.  
501
         */
502
0
        DEBUGMSGTL(("agentx/master",
503
0
                    "agentx_got_response() beginning...\n"));
504
0
        for (var = pdu->variables, request = requests; request && var;
505
0
             request = request->next, var = var->next_variable) {
506
            /*
507
             * Otherwise, process successful requests
508
             */
509
0
            DEBUGMSGTL(("agentx/master",
510
0
                        "  handle_agentx_response: processing: "));
511
0
            DEBUGMSGOID(("agentx/master", var->name, var->name_length));
512
0
            DEBUGMSG(("agentx/master", "\n"));
513
0
            if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_VERBOSE)) {
514
0
                DEBUGMSGTL(("agentx/master", "    >> "));
515
0
                DEBUGMSGVAR(("agentx/master", var));
516
0
                DEBUGMSG(("agentx/master", "\n"));
517
0
            }
518
519
            /*
520
             * update the oid in the original request 
521
             */
522
0
            if (var->type != SNMP_ENDOFMIBVIEW) {
523
0
                snmp_set_var_typed_value(request->requestvb, var->type,
524
0
                                         var->val.string, var->val_len);
525
0
                snmp_set_var_objid(request->requestvb, var->name,
526
0
                                   var->name_length);
527
0
            }
528
0
            request->delegated = REQUEST_IS_NOT_DELEGATED;
529
0
        }
530
531
0
        if (request || var) {
532
            /*
533
             * ack, this is bad.  The # of varbinds don't match and
534
             * there is no way to fix the problem 
535
             */
536
0
            snmp_log(LOG_ERR,
537
0
                     "response to agentx request illegal.  bailing out.\n");
538
0
            netsnmp_set_request_error(cache->reqinfo, requests,
539
0
                                      SNMP_ERR_GENERR);
540
0
        }
541
542
0
        if (cache->reqinfo->mode == MODE_GETBULK)
543
0
            netsnmp_bulk_to_next_fix_requests(requests);
544
0
    } else {
545
        /*
546
         * mark set requests as handled 
547
         */
548
0
        for (request = requests; request; request = request->next) {
549
0
            request->delegated = REQUEST_IS_NOT_DELEGATED;
550
0
        }
551
0
    }
552
0
    DEBUGMSGTL(("agentx/master",
553
0
                "handle_agentx_response() finishing...\n"));
554
0
    netsnmp_free_delegated_cache(cache);
555
0
    netsnmp_delete_delegated_box(cache_box);
556
0
    return 1;
557
0
}
558
559
560
/*
561
 *
562
 * AgentX State diagram.  [mode] = internal mode it's mapped from:
563
 *
564
 * TESTSET -success-> COMMIT -success-> CLEANUP
565
 * [RESERVE1]         [ACTION]          [COMMIT]
566
 *    |                 |
567
 *    |                 \--failure-> UNDO
568
 *    |                              [UNDO]
569
 *    |
570
 *     --failure-> CLEANUP
571
 *                 [FREE]
572
 */
573
int
574
agentx_master_handler(netsnmp_mib_handler *handler,
575
                      netsnmp_handler_registration *reginfo,
576
                      netsnmp_agent_request_info *reqinfo,
577
                      netsnmp_request_info *requests)
578
0
{
579
0
    netsnmp_session *ax_session = (netsnmp_session *) handler->myvoid;
580
0
    netsnmp_request_info *request = requests;
581
0
    netsnmp_pdu    *pdu;
582
0
    void           *cb_data;
583
0
    void           *cb_data_box;
584
0
    snmp_callback  callback;
585
0
    int            result;
586
587
0
    DEBUGMSGTL(("agentx/master",
588
0
                "agentx master handler starting, mode = 0x%02x\n",
589
0
                reqinfo->mode));
590
591
0
    if (!ax_session) {
592
0
        netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_GENERR);
593
0
        return SNMP_ERR_NOERROR;
594
0
    }        
595
596
    /*
597
     * build a new pdu based on the pdu type coming in 
598
     */
599
0
    switch (reqinfo->mode) {
600
0
    case MODE_GET:
601
0
        pdu = snmp_pdu_create(AGENTX_MSG_GET);
602
0
        break;
603
604
0
    case MODE_GETNEXT:
605
0
        pdu = snmp_pdu_create(AGENTX_MSG_GETNEXT);
606
0
        break;
607
608
0
    case MODE_GETBULK:         /* WWWXXX */
609
0
        pdu = snmp_pdu_create(AGENTX_MSG_GETNEXT);
610
0
        break;
611
612
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
613
0
    case MODE_SET_RESERVE1:
614
0
        pdu = snmp_pdu_create(AGENTX_MSG_TESTSET);
615
0
        break;
616
617
0
    case MODE_SET_RESERVE2:
618
        /*
619
         * don't do anything here for AgentX.  Assume all is fine
620
         * and go on since AgentX only has one test phase. 
621
         */
622
0
        return SNMP_ERR_NOERROR;
623
624
0
    case MODE_SET_ACTION:
625
0
        pdu = snmp_pdu_create(AGENTX_MSG_COMMITSET);
626
0
        break;
627
628
0
    case MODE_SET_UNDO:
629
0
        pdu = snmp_pdu_create(AGENTX_MSG_UNDOSET);
630
0
        break;
631
632
0
    case MODE_SET_COMMIT:
633
0
    case MODE_SET_FREE:
634
0
        pdu = snmp_pdu_create(AGENTX_MSG_CLEANUPSET);
635
0
        break;
636
0
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
637
638
0
    default:
639
0
        snmp_log(LOG_WARNING,
640
0
                 "unsupported mode for agentx/master called\n");
641
0
        return SNMP_ERR_NOERROR;
642
0
    }
643
644
0
    if (!pdu) {
645
0
        netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_GENERR);
646
0
        return SNMP_ERR_NOERROR;
647
0
    }
648
649
0
    pdu->version = AGENTX_VERSION_1;
650
0
    pdu->reqid = snmp_get_next_transid();
651
0
    pdu->transid = reqinfo->asp->pdu->transid;
652
0
    pdu->sessid = ax_session->subsession->sessid;
653
0
    if (reginfo->contextName) {
654
0
        pdu->community = (u_char *) strdup(reginfo->contextName);
655
0
        pdu->community_len = strlen(reginfo->contextName);
656
0
        pdu->flags |= AGENTX_MSG_FLAG_NON_DEFAULT_CONTEXT;
657
0
    }
658
0
    if (ax_session->subsession->flags & AGENTX_MSG_FLAG_NETWORK_BYTE_ORDER)
659
0
        pdu->flags |= AGENTX_MSG_FLAG_NETWORK_BYTE_ORDER;
660
661
0
    while (request) {
662
663
0
        size_t nlen = request->requestvb->name_length;
664
0
        oid   *nptr = request->requestvb->name;
665
        
666
0
        DEBUGMSGTL(("agentx/master","request for variable ("));
667
0
        DEBUGMSGOID(("agentx/master", nptr, nlen));
668
0
        DEBUGMSG(("agentx/master", ")\n"));
669
        
670
        /*
671
         * loop through all the requests and create agentx ones out of them 
672
         */
673
674
0
        if (reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GETBULK) {
675
676
0
            if (snmp_oid_compare(nptr, nlen, request->subtree->start_a,
677
0
                                 request->subtree->start_len) < 0) {
678
0
                DEBUGMSGTL(("agentx/master","inexact request preceding region ("));
679
0
                DEBUGMSGOID(("agentx/master", request->subtree->start_a,
680
0
                             request->subtree->start_len));
681
0
                DEBUGMSG(("agentx/master", ")\n"));
682
0
                nptr = request->subtree->start_a;
683
0
                nlen = request->subtree->start_len;
684
0
                request->inclusive = 1;
685
0
            }
686
687
0
            if (request->inclusive) {
688
0
                DEBUGMSGTL(("agentx/master", "INCLUSIVE varbind "));
689
0
                DEBUGMSGOID(("agentx/master", nptr, nlen));
690
0
                DEBUGMSG(("agentx/master", " scoped to "));
691
0
                DEBUGMSGOID(("agentx/master", request->range_end,
692
0
                             request->range_end_len));
693
0
                DEBUGMSG(("agentx/master", "\n"));
694
0
                snmp_pdu_add_variable(pdu, nptr, nlen, ASN_PRIV_INCL_RANGE,
695
0
                                      (u_char *) request->range_end,
696
0
                                      request->range_end_len *
697
0
                                      sizeof(oid));
698
0
                request->inclusive = 0;
699
0
            } else {
700
0
                DEBUGMSGTL(("agentx/master", "EXCLUSIVE varbind "));
701
0
                DEBUGMSGOID(("agentx/master", nptr, nlen));
702
0
                DEBUGMSG(("agentx/master", " scoped to "));
703
0
                DEBUGMSGOID(("agentx/master", request->range_end,
704
0
                             request->range_end_len));
705
0
                DEBUGMSG(("agentx/master", "\n"));
706
0
                snmp_pdu_add_variable(pdu, nptr, nlen, ASN_PRIV_EXCL_RANGE,
707
0
                                      (u_char *) request->range_end,
708
0
                                      request->range_end_len *
709
0
                                      sizeof(oid));
710
0
            }
711
0
        } else {
712
0
            snmp_pdu_add_variable(pdu, request->requestvb->name,
713
0
                                  request->requestvb->name_length,
714
0
                                  request->requestvb->type,
715
0
                                  request->requestvb->val.string,
716
0
                                  request->requestvb->val_len);
717
0
        }
718
719
        /*
720
         * mark the request as delayed 
721
         */
722
0
        if (pdu->command != AGENTX_MSG_CLEANUPSET)
723
0
            request->delegated = REQUEST_IS_DELEGATED;
724
0
        else
725
0
            request->delegated = REQUEST_IS_NOT_DELEGATED;
726
727
        /*
728
         * next... 
729
         */
730
0
        request = request->next;
731
0
    }
732
733
    /*
734
     * When the master sends a CleanupSet PDU, it will never get a response
735
     * back from the subagent. So we shouldn't allocate the
736
     * netsnmp_delegated_cache structure in this case, nor register a callback.
737
     */
738
0
    if (pdu->command != AGENTX_MSG_CLEANUPSET) {
739
0
        cb_data = netsnmp_create_delegated_cache(handler, reginfo,
740
0
                                                 reqinfo, requests,
741
0
                                                 (void *) ax_session);
742
0
        cb_data_box = netsnmp_create_delegated_box(cb_data);
743
0
        callback = agentx_got_response;
744
0
    } else {
745
0
        cb_data_box = NULL;
746
0
        callback = NULL;
747
0
    }
748
749
    /*
750
     * send the requests out.
751
     */
752
0
    DEBUGMSGTL(("agentx/master", "sending pdu (req=0x%x,trans=0x%x,sess=0x%x)\n",
753
0
                (unsigned)pdu->reqid, (unsigned)pdu->transid, (unsigned)pdu->sessid));
754
0
    result = snmp_async_send_cp(ax_session, pdu, callback, cb_data_box, 1);
755
0
    if (result == 0) {
756
0
        snmp_free_pdu(pdu);
757
0
    }
758
759
0
    return SNMP_ERR_NOERROR;
760
0
}