Coverage Report

Created: 2024-07-27 06:09

/src/net-snmp/snmplib/transports/snmpCallbackDomain.c
Line
Count
Source (jump to first uncovered line)
1
#include <net-snmp/net-snmp-config.h>
2
3
#include <net-snmp/library/snmpCallbackDomain.h>
4
5
#include <stdio.h>
6
#include <sys/types.h>
7
#include <ctype.h>
8
#include <errno.h>
9
10
#ifdef WIN32
11
#include <net-snmp/library/winpipe.h>
12
#endif
13
#ifdef HAVE_STRING_H
14
#include <string.h>
15
#else
16
#include <strings.h>
17
#endif
18
#ifdef HAVE_STDLIB_H
19
#include <stdlib.h>
20
#endif
21
#ifdef HAVE_UNISTD_H
22
#include <unistd.h>
23
#endif
24
#ifdef HAVE_SYS_SOCKET_H
25
#include <sys/socket.h>
26
#endif
27
#ifdef HAVE_SYS_UN_H
28
#include <sys/un.h>
29
#endif
30
#ifdef HAVE_IO_H
31
#include <io.h>
32
#endif
33
#ifdef HAVE_FCNTL_H
34
#include <fcntl.h>
35
#endif
36
37
#include <net-snmp/types.h>
38
#include <net-snmp/output_api.h>
39
#include <net-snmp/config_api.h>
40
#include <net-snmp/utilities.h>
41
42
#include <net-snmp/library/snmp_transport.h>
43
#include <net-snmp/library/snmp_api.h>
44
#include <net-snmp/library/snmp_client.h>
45
46
#ifndef NETSNMP_STREAM_QUEUE_LEN
47
#define NETSNMP_STREAM_QUEUE_LEN  5
48
#endif
49
50
#ifdef NETSNMP_TRANSPORT_CALLBACK_DOMAIN
51
52
static netsnmp_transport_list *trlist = NULL;
53
54
static int      callback_count = 0;
55
56
typedef struct callback_hack_s {
57
    void           *orig_transport_data;
58
    netsnmp_pdu    *pdu;
59
} callback_hack;
60
61
typedef struct callback_queue_s {
62
    int             callback_num;
63
    netsnmp_callback_pass *item;
64
    struct callback_queue_s *next, *prev;
65
} callback_queue;
66
67
callback_queue *thequeue;
68
69
static netsnmp_transport *
70
find_transport_from_callback_num(int num)
71
0
{
72
0
    netsnmp_transport_list *ptr;
73
0
    netsnmp_callback_info *ci;
74
75
0
    for (ptr = trlist; ptr; ptr = ptr->next) {
76
0
        ci = ptr->transport->data;
77
0
        if (ci->callback_num == num)
78
0
            return ptr->transport;
79
0
    }
80
0
    return NULL;
81
0
}
82
83
static void
84
callback_debug_pdu(const char *ourstring, netsnmp_pdu *pdu)
85
0
{
86
0
    netsnmp_variable_list *vb;
87
0
    int             i = 1;
88
0
    DEBUGMSGTL((ourstring,
89
0
                "PDU: command = %d, errstat = %ld, errindex = %ld\n",
90
0
                pdu->command, pdu->errstat, pdu->errindex));
91
0
    for (vb = pdu->variables; vb; vb = vb->next_variable) {
92
0
        DEBUGMSGTL((ourstring, "  var %d:", i++));
93
0
        DEBUGMSGVAR((ourstring, vb));
94
0
        DEBUGMSG((ourstring, "\n"));
95
0
    }
96
0
}
97
98
void
99
callback_push_queue(int num, netsnmp_callback_pass *item)
100
0
{
101
0
    callback_queue *newitem = SNMP_MALLOC_TYPEDEF(callback_queue);
102
0
    callback_queue *ptr;
103
104
0
    if (newitem == NULL)
105
0
        return;
106
0
    newitem->callback_num = num;
107
0
    newitem->item = item;
108
0
    if (thequeue) {
109
0
        for (ptr = thequeue; ptr && ptr->next; ptr = ptr->next) {
110
0
        }
111
0
        ptr->next = newitem;
112
0
        newitem->prev = ptr;
113
0
    } else {
114
0
        thequeue = newitem;
115
0
    }
116
0
    DEBUGIF("dump_send_callback_transport") {
117
0
        callback_debug_pdu("dump_send_callback_transport", item->pdu);
118
0
    }
119
0
}
120
121
netsnmp_callback_pass *
122
callback_pop_queue(int num)
123
0
{
124
0
    netsnmp_callback_pass *cp;
125
0
    callback_queue *ptr;
126
127
0
    for (ptr = thequeue; ptr; ptr = ptr->next) {
128
0
        if (ptr->callback_num == num) {
129
0
            if (ptr->prev) {
130
0
                ptr->prev->next = ptr->next;
131
0
            } else {
132
0
                thequeue = ptr->next;
133
0
            }
134
0
            if (ptr->next) {
135
0
                ptr->next->prev = ptr->prev;
136
0
            }
137
0
            cp = ptr->item;
138
0
            SNMP_FREE(ptr);
139
0
            DEBUGIF("dump_recv_callback_transport") {
140
0
                callback_debug_pdu("dump_recv_callback_transport",
141
0
                                   cp->pdu);
142
0
            }
143
0
            return cp;
144
0
        }
145
0
    }
146
0
    return NULL;
147
0
}
148
149
/*
150
 * Return a string representing the address in data, or else the "far end"
151
 * address if data is NULL.  
152
 */
