Coverage Report

Created: 2025-08-11 06:35

/src/net-snmp/agent/mibgroup/agentx/subagent.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *  AgentX sub-agent
3
 */
4
#include <net-snmp/net-snmp-config.h>
5
#include <net-snmp/net-snmp-features.h>
6
7
#include <sys/types.h>
8
#ifdef HAVE_STDLIB_H
9
#include <stdlib.h>
10
#endif
11
#ifdef TIME_WITH_SYS_TIME
12
# include <sys/time.h>
13
# include <time.h>
14
#else
15
# ifdef HAVE_SYS_TIME_H
16
#  include <sys/time.h>
17
# else
18
#  include <time.h>
19
# endif
20
#endif
21
#ifdef HAVE_SYS_SOCKET_H
22
#include <sys/socket.h>
23
#endif
24
#ifdef HAVE_STRING_H
25
#include <string.h>
26
#else
27
#include <strings.h>
28
#endif
29
#ifdef HAVE_NETINET_IN_H
30
#include <netinet/in.h>
31
#endif
32
33
#include <net-snmp/net-snmp-includes.h>
34
#include <net-snmp/agent/agent_index.h>
35
#include <net-snmp/agent/net-snmp-agent-includes.h>
36
#include <net-snmp/library/snmp_assert.h>
37
38
#include "snmpd.h"
39
#include "agent_global_vars.h"
40
#include "agentx/protocol.h"
41
#include "agentx/client.h"
42
#include "agentx/agentx_config.h"
43
#include <net-snmp/agent/agent_callbacks.h>
44
#include <net-snmp/agent/agent_trap.h>
45
#include <net-snmp/agent/sysORTable.h>
46
#include <net-snmp/agent/agent_sysORTable.h>
47
48
#include "subagent.h"
49
50
netsnmp_feature_child_of(agentx_subagent, agentx_all);
51
netsnmp_feature_child_of(agentx_enable_subagent, agentx_subagent);
52
53
netsnmp_feature_require(remove_trap_session);
54
55
#ifdef USING_AGENTX_SUBAGENT_MODULE
56
57
static SNMPCallback subagent_register_ping_alarm;
58
static SNMPAlarmCallback agentx_reopen_session;
59
void            agentx_register_callbacks(netsnmp_session * s);
60
void            agentx_unregister_callbacks(netsnmp_session * ss);
61
int             handle_subagent_response(int op, netsnmp_session * session,
62
                                         int reqid, netsnmp_pdu *pdu,
63
                                         void *magic);
64
#ifndef NETSNMP_NO_WRITE_SUPPORT
65
int             handle_subagent_set_response(int op,
66
                                             netsnmp_session * session,
67
                                             int reqid, netsnmp_pdu *pdu,
68
                                             void *magic);
69
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
70
void            subagent_startup_callback(unsigned int clientreg,
71
                                          void *clientarg);
72
int             subagent_open_master_session(void);
73
74
typedef struct _net_snmpsubagent_magic_s {
75
    int             original_command;
76
    netsnmp_session *session;
77
    netsnmp_variable_list *ovars;
78
} ns_subagent_magic;
79
80
struct agent_netsnmp_set_info {
81
    int             transID;
82
    int             mode;
83
    int             errstat;
84
    time_t          uptime;
85
    netsnmp_session *sess;
86
    netsnmp_variable_list *var_list;
87
88
    struct agent_netsnmp_set_info *next;
89
};
90
91
#ifndef NETSNMP_NO_WRITE_SUPPORT
92
static struct agent_netsnmp_set_info *Sets = NULL;
93
#endif
94
95
netsnmp_session *agentx_callback_sess = NULL;
96
97
int
98
subagent_startup(int majorID, int minorID,
99
                             void *serverarg, void *clientarg)
100
0
{
101
0
    DEBUGMSGTL(("agentx/subagent", "connecting to master...\n"));
102
    /*
103
     * if a valid ping interval has been defined, call agentx_reopen_session
104
     * to try to connect to master or setup a ping alarm if it couldn't
105
     * succeed. if no ping interval was set up, just try to connect once.
106
     */
107
0
    if (netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
108
0
                           NETSNMP_DS_AGENT_AGENTX_PING_INTERVAL) > 0)
109
0
        agentx_reopen_session(0, NULL);
110
0
    else {
111
0
        subagent_open_master_session();
112
0
    }
113
0
    return 0;
114
0
}
115
116
static void
117
subagent_init_callback_session(void)
118
0
{
119
0
    if (agentx_callback_sess == NULL) {
120
0
        agentx_callback_sess = netsnmp_callback_open(callback_master_num,
121
0
                                                     handle_subagent_response,
122
0
                                                     NULL, NULL);
123
0
        DEBUGMSGTL(("agentx/subagent", "subagent_init sess %p\n",
124
0
                    agentx_callback_sess));
125
0
    }
126
0
}
127
128
static int subagent_init_init = 0;
129
/**
130
 * init subagent callback (local) session and connect to master agent
131
 *
132
 * @returns 0 for success, !0 otherwise
133
 */
134
int
135
subagent_init(void)
136
0
{
137
0
    int rc = 0;
138
139
0
    DEBUGMSGTL(("agentx/subagent", "initializing....\n"));
140
141
0
    if (++subagent_init_init != 1)
142
0
        return 0;
143
144
0
    netsnmp_assert(netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
145
0
                                          NETSNMP_DS_AGENT_ROLE) == SUB_AGENT);
146
147
#ifndef NETSNMP_TRANSPORT_CALLBACK_DOMAIN
148
    snmp_log(LOG_WARNING,"AgentX subagent has been disabled because "
149
               "the callback transport is not available.\n");
150
    return -1;
151
#endif /* NETSNMP_TRANSPORT_CALLBACK_DOMAIN */
152
153
    /*
154
     * open (local) callback session
155
     */
156
0
    subagent_init_callback_session();
157
0
    if (NULL == agentx_callback_sess)
158
0
        return -1;
