Coverage Report

Created: 2026-07-16 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/net-snmp/agent/mibgroup/agentx/subagent.c
Line
Count
Source
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
        if (asi->mode != SNMP_MSG_INTERNAL_SET_ACTION) {
488
0
            SNMP_FREE(smagic);
489
0
            snmp_log(LOG_WARNING,
490
0
                     "dropping bad AgentX request (wrong mode %d)\n",
491
0
                     asi->mode);
492
0
            send_agentx_error(session, pdu, AGENTX_ERR_PROCESSING_ERROR, 0);
493
0
            return 1;
494
0
        }
495
0
        asi->mode = pdu->command = SNMP_MSG_INTERNAL_SET_UNDO;
496
0
        mycallback = handle_subagent_set_response;
497
0
        retmagic = asi;
498
0
        break;
499
0
#endif /* !NETSNMP_NO_WRITE_SUPPORT */ 
500
501
0
    default:
502
0
        SNMP_FREE(smagic);
503
0
        DEBUGMSGTL(("agentx/subagent", "  -> unknown command %d (%02x)\n",
504
0
                    pdu->command, pdu->command));
505
0
        return 0;
506
0
    }
507
508
    /*
509
     * submit the pdu to the internal handler 
510
     */
511
512
    /*
513
     * We have to clone the PDU here, because when we return from this
514
     * callback, sess_process_packet will free(pdu), but this call also
515
     * free()s its argument PDU.  
516
     */
517
518
0
    internal_pdu = snmp_clone_pdu(pdu);
519
0
    if (!internal_pdu) {
520
0
        free(smagic);
521
0
        return 1;
522
0
    }
523
0
    free(internal_pdu->contextName);
524
0
    internal_pdu->contextName = (char *) internal_pdu->community;
525
0
    internal_pdu->contextNameLen = internal_pdu->community_len;
526
0
    internal_pdu->community = NULL;
527
0
    internal_pdu->community_len = 0;
528
0
    result = snmp_async_send(agentx_callback_sess, internal_pdu, mycallback,
529
0
                    retmagic);
530
0
    if (result == 0) {
531
0
        snmp_free_pdu( internal_pdu );
532
0
    }
533
0
    return 1;
534
0
}
535
536
static int
537
_invalid_op_and_magic(int op, ns_subagent_magic *smagic)
538
0
{
539
0
    int invalid = 0;
540
541
0
    if (smagic && (snmp_sess_pointer(smagic->session) == NULL ||
542
0
        op == NETSNMP_CALLBACK_OP_TIMED_OUT)) {
543
0
        if (smagic->ovars != NULL) {
544
0
            snmp_free_varbind(smagic->ovars);
545
0
        }
546
0
        free(smagic);
547
0
        invalid = 1;
548
0
    }
549
550
0
    if (op != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE || smagic == NULL)
551
0
        invalid = 1;
552
553
0
    return invalid;
554
0
}
555
556
int
557
handle_subagent_response(int op, netsnmp_session * session, int reqid,
558
                         netsnmp_pdu *pdu, void *magic)