153
154
char *
155
netsnmp_callback_fmtaddr(netsnmp_transport *t, const void *data, int len)
156
0
{
157
0
    char *buf;
158
0
    netsnmp_callback_info *mystuff;
159
160
0
    if (!t || !t->data)
161
0
        return strdup("callback: unknown");
162
163
0
    mystuff = t->data;
164
0
    if (asprintf(&buf, "callback: %d on fd %d", mystuff->callback_num,
165
0
     mystuff->pipefds[0]) < 0)
166
0
        buf = NULL;
167
0
    return buf;
168
0
}
169
170
171
172
/*
173
 * You can write something into opaque that will subsequently get passed back 
174
 * to your send function if you like.  For instance, you might want to
175
 * remember where a PDU came from, so that you can send a reply there...  
176
 */
177
178
int
179
netsnmp_callback_recv(netsnmp_transport *t, void *buf, int size,
180
          void **opaque, int *olength)
181
0
{
182
0
    int rc = -1;
183
0
    char newbuf[1];
184
0
    netsnmp_callback_info *mystuff = t->data;
185
186
0
    DEBUGMSGTL(("transport_callback", "hook_recv enter\n"));
187
188
0
    while (rc < 0) {
189
#ifdef WIN32
190
  rc = recvfrom(mystuff->pipefds[0], newbuf, 1, 0, NULL, 0);
191
#else
192
0
  rc = read(mystuff->pipefds[0], newbuf, 1);
193
0
#endif
194
0
  if (rc < 0 && errno != EINTR) {
195
0
      break;
196
0
  }
197
0
    }
198
0
    if (rc > 0)
199
0
        memset(buf, 0, rc);
200
201
0
    if (mystuff->linkedto) {
202
        /*
203
         * we're the client.  We don't need to do anything. 
204
         */
205
0
    } else {
206
        /*
207
         * malloc the space here, but it's filled in by
208
         * snmp_callback_created_pdu() below 
209
         */
210
0
        *opaque = calloc(1, sizeof(int));
211
0
        *olength = sizeof(int);
212
0
    }
213
0
    DEBUGMSGTL(("transport_callback", "hook_recv exit\n"));
214
0
    return rc;
215
0
}
216
217
218
219
int
220
netsnmp_callback_send(netsnmp_transport *t, const void *buf, int size,
221
          void **opaque, int *olength)
