Coverage Report

Created: 2025-11-11 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/net-snmp/snmplib/transports/snmpUDPsharedDomain.c
Line
Count
Source
1
/* UDPshared transport support functions
2
 *
3
 * Portions of this file are copyrighted by:
4
 * Copyright (c) 2016 VMware, Inc. All rights reserved.
5
 * Use is subject to license terms specified in the COPYING file
6
 * distributed with the Net-SNMP package.
7
 */
8
9
#include <net-snmp/net-snmp-config.h>
10
11
#include <net-snmp/types.h>
12
#include "snmpIPBaseDomain.h"
13
#include <net-snmp/library/snmpUDPsharedDomain.h>
14
15
#include <stddef.h>
16
#include <stdio.h>
17
#include <sys/types.h>
18
#include <ctype.h>
19
#ifdef HAVE_STDLIB_H
20
#include <stdlib.h>
21
#endif
22
#ifdef HAVE_STRING_H
23
#include <string.h>
24
#else
25
#include <strings.h>
26
#endif
27
#ifdef HAVE_SYS_SOCKET_H
28
#include <sys/socket.h>
29
#endif
30
#ifdef HAVE_NETINET_IN_H
31
#include <netinet/in.h>
32
#endif
33
#ifdef HAVE_ARPA_INET_H
34
#include <arpa/inet.h>
35
#endif
36
#ifdef HAVE_NETDB_H
37
#include <netdb.h>
38
#endif
39
#include <errno.h>
40
41
#include <net-snmp/types.h>
42
#include <net-snmp/library/snmp_debug.h>
43
#include <net-snmp/library/tools.h>
44
#include <net-snmp/library/snmp_assert.h>
45
46
#include <net-snmp/library/snmpSocketBaseDomain.h>
47
#include <net-snmp/library/snmpUDPDomain.h>
48
#include <net-snmp/library/snmpUDPIPv4BaseDomain.h>
49
50
netsnmp_feature_require(transport_cache);
51
52
const oid netsnmpUDPsharedDomain[] = { 1,3,6,1,2,1,100,1,999 }; /** made up */
53
size_t netsnmpUDPsharedDomain_len = OID_LENGTH(netsnmpUDPsharedDomain);
54
55
/* ***************************************************************************
56
 */
57
static int
58
_udpshared_close(netsnmp_transport *t)
59
0
{
60
0
    netsnmp_transport *b = t? t->base_transport : NULL;
61
62
0
    DEBUGMSGTL(("udpshared:close", "%p/%p\n", t,b ));
63
64
    /** we don't really have a socket - base transport does */
65
0
    if (NULL == t || NULL == b)
66
0
        return -1;
67
68
0
    t->base_transport = NULL;
69
0
    t->sock = -1; /* we just had a copy of base's fd */
70
71
    /** remove base transport. if it is still in use, return. */
72
0
    if (netsnmp_transport_cache_remove(b) == 1)
73
0
        DEBUGMSGTL(("udpshared:close", "keeping socket open for %d user(s)\n",
74
0
                    b->local_length));
75
0
    else
76
0
        b->f_close(b); /** close base transport */
77
78
0
    return 0;
79
0
}
80
81
static int
82
_udpshared_recv(netsnmp_transport *t, void *buf, int size,
83
                void **opaque, int *olength)
84
0
{
85
0
    if (NULL == t || NULL == t->base_transport)
86
0
        return -1;
87
88
0
    return t->base_transport->f_recv(t->base_transport, buf, size, opaque,
89
0
                                     olength);
90
0
}
91
92
static int
93
_udpshared_send(netsnmp_transport *t, const void *buf, int size,
94
                void **opaque, int *olength)
