Coverage Report

Created: 2026-10-19 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/net-snmp/snmplib/transports/snmpTCPIPv6Domain.c
Line
Count
Source
1
#include <net-snmp/net-snmp-config.h>
2
3
#ifdef NETSNMP_TRANSPORT_TCPIPV6_DOMAIN
4
5
#include <net-snmp/types.h>
6
#include "snmpIPBaseDomain.h"
7
#include <net-snmp/library/snmpTCPIPv6Domain.h>
8
9
#include <stdio.h>
10
#include <sys/types.h>
11
#include <errno.h>
12
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_NETINET_IN_H
28
#include <netinet/in.h>
29
#endif
30
#ifdef HAVE_ARPA_INET_H
31
#include <arpa/inet.h>
32
#endif
33
#ifdef HAVE_NETDB_H
34
#include <netdb.h>
35
#endif
36
#ifdef HAVE_FCNTL_H
37
#include <fcntl.h>
38
#endif
39
40
#include <net-snmp/types.h>
41
#include <net-snmp/output_api.h>
42
#include <net-snmp/config_api.h>
43
44
#include <net-snmp/library/snmp.h>
45
#include <net-snmp/library/snmp_transport.h>
46
#include <net-snmp/library/snmpSocketBaseDomain.h>
47
#include <net-snmp/library/snmpTCPBaseDomain.h>
48
#include <net-snmp/library/tools.h>
49
50
#ifndef NETSNMP_NO_SYSTEMD
51
#include <net-snmp/library/sd-daemon.h>
52
#endif
53
54
#include "inet_ntop.h"
55
56
/*
57
 * needs to be in sync with the definitions in snmplib/snmpTCPDomain.c
58
 * and perl/agent/agent.xs
59
 */
60
typedef netsnmp_indexed_addr_pair netsnmp_udp_addr_pair;
61
62
const oid netsnmp_TCPIPv6Domain[] = { TRANSPORT_DOMAIN_TCP_IPV6 };
63
static netsnmp_tdomain tcp6Domain;
64
65
/*
66
 * Return a string representing the address in data, or else the "far end"
67
 * address if data is NULL.  
68
 */
69
70
static char *
71
netsnmp_tcp6_fmtaddr(netsnmp_transport *t, const void *data, int len)
72
0
{
73
0
    return netsnmp_ipv6_fmtaddr("TCP/IPv6", t, data, len);
74
0
}
75
76
static NETSNMP_SOCKET
77
netsnmp_tcp6_accept(netsnmp_transport *t)
78
0
{
79
0
    netsnmp_udp_addr_pair *addr_pair;
80
0
    struct sockaddr       *farend;
81
0
    NETSNMP_SOCKET         newsock = NETSNMP_INVALID_SOCKET;
82
0
    socklen_t              farendlen;
83
84
0
    addr_pair = calloc(1, sizeof(*addr_pair));
85
0
    if (addr_pair == NULL) {
86
        /*
87
         * Indicate that the acceptance of this socket failed.  
88
         */
89
0
        DEBUGMSGTL(("netsnmp_tcp6", "accept: malloc failed\n"));
90
0
        return NETSNMP_INVALID_SOCKET;
91
0
    }
92
93
0
    farend = &addr_pair->remote_addr.sa;
94
0
    farendlen = sizeof(addr_pair->remote_addr.sin6);
95
96
0
    if (t && NETSNMP_IS_VALID_SOCKET(t->sock)) {
97
0
        newsock = accept(t->sock, farend, &farendlen);
98
99
0
        if (!NETSNMP_IS_VALID_SOCKET(newsock)) {
100
0
            DEBUGMSGTL(("netsnmp_tcp6", "accept failed errno %d \"%s\"\n",
101
0
                        errno, strerror(errno)));
102
0
            free(addr_pair);
103
0
            return newsock;
104
0
        }
105
106
0
        free(t->data);
107
0
        t->data = addr_pair;
108
0
        t->data_length = sizeof(netsnmp_udp_addr_pair);
109
0
        DEBUGIF("netsnmp_tcp6") {
110
0
            char *str = netsnmp_tcp6_fmtaddr(NULL, addr_pair,
111
0
                                             sizeof(netsnmp_udp_addr_pair));
112
113
0
            DEBUGMSGTL(("netsnmp_tcp6", "accept succeeded (from %s)\n", str));
114
0
            free(str);
115
0
        }
116
117
        /*
118
         * Try to make the new socket blocking.  
119
         */
120
121
0
        if (netsnmp_set_non_blocking_mode(newsock, FALSE) < 0)
122
0
            DEBUGMSGTL(("netsnmp_tcp6",
123
0
                        "accept: couldn't f_getfl of fd %" NETSNMP_FMT_SKT "\n",
124
0
                        newsock));
125
126
0
        netsnmp_set_tcp_nodelay(newsock, 1);
127
128
        /*
129
         * Allow user to override the send and receive buffers. Default is
130
         * to use os default.  Don't worry too much about errors --
131
         * just plough on regardless.  
132
         */
133
0
        netsnmp_sock_buffer_set(newsock, SO_SNDBUF, 1, 0);
134
0
        netsnmp_sock_buffer_set(newsock, SO_RCVBUF, 1, 0);
135
136
0
        return newsock;
137
0
    } else {
138
0
        free(addr_pair);
139
0
        return NETSNMP_INVALID_SOCKET;
140
0
    }
141
0
}
142
143
144
145
/*
146
 * Open a TCP/IPv6-based transport for SNMP.  Local is TRUE if addr is the
147
 * local address to bind to (i.e. this is a server-type session); otherwise
148
 * addr is the remote address to send things to.  
149
 */