159
160
0
    snmp_register_callback(SNMP_CALLBACK_LIBRARY,
161
0
                           SNMP_CALLBACK_POST_READ_CONFIG,
162
0
                           subagent_startup, NULL);
163
164
0
    DEBUGMSGTL(("agentx/subagent", "initializing....  DONE\n"));
165
166
0
    return rc;
167
0
}
168
169
#ifndef NETSNMP_FEATURE_REMOVE_AGENTX_ENABLE_SUBAGENT
170
void
171
0
netsnmp_enable_subagent(void) {
172
0
    netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE,
173
0
                           SUB_AGENT);
174
0
}
175
#endif /* NETSNMP_FEATURE_REMOVE_AGENTX_ENABLE_SUBAGENT */
176
177
#ifndef NETSNMP_NO_WRITE_SUPPORT
178
struct agent_netsnmp_set_info *
179
save_set_vars(netsnmp_session * ss, netsnmp_pdu *pdu)
180
0
{
181
0
    struct agent_netsnmp_set_info *ptr;
182
183
0
    ptr = (struct agent_netsnmp_set_info *)
184
0
        malloc(sizeof(struct agent_netsnmp_set_info));
185
0
    if (ptr == NULL)
186
0
        return NULL;
187
188
    /*
189
     * Save the important information
190
     */
191
0
    ptr->transID = pdu->transid;
192
0
    ptr->sess = ss;
193
0
    ptr->mode = SNMP_MSG_INTERNAL_SET_RESERVE1;
194
0
    ptr->uptime = netsnmp_get_agent_uptime();
195
196
0
    ptr->var_list = snmp_clone_varbind(pdu->variables);
197
0
    if (ptr->var_list == NULL) {
198
0
        free(ptr);
199
0
        return NULL;
200
0
    }
201
202
0
    ptr->next = Sets;
203
0
    Sets = ptr;
204
205
0
    return ptr;
206
0
}
207
208
struct agent_netsnmp_set_info *
209
restore_set_vars(netsnmp_session * sess, netsnmp_pdu *pdu)
210
0
{
211
0
    struct agent_netsnmp_set_info *ptr;
212
213
0
    for (ptr = Sets; ptr != NULL; ptr = ptr->next)
214
0
        if (ptr->sess == sess && ptr->transID == pdu->transid)
215
0
            break;
216
217
0
    if (ptr == NULL || ptr->var_list == NULL)
218
0
        return NULL;
219
220
0
    pdu->variables = snmp_clone_varbind(ptr->var_list);
221
0
    if (pdu->variables == NULL)
222
0
        return NULL;
223
224
0
    return ptr;
225
0
}
226
227
228
void
229
free_set_vars(netsnmp_session * ss, netsnmp_pdu *pdu)
230
0
{
231
0
    struct agent_netsnmp_set_info *ptr, *prev = NULL;
232
233
0
    for (ptr = Sets; ptr != NULL; ptr = ptr->next) {
234
0
        if (ptr->sess == ss && ptr->transID == pdu->transid) {
235
0
            if (prev)
236
0
                prev->next = ptr->next;
237
0
            else
238
0
                Sets = ptr->next;
239
0
            snmp_free_varbind(ptr->var_list);
240
0
            free(ptr);
241
0
            return;
242
0
        }
243
0
        prev = ptr;
244
0
    }
245
0
}
246
247
static void
248
send_agentx_error(netsnmp_session *session, netsnmp_pdu *pdu, int errstat, int errindex)
249
0
{
250
0
    pdu = snmp_clone_pdu(pdu);
251
0
    if (!pdu)
252
0
        return;
253
0
    pdu->command   = AGENTX_MSG_RESPONSE;
254
0
    pdu->version   = session->version;
255
0
    pdu->errstat   = errstat;
256
0
    pdu->errindex  = errindex;
257
0
    snmp_free_varbind(pdu->variables);
258
0
    pdu->variables = NULL;
259
260
0
    DEBUGMSGTL(("agentx/subagent", "Sending AgentX response error stat %d idx %d\n",
261
0
             errstat, errindex));
262
0
    if (!snmp_send(session, pdu)) {
263
0
        snmp_free_pdu(pdu);
264
0
    }
265
0
}
266
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
267
268
int
269
handle_agentx_packet(int operation, netsnmp_session * session, int reqid,
270
                     netsnmp_pdu *pdu, void *magic)
271
0
{
272
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
273
0
    struct agent_netsnmp_set_info *asi = NULL;
274
0
#endif
275
0
    snmp_callback   mycallback;
276
0
    netsnmp_pdu    *internal_pdu = NULL;
277
0
    void           *retmagic = NULL;
278
0
    ns_subagent_magic *smagic = NULL;
279
0
    int             result;
280
281
0
    if (operation == NETSNMP_CALLBACK_OP_DISCONNECT) {
282
0
        struct synch_state *state = (struct synch_state *) magic;
283
0
        int             period =
284
0
            netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
285
0
                               NETSNMP_DS_AGENT_AGENTX_PING_INTERVAL);
286
0
        DEBUGMSGTL(("agentx/subagent",
287
0
                    "transport disconnect indication\n"));
288
289
        /*
290
         * deal with existing session. This happened if agentx sends
291
         * a message to the master, but the master goes away before
292
         * a response is sent. agentx will spin in snmp_synch_response_cb,
293
         * waiting for a response. At the very least, the waiting
294
         * flag must be set to break that loop. The rest is copied
295
         * from disconnect handling in snmp_sync_input.
296
         */
297
0
        if(state) {
298
0
            state->waiting = 0;
299
0
            state->pdu = NULL;
300
0
            state->status = STAT_ERROR;
301
0
            session->s_snmp_errno = SNMPERR_ABORT;
302
0
            SET_SNMP_ERROR(SNMPERR_ABORT);
303
0
        }
304
305
        /*
306
         * Deregister the ping alarm, if any, and invalidate all other
307
         * references to this session.  
308
         */
309
0
        if (session->securityModel != SNMP_DEFAULT_SECMODEL) {
310
0
            snmp_alarm_unregister(session->securityModel);
311
0
        }
312
0
        snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
313
0
                            SNMPD_CALLBACK_INDEX_STOP, (void *) session);