559
0
{
560
0
    ns_subagent_magic *smagic = (ns_subagent_magic *) magic;
561
0
    netsnmp_variable_list *u = NULL, *v = NULL;
562
0
    int             rc = 0;
563
564
0
    if (_invalid_op_and_magic(op, magic)) {
565
0
        return 1;
566
0
    }
567
568
0
    pdu = snmp_clone_pdu(pdu);
569
0
    if (!pdu)
570
0
        return 1;
571
0
    DEBUGMSGTL(("agentx/subagent",
572
0
                "handling AgentX response (cmd 0x%02x orig_cmd 0x%02x)"
573
0
                " (req=0x%x,trans=0x%x,sess=0x%x)\n",
574
0
                pdu->command, smagic->original_command,
575
0
                (unsigned)pdu->reqid, (unsigned)pdu->transid,
576
0
                (unsigned)pdu->sessid));
577
578
0
#ifndef NETSNMP_NO_WRITE_SUPPORT
579
0
    if (pdu->command == SNMP_MSG_INTERNAL_SET_FREE ||
580
0
        pdu->command == SNMP_MSG_INTERNAL_SET_UNDO ||
581
0
        pdu->command == SNMP_MSG_INTERNAL_SET_COMMIT) {
582
0
        free_set_vars(smagic->session, pdu);
583
0
    }
584
0
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
585
586
0
    if (smagic->original_command == AGENTX_MSG_GETNEXT) {
587
0
        DEBUGMSGTL(("agentx/subagent",
588
0
                    "do getNext scope processing %p %p\n", smagic->ovars,
589
0
                    pdu->variables));
590
0
        for (u = smagic->ovars, v = pdu->variables; u != NULL && v != NULL;
591
0
             u = u->next_variable, v = v->next_variable) {
592
0
            if (snmp_oid_compare
593
0
                (u->val.objid, u->val_len / sizeof(oid), nullOid,
594
0
                 nullOidLen/sizeof(oid)) != 0) {
595
                /*
596
                 * The master agent requested scoping for this variable.  
597
                 */
598
0
                rc = snmp_oid_compare(v->name, v->name_length,
599
0
                                      u->val.objid,
600
0
                                      u->val_len / sizeof(oid));
601
0
                DEBUGMSGTL(("agentx/subagent", "result "));
602
0
                DEBUGMSGOID(("agentx/subagent", v->name, v->name_length));
603
0
                DEBUGMSG(("agentx/subagent", " scope to "));
604
0
                DEBUGMSGOID(("agentx/subagent",
605
0
                             u->val.objid, u->val_len / sizeof(oid)));
606
0
                DEBUGMSG(("agentx/subagent", " result %d\n", rc));
607
608
0
                if (rc >= 0) {
609
                    /*
610
                     * The varbind is out of scope.  From RFC2741, p. 66: "If
611
                     * the subagent cannot locate an appropriate variable,
612
                     * v.name is set to the starting OID, and the VarBind is
613
                     * set to `endOfMibView'".  
614
                     */
615
0
                    snmp_set_var_objid(v, u->name, u->name_length);
616
0
                    snmp_set_var_typed_value(v, SNMP_ENDOFMIBVIEW, NULL, 0);
617
0
                    DEBUGMSGTL(("agentx/subagent",
618
0
                                "scope violation -- return endOfMibView\n"));
619
0
                }
620
0
            } else {
621
0
                DEBUGMSGTL(("agentx/subagent", "unscoped var\n"));
622
0
            }
623
0
        }
624
0
    }
625
626
    /*
627
     * XXXJBPN: similar for GETBULK but the varbinds can get re-ordered I
628
     * think which makes it er more difficult.  
629
     */
630
631
0
    if (smagic->ovars != NULL) {
632
0
        snmp_free_varbind(smagic->ovars);
633
0
    }
634
635
0
    pdu->command = AGENTX_MSG_RESPONSE;
636
0
    pdu->version = smagic->session->version;
637
638
0
    if (!snmp_send(smagic->session, pdu)) {
639
0
        snmp_free_pdu(pdu);
640
0
    }
641
0
    DEBUGMSGTL(("agentx/subagent", "  FINISHED\n"));
642
0
    free(smagic);
643
0
    return 1;
644
0
}
645
646
#ifndef NETSNMP_NO_WRITE_SUPPORT
647
int
648
handle_subagent_set_response(int op, netsnmp_session * session, int reqid,
649
                             netsnmp_pdu *pdu, void *magic)