222
0
{
223
0
    int from, rc = -1;
224
0
    netsnmp_callback_info *mystuff = t->data, *theirstuff;
225
0
    netsnmp_callback_pass *cp;
226
227
    /*
228
     * extract the pdu from the hacked buffer 
229
     */
230
0
    netsnmp_transport *other_side;
231
0
    callback_hack  *ch = *opaque;
232
0
    netsnmp_pdu    *pdu = ch->pdu;
233
234
0
    *opaque = ch->orig_transport_data;
235
0
    SNMP_FREE(ch);
236
237
0
    DEBUGMSGTL(("transport_callback", "hook_send enter\n"));
238
239
0
    cp = SNMP_MALLOC_TYPEDEF(netsnmp_callback_pass);
240
0
    if (!cp)
241
0
        return -1;
242
243
0
    cp->pdu = snmp_clone_pdu(pdu);
244
0
    if (cp->pdu->transport_data) {
245
        /*
246
         * not needed and not properly freed later 
247
         */
248
0
        SNMP_FREE(cp->pdu->transport_data);
249
0
    }
250
251
0
    if (cp->pdu->flags & UCD_MSG_FLAG_EXPECT_RESPONSE)
252
0
        cp->pdu->flags ^= UCD_MSG_FLAG_EXPECT_RESPONSE;
253
254
    /*
255
     * push the sent pdu onto the stack 
256
     */
257
    /*
258
     * AND send a bogus byte to the remote callback receiver's pipe 
259
     */
260
0
    if (mystuff->linkedto) {
261
        /*
262
         * we're the client, send it to the parent 
263
         */
264
0
        cp->return_transport_num = mystuff->callback_num;
265
266
0
        other_side = find_transport_from_callback_num(mystuff->linkedto);
267
0
        if (!other_side) {
268
0
            snmp_free_pdu(cp->pdu);
269
0
            SNMP_FREE(cp);
270
0
            return -1;
271
0
        }
272
273
0
        theirstuff = other_side->data;
274
0
  while (rc < 0) {
275
#ifdef WIN32
276
      rc = sendto(theirstuff->pipefds[1], " ", 1, 0, NULL, 0);
277
#else
278
0
      rc = write(theirstuff->pipefds[1], " ", 1);
279
0
#endif
280
0
      if (rc < 0 && errno != EINTR) {
281
0
    break;
282
0
      }
283
0
  }
284
0
        callback_push_queue(mystuff->linkedto, cp);
285
        /*
286
         * we don't need the transport data any more 
287
         */
288
0
        SNMP_FREE(*opaque);
289
0
    } else {
290
        /*
291
         * we're the server, send it to the person that sent us the request 
292
         */
293
0
        from = **((int **) opaque);
294
        /*
295
         * we don't need the transport data any more 
296
         */
297
0
        SNMP_FREE(*opaque);
298
0
        other_side = find_transport_from_callback_num(from);
299
0
        if (!other_side) {
300
0
            snmp_free_pdu(cp->pdu);
301
0
            SNMP_FREE(cp);
302
0
            return -1;
303
0
        }
304
0
        theirstuff = other_side->data;
305
0
  while (rc < 0) {
306
#ifdef WIN32
307
      rc = sendto(theirstuff->pipefds[1], " ", 1, 0, NULL, 0);
308
#else
309
0
      rc = write(theirstuff->pipefds[1], " ", 1);
310
0
#endif
311
0
      if (rc < 0 && errno != EINTR) {
312
0
    break;
313
0
      }
314
0
  }
315
0
        callback_push_queue(from, cp);
316
0
    }
317
318
0
    DEBUGMSGTL(("transport_callback", "hook_send exit\n"));
319
0
    return 0;
320
0
}
321
322
323
324
int
325
netsnmp_callback_close(netsnmp_transport *t)
326
2.81k
{
327
2.81k
    int             rc;
328
2.81k
    netsnmp_callback_info *mystuff = t->data;
329
2.81k
    DEBUGMSGTL(("transport_callback", "hook_close enter\n"));
330
331
#ifdef HAVE_CLOSESOCKET
332
    rc  = closesocket(mystuff->pipefds[0]);
333
    rc |= closesocket(mystuff->pipefds[1]);
334
#else
335
2.81k
    rc  = close(mystuff->pipefds[0]);
336
2.81k
    rc |= close(mystuff->pipefds[1]);
337
2.81k
#endif
338
339
2.81k
    rc |= netsnmp_transport_remove_from_list(&trlist, t);
340
341
2.81k
    DEBUGMSGTL(("transport_callback", "hook_close exit\n"));
342
2.81k
    return rc;
343
2.81k
}
344
345
346
347
int
348
netsnmp_callback_accept(netsnmp_transport *t)
349
0
{
350
0
    DEBUGMSGTL(("transport_callback", "hook_accept enter\n"));
351
0
    DEBUGMSGTL(("transport_callback", "hook_accept exit\n"));
352
0
    return -1;
353
0
}
354
355
356
357
/*
358
 * Open a Callback-domain transport for SNMP.  Local is TRUE if addr
359
 * is the local address to bind to (i.e. this is a server-type
360
 * session); otherwise addr is the remote address to send things to
361
 * (and we make up a temporary name for the local end of the
362
 * connection).  
363
 */