314
0
        agentx_unregister_callbacks(session);
315
0
        remove_trap_session(session);
316
0
        register_mib_detach();
317
0
        main_session = NULL;
318
0
        if (period != 0) {
319
            /*
320
             * Pings are enabled, so periodically attempt to re-establish contact 
321
             * with the master agent.  Don't worry about the handle,
322
             * agentx_reopen_session unregisters itself if it succeeds in talking 
323
             * to the master agent.  
324
             */
325
0
            snmp_alarm_register(period, SA_REPEAT, agentx_reopen_session, NULL);
326
0
            snmp_log(LOG_INFO, "AgentX master disconnected us, reconnecting in %d\n", period);
327
0
        } else {
328
0
            snmp_log(LOG_INFO, "AgentX master disconnected us, not reconnecting\n");
329
0
        }
330
0
        return 0;
331
0
    } else if (operation != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
332
0
        DEBUGMSGTL(("agentx/subagent", "unexpected callback op %d\n",
333
0
                    operation));
334
0
        return 1;
335
0
    }
336
337
    /*
338
     * ok, we have a pdu from the net. Modify as needed 
339
     */
340
341
0
    DEBUGMSGTL(("agentx/subagent", "handling AgentX request (req=0x%x,trans="
342
0
                "0x%x,sess=0x%x)\n", (unsigned)pdu->reqid,
343
0
    (unsigned)pdu->transid, (unsigned)pdu->sessid));
344
0
    pdu->version = AGENTX_VERSION_1;
345
0
    pdu->flags |= UCD_MSG_FLAG_ALWAYS_IN_VIEW;
346
347
    /* Master agent is alive, no need to ping */
348
0
    if (session->securityModel != SNMP_DEFAULT_SECMODEL) {
349
0
        snmp_alarm_reset(session->securityModel);
350
0
    }
351
352
0
    if (pdu->command == AGENTX_MSG_GET
353
0
        || pdu->command == AGENTX_MSG_GETNEXT
354
0
        || pdu->command == AGENTX_MSG_GETBULK) {
355
0
        smagic = calloc(1, sizeof(ns_subagent_magic));
356
0
        if (smagic == NULL) {
357
0
            DEBUGMSGTL(("agentx/subagent", "couldn't malloc() smagic\n"));
358
            /* would like to send_agentx_error(), but it needs memory too */
359
0
            return 1;
360
0
        }
361
0
        smagic->original_command = pdu->command;
362
0
        smagic->session = session;
363
0
        smagic->ovars = NULL;
364
0
        retmagic = (void *) smagic;
365
0
    }