650
0
{
651
0
    netsnmp_session *retsess;
652
0
    struct agent_netsnmp_set_info *asi;
653
0
    int result;
654
655
0
    if (op != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE || magic == NULL) {
656
0
        return 1;
657
0
    }
658
659
0
    DEBUGMSGTL(("agentx/subagent",
660
0
                "handling agentx subagent set response (mode=%d,req=0x%x,"
661
0
                "trans=0x%x,sess=0x%x)\n",
662
0
                (unsigned)pdu->command, (unsigned)pdu->reqid,
663
0
    (unsigned)pdu->transid, (unsigned)pdu->sessid));
664
0
    pdu = snmp_clone_pdu(pdu);
665
0
    if (!pdu)
666
0
        return 1;
667
668
0
    asi = (struct agent_netsnmp_set_info *) magic;
669
0
    retsess = asi->sess;
670
0
    asi->errstat = pdu->errstat;
671
672
0
    if (asi->mode == SNMP_MSG_INTERNAL_SET_RESERVE1) {
673
        /*
674
         * reloop for RESERVE2 mode, an internal only agent mode 
675
         */
676
        /*
677
         * XXX: check exception statuses of reserve1 first 
678
         */
679
0
        if (!pdu->errstat) {
680
0
            asi->mode = pdu->command = SNMP_MSG_INTERNAL_SET_RESERVE2;
681
0
            result = snmp_async_send(agentx_callback_sess, pdu,
682
0
                            handle_subagent_set_response, asi);
683
0
            if (result == 0) {
684
0
                snmp_free_pdu( pdu );
685
0
            }
686
0
            DEBUGMSGTL(("agentx/subagent",
687
0
                        "  going from RESERVE1 -> RESERVE2\n"));
688
0
            return 1;
689
0
        }
690
0
    } else {
691
0
        if (asi->mode == SNMP_MSG_INTERNAL_SET_FREE ||
692
0
            asi->mode == SNMP_MSG_INTERNAL_SET_UNDO ||
693
0
            asi->mode == SNMP_MSG_INTERNAL_SET_COMMIT) {
694
0
            free_set_vars(retsess, pdu);
695
0
        }
696
0
        snmp_free_varbind(pdu->variables);
697
0
        pdu->variables = NULL;  /* the variables were added by us */
698
0
    }
699
700
0
    if (retsess && pdu) {
701
0
        pdu->command = AGENTX_MSG_RESPONSE;
702
0
        pdu->version = retsess->version;
703
704
0
        if (!snmp_send(retsess, pdu))
705
0
            snmp_free_pdu(pdu);
706
0
    } else if (pdu) {
707
0
        snmp_free_pdu(pdu);
708
0
    }
709
0
    DEBUGMSGTL(("agentx/subagent", "  FINISHED\n"));
710
0
    return 1;
711
0
}
712
#endif /* !NETSNMP_NO_WRITE_SUPPORT */
713
714
715
int
716
agentx_registration_callback(int majorID, int minorID, void *serverarg,
717
                             void *clientarg)
718
0
{
719
0
    struct register_parameters *reg_parms =
720
0
        (struct register_parameters *) serverarg;
721
0
    netsnmp_session *agentx_ss = *(netsnmp_session **)clientarg;
722
723
0
    if (minorID == SNMPD_CALLBACK_REGISTER_OID)
724
0
        return agentx_register(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, reg_parms->timeout,
729
0
                               reg_parms->flags,
730
0
                               reg_parms->contextName);
731
0
    else
732
0
        return agentx_unregister(agentx_ss,
733
0
                                 reg_parms->name, reg_parms->namelen,
734
0
                                 reg_parms->priority,
735
0
                                 reg_parms->range_subid,
736
0
                                 reg_parms->range_ubound,
737
0
                                 reg_parms->contextName);
738
0
}
739
740
741
static int
742
agentx_sysOR_callback(int majorID, int minorID, void *serverarg,
743
                      void *clientarg)
