Coverage Report

Created: 2024-02-25 06:16

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