366
367
0
    switch (pdu->command) {
368
0
    case AGENTX_MSG_GET:
369
0
        DEBUGMSGTL(("agentx/subagent", "  -> get\n"));
370
0
        pdu->command = SNMP_MSG_GET;
371
0
        mycallback = handle_subagent_response;
372
0
        break;
373
374
0
    case AGENTX_MSG_GETNEXT:
375
0
        DEBUGMSGTL(("agentx/subagent", "  -> getnext\n"));
376
0
        pdu->command = SNMP_MSG_GETNEXT;
377
378
        /*
379
         * We have to save a copy of the original variable list here because
380
         * if the master agent has requested scoping for some of the varbinds
381
         * that information is stored there.  
382
         */
383
384
0
        smagic->ovars = snmp_clone_varbind(pdu->variables);
385
0
        DEBUGMSGTL(("agentx/subagent", "saved variables\n"));
386
0
        mycallback = handle_subagent_response;
387
0
        break;
388
389
0
    case AGENTX_MSG_GETBULK:
390
        /*
391
         * WWWXXX 
392
         */
393
0
        DEBUGMSGTL(("agentx/subagent", "  -> getbulk\n"));
394
0
        pdu->command = SNMP_MSG_GETBULK;
395
396
        /*
397
         * We have to save a copy of the original variable list here because
398
         * if the master agent has requested scoping for some of the varbinds
399
         * that information is stored there.  
400
         */
401
402
0
        smagic->ovars = snmp_clone_varbind(pdu->variables);
403
0
        DEBUGMSGTL(("agentx/subagent", "saved variables at %p\n",
404
0
                    smagic->ovars));
405
0
        mycallback = handle_subagent_response;
406
0
        break;
407
408
0
    case AGENTX_MSG_RESPONSE:
409
0
        SNMP_FREE(smagic);
410
0
        DEBUGMSGTL(("agentx/subagent", "  -> response\n"));
411
0
        return 1;
412
413
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
414
0
    case AGENTX_MSG_TESTSET:
415
        /*
416
         * XXXWWW we have to map this twice to both RESERVE1 and RESERVE2 
417
         */
418
0
        DEBUGMSGTL(("agentx/subagent", "  -> testset\n"));
419
0
        asi = save_set_vars(session, pdu);
420
0
        if (asi == NULL) {
421
0
            SNMP_FREE(smagic);
422
0
            snmp_log(LOG_WARNING, "save_set_vars() failed\n");
423
0
            send_agentx_error(session, pdu, AGENTX_ERR_PARSE_FAILED, 0);
424
0
            return 1;
425
0
        }
426
0
        asi->mode = pdu->command = SNMP_MSG_INTERNAL_SET_RESERVE1;
427
0
        mycallback = handle_subagent_set_response;
428
0
        retmagic = asi;
429
0
        break;
430
431
0
    case AGENTX_MSG_COMMITSET:
432
0
        DEBUGMSGTL(("agentx/subagent", "  -> commitset\n"));
433
0
        asi = restore_set_vars(session, pdu);
434
0
        if (asi == NULL) {
435
0
            SNMP_FREE(smagic);
436
0
            snmp_log(LOG_WARNING, "restore_set_vars() failed\n");
437
0
            send_agentx_error(session, pdu, AGENTX_ERR_PROCESSING_ERROR, 0);
438
0
            return 1;
439
0
        }
440
0
        if (asi->mode != SNMP_MSG_INTERNAL_SET_RESERVE2) {
441
0
            SNMP_FREE(smagic);
442
0
            snmp_log(LOG_WARNING,
443
0
                     "dropping bad AgentX request (wrong mode %d)\n",
444
0
                     asi->mode);
445
0
            send_agentx_error(session, pdu, AGENTX_ERR_PROCESSING_ERROR, 0);
446
0
            return 1;
447
0
        }
448
0
        asi->mode = pdu->command = SNMP_MSG_INTERNAL_SET_ACTION;
449
0
        mycallback = handle_subagent_set_response;
450
0
        retmagic = asi;
451
0
        break;
452
453
0
    case AGENTX_MSG_CLEANUPSET:
454
0
        DEBUGMSGTL(("agentx/subagent", "  -> cleanupset\n"));
455
0
        asi = restore_set_vars(session, pdu);
456
0
        if (asi == NULL) {
457
0
            SNMP_FREE(smagic);
458
0
            snmp_log(LOG_WARNING, "restore_set_vars() failed\n");
459
0
            send_agentx_error(session, pdu, AGENTX_ERR_PROCESSING_ERROR, 0);
460
0
            return 1;
461
0
        }
462
0
        if (asi->mode == SNMP_MSG_INTERNAL_SET_RESERVE1 ||
463
0
            asi->mode == SNMP_MSG_INTERNAL_SET_RESERVE2) {
464
0
            asi->mode = pdu->command = SNMP_MSG_INTERNAL_SET_FREE;
465
0
        } else if (asi->mode == SNMP_MSG_INTERNAL_SET_ACTION) {
466
0
            asi->mode = pdu->command = SNMP_MSG_INTERNAL_SET_COMMIT;
467
0
        } else {
468
0
            snmp_log(LOG_WARNING,
469
0
                     "dropping bad AgentX request (wrong mode %d)\n",
470
0
                     asi->mode);
471
0
            SNMP_FREE(retmagic);
472
0
            return 1;
473
0
        }
474
0
        mycallback = handle_subagent_set_response;
475
0
        retmagic = asi;
476
0
        break;
477
478
0
    case AGENTX_MSG_UNDOSET:
479
0
        DEBUGMSGTL(("agentx/subagent", "  -> undoset\n"));
480
0
        asi = restore_set_vars(session, pdu);
481
0
        if (asi == NULL) {
482
0
            SNMP_FREE(smagic);
483
0
            snmp_log(LOG_WARNING, "restore_set_vars() failed\n");
484
0
            send_agentx_error(session, pdu, AGENTX_ERR_PROCESSING_ERROR, 0);
485
0
            return 1;
486
0
        }
487
0
        asi->mode = pdu->command = SNMP_MSG_INTERNAL_SET_UNDO;
488
0
        mycallback = handle_subagent_set_response;
489
0
        retmagic = asi;
490
0
        break;
491
0
#endif /* !NETSNMP_NO_WRITE_SUPPORT */ 
492
493
0
    default:
494
0
        SNMP_FREE(smagic);
495
0
        DEBUGMSGTL(("agentx/subagent", "  -> unknown command %d (%02x)\n",
496
0
                    pdu->command, pdu->command));
497
0
        return 0;
498
0
    }
499
500
    /*
501
     * submit the pdu to the internal handler 
502
     */
503
504
    /*
505
     * We have to clone the PDU here, because when we return from this
506
     * callback, sess_process_packet will free(pdu), but this call also
507
     * free()s its argument PDU.  
508
     */
509
510
0
    internal_pdu = snmp_clone_pdu(pdu);
511
0
    if (!internal_pdu) {
512
0
        free(smagic);
513
0
        return 1;
514
0
    }
515
0
    free(internal_pdu->contextName);
516
0
    internal_pdu->contextName = (char *) internal_pdu->community;
517
0
    internal_pdu->contextNameLen = internal_pdu->community_len;
518
0
    internal_pdu->community = NULL;
519
0
    internal_pdu->community_len = 0;
520
0
    result = snmp_async_send(agentx_callback_sess, internal_pdu, mycallback,
521
0
                    retmagic);
522
0
    if (result == 0) {
523
0
        snmp_free_pdu( internal_pdu );
524
0
    }
525
0
    return 1;
526
0
}
527
528
static int
529
_invalid_op_and_magic(int op, ns_subagent_magic *smagic)
530
0
{
531
0
    int invalid = 0;
532
533
0
    if (smagic && (snmp_sess_pointer(smagic->session) == NULL ||
534
0
        op == NETSNMP_CALLBACK_OP_TIMED_OUT)) {
535
0
        if (smagic->ovars != NULL) {
536
0
            snmp_free_varbind(smagic->ovars);
537
0
        }
538
0
        free(smagic);
539
0
        invalid = 1;
540
0
    }
541
542
0
    if (op != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE || smagic == NULL)
543
0
        invalid = 1;
544
545
0
    return invalid;
546
0
}
547
548
int
549
handle_subagent_response(int op, netsnmp_session * session, int reqid,
550
                         netsnmp_pdu *pdu, void *magic)