744
0
{
745
0
    const struct register_sysOR_parameters *reg_parms =
746
0
        (const struct register_sysOR_parameters *) serverarg;
747
0
    netsnmp_session *agentx_ss = *(netsnmp_session **)clientarg;
748
749
0
    if (minorID == SNMPD_CALLBACK_REG_SYSOR)
750
0
        return agentx_add_agentcaps(agentx_ss,
751
0
                                    reg_parms->name, reg_parms->namelen,
752
0
                                    reg_parms->descr);
753
0
    else
754
0
        return agentx_remove_agentcaps(agentx_ss,
755
0
                                       reg_parms->name,
756
0
                                       reg_parms->namelen);
757
0
}
758
759
760
static int
761
subagent_shutdown(int majorID, int minorID, void *serverarg, void *clientarg)
762
0
{
763
0
    netsnmp_session *thesession = *(netsnmp_session **)clientarg;
764
0
    DEBUGMSGTL(("agentx/subagent", "shutting down session....\n"));
765
0
    if (thesession == NULL) {
766
0
  DEBUGMSGTL(("agentx/subagent", "Empty session to shutdown\n"));
767
0
  main_session = NULL;
768
0
  return 0;
769
0
    }
770
0
    agentx_unregister_callbacks(thesession);
771
0
    agentx_close_session(thesession, AGENTX_CLOSE_SHUTDOWN);
772
0
    if (main_session != NULL) {
773
0
        remove_trap_session(main_session);
774
0
        main_session = NULL;
775
0
    }
776
0
    snmp_close(thesession);
777
0
    DEBUGMSGTL(("agentx/subagent", "shut down finished.\n"));
778
779
0
    subagent_init_init = 0;
780
0
    return 1;
781
0
}
782
783
784
785
/*
786
 * Register all the "standard" AgentX callbacks for the given session.  
787
 */
788
789
void
790
agentx_register_callbacks(netsnmp_session * s)
791
0
{
792
0
    netsnmp_session *sess_p;
793
794
0
    DEBUGMSGTL(("agentx/subagent",
795
0
                "registering callbacks for session %p\n", s));
796
0
    sess_p = netsnmp_memdup(&s, sizeof(s));
797
0
    netsnmp_assert(sess_p);
798
0
    s->myvoid = sess_p;
799
0
    if (!sess_p)
800
0
        return;
801
0
    snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_SHUTDOWN,
802
0
                           subagent_shutdown, sess_p);
803
0
    snmp_register_callback(SNMP_CALLBACK_APPLICATION,
804
0
                           SNMPD_CALLBACK_REGISTER_OID,
805
0
                           agentx_registration_callback, sess_p);
806
0
    snmp_register_callback(SNMP_CALLBACK_APPLICATION,
807
0
                           SNMPD_CALLBACK_UNREGISTER_OID,
808
0
                           agentx_registration_callback, sess_p);
809
0
    snmp_register_callback(SNMP_CALLBACK_APPLICATION,
810
0
                           SNMPD_CALLBACK_REG_SYSOR,
811
0
                           agentx_sysOR_callback, sess_p);
812
0
    snmp_register_callback(SNMP_CALLBACK_APPLICATION,
813
0
                           SNMPD_CALLBACK_UNREG_SYSOR,
814
0
                           agentx_sysOR_callback, sess_p);
815
0
}
816
817
/*
818
 * Unregister all the callbacks associated with this session.  
819
 */
820
821
void
822
agentx_unregister_callbacks(netsnmp_session * ss)
823
0
{
824
0
    DEBUGMSGTL(("agentx/subagent",
825
0
                "unregistering callbacks for session %p\n", ss));
826
0
    snmp_unregister_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_SHUTDOWN,
827
0
                             subagent_shutdown, ss->myvoid, 1);
828
0
    snmp_unregister_callback(SNMP_CALLBACK_APPLICATION,
829
0
                             SNMPD_CALLBACK_REGISTER_OID,
830
0
                             agentx_registration_callback, ss->myvoid, 1);
831
0
    snmp_unregister_callback(SNMP_CALLBACK_APPLICATION,
832
0
                             SNMPD_CALLBACK_UNREGISTER_OID,
833
0
                             agentx_registration_callback, ss->myvoid, 1);