95
0
{
96
0
    void *_opaque, **_opaque_p = &_opaque;
97
0
    int  _olength, *_olength_p = &_olength;
98
99
0
    if (NULL == t || NULL == t->base_transport)
100
0
        return -1;
101
102
    /*
103
     * opaque points to an address pair to use to send, overriding the
104
     * address pair in the transport. So if no address was specified by
105
     * the caller, use the udpshared transport address, overriding the
106
     * empty base_transport address.
107
     */
108
0
    if (NULL == opaque || NULL == *opaque) {
109
0
        _opaque = t->data;
110
0
        _olength = t->data_length;
111
0
    } else {
112
0
        _opaque_p = opaque;
113
0
        _olength_p = olength;
114
0
    }
115
0
    return t->base_transport->f_send(t->base_transport, buf, size, _opaque_p,
116
0
                                     _olength_p);
117
0
}
118
119
static char *
120
_udpshared_fmtaddr(netsnmp_transport *t, const void *data, int len)
121
0
{
122
0
    if (NULL == t || NULL == t->base_transport ||
123
0
        NULL == t->base_transport->f_fmtaddr)
124
0
        return strdup("<UNKNOWN>");
125
126
0
    return t->base_transport->f_fmtaddr(t->base_transport, data, len);
127
0
}
128
129
static int
130
_setup_session(netsnmp_transport *t, netsnmp_session *session)
131
0
{
132
0
    if (NULL == t || NULL == session)
133
0
        return -1;
134
135
0
    session->flags |= SNMP_FLAGS_SHARED_SOCKET;
136
137
0
    return 0;
138
0
}
139
140
static netsnmp_transport *
141
_transport_common(netsnmp_transport *t)
142
0
{
143
0
    void *save_data;
144
0
    int   save_data_len;
145
146
0
    DEBUGTRACETOK("9:udpshared");
147
148
0
    if (NULL == t)
149
0
        return NULL;
150
151
    /*
152
     * t->data contains remote addr, which can/will vary, so don't
153
     * copy it to base transport
154
     */
155
0
    save_data = t->data;
156
0
    save_data_len = t->data_length;
157
0
    t->data = NULL;
158
0
    t->data_length = 0;
159
160
    /** save base transport for clients; need in send/recv functions later */
161
0
    t->base_transport = netsnmp_transport_copy(t);
162
0
    if (NULL == t->base_transport) {
163
0
        free(save_data);
164
0
        netsnmp_transport_free(t);
165
0
        return NULL;
166
0
    }
167
168
    /** restore remote addr */
169
0
    t->data = save_data;
170
0
    t->data_length = save_data_len;
171
172
    /** Set UDPsharedDomain specifics */
173
0
    t->domain = netsnmpUDPsharedDomain;
174
0
    t->domain_length = netsnmpUDPsharedDomain_len;
175
176
0
    t->f_recv          = _udpshared_recv;
177
0
    t->f_send          = _udpshared_send;
178
0
    t->f_close         = _udpshared_close;
179
0
    t->f_fmtaddr       = _udpshared_fmtaddr;
180
0
    t->f_setup_session = _setup_session;
181
0
    t->flags = NETSNMP_TRANSPORT_FLAG_SHARED;
182
0
    if (t->base_transport->domain == netsnmpUDPDomain)
183
0
        t->f_get_taddr = netsnmp_ipv4_get_taddr;
184
0
#ifdef NETSNMP_ENABLE_IPV6
185
0
    else if (t->base_transport->domain == netsnmp_UDPIPv6Domain)
186
0
        t->f_get_taddr = netsnmp_ipv6_get_taddr;
187
0
#endif
188
0
    else
189
0
        netsnmp_assert(0);
190
191
0
    return t;
192
0
}
193
194
netsnmp_transport *
195
netsnmp_udpshared_transport(const struct netsnmp_ep *ep, int local)
196
0
{
197
0
    netsnmp_transport *t = NULL;
198
199
0
    t = netsnmp_udp_transport(ep, local);
200
0
    if (NULL == t)
201
0
        return NULL;
202
203
0
    t = _transport_common(t);
204
205
0
    return t;
206
0
}
207
208
netsnmp_transport *
209
netsnmp_udpshared_transport_with_source(const struct netsnmp_ep *ep,
210
                                        int flags,
211
                                        const struct netsnmp_ep *src_addr)
212
0
{
213
0
    netsnmp_transport *t = NULL, *b = NULL;
214
0
    int                local = flags & NETSNMP_TSPEC_LOCAL;
215
216
0
    DEBUGMSGTL(("udpshared:create", "from addr with source\n"));
217
218
    /** init common parts of parent transport */
219
0
    t = netsnmp_udpipv4base_transport_init(ep, local);
220
0
    if (NULL == t)
221
0
        return NULL;
222
223
0
    if (!_transport_common(t))
224
0
        return NULL;
225
226
0
    if (!local && src_addr) {
227
        /** check for existing base transport */
228
0
        b = netsnmp_transport_cache_get(PF_INET, SOCK_DGRAM, local,
229
0
                                        (const void *)src_addr,
230
0
                                        sizeof(*src_addr));
231
0
        if (NULL != b && NULL != b->local) {
232
            /*
233
             * uh-oh. we've assumed sharedudp is just for clients, and we're
234
             * using local_length as a reference count.
235
             */
236
0
            snmp_log(LOG_ERR,
237
0
                     "sharedudp transport is only for client/remote\n");
238
0
            netsnmp_transport_free(t);
239
0
            return NULL;
240
0
        }
241
0
    }
242
243
    /** if no base transport found, create one */
244
0
    if (NULL == b) {
245
0
        b = netsnmp_udp_transport_with_source(ep, local, src_addr);
246
0
        if (NULL == b) {
247
0
            netsnmp_transport_free(t);
248
0
            return NULL;
249
0
        }
250
0
    }
251
0
    ++b->local_length; /* reference count */
252
0
    t->base_transport = b;
253
0
    t->msgMaxSize = b->msgMaxSize;
254
0
    t->flags |= NETSNMP_TRANSPORT_FLAG_SHARED;
255
256
    /** get local socket address */
257
0
    if (!local) {
258
0
        t->sock = b->sock;
259
0
        netsnmp_udpipv4base_transport_get_bound_addr(t);
260
0
    }
261
262
    /** cache base transport for future use */
263
0
    if (!local && src_addr && 1 == b->local_length) {
264
0
        netsnmp_transport_cache_save(PF_INET, SOCK_DGRAM, local,
265
0
                                     (const void *)src_addr, sizeof(*src_addr),
266
0
                                     b);
267
0
    }
268
269
0
    return t;
270
0
}
271
272
#ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN
273
274
/*
275
 * Open a shared UDP transport for SNMP.  Local is TRUE if addr is the local
276
 * address to bind to (i.e. this is a server-type session); otherwise addr is
277
 * the remote address to send things to.
278
 */