364
365
netsnmp_transport *
366
netsnmp_callback_transport(int to)
367
2.81k
{
368
369
2.81k
    netsnmp_transport *t = NULL;
370
2.81k
    netsnmp_callback_info *mydata;
371
2.81k
    int             rc;
372
373
    /*
374
     * transport 
375
     */
376
2.81k
    t = SNMP_MALLOC_TYPEDEF(netsnmp_transport);
377
2.81k
    if (!t)
378
0
        return NULL;
379
380
    /*
381
     * our stuff 
382
     */
383
2.81k
    mydata = SNMP_MALLOC_TYPEDEF(netsnmp_callback_info);
384
2.81k
    if (!mydata) {
385
0
        SNMP_FREE(t);
386
0
        return NULL;
387
0
    }
388
2.81k
    mydata->linkedto = to;
389
2.81k
    mydata->callback_num = ++callback_count;
390
2.81k
    mydata->data = NULL;
391
2.81k
    t->data = mydata;
392
393
#ifdef WIN32
394
    rc = create_winpipe_transport(mydata->pipefds);
395
#else
396
2.81k
    rc = pipe(mydata->pipefds);
397
2.81k
#endif
398
399
2.81k
    if (rc) {
400
0
        netsnmp_transport_free(t);
401
0
        return NULL;
402
0
    }
403
404
2.81k
    netsnmp_assert(mydata->pipefds[0] != -1);
405
2.81k
    t->sock      = mydata->pipefds[0];
406
407
    /*
408
     * Message size is not limited by this transport (hence msgMaxSize
409
     * is equal to the maximum legal size of an SNMP message).  
410
     */
411
412
2.81k
    t->msgMaxSize = SNMP_MAX_PACKET_LEN;
413
2.81k
    t->f_recv    = netsnmp_callback_recv;
414
2.81k
    t->f_send    = netsnmp_callback_send;
415
2.81k
    t->f_close   = netsnmp_callback_close;
416
2.81k
    t->f_accept  = netsnmp_callback_accept;
417
2.81k
    t->f_fmtaddr = netsnmp_callback_fmtaddr;
418
419
2.81k
    netsnmp_transport_add_to_list(&trlist, t);
420
421
2.81k
    if (to)
422
0
        DEBUGMSGTL(("transport_callback", "initialized %d linked to %d\n",
423
2.81k
                    mydata->callback_num, to));
424
2.81k
    else
425
2.81k
        DEBUGMSGTL(("transport_callback",
426
2.81k
                    "initialized master listening on %d\n",
427
2.81k
                    mydata->callback_num));
428
2.81k
    return t;
429
2.81k
}
430
431
int
432
netsnmp_callback_hook_parse(netsnmp_session * sp,
433
                            netsnmp_pdu *pdu,
434
                            u_char * packetptr, size_t len)