834
0
    snmp_unregister_callback(SNMP_CALLBACK_APPLICATION,
835
0
                             SNMPD_CALLBACK_REG_SYSOR,
836
0
                             agentx_sysOR_callback, ss->myvoid, 1);
837
0
    snmp_unregister_callback(SNMP_CALLBACK_APPLICATION,
838
0
                             SNMPD_CALLBACK_UNREG_SYSOR,
839
0
                             agentx_sysOR_callback, ss->myvoid, 1);
840
0
    SNMP_FREE(ss->myvoid);
841
0
}
842
843
/*
844
 * Open a session to the master agent.  
845
 */
846
int
847
subagent_open_master_session(void)
848
0
{
849
0
    netsnmp_transport *t;
850
0
    netsnmp_session sess;
851
0
    const char *agentx_socket;
852
853
0
    DEBUGMSGTL(("agentx/subagent", "opening session...\n"));
854
855
0
    if (main_session) {
856
0
        snmp_log(LOG_WARNING,
857
0
                 "AgentX session to master agent attempted to be re-opened.\n");
858
0
        return -1;
859
0
    }
860
861
0
    snmp_sess_init(&sess);
862
0
    sess.version = AGENTX_VERSION_1;
863
0
    sess.retries = SNMP_DEFAULT_RETRIES;
864
0
    sess.timeout = SNMP_DEFAULT_TIMEOUT;
865
0
    sess.flags |= SNMP_FLAGS_STREAM_SOCKET;
866
0
    sess.callback = handle_agentx_packet;
867
0
    sess.authenticator = NULL;
868
869
0
    agentx_socket = netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID,
870
0
                                          NETSNMP_DS_AGENT_X_SOCKET);
871
0
    t = netsnmp_transport_open_client("agentx", agentx_socket);
872
0
    if (t == NULL) {
873
        /*
874
         * Diagnose snmp_open errors with the input
875
         * netsnmp_session pointer.  
876
         */
877
0
        if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
878
0
                                    NETSNMP_DS_AGENT_NO_CONNECTION_WARNINGS)) {
879
0
            char buf[1024];
880
0
            snprintf(buf, sizeof(buf), "Warning: "
881
0
                     "Failed to connect to the agentx master agent (%s)",
882
0
                     agentx_socket ? agentx_socket : "[NIL]");
883
0
            if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
884
0
                                        NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) {
885
0
                netsnmp_sess_log_error(LOG_WARNING, buf, &sess);
886
0
            } else {
887
0
                snmp_sess_perror(buf, &sess);
888
0
            }
889
0
        }
890
0
        return -1;
891
0
    }
892
893
0
    main_session =
894
0
        snmp_add_full(&sess, t, NULL, agentx_parse, NULL, NULL,
895
0
                      agentx_realloc_build, agentx_check_packet, NULL);
896
897
0
    if (main_session == NULL) {
898
        /* snmp_add_full() frees 't' upon failure. */
899
0
        t = NULL;
900
0
        if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID,
901
0
                                    NETSNMP_DS_AGENT_NO_CONNECTION_WARNINGS)) {
902
0
            char buf[1024];
903
0
            snprintf(buf, sizeof(buf), "Error: "
904
0
                     "Failed to create the agentx master agent session (%s)",
905
0
                     agentx_socket);
906
0
            snmp_sess_perror(buf, &sess);
907
0
        }
908
0
        netsnmp_transport_free(t);
909
0
        return -1;
910
0
    }
911
912
    /*
913
     * I don't know why 1 is success instead of the usual 0 = noerr, 
914
     * but that's what the function returns.
915
     */
916
0
    if (1 != agentx_open_session(main_session)) {
917
0
        snmp_close(main_session);
918
0
        main_session = NULL;
919
0
        return -1;
920
0
    }
921
922
    /*
923
     * subagent_register_ping_alarm assumes that securityModel will
924
     *  be set to SNMP_DEFAULT_SECMODEL on new AgentX sessions.
925
     *  This field is then (ab)used to hold the alarm stash.
926
     *
927
     * Why is the securityModel field used for this purpose, I hear you ask.
928
     * Damn good question!   (See SVN revision 4886)
929
     */
