Coverage Report

Created: 2026-09-01 08:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/net-snmp/snmplib/transports/snmpUDPIPv4BaseDomain.c
Line
Count
Source
1
/* IPV4 base transport support functions
2
 *
3
 * Portions of this file are subject to the following copyright(s).  See
4
 * the Net-SNMP's COPYING file for more details and other copyrights
5
 * that may apply:
6
 *
7
 * Portions of this file are copyrighted by:
8
 * Copyright (c) 2016 VMware, Inc. All rights reserved.
9
 * Use is subject to license terms specified in the COPYING file
10
 * distributed with the Net-SNMP package.
11
 */
12
13
#include <net-snmp/net-snmp-config.h>
14
15
#include <net-snmp/types.h>
16
#include "snmpIPBaseDomain.h"
17
#include <net-snmp/library/snmpUDPDomain.h>
18
#include <net-snmp/library/snmpUDPIPv4BaseDomain.h>
19
20
#include <stddef.h>
21
#include <stdio.h>
22
#include <sys/types.h>
23
#include <ctype.h>
24
#ifdef HAVE_STDLIB_H
25
#include <stdlib.h>
26
#endif
27
#ifdef HAVE_STRING_H
28
#include <string.h>
29
#else
30
#include <strings.h>
31
#endif
32
#ifdef HAVE_SYS_SOCKET_H
33
#include <sys/socket.h>
34
#endif
35
#ifdef HAVE_NETINET_IN_H
36
#include <netinet/in.h>
37
#endif
38
#ifdef HAVE_ARPA_INET_H
39
#include <arpa/inet.h>
40
#endif
41
#ifdef HAVE_NETDB_H
42
#include <netdb.h>
43
#endif
44
#include <errno.h>
45
46
#include <net-snmp/types.h>
47
#include <net-snmp/library/snmp_debug.h>
48
#include <net-snmp/library/tools.h>
49
#include <net-snmp/library/snmp_assert.h>
50
#include <net-snmp/library/default_store.h>
51
#include <net-snmp/library/snmp_transport.h>
52
53
#include <net-snmp/library/snmpSocketBaseDomain.h>
54
55
#ifndef NETSNMP_NO_SYSTEMD
56
#include <net-snmp/library/sd-daemon.h>
57
#endif
58
59
#if defined(HAVE_IP_PKTINFO) || (defined(HAVE_IP_RECVDSTADDR) && defined(HAVE_IP_SENDSRCADDR))
60
int netsnmp_udpipv4_recvfrom(NETSNMP_SOCKET sock, void *buf, int len,
61
                             struct sockaddr *from, socklen_t *fromlen,
62
                             struct sockaddr *dstip, socklen_t *dstlen,
63
                             int *if_index)
64
56
{
65
56
    return netsnmp_udpbase_recvfrom(sock, buf, len, from, fromlen, dstip, dstlen,
66
56
                                    if_index);
67
56
}
68
69
int netsnmp_udpipv4_sendto(NETSNMP_SOCKET sock, const struct in_addr *srcip,
70
                           int if_index, const struct sockaddr *remote,
71
                           const void *data, int len)