551
0
{
552
0
    ns_subagent_magic *smagic = (ns_subagent_magic *) magic;
553
0
    netsnmp_variable_list *u = NULL, *v = NULL;
554
0
    int             rc = 0;
555
556
0
    if (_invalid_op_and_magic(op, magic)) {
557
0
        return 1;
558
0
    }
559
560
0
    pdu = snmp_clone_pdu(pdu);
561
0
    if (!pdu)
562
0
        return 1;
563
0
    DEBUGMSGTL(("agentx/subagent",
564
0
                "handling AgentX response (cmd 0x%02x orig_cmd 0x%02x)"
565
0
                " (req=0x%x,trans=0x%x,sess=0x%x)\n",
566
0
                pdu->command, smagic->original_command,
567
0
                (unsigned)pdu->reqid, (unsigned)pdu->transid,
568
0
                (unsigned)pdu->sessid));
569
570
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
571
0
    if (pdu->command == SNMP_MSG_INTERNAL_SET_FREE ||
572
0
        pdu->command == SNMP_MSG_INTERNAL_SET_UNDO ||
573
0
        pdu->command == SNMP_MSG_INTERNAL_SET_COMMIT) {
574
0
        free_set_vars(smagic->session, pdu);
575
0
    }
576
0
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
577
578
0
    if (smagic->original_command == AGENTX_MSG_GETNEXT) {
579
0
        DEBUGMSGTL(("agentx/subagent",
580
0
                    "do getNext scope processing %p %p\n", smagic->ovars,
581
0
                    pdu->variables));
582
0
        for (u = smagic->ovars, v = pdu->variables; u != NULL && v != NULL;
583
0
             u = u->next_variable, v = v->next_variable) {
584
0
            if (snmp_oid_compare
585
0
                (u->val.objid, u->val_len / sizeof(oid), nullOid,
586
0
                 nullOidLen/sizeof(oid)) != 0) {
587
                /*
588
                 * The master agent requested scoping for this variable.  
589
                 */
590
0
                rc = snmp_oid_compare(v->name, v->name_length,
591
0
                                      u->val.objid,
592
0
                                      u->val_len / sizeof(oid));
593
0
                DEBUGMSGTL(("agentx/subagent", "result "));
594
0
                DEBUGMSGOID(("agentx/subagent", v->name, v->name_length));
595
0
                DEBUGMSG(("agentx/subagent", " scope to "));
596
0
                DEBUGMSGOID(("agentx/subagent",
597
0
                             u->val.objid, u->val_len / sizeof(oid)));
598
0
                DEBUGMSG(("agentx/subagent", " result %d\n", rc));
599
600
0
                if (rc >= 0) {
601
                    /*
602
                     * The varbind is out of scope.  From RFC2741, p. 66: "If
603
                     * the subagent cannot locate an appropriate variable,
604
                     * v.name is set to the starting OID, and the VarBind is
605
                     * set to `endOfMibView'".  
606
                     */
607
0
                    snmp_set_var_objid(v, u->name, u->name_length);
608
0
                    snmp_set_var_typed_value(v, SNMP_ENDOFMIBVIEW, NULL, 0);
609
0
                    DEBUGMSGTL(("agentx/subagent",
610
0
                                "scope violation -- return endOfMibView\n"));
611
0
                }
612
0
            } else {
613
0
                DEBUGMSGTL(("agentx/subagent", "unscoped var\n"));
614
0
            }
615
0
        }
616
0
    }
617
618
    /*
619
     * XXXJBPN: similar for GETBULK but the varbinds can get re-ordered I
620
     * think which makes it er more difficult.  
621
     */
622
623
0
    if (smagic->ovars != NULL) {
624
0
        snmp_free_varbind(smagic->ovars);
625
0
    }
626
627
0
    pdu->command = AGENTX_MSG_RESPONSE;
628
0
    pdu->version = smagic->session->version;
629
630
0
    if (!snmp_send(smagic->session, pdu)) {
631
0
        snmp_free_pdu(pdu);
632
0
    }
633
0
    DEBUGMSGTL(("agentx/subagent", "  FINISHED\n"));
634
0
    free(smagic);
635
0
    return 1;
636
0
}
637
638
#ifndef NETSNMP_NO_WRITE_SUPPORT
639
int
640
handle_subagent_set_response(int op, netsnmp_session * session, int reqid,
641
                             netsnmp_pdu *pdu, void *magic)
642
0
{
643
0
    netsnmp_session *retsess;
644
0
    struct agent_netsnmp_set_info *asi;
645
0
    int result;
646
647
0
    if (op != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE || magic == NULL) {
648
0
        return 1;
649
0
    }
650
651
0
    DEBUGMSGTL(("agentx/subagent",
652
0
                "handling agentx subagent set response (mode=%d,req=0x%x,"
653
0
                "trans=0x%x,sess=0x%x)\n",
654
0
                (unsigned)pdu->command, (unsigned)pdu->reqid,
655
0
    (unsigned)pdu->transid, (unsigned)pdu->sessid));
656
0
    pdu = snmp_clone_pdu(pdu);
657
0
    if (!pdu)
658
0
        return 1;
659
660
0
    asi = (struct agent_netsnmp_set_info *) magic;
661
0
    retsess = asi->sess;
662
0
    asi->errstat = pdu->errstat;
663
664
0
    if (asi->mode == SNMP_MSG_INTERNAL_SET_RESERVE1) {
665
        /*
666
         * reloop for RESERVE2 mode, an internal only agent mode 
667
         */
668
        /*
669
         * XXX: check exception statuses of reserve1 first 
670
         */
671
0
        if (!pdu->errstat) {
672
0
            asi->mode = pdu->command = SNMP_MSG_INTERNAL_SET_RESERVE2;
673
0
            result = snmp_async_send(agentx_callback_sess, pdu,
674
0
                            handle_subagent_set_response, asi);
675
0
            if (result == 0) {
676
0
                snmp_free_pdu( pdu );
677
0
            }
678
0
            DEBUGMSGTL(("agentx/subagent",
679
0
                        "  going from RESERVE1 -> RESERVE2\n"));
680
0
            return 1;
681
0
        }
682
0
    } else {
683
0
        if (asi->mode == SNMP_MSG_INTERNAL_SET_FREE ||
684
0
            asi->mode == SNMP_MSG_INTERNAL_SET_UNDO ||
685
0
            asi->mode == SNMP_MSG_INTERNAL_SET_COMMIT) {
686
0
            free_set_vars(retsess, pdu);
687
0
        }
688
0
        snmp_free_varbind(pdu->variables);
689
0
        pdu->variables = NULL;  /* the variables were added by us */
690
0
    }
691
692
0
    if (retsess && pdu) {
693
0
        pdu->command = AGENTX_MSG_RESPONSE;
694
0
        pdu->version = retsess->version;
695
696
0
        if (!snmp_send(retsess, pdu))
697
0
            snmp_free_pdu(pdu);
698
0
    } else if (pdu) {
699
0
        snmp_free_pdu(pdu);
700
0
    }
701
0
    DEBUGMSGTL(("agentx/subagent", "  FINISHED\n"));
702
0
    return 1;
703
0
}
704
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
705
706
707
int
708
agentx_registration_callback(int majorID, int minorID, void *serverarg,
709
                             void *clientarg)