930
0
    main_session->securityModel = SNMP_DEFAULT_SECMODEL;
931
932
0
    if (add_trap_session(main_session, AGENTX_MSG_NOTIFY, 1,
933
0
                         AGENTX_VERSION_1)) {
934
0
        DEBUGMSGTL(("agentx/subagent", " trap session registered OK\n"));
935
0
    } else {
936
0
        DEBUGMSGTL(("agentx/subagent",
937
0
                    "trap session registration failed\n"));
938
0
        snmp_close(main_session);
939
0
        main_session = NULL;
940
0
        return -1;
941
0
    }
942
943
0
    agentx_register_callbacks(main_session);
944
945
0
    snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
946
0
                        SNMPD_CALLBACK_INDEX_START, (void *) main_session);
947
948
0
    snmp_log(LOG_INFO, "NET-SNMP version %s AgentX subagent connected\n",
949
0
             netsnmp_get_version());
950
0
    DEBUGMSGTL(("agentx/subagent", "opening session...  DONE (%p)\n",
951
0
                main_session));
952
953
0
    return 0;
954
0
}
955
956
static void
957
agentx_reopen_sysORTable(const struct sysORTable* data, void* v)
958
0
{
959
0
    netsnmp_session *agentx_ss = (netsnmp_session *) v;
960
  
961
0
    agentx_add_agentcaps(agentx_ss, data->OR_oid, data->OR_oidlen,
962
0
                         data->OR_descr);
963
0
}
964
965
/*
966
 * Alarm callback function to open a session to the master agent.  If a
967
 * transport disconnection callback occurs, indicating that the master agent
968
 * has died (or there has been some strange communication problem), this
969
 * alarm is called repeatedly to try to re-open the connection.  
970
 */
971
972
void
973
agentx_reopen_session(unsigned int clientreg, void *clientarg)
974
0
{
975
0
    DEBUGMSGTL(("agentx/subagent", "agentx_reopen_session(%d) called\n",
976
0
                clientreg));
977
978
0
    if (subagent_open_master_session() == 0) {
979
        /*
980
         * Successful.  Delete the alarm handle if one exists.  
981
         */
982
0
        if (clientreg != 0) {
983
0
            snmp_alarm_unregister(clientreg);
984
0
        }
985
986
        /*
987
         * Reregister all our nodes.  
988
         */
989
0
        register_mib_reattach();
990
991
        /*
992
         * Reregister all our sysOREntries
993
         */
994
0
        netsnmp_sysORTable_foreach(&agentx_reopen_sysORTable, main_session);
995
996
        /*
997
         * Register a ping alarm (if need be).  
998
         */
999
0
        subagent_register_ping_alarm(0, 0, NULL, main_session);
1000
0
    } else {
1001
0
        if (clientreg == 0) {
1002
            /*
1003
             * Register a reattach alarm for later 
1004
             */
1005
0
            subagent_register_ping_alarm(0, 0, NULL, main_session);
1006
0
        }
1007
0
    }
1008
0
}
1009
1010
/*
1011
 * If a valid session is passed in (through clientarg), register a
1012
 * ping handler to ping it frequently, else register an attempt to try
1013
 * and open it again later. 
1014
 */
1015
1016
static int
1017
subagent_register_ping_alarm(int majorID, int minorID,
1018
                             void *serverarg, void *clientarg)
1019
0
{
1020
1021
0
    netsnmp_session *ss = (netsnmp_session *) clientarg;
1022
0
    int             ping_interval =
1023
0
        netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID,
1024
0
                           NETSNMP_DS_AGENT_AGENTX_PING_INTERVAL);
1025
1026
0
    if (!ping_interval)         /* don't do anything if not setup properly */
1027
0
        return 0;
1028
1029
    /*
1030
     * register a ping alarm, if desired 
1031
     */