72
13
{
73
13
    return netsnmp_udpbase_sendto(sock, srcip, if_index, remote, data, len);
74
13
}
75
#endif /* HAVE_IP_PKTINFO || HAVE_IP_RECVDSTADDR */
76
77
netsnmp_transport *
78
netsnmp_udpipv4base_transport_init(const struct netsnmp_ep *ep, int local)
79
3.40k
{
80
3.40k
    netsnmp_transport *t;
81
3.40k
    const struct sockaddr_in *addr = &ep->a.sin;
82
3.40k
    u_char *addr_ptr;
83
84
3.40k
    if (addr == NULL || addr->sin_family != AF_INET) {
85
0
        return NULL;
86
0
    }
87
88
3.40k
    t = netsnmp_transport_alloc();
89
3.40k
    if (NULL == t)
90
0
        return NULL;
91
92
3.40k
    addr_ptr = netsnmp_memdup(addr, sizeof(*addr));
93
3.40k
    if (NULL == addr_ptr) {
94
0
        free(t);
95
0
        return NULL;
96
0
    }
97
98
3.40k
    if (local) {
99
        /** This is a server session. */
100
3.40k
        t->local_length = sizeof(*addr);
101
3.40k
        t->local = addr_ptr;
102
3.40k
    } else {
103
        /** This is a client session. */
104
0
        t->remote = addr_ptr;
105
0
        t->remote_length = sizeof(*addr);
106
0
    }
107
108
3.40k
    DEBUGIF("netsnmp_udpbase") {
109
0
        netsnmp_indexed_addr_pair addr_pair;
110
0
        char                      *str;
111
0
        memset(&addr_pair, 0, sizeof(netsnmp_indexed_addr_pair));
112
0
        memcpy(&(addr_pair.remote_addr), addr, sizeof(*addr));
113
0
        str = netsnmp_udp_fmtaddr(NULL, (void *)&addr_pair,
114
0
                                  sizeof(netsnmp_indexed_addr_pair));
115
0
        DEBUGMSGTL(("netsnmp_udpbase", "open %s %s\n",
116
0
                    local ? "local" : "remote", str));
117
0
        free(str);
118
0
    }
119
120
3.40k
    if (!local) {
121
0
        netsnmp_indexed_addr_pair *addr_pair;
122
123
        /*
124
         * allocate space to save the (remote) address in the
125
         * transport-specific data pointer for later use by netsnmp_udp_send.
126
         */
127
0
        t->data = calloc(1, sizeof(netsnmp_indexed_addr_pair));
128
0
        if (NULL == t->data) {
129
0
            netsnmp_transport_free(t);
130
0
            return NULL;
131
0
        }
132
0
        t->data_length = sizeof(netsnmp_indexed_addr_pair);
133
134
0
        addr_pair = (netsnmp_indexed_addr_pair *)t->data;
135
0
        memcpy(&addr_pair->remote_addr, addr, sizeof(*addr));
136
0
    }
137
3.40k
    return t;
138
3.40k
}
139
140
NETSNMP_SOCKET
141
netsnmp_udpipv4base_transport_socket(int flags)
142
3.40k
{
143
3.40k
    int local = flags & NETSNMP_TSPEC_LOCAL;
144
3.40k
    NETSNMP_SOCKET sock;
145
146
3.40k
    sock = socket(PF_INET, SOCK_DGRAM, 0);
147
3.40k
    DEBUGMSGTL(("UDPBase", "opened socket %" NETSNMP_FMT_SKT " as local=%d\n",
148
3.40k
                sock, local));
149
3.40k
    if (!NETSNMP_IS_VALID_SOCKET(sock))
150
0
        return NETSNMP_INVALID_SOCKET;
151
152
3.40k
    _netsnmp_udp_sockopt_set(sock, local);
153
154
3.40k
    return sock;
155
3.40k
}
156
157
int
158
netsnmp_udpipv4base_transport_bind(netsnmp_transport *t,
159
                                   const struct netsnmp_ep *ep, int flags)