710
0
{
711
0
    struct register_parameters *reg_parms =
712
0
        (struct register_parameters *) serverarg;
713
0
    netsnmp_session *agentx_ss = *(netsnmp_session **)clientarg;
714
715
0
    if (minorID == SNMPD_CALLBACK_REGISTER_OID)
716
0
        return agentx_register(agentx_ss,
717
0
                               reg_parms->name, reg_parms->namelen,
718
0
                               reg_parms->priority,
719
0
                               reg_parms->range_subid,
720
0
                               reg_parms->range_ubound, reg_parms->timeout,
721
0
                               reg_parms->flags,
722
0
                               reg_parms->contextName);
723
0
    else
724
0
        return agentx_unregister(agentx_ss,
725
0
                                 reg_parms->name, reg_parms->namelen,
726
0
                                 reg_parms->priority,
727
0
                                 reg_parms->range_subid,
728
0
                                 reg_parms->range_ubound,
729
0
                                 reg_parms->contextName);
730
0
}
731
732
733
static int
734
agentx_sysOR_callback(int majorID, int minorID, void *serverarg,
735
                      void *clientarg)
736
0
{
737
0
    const struct register_sysOR_parameters *reg_parms =
738
0
        (const struct register_sysOR_parameters *) serverarg;
739
0
    netsnmp_session *agentx_ss = *(netsnmp_session **)clientarg;
740
741
0
    if (minorID == SNMPD_CALLBACK_REG_SYSOR)
742
0
        return agentx_add_agentcaps(agentx_ss,
743
0
                                    reg_parms->name, reg_parms->namelen,
744
0
                                    reg_parms->descr);
745
0
    else
746
0
        return agentx_remove_agentcaps(agentx_ss,
747
0
                                       reg_parms->name,
748
0
                                       reg_parms->namelen);
749
0
}
750
751
752
static int
753
subagent_shutdown(int majorID, int minorID, void *serverarg, void *clientarg)
754
0
{
755
0
    netsnmp_session *thesession = *(netsnmp_session **)clientarg;
756
0
    DEBUGMSGTL(("agentx/subagent", "shutting down session....\n"));
757
0
    if (thesession == NULL) {
758
0
  DEBUGMSGTL(("agentx/subagent", "Empty session to shutdown\n"));
759
0
  main_session = NULL;
760
0
  return 0;
761
0
    }
762
0
    agentx_close_session(thesession, AGENTX_CLOSE_SHUTDOWN);
763
0
    if (main_session != NULL) {
764
0
        remove_trap_session(main_session);
765
0
        main_session = NULL;
766
0
    }
767
0
    snmp_close(thesession);
768
0
    DEBUGMSGTL(("agentx/subagent", "shut down finished.\n"));
769
770
0
    subagent_init_init = 0;
771
0
    return 1;
772
0
}
773
774
775
776
/*
777
 * Register all the "standard" AgentX callbacks for the given session.  
778
 */
779
780
void
781
agentx_register_callbacks(netsnmp_session * s)
782
0
{
783
0
    netsnmp_session *sess_p;
784
785
0
    DEBUGMSGTL(("agentx/subagent",
786
0
                "registering callbacks for session %p\n", s));
787
0
    sess_p = netsnmp_memdup(&s, sizeof(s));
788
0
    netsnmp_assert(sess_p);
789
0
    s->myvoid = sess_p;
790
0
    if (!sess_p)
791
0
        return;
792
0
    snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_SHUTDOWN,
793
0
                           subagent_shutdown, sess_p);
794
0
    snmp_register_callback(SNMP_CALLBACK_APPLICATION,
795
0
                           SNMPD_CALLBACK_REGISTER_OID,
796
0
                           agentx_registration_callback, sess_p);
797
0
    snmp_register_callback(SNMP_CALLBACK_APPLICATION,
798
0
                           SNMPD_CALLBACK_UNREGISTER_OID,
799
0
                           agentx_registration_callback, sess_p);
800
0
    snmp_register_callback(SNMP_CALLBACK_APPLICATION,
801
0
                           SNMPD_CALLBACK_REG_SYSOR,
802
0
                           agentx_sysOR_callback, sess_p);
803
0
    snmp_register_callback(SNMP_CALLBACK_APPLICATION,
804
0
                           SNMPD_CALLBACK_UNREG_SYSOR,
805
0
                           agentx_sysOR_callback, sess_p);
806
0
}
807
808
/*
809
 * Unregister all the callbacks associated with this session.  
810
 */
811
812
void
813
agentx_unregister_callbacks(netsnmp_session * ss)
814
0
{
815
0
    DEBUGMSGTL(("agentx/subagent",
816
0
                "unregistering callbacks for session %p\n", ss));
817
0
    snmp_unregister_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_SHUTDOWN,
818
0
                             subagent_shutdown, ss->myvoid, 1);
819
0
    snmp_unregister_callback(SNMP_CALLBACK_APPLICATION,
820
0
                             SNMPD_CALLBACK_REGISTER_OID,
821
0
                             agentx_registration_callback, ss->myvoid, 1);