279
netsnmp_transport *
280
netsnmp_udpshared6_transport(const struct netsnmp_ep *ep, int local)
281
0
{
282
0
    netsnmp_transport *t = NULL;
283
284
0
    t = netsnmp_udp6_transport(ep, local);
285
0
    if (NULL != t)
286
0
        t = _transport_common(t);
287
288
0
    return t;
289
0
}
290
291
netsnmp_transport *
292
netsnmp_udpshared6_transport_with_source(const struct netsnmp_ep *ep,
293
                                         int flags,
294
                                         const struct netsnmp_ep *src_addr6)
295
0
{
296
0
    netsnmp_transport *t = NULL, *b = NULL;
297
0
    int                local = flags & NETSNMP_TSPEC_LOCAL;
298
299
0
    DEBUGMSGTL(("udpshared:create", "from addr6 with source\n"));
300
301
    /** init common parts of parent transport */
302
0
    t = netsnmp_udp6_transport_init(ep, local);
303
0
    if (NULL == t)
304
0
        return NULL;
305
306
0
    if (!_transport_common(t))
307
0
        return NULL;
308
309
0
    if (!local && src_addr6) {
310
        /** check for existing base transport */
311
0
        b = netsnmp_transport_cache_get(PF_INET6, SOCK_DGRAM, local,
312
0
                                        (const void *)src_addr6,
313
0
                                        sizeof(*src_addr6));
314
0
        if (NULL != b && NULL != b->local) {
315
            /*
316
             * uh-oh. we've assumed sharedudp is just for clients, and we're
317
             * using local_length as a reference count.
318
             */
319
0
            snmp_log(LOG_ERR,
320
0
                     "sharedudp transport is only for client/remote\n");
321
0
            netsnmp_transport_free(t);
322
0
            return NULL;
323
0
        }
324
0
    }
325
326
    /** if no base transport found, create one */
327
0
    if (NULL == b) {
328
0
        b = netsnmp_udp6_transport_with_source(ep, local, src_addr6);
329
0
        if (NULL == b) {
330
0
            netsnmp_transport_free(t);
331
0
            return NULL;
332
0
        }
333
0
    }
334
0
    ++b->local_length; /* reference count */
335
0
    t->base_transport = b;
336
0
    t->flags |= NETSNMP_TRANSPORT_FLAG_SHARED;
337
338
    /** get local socket address */
339
0
    if (!local) {
340
0
        t->sock = b->sock;
341
0
        netsnmp_udp6_transport_get_bound_addr(t);
342
0
    }
343
344
    /** cache base transport for future use */
345
0
    if (!local && src_addr6 && 1 == b->local_length) {
346
0
        netsnmp_transport_cache_save(PF_INET6, SOCK_DGRAM, local,
347
0
                                     (const void *)src_addr6,
348
0
                                     sizeof(*src_addr6), b);
349
0
    }
350
351
0
    return t;
352
0
}
353
#endif /* NETSNMP_TRANSPORT_UDPIPV6_DOMAIN */
354
355
netsnmp_transport *
356
netsnmp_udpshared_create_ostring(const void *o, size_t o_len, int local)
357
0
{
358
0
    struct netsnmp_ep ep;
359
360
0
    DEBUGMSGTL(("udpshared:create", "from ostring\n"));
361
362
0
    memset(&ep, 0, sizeof(ep));
363
0
    if (netsnmp_ipv4_ostring_to_sockaddr(&ep.a.sin, o, o_len))
364
0
        return netsnmp_udpshared_transport(&ep, local);
365
0
#ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN
366
0
    else if (netsnmp_ipv6_ostring_to_sockaddr(&ep.a.sin6, o, o_len))
367
0
        return netsnmp_udpshared6_transport(&ep, local);
368
0
#endif
369
0
    return NULL;
370
0
}
371
372
netsnmp_transport *
373
netsnmp_udpshared_create_tstring(const char *str, int isserver,
374
                                 const char *default_target)