160
3.40k
{
161
3.40k
    const struct sockaddr_in *addr = &ep->a.sin;
162
3.40k
#if defined(HAVE_IP_PKTINFO) || defined(HAVE_IP_RECVDSTADDR) || defined(WIN32)
163
3.40k
    int                sockopt = 1;
164
3.40k
#endif
165
3.40k
    int                rc;
166
167
3.40k
    if (flags & NETSNMP_TSPEC_LOCAL) {
168
#ifdef NETSNMP_NO_LISTEN_SUPPORT
169
        return NULL;
170
#endif /* NETSNMP_NO_LISTEN_SUPPORT */
171
3.40k
#if defined(HAVE_IP_PKTINFO) && !defined(WIN32)
172
3.40k
        if (setsockopt(t->sock, SOL_IP, IP_PKTINFO, &sockopt, sizeof sockopt) == -1) {
173
0
            DEBUGMSGTL(("netsnmp_udpbase", "couldn't set IP_PKTINFO: %s\n",
174
0
                        strerror(errno)));
175
0
            return 1;
176
0
        }
177
3.40k
        DEBUGMSGTL(("netsnmp_udpbase", "set IP_PKTINFO\n"));
178
#elif defined(HAVE_IP_RECVDSTADDR)
179
        if (setsockopt(t->sock, IPPROTO_IP, IP_RECVDSTADDR, &sockopt, sizeof sockopt) == -1) {
180
            DEBUGMSGTL(("netsnmp_udp", "couldn't set IP_RECVDSTADDR: %s\n",
181
                        strerror(errno)));
182
            return 1;
183
        }
184
        DEBUGMSGTL(("netsnmp_udp", "set IP_RECVDSTADDR\n"));
185
#elif defined(WIN32)
186
        if (setsockopt(t->sock, IPPROTO_IP, IP_PKTINFO, (void *)&sockopt,
187
                       sizeof(sockopt)) == -1) {
188
            DEBUGMSGTL(("netsnmp_udpbase", "couldn't set IP_PKTINFO: %d\n",
189
                        WSAGetLastError()));
190
        } else {
191
            DEBUGMSGTL(("netsnmp_udpbase", "set IP_PKTINFO\n"));
192
        }
193
#endif
194
3.40k
    }
195
196
3.40k
    DEBUGIF("netsnmp_udpbase") {
197
0
        netsnmp_indexed_addr_pair addr_pair;
198
0
        char *str;
199
0
        memset(&addr_pair, 0x0, sizeof(addr_pair));
200
0
        memcpy(&(addr_pair.local_addr), addr, sizeof(*addr));
201
0
        str = netsnmp_udp_fmtaddr(NULL, (void *)&addr_pair,
202
0
                                  sizeof(netsnmp_indexed_addr_pair));
203
0
        DEBUGMSGTL(("netsnmp_udpbase",
204
0
                    "binding socket: %" NETSNMP_FMT_SKT " to %s\n",
205
0
                    t->sock, str));
206
0
        free(str);
207
0
    }
208
3.40k
    if (flags & NETSNMP_TSPEC_PREBOUND) {
209
0
        DEBUGMSGTL(("netsnmp_udpbase",
210
0
                    "socket %" NETSNMP_FMT_SKT " is prebound, nothing to do\n",
211
0
                    t->sock));
212
0
        return 0;
213
0
    }
214
3.40k
    rc = netsnmp_bindtodevice(t->sock, ep->iface);
215
3.40k
    if (rc != 0) {
216
0
        DEBUGMSGTL(("netsnmp_udpbase", "failed to bind to iface %s: %s\n",
217
0
                    ep->iface, strerror(errno)));
218
0
        goto err;
219
0
    }
220
3.40k
    rc = bind(t->sock, (const struct sockaddr *)addr, sizeof(*addr));
221
3.40k
    if ( rc != 0 ) {
222
0
        DEBUGMSGTL(("netsnmp_udpbase",
223
0
                    "failed to bind for clientaddr: %d %s\n",
224
0
                    errno, strerror(errno)));
225
0
        goto err;
226
0
    }
227
228
3.40k
    return 0;
229
230
0
err:
231
0
    netsnmp_socketbase_close(t);
232
0
    return 1;
233
3.40k
}
234
235
void
236
netsnmp_udpipv4base_transport_get_bound_addr(netsnmp_transport *t)
237
0
{
238
0
    netsnmp_indexed_addr_pair *addr_pair;
239
0
    socklen_t                  local_addr_len = sizeof(addr_pair->local_addr);
240
0
    int                        rc;
241
242
    /** only for client transports: must have data and not local */
243
0
    if (NULL == t || NULL != t->local || NULL == t->data ||
244
0
        t->data_length < local_addr_len) {
245
0
        snmp_log(LOG_ERR, "bad parameters for get bound addr\n");
246
0
        return;
247
0
    }
248
249
0
    addr_pair = (netsnmp_indexed_addr_pair *)t->data;
250
251
    /** get local socket address for client session */
252
0
    rc = getsockname(t->sock, (struct sockaddr*)&addr_pair->local_addr,
253
0
                     &local_addr_len);
254
0
    netsnmp_assert(rc == 0);
255
0
    DEBUGIF("netsnmp_udpbase") {
256
0
        char *str;
257
0
        str = netsnmp_udp_fmtaddr(NULL, (void *)addr_pair,
258
0
                                  sizeof(netsnmp_indexed_addr_pair));
259
0
        DEBUGMSGTL(("netsnmp_udpbase",
260
0
                    "socket %" NETSNMP_FMT_SKT " bound to %s\n",
261
0
                    t->sock, str));
262
0
        free(str);
263
0
    }
264
0
}
265
266
netsnmp_transport *
267
netsnmp_udpipv4base_transport_with_source(const struct netsnmp_ep *ep,
268
                                          int local,
269
                                          const struct netsnmp_ep *src_addr)