435
0
{
436
0
    if (SNMP_MSG_RESPONSE == pdu->command ||
437
0
        SNMP_MSG_REPORT == pdu->command)
438
0
        pdu->flags |= UCD_MSG_FLAG_RESPONSE_PDU;
439
0
    else
440
0
        pdu->flags &= (~UCD_MSG_FLAG_RESPONSE_PDU);
441
442
0
    return SNMP_ERR_NOERROR;
443
0
}
444
445
int
446
netsnmp_callback_hook_build(netsnmp_session * sp,
447
                            netsnmp_pdu *pdu, u_char * ptk, size_t * len)
448
0
{
449
    /*
450
     * very gross hack, as this is passed later to the transport_send
451
     * function 
452
     */
453
0
    callback_hack  *ch = SNMP_MALLOC_TYPEDEF(callback_hack);
454
0
    if (ch == NULL)
455
0
        return -1;
456
0
    DEBUGMSGTL(("transport_callback", "hook_build enter\n"));
457
0
    ch->pdu = pdu;
458
0
    ch->orig_transport_data = pdu->transport_data;
459
0
    pdu->transport_data = ch;
460
0
    switch (pdu->command) {
461
0
    case SNMP_MSG_GETBULK:
462
0
        if (pdu->max_repetitions < 0) {
463
0
            sp->s_snmp_errno = SNMPERR_BAD_REPETITIONS;
464
0
            return -1;
465
0
        }
466
0
        if (pdu->non_repeaters < 0) {
467
0
            sp->s_snmp_errno = SNMPERR_BAD_REPEATERS;
468
0
            return -1;
469
0
        }
470
0
        break;
471
0
    case SNMP_MSG_RESPONSE:
472
0
    case SNMP_MSG_TRAP:
473
0
    case SNMP_MSG_TRAP2:
474
0
        pdu->flags &= (~UCD_MSG_FLAG_EXPECT_RESPONSE);
475
0
        NETSNMP_FALLTHROUGH;
476
0
    default:
477
0
        if (pdu->errstat == SNMP_DEFAULT_ERRSTAT)
478
0
            pdu->errstat = 0;
479
0
        if (pdu->errindex == SNMP_DEFAULT_ERRINDEX)
480
0
            pdu->errindex = 0;
481
0
        break;
482
0
    }
483
484
    /*
485
     * Copy missing values from session defaults
486
     */
487
0
    switch (pdu->version) {
488
0
#ifndef NETSNMP_DISABLE_SNMPV1
489
0
    case SNMP_VERSION_1:
490
0
#endif
491
0
#ifndef NETSNMP_DISABLE_SNMPV2C
492
0
    case SNMP_VERSION_2c:
493
0
#endif
494
0
#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C)
495
0
        if (pdu->community_len == 0) {
496
0
            if (sp->community_len == 0) {
497
0
                sp->s_snmp_errno = SNMPERR_BAD_COMMUNITY;
498
0
                return -1;
499
0
            }
500
0
            pdu->community = netsnmp_memdup(sp->community, sp->community_len);
501
0
            if (pdu->community == NULL) {
502
0
                sp->s_snmp_errno = SNMPERR_MALLOC;
503
0
                return -1;
504
0
            }
505
0
            pdu->community_len = sp->community_len;
506
0
        }
507
0
        break;
508
0
#endif
509
0
    case SNMP_VERSION_3:
510
0
        if (pdu->securityNameLen == 0) {
511
0
            pdu->securityName = netsnmp_memdup(sp->securityName,
512
0
                                               sp->securityNameLen + 1);
513
0
            if (pdu->securityName == NULL) {
514
0
                sp->s_snmp_errno = SNMPERR_MALLOC;
515
0
                return -1;
516
0
            }
517
0
            pdu->securityNameLen = sp->securityNameLen;
518
0
        }
519
0
        if (pdu->securityModel == -1)
520
0
            pdu->securityModel = sp->securityModel;
521
0
        if (pdu->securityLevel == 0)
522
0
            pdu->securityLevel = sp->securityLevel;
523
        /* WHAT ELSE ?? */
524
0
    }
525
0
    ptk[0] = 0;
526
0
    *len = 1;
527
0
    DEBUGMSGTL(("transport_callback", "hook_build exit\n"));
528
0
    return 1;
529
0
}
530
531
int
532
netsnmp_callback_check_packet(u_char * pkt, size_t len)
533
0
{
534
0
    return 1;
535
0
}
536
537
netsnmp_pdu    *
538
netsnmp_callback_create_pdu(netsnmp_transport *transport,
539
                            void *opaque, size_t olength)