822
0
    snmp_unregister_callback(SNMP_CALLBACK_APPLICATION,
823
0
                             SNMPD_CALLBACK_UNREGISTER_OID,
824
0
                             agentx_registration_callback, ss->myvoid, 1);
825
0
    snmp_unregister_callback(SNMP_CALLBACK_APPLICATION,
826
0
                             SNMPD_CALLBACK_REG_SYSOR,
827
0
                             agentx_sysOR_callback, ss->myvoid, 1);
828
0
    snmp_unregister_callback(SNMP_CALLBACK_APPLICATION,
829
0
                             SNMPD_CALLBACK_UNREG_SYSOR,
830
0
                             agentx_sysOR_callback, ss->myvoid, 1);
831
0
    SNMP_FREE(ss->myvoid);
832
0
}
833
834
/*
835
 * Open a session to the master agent.  
836
 */
837
int
838
subagent_open_master_session(void)
839
0
{
840
0
    netsnmp_transport *t;
841
0
    netsnmp_session sess;
842
0
    const char *agentx_socket;
843
844
0
    DEBUGMSGTL(("agentx/subagent", "opening session...\n"));
845
846
0
    if (main_session) {
847
0
        snmp_log(LOG_WARNING,
848
0
                 "AgentX session to master agent attempted to be re-opened.\n");
849
0
        return -1;
850
0
    }
851
852
0
    snmp_sess_init(&sess);
853
0
    sess.version = AGENTX_VERSION_1;
854
0
    sess.retries = SNMP_DEFAULT_RETRIES;
855
0
    sess.timeout = SNMP_DEFAULT_TIMEOUT;
856
0
    sess.flags |= SNMP_FLAGS_STREAM_SOCKET;
857
0
    sess.callback = handle_agentx_packet;
858
0
    sess.authenticator = NULL;
859
860
0
    agentx_socket = netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID,
861
0
                                          NETSNMP_DS_AGENT_X_SOCKET);
862
0
    t = netsnmp_transport_open_client("agentx", agentx_socket);
863
0
    if (t == NULL) {
864
        /*
865
         * Diagnose snmp_open errors with the input
866
         * netsnmp_session pointer.  
867
         */
868
0
        if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
869
0
                                    NETSNMP_DS_AGENT_NO_CONNECTION_WARNINGS)) {
870
0
            char buf[1024];
871
0
            snprintf(buf, sizeof(buf), "Warning: "
872
0
                     "Failed to connect to the agentx master agent (%s)",
873
0
                     agentx_socket ? agentx_socket : "[NIL]");
874
0
            if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
875
0
                                        NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) {
876
0
                netsnmp_sess_log_error(LOG_WARNING, buf, &sess);
877
0
            } else {
878
0
                snmp_sess_perror(buf, &sess);
879
0
            }
880
0
        }
881
0
        return -1;
882
0
    }
883
884
0
    main_session =
885
0
        snmp_add_full(&sess, t, NULL, agentx_parse, NULL, NULL,
886
0
                      agentx_realloc_build, agentx_check_packet, NULL);
887
888
0
    if (main_session == NULL) {
889
        /* snmp_add_full() frees 't' upon failure. */
890
0
        t = NULL;
891
0
        if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
892
0
                                    NETSNMP_DS_AGENT_NO_CONNECTION_WARNINGS)) {
893
0
            char buf[1024];
894
0
            snprintf(buf, sizeof(buf), "Error: "
895
0
                     "Failed to create the agentx master agent session (%s)",
896
0
                     agentx_socket);
897
0
            snmp_sess_perror(buf, &sess);
898
0
        }
899
0
        netsnmp_transport_free(t);
900
0
        return -1;
901
0
    }
902
903
    /*
904
     * I don't know why 1 is success instead of the usual 0 = noerr, 
905
     * but that's what the function returns.
906
     */
907
0
    if (1 != agentx_open_session(main_session)) {
908
0
        snmp_close(main_session);
909
0
        main_session = NULL;
910
0
        return -1;
911
0
    }
912
913
    /*
914
     * subagent_register_ping_alarm assumes that securityModel will
915
     *  be set to SNMP_DEFAULT_SECMODEL on new AgentX sessions.
916
     *  This field is then (ab)used to hold the alarm stash.
917
     *
918
     * Why is the securityModel field used for this purpose, I hear you ask.
919
     * Damn good question!   (See SVN revision 4886)
920
     */
921
0
    main_session->securityModel = SNMP_DEFAULT_SECMODEL;
922
923
0
    if (add_trap_session(main_session, AGENTX_MSG_NOTIFY, 1,
924
0
                         AGENTX_VERSION_1)) {
925
0
        DEBUGMSGTL(("agentx/subagent", " trap session registered OK\n"));
926
0
    } else {
927
0
        DEBUGMSGTL(("agentx/subagent",
928
0
                    "trap session registration failed\n"));
929
0
        snmp_close(main_session);
930
0
        main_session = NULL;
931
0
        return -1;
932
0
    }
933
934
0
    agentx_register_callbacks(main_session);
935
936
0
    snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
937
0
                        SNMPD_CALLBACK_INDEX_START, (void *) main_session);
938
939
0
    snmp_log(LOG_INFO, "NET-SNMP version %s AgentX subagent connected\n",
940
0
             netsnmp_get_version());
941
0
    DEBUGMSGTL(("agentx/subagent", "opening session...  DONE (%p)\n",
942
0
                main_session));
943
944
0
    return 0;
945
0
}
946
947
static void
948
agentx_reopen_sysORTable(const struct sysORTable* data, void* v)
949
0
{
950
0
    netsnmp_session *agentx_ss = (netsnmp_session *) v;
951
  
952
0
    agentx_add_agentcaps(agentx_ss, data->OR_oid, data->OR_oidlen,
953
0
                         data->OR_descr);
954
0
}
955
956
/*
957
 * Alarm callback function to open a session to the master agent.  If a
958
 * transport disconnection callback occurs, indicating that the master agent
959
 * has died (or there has been some strange communication problem), this
960
 * alarm is called repeatedly to try to re-open the connection.  
961
 */