270
3.40k
{
271
3.40k
    netsnmp_transport         *t = NULL;
272
3.40k
    const struct netsnmp_ep   *bind_addr;
273
3.40k
    int                        rc, flags = 0;
274
275
3.40k
    t = netsnmp_udpipv4base_transport_init(ep, local);
276
3.40k
    if (NULL == t)
277
0
         return NULL;
278
279
3.40k
    if (local) {
280
3.40k
        bind_addr = ep;
281
3.40k
        flags |= NETSNMP_TSPEC_LOCAL;
282
283
3.40k
#ifndef NETSNMP_NO_SYSTEMD
284
        /*
285
         * Maybe the socket was already provided by systemd...
286
         */
287
3.40k
        t->sock = netsnmp_sd_find_inet_socket(PF_INET, SOCK_DGRAM, -1,
288
3.40k
                                              ntohs(ep->a.sin.sin_port));
289
3.40k
        if (t->sock >= 0)
290
0
            flags |= NETSNMP_TSPEC_PREBOUND;
291
3.40k
#endif
292
3.40k
    }
293
0
    else
294
0
        bind_addr = src_addr;
295
296
3.40k
    if (!NETSNMP_IS_VALID_SOCKET(t->sock))
297
3.40k
        t->sock = netsnmp_udpipv4base_transport_socket(flags);
298
3.40k
    if (!NETSNMP_IS_VALID_SOCKET(t->sock)) {
299
0
        netsnmp_transport_free(t);
300
0
        return NULL;
301
0
    }
302
303
    /*
304
     * If we've been given an address to bind to, then bind to it.
305
     * Otherwise the OS will use "something sensible".
306
     */
307
3.40k
    if (NULL == bind_addr)
308
0
        return t;
309
310
    /* for Linux VRF Traps we try to bind the iface if clientaddr is not set */
311
3.40k
    if (ep && ep->iface[0]) {
312
0
        rc = netsnmp_bindtodevice(t->sock, ep->iface);
313
0
        if (rc)
314
0
            DEBUGMSGTL(("netsnmp_udpbase",
315
0
                        "VRF: Could not bind socket %" NETSNMP_FMT_SKT " to %s\n",
316
0
                        t->sock, ep->iface));
317
0
        else
318
0
            DEBUGMSGTL(("netsnmp_udpbase",
319
0
                        "VRF: Bound socket %" NETSNMP_FMT_SKT " to %s\n",
320
0
                        t->sock, ep->iface));
321
0
    }
322
323
3.40k
    rc = netsnmp_udpipv4base_transport_bind(t, bind_addr, flags);
324
3.40k
    if (rc) {
325
0
        netsnmp_transport_free(t);
326
0
        t = NULL;
327
0
    }
328
3.40k
    else if (!local)
329
0
        netsnmp_udpipv4base_transport_get_bound_addr(t);
330
331
3.40k
    return t;
332
3.40k
}
333
334
netsnmp_transport *
335
netsnmp_udpipv4base_tspec_transport(netsnmp_tdomain_spec *tspec)
336
3.40k
{
337
3.40k
    struct netsnmp_ep addr;
338
3.40k
    int local;
339
340
3.40k
    if (NULL == tspec)
341
0
        return NULL;
342
343
3.40k
    local = tspec->flags & NETSNMP_TSPEC_LOCAL;
344
345
    /** get address from target */
346
3.40k
    if (!netsnmp_sockaddr_in3(&addr, tspec->target, tspec->default_target))
347
0
        return NULL;
348
349
3.40k
    if (NULL != tspec->source) {
350
0
        struct netsnmp_ep src_addr;
351
352
        /** get sockaddr from source */
353
0
        if (!netsnmp_sockaddr_in3(&src_addr, tspec->source, ":0"))
354
0
            return NULL;
355
0
        return netsnmp_udpipv4base_transport_with_source(&addr, local,
356
0
                                                         &src_addr);
357
0
    }
358
359
    /** no source and default client address ok */
360
3.40k
    return netsnmp_udpipv4base_transport(&addr, local);
361
3.40k
}
362
363
netsnmp_transport *
364
netsnmp_udpipv4base_transport(const struct netsnmp_ep *ep, int local)
365
3.40k
{
366
3.40k
    struct netsnmp_ep client_ep;
367
3.40k
    const char *client_addr;
368
3.40k
    int uses_port;
369
370
3.40k
    memset(&client_ep, 0, sizeof(client_ep));
371
3.40k
    client_ep.a.sin.sin_family = AF_INET;
372
373
3.40k
    if (local)
374
3.40k
        goto out;
375
376
0
    client_addr = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
377
0
                                        NETSNMP_DS_LIB_CLIENT_ADDR);
378
0
    if (!client_addr)
379
0
        goto out;
380
381
0
    if (netsnmp_sockaddr_in3(&client_ep, client_addr, ":0") < 0) {
382
0
        snmp_log(LOG_ERR, "Parsing clientaddr %s failed\n", client_addr);
383
0
        goto out;
384
0
    }
385
386
0
    uses_port = netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
387
0
                                       NETSNMP_DS_LIB_CLIENT_ADDR_USES_PORT);
388
0
    if (!uses_port)
389
0
        client_ep.a.sin.sin_port = 0;
390
391
3.40k
out:
392
3.40k
    return netsnmp_udpipv4base_transport_with_source(ep, local, &client_ep);
393
0
}