150
151
netsnmp_transport *
152
netsnmp_tcp6_transport(const struct netsnmp_ep *ep, int local)
153
0
{
154
0
    const struct sockaddr_in6 *addr = &ep->a.sin6;
155
0
    netsnmp_transport *t = NULL;
156
0
    netsnmp_udp_addr_pair     *addr_pair = NULL;
157
0
    int             rc = 0;
158
0
    int             socket_initialized = 0;
159
160
#ifdef NETSNMP_NO_LISTEN_SUPPORT
161
    if (local)
162
        return NULL;
163
#endif /* NETSNMP_NO_LISTEN_SUPPORT */
164
165
0
    if (addr == NULL || addr->sin6_family != AF_INET6)
166
0
        return NULL;
167
168
0
    t = netsnmp_transport_alloc();
169
0
    if (t == NULL)
170
0
        return NULL;
171
172
0
    DEBUGIF("netsnmp_tcp6") {
173
0
        char *str = netsnmp_tcp6_fmtaddr(NULL, addr,
174
0
                                         sizeof(struct sockaddr_in6));
175
176
0
        DEBUGMSGTL(("netsnmp_tcp6", "open %s %s\n", local ? "local" : "remote",
177
0
                    str));
178
0
        free(str);
179
0
    }
180
181
0
    addr_pair = calloc(1, sizeof(netsnmp_udp_addr_pair));
182
0
    if (addr_pair == NULL)
183
0
        goto err;
184
0
    t->data = addr_pair;
185
0
    t->data_length = sizeof(netsnmp_udp_addr_pair);
186
0
    memcpy(&addr_pair->remote_addr.sin6, addr, sizeof(struct sockaddr_in6));
187
188
0
    t->domain = netsnmp_TCPIPv6Domain;
189
0
    t->domain_length = OID_LENGTH(netsnmp_TCPIPv6Domain);
190
191
0
#ifndef NETSNMP_NO_SYSTEMD
192
    /*
193
     * Maybe the socket was already provided by systemd...
194
     */
195
0
    if (local) {
196
0
        t->sock = netsnmp_sd_find_inet_socket(PF_INET6, SOCK_STREAM, 1,
197
0
                ntohs(addr->sin6_port));
198
0
        if (t->sock >= 0)
199
0
            socket_initialized = 1;
200
0
    }
201
0
#endif
202
0
    if (!socket_initialized)
203
0
        t->sock = socket(PF_INET6, SOCK_STREAM, 0);
204
0
    if (!NETSNMP_IS_VALID_SOCKET(t->sock))
205
0
        goto err;
206
207
0
    t->flags = NETSNMP_TRANSPORT_FLAG_STREAM;
208
209
    /* for Linux VRF Traps we try to bind the iface if clientaddr is not set */
210
0
    if (local == 0 && ep) {
211
0
        rc = netsnmp_bindtodevice(t->sock, ep->iface);
212
0
        if (rc)
213
0
            DEBUGMSGTL(("netsnmp_tcp",
214
0
                        "VRF: Could not bind socket %" NETSNMP_FMT_SKT " to %s\n",
215
0
                        t->sock, ep->iface));
216
0
        else
217
0
            DEBUGMSGTL(("netsnmp_tcp",
218
0
                        "VRF: Bound socket %" NETSNMP_FMT_SKT " to %s\n",
219
0
                        t->sock, ep->iface));
220
0
    }
221
222
0
    if (local) {
223
0
#ifndef NETSNMP_NO_LISTEN_SUPPORT
224
0
        int opt = 1;
225
226
        /*
227
         * This session is intended as a server, so we must bind on to the
228
         * given IP address, which may include an interface address, or could
229
         * be INADDR_ANY, but certainly includes a port number.
230
         */
231
232
0
#ifdef IPV6_V6ONLY
233
        /* Try to restrict PF_INET6 socket to IPv6 communications only. */
234
0
        {
235
0
    int one=1;
236
0
    if (setsockopt(t->sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&one,
237
0
                         sizeof(one)) != 0) {
238
0
      DEBUGMSGTL(("netsnmp_tcp6",
239
0
                        "couldn't set IPV6_V6ONLY to %d bytes: %s\n", one,
240
0
                        strerror(errno)));
241
0
    } 
242
0
  }
243
0
#endif
244
245
0
        t->flags |= NETSNMP_TRANSPORT_FLAG_LISTEN;
246
0
        t->local_length = sizeof(*addr);
247
0
        t->local = netsnmp_memdup(addr, sizeof(*addr));
248
0
        if (!t->local)
249
0
            goto err;
250
251
        /*
252
         * We should set SO_REUSEADDR too.  
253
         */
254
255
0
        setsockopt(t->sock, SOL_SOCKET, SO_REUSEADDR, (void *)&opt,
256
0
                   sizeof(opt));
257
258
0
        if (!socket_initialized) {
259
0
            rc = netsnmp_bindtodevice(t->sock, ep->iface);
260
0
            if (rc != 0) {
261
0
                DEBUGMSGTL(("netsnmp_tcp6", "failed to bind to iface %s: %s\n",
262
0
                            ep->iface, strerror(errno)));
263
0
                goto err;
264
0
            }
265
0
            rc = bind(t->sock, (const struct sockaddr *)addr, sizeof(*addr));
266
0
            if (rc != 0)
267
0
                goto err;
268
0
        }
269
270
        /*
271
         * Since we are going to be letting select() tell us when connections
272
         * are ready to be accept()ed, we need to make the socket n0n-blocking
273
         * to avoid the race condition described in W. R. Stevens, ``Unix
274
         * Network Programming Volume I Second Edition'', pp. 422--4, which
275
         * could otherwise wedge the agent.
276
         */
277
278
0
        if (netsnmp_set_non_blocking_mode(t->sock, TRUE) < 0)
279
0
            DEBUGMSGTL(("netsnmp_tcp6",
280
0
                        "couldn't set non-blocking mode on fd %" NETSNMP_FMT_SKT "\n",
281
0
                        t->sock));
282
283
        /*
284
         * Now sit here and wait for connections to arrive.  
285
         */
286
287
0
        if (!socket_initialized) {
288
0
            rc = listen(t->sock, NETSNMP_STREAM_QUEUE_LEN);
289
0
            if (rc != 0)
290
0
                goto err;
291
0
        }
292
293
        /*
294
         * no buffer size on listen socket - doesn't make sense
295
         */
296
#else /* NETSNMP_NO_LISTEN_SUPPORT */
297
        return NULL;
298
#endif /* NETSNMP_NO_LISTEN_SUPPORT */
299
0
    } else {
300
0
        t->remote_length = sizeof(*addr);
301
0
        t->remote = netsnmp_memdup(addr, sizeof(*addr));
302
0
        if (!t->remote)
303
0
            goto err;
304
305
        /*
306
         * This is a client-type session, so attempt to connect to the far
307
         * end.  We don't go non-blocking here because it's not obvious what
308
         * you'd then do if you tried to do snmp_sends before the connection
309
         * had completed.  So this can block.
310
         */
311
312
0
        rc = connect(t->sock, (const struct sockaddr *)addr, sizeof(*addr));
313
0
        DEBUGMSGTL(("netsnmp_tcp6", "connect returns %d\n", rc));
314
0
        if (rc < 0)
315
0
            goto err;
316
317
0
        netsnmp_set_tcp_nodelay(t->sock, 1);
318
319
        /*
320
         * Allow user to override the send and receive buffers. Default is
321
         * to use os default.  Don't worry too much about errors --
322
         * just plough on regardless.  
323
         */
324
0
        netsnmp_sock_buffer_set(t->sock, SO_SNDBUF, local, 0);
325
0
        netsnmp_sock_buffer_set(t->sock, SO_RCVBUF, local, 0);
326
0
    }
327
328
    /*
329
     * Message size is not limited by this transport (hence msgMaxSize
330
     * is equal to the maximum legal size of an SNMP message).  
331
     */
332
333
0
    t->msgMaxSize = SNMP_MAX_PACKET_LEN;
334
0
    t->f_recv     = netsnmp_tcpbase_recv;
335
0
    t->f_send     = netsnmp_tcpbase_send;
336
0
    t->f_close    = netsnmp_socketbase_close;
337
0
    t->f_accept   = netsnmp_tcp6_accept;
338
0
    t->f_setup_session = netsnmp_ipbase_session_init;
339
0
    t->f_fmtaddr  = netsnmp_tcp6_fmtaddr;
340
0
    t->f_get_taddr = netsnmp_ipv6_get_taddr;
341
342
0
    return t;
343
344
0
err:
345
0
    netsnmp_socketbase_close(t);
346
0
    netsnmp_transport_free(t);
347
0
    return NULL;
348
0
}
349
350
netsnmp_transport *
351
netsnmp_tcp6_create_tstring(const char *str, int local,
352
          const char *default_target)