1032
0
    if (ss) {
1033
0
        if (ss->securityModel != SNMP_DEFAULT_SECMODEL) {
1034
0
            DEBUGMSGTL(("agentx/subagent",
1035
0
                        "unregister existing alarm %d\n",
1036
0
                        ss->securityModel));
1037
0
            snmp_alarm_unregister(ss->securityModel);
1038
0
        }
1039
1040
0
        DEBUGMSGTL(("agentx/subagent",
1041
0
                    "register ping alarm every %d seconds\n",
1042
0
                    ping_interval));
1043
        /*
1044
         * we re-use the securityModel parameter for an alarm stash,
1045
         * since agentx doesn't need it 
1046
         */
1047
0
        ss->securityModel = snmp_alarm_register(ping_interval, SA_REPEAT,
1048
0
                                                agentx_check_session, ss);
1049
0
    } else {
1050
        /*
1051
         * attempt to open it later instead 
1052
         */
1053
0
        DEBUGMSGTL(("agentx/subagent",
1054
0
                    "subagent not properly attached, postponing registration till later....\n"));
1055
0
        snmp_alarm_register(ping_interval, SA_REPEAT,
1056
0
                            agentx_reopen_session, NULL);
1057
0
    }
1058
0
    return 0;
1059
0
}
1060
1061
/*
1062
 * check a session validity for connectivity to the master agent.  If
1063
 * not functioning, close and start attempts to reopen the session 
1064
 */
1065
void
1066
agentx_check_session(unsigned int clientreg, void *clientarg)
1067
0
{
1068
0
    netsnmp_session *ss = (netsnmp_session *) clientarg;
1069
0
    if (!ss) {
1070
0
        if (clientreg)
1071
0
            snmp_alarm_unregister(clientreg);
1072
0
        return;
1073
0
    }
1074
0
    DEBUGMSGTL(("agentx/subagent", "checking status of session %p\n", ss));
1075
1076
0
    if (!agentx_send_ping(ss)) {
1077
0
        snmp_log(LOG_WARNING,
1078
0
                 "AgentX master agent failed to respond to ping.  Attempting to re-register.\n");
1079
        /*
1080
         * master agent disappeared?  Try and re-register.
1081
         * close first, just to be sure .
1082
         */
1083
0
        agentx_unregister_callbacks(ss);
1084
0
        agentx_close_session(ss, AGENTX_CLOSE_TIMEOUT);
1085
0
        snmp_alarm_unregister(clientreg);       /* delete ping alarm timer */
1086
0
        snmp_call_callbacks(SNMP_CALLBACK_APPLICATION,
1087
0
                            SNMPD_CALLBACK_INDEX_STOP, (void *) ss);
1088
0
        register_mib_detach();
1089
0
        if (main_session != NULL) {
1090
0
            remove_trap_session(ss);
1091
0
            snmp_close(main_session);
1092
            /*
1093
             * We need to remove the callbacks attached to the callback
1094
             * session because they have a magic callback data structure
1095
             * which includes a pointer to the main session
1096
             *    (which is no longer valid).
1097
             * 
1098
             * Given that the main session is not responsive anyway.
1099
             * it shouldn't matter if we lose some outstanding requests.
1100
             */
1101
0
            if (agentx_callback_sess != NULL ) {
1102
0
                snmp_close(agentx_callback_sess);
1103
0
                agentx_callback_sess = NULL;
1104
    
1105
0
                subagent_init_callback_session();
1106
0
            }
1107
0
            main_session = NULL;
1108
0
            agentx_reopen_session(0, NULL);
1109
0
        }
1110
0
        else {
1111
0
            snmp_close(main_session);
1112
0
            main_session = NULL;
1113
0
        }
1114
0
    } else {
1115
0
        DEBUGMSGTL(("agentx/subagent", "session %p responded to ping\n",
1116
0
                    ss));
1117
0
    }
1118
0
}
1119
1120
1121
#endif /* USING_AGENTX_SUBAGENT_MODULE */