375
0
{
376
0
    struct netsnmp_ep ep;
377
0
    netsnmp_transport *t;
378
379
0
    DEBUGMSGTL(("udpshared:create", "from tstring %s\n", str));
380
381
0
    if (netsnmp_sockaddr_in3(&ep, str, default_target))
382
0
        t = netsnmp_udpshared_transport(&ep, isserver);
383
0
#ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN
384
0
    else if (netsnmp_sockaddr_in6_3(&ep, str, default_target))
385
0
        t = netsnmp_udpshared6_transport(&ep, isserver);
386
0
#endif
387
0
    else
388
0
        return NULL;
389
390
0
    return t;
391
0
}
392
393
static netsnmp_transport *
394
_tspec_v4(const struct netsnmp_ep *ep, netsnmp_tdomain_spec *tspec)
395
0
{
396
0
    int local = tspec->flags & NETSNMP_TSPEC_LOCAL;
397
398
0
    if (NULL != tspec->source) {
399
0
        struct netsnmp_ep src_addr;
400
401
        /** get sockaddr from source */
402
0
        if (!netsnmp_sockaddr_in3(&src_addr, tspec->source, NULL))
403
0
            return NULL;
404
0
        return netsnmp_udpshared_transport_with_source(ep, local, &src_addr);
405
0
    }
406
407
    /** no source and default client address ok */
408
0
    return netsnmp_udpshared_transport(ep, local);
409
0
}
410
411
#ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN
412
static netsnmp_transport *
413
_tspec_v6(const struct netsnmp_ep *ep, netsnmp_tdomain_spec *tspec)
414
0
{
415
0
    int local = tspec->flags & NETSNMP_TSPEC_LOCAL;
416
417
0
    if (NULL != tspec->source) {
418
0
        struct netsnmp_ep src_addr;
419
420
        /** get sockaddr from source */
421
0
        if (!netsnmp_sockaddr_in6_3(&src_addr, tspec->source, NULL))
422
0
            return NULL;
423
0
        return netsnmp_udpshared6_transport_with_source(ep, local, &src_addr);
424
0
    }
425
426
    /** no source and default client address ok */
427
0
    return netsnmp_udpshared6_transport(ep, local);
428
0
}
429
#endif /* NETSNMP_TRANSPORT_UDPIPV6_DOMAIN */
430
431
netsnmp_transport *
432
netsnmp_udpshared_create_tspec(netsnmp_tdomain_spec *tspec)
433
0
{
434
0
    struct netsnmp_ep ep;
435
436
0
    DEBUGMSGTL(("udpshared:create", "from tspec\n"));
437
438
0
    if (NULL == tspec)
439
0
        return NULL;
440
441
0
    if (netsnmp_sockaddr_in3(&ep, tspec->target, tspec->default_target))
442
0
        return _tspec_v4(&ep, tspec);
443
0
#ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN
444
0
    else if (netsnmp_sockaddr_in6_3(&ep, tspec->target, tspec->default_target))
445
0
        return _tspec_v6(&ep, tspec);
446
0
#endif
447
448
0
    return NULL;
449
0
}
450
451
void
452
netsnmp_udpshared_ctor(void)
453
3.98k
{
454
3.98k
    static netsnmp_tdomain domain;
455
3.98k
    static int done = 0;
456
457
3.98k
    if (done)
458
3.98k
        return;
459
4
    done = 1;
460
461
4
    domain.name = netsnmpUDPsharedDomain;
462
4
    domain.name_length = netsnmpUDPsharedDomain_len;
463
464
4
    domain.prefix = calloc(2, sizeof(char *));
465
4
    if (!domain.prefix) {
466
0
        snmp_log(LOG_ERR, "calloc() failed - out of memory\n");
467
0
        return;
468
0
    }
469
4
    domain.prefix[0] = "udpshared";
470
471
4
    domain.f_create_from_tstring_new = netsnmp_udpshared_create_tstring;
472
4
    domain.f_create_from_tspec       = netsnmp_udpshared_create_tspec;
473
4
    domain.f_create_from_ostring     = netsnmp_udpshared_create_ostring;
474
475
4
    netsnmp_tdomain_register(&domain);
476
4
}