/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 | } |