540
0
{
541
0
    netsnmp_pdu    *pdu;
542
0
    netsnmp_callback_pass *cp =
543
0
        callback_pop_queue(((netsnmp_callback_info *) transport->data)->
544
0
                           callback_num);
545
0
    if (!cp)
546
0
        return NULL;
547
0
    pdu = cp->pdu;
548
0
    pdu->transport_data = opaque;
549
0
    pdu->transport_data_length = olength;
550
0
    if (opaque)                 /* if created, we're the server */
551
0
        *((int *) opaque) = cp->return_transport_num;
552
0
    SNMP_FREE(cp);
553
0
    return pdu;
554
0
}
555
556
netsnmp_session *
557
netsnmp_callback_open(int attach_to,
558
                      int (*return_func) (int op,
559
                                          netsnmp_session * session,
560
                                          int reqid, netsnmp_pdu *pdu,
561
                                          void *magic),
562
                      int (*fpre_parse) (netsnmp_session *,
563
                                         struct netsnmp_transport_s *,
564
                                         void *, int),
565
                      int (*fpost_parse) (netsnmp_session *, netsnmp_pdu *,
566
                                          int))
567
2.81k
{
568
2.81k
    netsnmp_session callback_sess, *callback_ss;
569
2.81k
    netsnmp_transport *callback_tr;
570
571
2.81k
    callback_tr = netsnmp_callback_transport(attach_to);
572
2.81k
    snmp_sess_init(&callback_sess);
573
2.81k
    callback_sess.callback = return_func;
574
2.81k
    if (attach_to) {
575
        /*
576
         * client 
577
         */
578
        /*
579
         * trysess.community = (u_char *) callback_ss; 
580
         */
581
2.81k
    } else {
582
2.81k
        callback_sess.isAuthoritative = SNMP_SESS_AUTHORITATIVE;
583
2.81k
    }
584
2.81k
    callback_sess.retries = 0;
585
2.81k
    callback_sess.timeout = 30000000;
586
2.81k
    callback_sess.version = SNMP_DEFAULT_VERSION;       /* (mostly) bogus */
587
2.81k
    callback_ss = snmp_add_full(&callback_sess, callback_tr,
588
2.81k
                                fpre_parse,
589
2.81k
                                netsnmp_callback_hook_parse, fpost_parse,
590
2.81k
                                netsnmp_callback_hook_build,
591
2.81k
                                NULL,
592
2.81k
                                netsnmp_callback_check_packet,
593
2.81k
                                netsnmp_callback_create_pdu);
594
2.81k
    if (callback_ss)
595
2.81k
        callback_ss->local_port =
596
2.81k
            ((netsnmp_callback_info *) callback_tr->data)->callback_num;
597
2.81k
    return callback_ss;
598
2.81k
}
599
600
601
602
void
603
netsnmp_clear_callback_list(void)
604
2.81k
{
605
606
2.81k
    netsnmp_transport_list *list = trlist, *next = NULL;
607
2.81k
    netsnmp_transport *tr = NULL;
608
609
2.81k
    DEBUGMSGTL(("callback_clear", "called netsnmp_callback_clear_list()\n"));
610
2.81k
    while (list != NULL) {
611
0
  next = list->next;
612
0
  tr = list->transport;
613
614
0
  if (tr != NULL) {
615
0
      tr->f_close(tr);
616
0
        netsnmp_transport_remove_from_list(&trlist, tr);
617
0
      netsnmp_transport_free(tr);
618
0
  }
619
0
  list = next;
620
0
    }
621
2.81k
    trlist = NULL;
622
623
2.81k
}
624
625
#endif /* NETSNMP_TRANSPORT_CALLBACK_DOMAIN */