962
963
void
964
agentx_reopen_session(unsigned int clientreg, void *clientarg)
965
0
{
966
0
    DEBUGMSGTL(("agentx/subagent", "agentx_reopen_session(%d) called\n",
967
0
                clientreg));
968
969
0
    if (subagent_open_master_session() == 0) {
970
        /*
971
         * Successful.  Delete the alarm handle if one exists.  
972
         */
973
0
        if (clientreg != 0) {
974
0
            snmp_alarm_unregister(clientreg);
975
0
        }
976
977
        /*
978
         * Reregister all our nodes.  
979
         */
980
0
        register_mib_reattach();
981
982
        /*
983
         * Reregister all our sysOREntries
984
         */
985
0
        netsnmp_sysORTable_foreach(&agentx_reopen_sysORTable, main_session);
986
987
        /*
988
         * Register a ping alarm (if need be).  
989
         */
990
0
        subagent_register_ping_alarm(0, 0, NULL, main_session);
991
0
    } else {
992
0
        if (clientreg == 0) {
993
            /*
994
             * Register a reattach alarm for later 
995
             */
996
0
            subagent_register_ping_alarm(0, 0, NULL, main_session);
997
0
        }
998
0
    }
999
0
}
1000
1001
/*
1002
 * If a valid session is passed in (through clientarg), register a
1003
 * ping handler to ping it frequently, else register an attempt to try
1004
 * and open it again later. 
1005
 */
1006
1007
static int
1008
subagent_register_ping_alarm(int majorID, int minorID,
1009
                             void *serverarg, void *clientarg)
1010
0
{
1011
1012
0
    netsnmp_session *ss = (netsnmp_session *) clientarg;
1013
0
    int             ping_interval =
1014
0
        netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
1015
0
                           NETSNMP_DS_AGENT_AGENTX_PING_INTERVAL);
1016
1017
0
    if (!ping_interval)         /* don't do anything if not setup properly */
1018
0
        return 0;
1019
1020
    /*
1021
     * register a ping alarm, if desired 
1022
     */
1023
0
    if (ss) {
1024
0
        if (ss->securityModel != SNMP_DEFAULT_SECMODEL) {
1025
0
            DEBUGMSGTL(("agentx/subagent",
1026
0
                        "unregister existing alarm %d\n",
1027
0
                        ss->securityModel));
1028
0
            snmp_alarm_unregister(ss->securityModel);
1029
0
        }
1030
1031
0
        DEBUGMSGTL(("agentx/subagent",
1032
0
                    "register ping alarm every %d seconds\n",
1033
0
                    ping_interval));
1034
        /*
1035
         * we re-use the securityModel parameter for an alarm stash,
1036
         * since agentx doesn't need it 
1037
         */
1038
0
        ss->securityModel = snmp_alarm_register(ping_interval, SA_REPEAT,
1039
0
                                                agentx_check_session, ss);
1040
0
    } else {
1041
        /*
1042
         * attempt to open it later instead 
1043
         */
1044
0
        DEBUGMSGTL(("agentx/subagent",
1045
0
                    "subagent not properly attached, postponing registration till later....\n"));
1046
0
        snmp_alarm_register(ping_interval, SA_REPEAT,
1047
0
                            agentx_reopen_session, NULL);
1048
0
    }
1049
0
    return 0;
1050
0
}
1051
1052
/*
1053
 * check a session validity for connectivity to the master agent.  If
1054
 * not functioning, close and start attempts to reopen the session 
1055
 */
1056
void
1057
agentx_check_session(unsigned int clientreg, void *clientarg)
1058
0
{
1059
0
    netsnmp_session *ss = (netsnmp_session *) clientarg;
1060
0
    if (!ss) {
1061
0
        if (clientreg)
1062
0
            snmp_alarm_unregister(clientreg);
1063
0
        return;
1064
0
    }
1065
0
    DEBUGMSGTL(("agentx/subagent", "checking status of session %p\n", ss));
1066
1067
0
    if (!agentx_send_ping(ss)) {
1068
0
        snmp_log(LOG_WARNING,
1069
0
                 "AgentX master agent failed to respond to ping.  Attempting to re-register.\n");
1070
        /*
1071
         * master agent disappeared?  Try and re-register.
1072
         * close first, just to be sure .
1073
         */
1074
0
        agentx_unregister_callbacks(ss);
1075
0
        agentx_close_session(ss, AGENTX_CLOSE_TIMEOUT);
1076
0
        snmp_alarm_unregister(clientreg);       /* delete ping alarm timer */
1077
0
        snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
1078
0
                            SNMPD_CALLBACK_INDEX_STOP, (void *) ss);
1079
0
        register_mib_detach();
1080
0
        if (main_session != NULL) {
1081
0
            remove_trap_session(ss);
1082
0
            snmp_close(main_session);
1083
            /*
1084
             * We need to remove the callbacks attached to the callback
1085
             * session because they have a magic callback data structure
1086
             * which includes a pointer to the main session
1087
             *    (which is no longer valid).
1088
             * 
1089
             * Given that the main session is not responsive anyway.
1090
             * it shouldn't matter if we lose some outstanding requests.
1091
             */
1092
0
            if (agentx_callback_sess != NULL ) {
1093
0
                snmp_close(agentx_callback_sess);
1094
0
                agentx_callback_sess = NULL;
1095
    
1096
0
                subagent_init_callback_session();
1097
0
            }
1098
0
            main_session = NULL;
1099
0
            agentx_reopen_session(0, NULL);
1100
0
        }
1101
0
        else {
1102
0
            snmp_close(main_session);
1103
0
            main_session = NULL;
1104
0
        }
1105
0
    } else {
1106
0
        DEBUGMSGTL(("agentx/subagent", "session %p responded to ping\n",
1107
0
                    ss));
1108
0
    }
1109
0
}
1110
1111
1112
#endif /* USING_AGENTX_SUBAGENT_MODULE */