353
0
{
354
0
    struct netsnmp_ep ep;
355
356
0
    if (netsnmp_sockaddr_in6_3(&ep, str, default_target)) {
357
0
        return netsnmp_tcp6_transport(&ep, local);
358
0
    } else {
359
0
        return NULL;
360
0
    }
361
0
}
362
363
364
/*
365
 * See:
366
 * 
367
 * http://www.ietf.org/internet-drafts/draft-ietf-ops-taddress-mib-01.txt
368
 * 
369
 * (or newer equivalent) for details of the TC which we are using for
370
 * the mapping here.  
371
 */
372
373
netsnmp_transport *
374
netsnmp_tcp6_create_ostring(const void *o, size_t o_len, int local)
375
0
{
376
0
    struct netsnmp_ep ep;
377
378
0
    memset(&ep, 0, sizeof(ep));
379
0
    if (netsnmp_ipv6_ostring_to_sockaddr(&ep.a.sin6, o, o_len))
380
0
        return netsnmp_tcp6_transport(&ep, local);
381
0
    return NULL;
382
0
}
383
384
385
void
386
netsnmp_tcpipv6_ctor(void)
387
4.35k
{
388
4.35k
    tcp6Domain.name = netsnmp_TCPIPv6Domain;
389
4.35k
    tcp6Domain.name_length = OID_LENGTH(netsnmp_TCPIPv6Domain);
390
4.35k
    tcp6Domain.f_create_from_tstring_new = netsnmp_tcp6_create_tstring;
391
4.35k
    tcp6Domain.f_create_from_ostring     = netsnmp_tcp6_create_ostring;
392
4.35k
    tcp6Domain.prefix = calloc(4, sizeof(char *));
393
4.35k
    if (!tcp6Domain.prefix) {
394
0
        snmp_log(LOG_ERR, "calloc() failed - out of memory\n");
395
0
        return;
396
0
    }
397
4.35k
    tcp6Domain.prefix[0] = "tcp6";
398
4.35k
    tcp6Domain.prefix[1] = "tcpv6";
399
4.35k
    tcp6Domain.prefix[2] = "tcpipv6";
400
401
4.35k
    netsnmp_tdomain_register(&tcp6Domain);
402
4.35k
}
403
404
#endif /* NETSNMP_TRANSPORT_TCPIPV6_DOMAIN */
405