/src/net-snmp/snmplib/transports/snmpUDPBaseDomain.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* UDP 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 <net-snmp/library/snmpUDPBaseDomain.h> |
17 | | |
18 | | #include <stddef.h> |
19 | | #include <stdio.h> |
20 | | #include <sys/types.h> |
21 | | #include <ctype.h> |
22 | | #ifdef HAVE_STDLIB_H |
23 | | #include <stdlib.h> |
24 | | #endif |
25 | | #ifdef HAVE_STRING_H |
26 | | #include <string.h> |
27 | | #else |
28 | | #include <strings.h> |
29 | | #endif |
30 | | #ifdef HAVE_NETINET_IN_H |
31 | | #include <netinet/in.h> |
32 | | #endif |
33 | | #ifdef HAVE_NET_IF_H |
34 | | #include <net/if.h> |
35 | | #endif |
36 | | #ifdef HAVE_ARPA_INET_H |
37 | | #include <arpa/inet.h> |
38 | | #endif |
39 | | #ifdef HAVE_NETDB_H |
40 | | #include <netdb.h> |
41 | | #endif |
42 | | #ifdef HAVE_SYS_UIO_H |
43 | | #include <sys/uio.h> |
44 | | #endif |
45 | | #ifdef WIN32 |
46 | | #include <mswsock.h> |
47 | | #endif |
48 | | #include <errno.h> |
49 | | |
50 | | #include <net-snmp/types.h> |
51 | | #include <net-snmp/library/snmpSocketBaseDomain.h> |
52 | | #include <net-snmp/library/snmpUDPDomain.h> |
53 | | #include <net-snmp/library/snmp_debug.h> |
54 | | #include <net-snmp/library/tools.h> |
55 | | #include <net-snmp/library/default_store.h> |
56 | | #include <net-snmp/library/system.h> |
57 | | #include <net-snmp/library/snmp_assert.h> |
58 | | |
59 | | #ifndef MSG_DONTWAIT |
60 | | #define MSG_DONTWAIT 0 |
61 | | #endif |
62 | | |
63 | | void |
64 | | _netsnmp_udp_sockopt_set(int fd, int local) |
65 | 2.81k | { |
66 | 2.81k | #ifdef SO_BSDCOMPAT |
67 | | /* |
68 | | * Patch for Linux. Without this, UDP packets that fail get an ICMP |
69 | | * response. Linux turns the failed ICMP response into an error message |
70 | | * and return value, unlike all other OS's. |
71 | | */ |
72 | 2.81k | if (0 == netsnmp_os_prematch("Linux","2.4")) |
73 | 0 | { |
74 | 0 | int one = 1; |
75 | 0 | DEBUGMSGTL(("socket:option", "setting socket option SO_BSDCOMPAT\n")); |
76 | 0 | setsockopt(fd, SOL_SOCKET, SO_BSDCOMPAT, (void *) &one, |
77 | 0 | sizeof(one)); |
78 | 0 | } |
79 | 2.81k | #endif /*SO_BSDCOMPAT */ |
80 | | /* |
81 | | * SO_REUSEADDR will allow multiple apps to open the same port at |
82 | | * the same time. Only the last one to open the socket will get |
83 | | * data. Obviously, for an agent, this is a bad thing. There should |
84 | | * only be one listener. |
85 | | */ |
86 | | #ifdef ALLOW_PORT_HIJACKING |
87 | | #ifdef SO_REUSEADDR |
88 | | /* |
89 | | * Allow the same port to be specified multiple times without failing. |
90 | | * (useful for a listener) |
91 | | */ |
92 | | { |
93 | | int one = 1; |
94 | | DEBUGMSGTL(("socket:option", "setting socket option SO_REUSEADDR\n")); |
95 | | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) &one, |
96 | | sizeof(one)); |
97 | | } |
98 | | #endif /*SO_REUSEADDR */ |
99 | | #endif |
100 | | |
101 | | /* |
102 | | * Try to set the send and receive buffers to a reasonably large value, so |
103 | | * that we can send and receive big PDUs (defaults to 8192 bytes (!) on |
104 | | * Solaris, for instance). Don't worry too much about errors -- just |
105 | | * plough on regardless. |
106 | | */ |
107 | 2.81k | netsnmp_sock_buffer_set(fd, SO_SNDBUF, local, 0); |
108 | 2.81k | netsnmp_sock_buffer_set(fd, SO_RCVBUF, local, 0); |
109 | 2.81k | } |
110 | | |
111 | | #if defined(HAVE_IP_PKTINFO) || (defined(HAVE_IP_RECVDSTADDR) && defined(HAVE_IP_SENDSRCADDR)) |
112 | | |
113 | | #define netsnmp_udpbase_recvfrom_sendto_defined |
114 | | |
115 | | enum { |
116 | | #if defined(HAVE_IP_PKTINFO) |
117 | | cmsg_data_size = sizeof(struct in_pktinfo) |
118 | | #elif defined(HAVE_IP_RECVDSTADDR) |
119 | | cmsg_data_size = sizeof(struct in_addr) |
120 | | #endif |
121 | | }; |
122 | | |
123 | | #ifdef WIN32 |
124 | | #ifndef WSAID_WSASENDMSG |
125 | | #define WSAID_WSASENDMSG \ |
126 | | {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}} |
127 | | typedef INT (WINAPI * LPFN_WSASENDMSG)(SOCKET, LPWSAMSG, DWORD, LPDWORD, |
128 | | LPWSAOVERLAPPED, |
129 | | LPWSAOVERLAPPED_COMPLETION_ROUTINE); |
130 | | #endif |
131 | | |
132 | | static LPFN_WSARECVMSG pfWSARecvMsg; |
133 | | static LPFN_WSASENDMSG pfWSASendMsg; |
134 | | #endif |
135 | | |
136 | | int |
137 | | netsnmp_udpbase_recvfrom(int s, void *buf, int len, struct sockaddr *from, |
138 | | socklen_t *fromlen, struct sockaddr *dstip, |
139 | | socklen_t *dstlen, int *if_index) |
140 | 0 | { |
141 | 0 | int r; |
142 | 0 | #if !defined(WIN32) |
143 | 0 | struct iovec iov; |
144 | 0 | char cmsg[CMSG_SPACE(cmsg_data_size)]; |
145 | 0 | struct cmsghdr *cm; |
146 | 0 | struct msghdr msg; |
147 | |
|
148 | 0 | iov.iov_base = buf; |
149 | 0 | iov.iov_len = len; |
150 | |
|
151 | 0 | memset(&msg, 0, sizeof msg); |
152 | 0 | msg.msg_name = from; |
153 | 0 | msg.msg_namelen = *fromlen; |
154 | 0 | msg.msg_iov = &iov; |
155 | 0 | msg.msg_iovlen = 1; |
156 | 0 | msg.msg_control = &cmsg; |
157 | 0 | msg.msg_controllen = sizeof(cmsg); |
158 | |
|
159 | 0 | r = recvmsg(s, &msg, MSG_DONTWAIT); |
160 | | #else /* !defined(WIN32) */ |
161 | | WSABUF wsabuf; |
162 | | char cmsg[WSA_CMSG_SPACE(sizeof(struct in_pktinfo))]; |
163 | | WSACMSGHDR *cm; |
164 | | WSAMSG msg; |
165 | | DWORD bytes_received; |
166 | | |
167 | | wsabuf.buf = buf; |
168 | | wsabuf.len = len; |
169 | | |
170 | | msg.name = from; |
171 | | msg.namelen = *fromlen; |
172 | | msg.lpBuffers = &wsabuf; |
173 | | msg.dwBufferCount = 1; |
174 | | msg.Control.len = sizeof(cmsg); |
175 | | msg.Control.buf = cmsg; |
176 | | msg.dwFlags = 0; |
177 | | |
178 | | if (pfWSARecvMsg) { |
179 | | r = pfWSARecvMsg(s, &msg, &bytes_received, NULL, NULL) == 0 ? |
180 | | bytes_received : -1; |
181 | | *fromlen = msg.namelen; |
182 | | } else { |
183 | | r = recvfrom(s, buf, len, MSG_DONTWAIT, from, fromlen); |
184 | | } |
185 | | #endif /* !defined(WIN32) */ |
186 | |
|
187 | 0 | if (r == -1) { |
188 | 0 | return -1; |
189 | 0 | } |
190 | | |
191 | 0 | DEBUGMSGTL(("udpbase:recv", "got source addr: %s\n", |
192 | 0 | inet_ntoa(((struct sockaddr_in *)from)->sin_addr))); |
193 | |
|
194 | 0 | { |
195 | | /* Get the local port number for use in diagnostic messages */ |
196 | 0 | int r2 = getsockname(s, dstip, dstlen); |
197 | 0 | netsnmp_assert(r2 == 0); |
198 | 0 | } |
199 | |
|
200 | 0 | #if !defined(WIN32) |
201 | 0 | for (cm = CMSG_FIRSTHDR(&msg); cm != NULL; cm = CMSG_NXTHDR(&msg, cm)) { |
202 | 0 | #if defined(HAVE_IP_PKTINFO) |
203 | 0 | if (cm->cmsg_level == SOL_IP && cm->cmsg_type == IP_PKTINFO) { |
204 | 0 | struct in_pktinfo* src = (struct in_pktinfo *)CMSG_DATA(cm); |
205 | 0 | netsnmp_assert(dstip->sa_family == AF_INET); |
206 | 0 | ((struct sockaddr_in*)dstip)->sin_addr = src->ipi_addr; |
207 | 0 | *if_index = src->ipi_ifindex; |
208 | 0 | DEBUGMSGTL(("udpbase:recv", |
209 | 0 | "got destination (local) addr %s, iface %d\n", |
210 | 0 | inet_ntoa(src->ipi_addr), *if_index)); |
211 | 0 | } |
212 | | #elif defined(HAVE_IP_RECVDSTADDR) |
213 | | if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_RECVDSTADDR) { |
214 | | struct in_addr* src = (struct in_addr *)CMSG_DATA(cm); |
215 | | ((struct sockaddr_in*)dstip)->sin_addr = *src; |
216 | | DEBUGMSGTL(("netsnmp_udp", "got destination (local) addr %s\n", |
217 | | inet_ntoa(*src))); |
218 | | } |
219 | | #endif |
220 | 0 | } |
221 | | #else /* !defined(WIN32) */ |
222 | | for (cm = WSA_CMSG_FIRSTHDR(&msg); cm; cm = WSA_CMSG_NXTHDR(&msg, cm)) { |
223 | | if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_PKTINFO) { |
224 | | struct in_pktinfo* src = (struct in_pktinfo *)WSA_CMSG_DATA(cm); |
225 | | netsnmp_assert(dstip->sa_family == AF_INET); |
226 | | ((struct sockaddr_in*)dstip)->sin_addr = src->ipi_addr; |
227 | | *if_index = src->ipi_ifindex; |
228 | | DEBUGMSGTL(("udpbase:recv", |
229 | | "got destination (local) addr %s, iface %d\n", |
230 | | inet_ntoa(src->ipi_addr), *if_index)); |
231 | | } |
232 | | } |
233 | | #endif /* !defined(WIN32) */ |
234 | 0 | return r; |
235 | 0 | } |
236 | | |
237 | | #if !defined(WIN32) |
238 | | int netsnmp_udpbase_sendto_unix(int fd, const struct in_addr *srcip, |
239 | | int if_index, const struct sockaddr *remote, |
240 | | const void *data, int len) |
241 | 0 | { |
242 | 0 | struct iovec iov; |
243 | 0 | struct msghdr m = { NULL }; |
244 | 0 | char cmsg[CMSG_SPACE(cmsg_data_size)]; |
245 | 0 | int rc; |
246 | 0 | char iface[IFNAMSIZ]; |
247 | 0 | socklen_t ifacelen = IFNAMSIZ; |
248 | |
|
249 | 0 | iov.iov_base = NETSNMP_REMOVE_CONST(void *, data); |
250 | 0 | iov.iov_len = len; |
251 | |
|
252 | 0 | m.msg_name = NETSNMP_REMOVE_CONST(void *, remote); |
253 | 0 | m.msg_namelen = sizeof(struct sockaddr_in); |
254 | 0 | m.msg_iov = &iov; |
255 | 0 | m.msg_iovlen = 1; |
256 | 0 | m.msg_flags = 0; |
257 | |
|
258 | 0 | if (srcip && srcip->s_addr != INADDR_ANY) { |
259 | 0 | struct cmsghdr *cm; |
260 | 0 | struct in_pktinfo ipi; |
261 | 0 | int use_sendto = FALSE; |
262 | |
|
263 | 0 | memset(cmsg, 0, sizeof(cmsg)); |
264 | |
|
265 | 0 | m.msg_control = &cmsg; |
266 | 0 | m.msg_controllen = sizeof(cmsg); |
267 | |
|
268 | 0 | cm = CMSG_FIRSTHDR(&m); |
269 | 0 | cm->cmsg_len = CMSG_LEN(cmsg_data_size); |
270 | |
|
271 | 0 | #if defined(HAVE_IP_PKTINFO) |
272 | 0 | cm->cmsg_level = SOL_IP; |
273 | 0 | cm->cmsg_type = IP_PKTINFO; |
274 | |
|
275 | 0 | memset(&ipi, 0, sizeof(ipi)); |
276 | 0 | #ifdef HAVE_SO_BINDTODEVICE |
277 | | /* |
278 | | * For asymmetric multihomed users, we only set ifindex to 0 to |
279 | | * let kernel handle return if there was no iface bound to the |
280 | | * socket. |
281 | | */ |
282 | 0 | if (getsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, iface, |
283 | 0 | &ifacelen) != 0) { |
284 | 0 | DEBUGMSGTL(("udpbase:sendto", |
285 | 0 | "getsockopt SO_BINDTODEVICE failed: %s\n", |
286 | 0 | strerror(errno))); |
287 | 0 | } else if (ifacelen == 0) { |
288 | 0 | DEBUGMSGTL(("udpbase:sendto", |
289 | 0 | "sendto: SO_BINDTODEVICE not set\n")); |
290 | 0 | } else { |
291 | 0 | DEBUGMSGTL(("udpbase:sendto", |
292 | 0 | "sendto: SO_BINDTODEVICE dev=%s using ifindex=%d\n", |
293 | 0 | iface, if_index)); |
294 | 0 | use_sendto = TRUE; |
295 | 0 | } |
296 | 0 | #endif /* HAVE_SO_BINDTODEVICE */ |
297 | |
|
298 | 0 | #ifdef HAVE_STRUCT_IN_PKTINFO_IPI_SPEC_DST |
299 | 0 | DEBUGMSGTL(("udpbase:sendto", "sending from %s\n", |
300 | 0 | inet_ntoa(*srcip))); |
301 | 0 | ipi.ipi_spec_dst.s_addr = srcip->s_addr; |
302 | | #else |
303 | | DEBUGMSGTL(("udpbase:sendto", "ignoring from address %s\n", |
304 | | inet_ntoa(*srcip))); |
305 | | #endif |
306 | 0 | memcpy(CMSG_DATA(cm), &ipi, sizeof(ipi)); |
307 | | |
308 | | /* |
309 | | * For Linux and VRF, use sendto() instead of sendmsg(). Do not pass a |
310 | | * cmsg with IP_PKTINFO set because that would override the bind to |
311 | | * VRF which is set by 'vrf exec' command. That would break VRF. |
312 | | */ |
313 | 0 | if (use_sendto) |
314 | 0 | rc = sendto(fd, data, len, MSG_DONTWAIT, remote, |
315 | 0 | sizeof(struct sockaddr)); |
316 | 0 | else |
317 | 0 | rc = sendmsg(fd, &m, MSG_DONTWAIT); |
318 | 0 | if (rc >= 0 || (errno != EINVAL && errno != ENETUNREACH)) |
319 | 0 | return rc; |
320 | | |
321 | | /* |
322 | | * The error might be caused by broadcast srcip (i.e. we're responding |
323 | | * to a broadcast request) - sendmsg does not like it. Try to resend it |
324 | | * using the interface on which it was received |
325 | | */ |
326 | | |
327 | 0 | DEBUGMSGTL(("udpbase:sendto", "re-sending on iface %d\n", if_index)); |
328 | |
|
329 | 0 | { |
330 | 0 | struct in_pktinfo ipi; |
331 | |
|
332 | 0 | memset(&ipi, 0, sizeof(ipi)); |
333 | 0 | ipi.ipi_ifindex = if_index; |
334 | 0 | #ifdef HAVE_STRUCT_IN_PKTINFO_IPI_SPEC_DST |
335 | 0 | ipi.ipi_spec_dst.s_addr = INADDR_ANY; |
336 | 0 | #endif |
337 | 0 | memcpy(CMSG_DATA(cm), &ipi, sizeof(ipi)); |
338 | 0 | } |
339 | | #elif defined(HAVE_IP_SENDSRCADDR) |
340 | | cm->cmsg_level = IPPROTO_IP; |
341 | | cm->cmsg_type = IP_SENDSRCADDR; |
342 | | memcpy((struct in_addr *)CMSG_DATA(cm), srcip, sizeof(struct in_addr)); |
343 | | #endif |
344 | 0 | rc = sendmsg(fd, &m, MSG_DONTWAIT); |
345 | 0 | if (rc >= 0 || errno != EINVAL) |
346 | 0 | return rc; |
347 | | |
348 | 0 | DEBUGMSGTL(("udpbase:sendto", "re-sending without source address\n")); |
349 | 0 | m.msg_control = NULL; |
350 | 0 | m.msg_controllen = 0; |
351 | 0 | } |
352 | | |
353 | 0 | return sendmsg(fd, &m, MSG_DONTWAIT); |
354 | 0 | } |
355 | | #else /* !defined(WIN32) */ |
356 | | int netsnmp_udpbase_sendto_win32(int fd, const struct in_addr *srcip, |
357 | | int if_index, const struct sockaddr *remote, |
358 | | const void *data, int len) |
359 | | { |
360 | | WSABUF wsabuf; |
361 | | WSAMSG m; |
362 | | char cmsg[WSA_CMSG_SPACE(sizeof(struct in_pktinfo))]; |
363 | | DWORD bytes_sent; |
364 | | int rc; |
365 | | |
366 | | wsabuf.buf = NETSNMP_REMOVE_CONST(void *, data); |
367 | | wsabuf.len = len; |
368 | | |
369 | | memset(&m, 0, sizeof(m)); |
370 | | m.name = NETSNMP_REMOVE_CONST(struct sockaddr *, remote); |
371 | | m.namelen = sizeof(struct sockaddr_in); |
372 | | m.lpBuffers = &wsabuf; |
373 | | m.dwBufferCount = 1; |
374 | | |
375 | | if (pfWSASendMsg && srcip && srcip->s_addr != INADDR_ANY) { |
376 | | WSACMSGHDR *cm; |
377 | | |
378 | | DEBUGMSGTL(("udpbase:sendto", "sending from [%d] %s\n", if_index, |
379 | | inet_ntoa(*srcip))); |
380 | | |
381 | | memset(cmsg, 0, sizeof(cmsg)); |
382 | | |
383 | | m.Control.buf = cmsg; |
384 | | m.Control.len = sizeof(cmsg); |
385 | | |
386 | | cm = WSA_CMSG_FIRSTHDR(&m); |
387 | | cm->cmsg_len = WSA_CMSG_LEN(cmsg_data_size); |
388 | | cm->cmsg_level = IPPROTO_IP; |
389 | | cm->cmsg_type = IP_PKTINFO; |
390 | | |
391 | | { |
392 | | struct in_pktinfo ipi = { 0 }; |
393 | | ipi.ipi_ifindex = if_index; |
394 | | ipi.ipi_addr.s_addr = srcip->s_addr; |
395 | | memcpy(WSA_CMSG_DATA(cm), &ipi, sizeof(ipi)); |
396 | | } |
397 | | |
398 | | rc = pfWSASendMsg(fd, &m, 0, &bytes_sent, NULL, NULL); |
399 | | if (rc == 0) |
400 | | return bytes_sent; |
401 | | DEBUGMSGTL(("udpbase:sendto", "sending from [%d] %s failed: %d\n", |
402 | | if_index, inet_ntoa(*srcip), WSAGetLastError())); |
403 | | } |
404 | | rc = sendto(fd, data, len, 0, remote, sizeof(struct sockaddr)); |
405 | | return rc; |
406 | | } |
407 | | #endif /* !defined(WIN32) */ |
408 | | |
409 | | int netsnmp_udpbase_sendto(int fd, const struct in_addr *srcip, int if_index, |
410 | | const struct sockaddr *remote, const void *data, |
411 | | int len) |
412 | 0 | { |
413 | 0 | #if !defined(WIN32) |
414 | 0 | return netsnmp_udpbase_sendto_unix(fd, srcip, if_index, remote, data, len); |
415 | | #else /* !defined(WIN32) */ |
416 | | return netsnmp_udpbase_sendto_win32(fd, srcip, if_index, remote, data, len); |
417 | | #endif /* !defined(WIN32) */ |
418 | 0 | } |
419 | | #endif /* HAVE_IP_PKTINFO || HAVE_IP_RECVDSTADDR */ |
420 | | |
421 | | /* |
422 | | * You can write something into opaque that will subsequently get passed back |
423 | | * to your send function if you like. For instance, you might want to |
424 | | * remember where a PDU came from, so that you can send a reply there... |
425 | | */ |
426 | | |
427 | | int |
428 | | netsnmp_udpbase_recv(netsnmp_transport *t, void *buf, int size, |
429 | | void **opaque, int *olength) |
430 | 0 | { |
431 | 0 | int rc = -1; |
432 | 0 | socklen_t fromlen = sizeof(netsnmp_sockaddr_storage); |
433 | 0 | netsnmp_indexed_addr_pair *addr_pair = NULL; |
434 | 0 | struct sockaddr *from; |
435 | |
|
436 | 0 | if (t != NULL && t->sock >= 0) { |
437 | 0 | addr_pair = SNMP_MALLOC_TYPEDEF(netsnmp_indexed_addr_pair); |
438 | 0 | if (addr_pair == NULL) { |
439 | 0 | *opaque = NULL; |
440 | 0 | *olength = 0; |
441 | 0 | return -1; |
442 | 0 | } else |
443 | 0 | from = &addr_pair->remote_addr.sa; |
444 | | |
445 | 0 | while (rc < 0) { |
446 | 0 | #ifdef netsnmp_udpbase_recvfrom_sendto_defined |
447 | 0 | socklen_t local_addr_len = sizeof(addr_pair->local_addr); |
448 | 0 | rc = netsnmp_udp_recvfrom(t->sock, buf, size, from, &fromlen, |
449 | 0 | &addr_pair->local_addr.sa, |
450 | 0 | &local_addr_len, &(addr_pair->if_index)); |
451 | | #else |
452 | | rc = recvfrom(t->sock, buf, size, MSG_DONTWAIT, from, &fromlen); |
453 | | #endif /* netsnmp_udpbase_recvfrom_sendto_defined */ |
454 | 0 | if (rc < 0 && errno != EINTR) { |
455 | 0 | break; |
456 | 0 | } |
457 | 0 | } |
458 | |
|
459 | 0 | if (rc >= 0) { |
460 | 0 | DEBUGIF("netsnmp_udp") { |
461 | 0 | char *str = netsnmp_udp_fmtaddr( |
462 | 0 | NULL, addr_pair, sizeof(netsnmp_indexed_addr_pair)); |
463 | 0 | DEBUGMSGTL(("netsnmp_udp", |
464 | 0 | "recvfrom fd %d got %d bytes (from %s)\n", |
465 | 0 | t->sock, rc, str)); |
466 | 0 | free(str); |
467 | 0 | } |
468 | 0 | } else { |
469 | 0 | DEBUGMSGTL(("netsnmp_udp", "recvfrom fd %d err %d (\"%s\")\n", |
470 | 0 | t->sock, errno, strerror(errno))); |
471 | 0 | } |
472 | 0 | *opaque = (void *)addr_pair; |
473 | 0 | *olength = sizeof(netsnmp_indexed_addr_pair); |
474 | 0 | } |
475 | 0 | return rc; |
476 | 0 | } |
477 | | |
478 | | |
479 | | |
480 | | int |
481 | | netsnmp_udpbase_send(netsnmp_transport *t, const void *buf, int size, |
482 | | void **opaque, int *olength) |
483 | 0 | { |
484 | 0 | int rc = -1; |
485 | 0 | const netsnmp_indexed_addr_pair *addr_pair = NULL; |
486 | 0 | const struct sockaddr *to = NULL; |
487 | |
|
488 | 0 | if (opaque != NULL && *opaque != NULL && NULL != olength && |
489 | 0 | ((*olength == sizeof(netsnmp_indexed_addr_pair) || |
490 | 0 | (*olength == sizeof(struct sockaddr_in))))) { |
491 | 0 | addr_pair = (const netsnmp_indexed_addr_pair *) (*opaque); |
492 | 0 | } else if (t != NULL && t->data != NULL && |
493 | 0 | t->data_length == sizeof(netsnmp_indexed_addr_pair)) { |
494 | 0 | addr_pair = (netsnmp_indexed_addr_pair *) (t->data); |
495 | 0 | } else { |
496 | 0 | int len = -1; |
497 | 0 | if (opaque != NULL && *opaque != NULL && NULL != olength) |
498 | 0 | len = *olength; |
499 | 0 | else if (t != NULL && t->data != NULL) |
500 | 0 | len = t->data_length; |
501 | 0 | snmp_log(LOG_ERR, "unknown addr type of size %d\n", len); |
502 | 0 | return SNMPERR_GENERR; |
503 | 0 | } |
504 | | |
505 | 0 | to = &addr_pair->remote_addr.sa; |
506 | |
|
507 | 0 | if (to != NULL && t != NULL && t->sock >= 0) { |
508 | 0 | DEBUGIF("netsnmp_udp") { |
509 | 0 | char *str = netsnmp_udp_fmtaddr(NULL, addr_pair, |
510 | 0 | sizeof(netsnmp_indexed_addr_pair)); |
511 | 0 | DEBUGMSGTL(("netsnmp_udp", "send %d bytes from %p to %s on fd %d\n", |
512 | 0 | size, buf, str, t->sock)); |
513 | 0 | free(str); |
514 | 0 | } |
515 | 0 | while (rc < 0) { |
516 | 0 | #ifdef netsnmp_udpbase_recvfrom_sendto_defined |
517 | 0 | rc = netsnmp_udp_sendto(t->sock, |
518 | 0 | addr_pair ? &(addr_pair->local_addr.sin.sin_addr) : NULL, |
519 | 0 | addr_pair ? addr_pair->if_index : 0, to, buf, size); |
520 | | #else |
521 | | rc = sendto(t->sock, buf, size, 0, to, sizeof(struct sockaddr)); |
522 | | #endif /* netsnmp_udpbase_recvfrom_sendto_defined */ |
523 | 0 | if (rc < 0 && errno != EINTR) { |
524 | 0 | DEBUGMSGTL(("netsnmp_udp", "sendto error, rc %d (errno %d)\n", |
525 | 0 | rc, errno)); |
526 | 0 | break; |
527 | 0 | } |
528 | 0 | } |
529 | 0 | } |
530 | 0 | return rc; |
531 | 0 | } |
532 | | |
533 | | void |
534 | | netsnmp_udp_base_ctor(void) |
535 | 0 | { |
536 | | #if defined(WIN32) && defined(HAVE_IP_PKTINFO) |
537 | | SOCKET s = socket(AF_INET, SOCK_DGRAM, 0); |
538 | | GUID WSARecvMsgGuid = WSAID_WSARECVMSG; |
539 | | GUID WSASendMsgGuid = WSAID_WSASENDMSG; |
540 | | DWORD nbytes; |
541 | | int result; |
542 | | |
543 | | netsnmp_static_assert(sizeof(in_addr_t) == |
544 | | sizeof((struct sockaddr_in *)NULL)->sin_addr); |
545 | | netsnmp_assert(s != SOCKET_ERROR); |
546 | | /* WSARecvMsg(): Windows XP / Windows Server 2003 and later */ |
547 | | result = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, |
548 | | &WSARecvMsgGuid, sizeof(WSARecvMsgGuid), |
549 | | &pfWSARecvMsg, sizeof(pfWSARecvMsg), &nbytes, NULL, NULL); |
550 | | if (result == SOCKET_ERROR) |
551 | | DEBUGMSGTL(("netsnmp_udp", "WSARecvMsg() not found (errno %d)\n", |
552 | | WSAGetLastError())); |
553 | | |
554 | | /* WSASendMsg(): Windows Vista / Windows Server 2008 and later */ |
555 | | result = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, |
556 | | &WSASendMsgGuid, sizeof(WSASendMsgGuid), |
557 | | &pfWSASendMsg, sizeof(pfWSASendMsg), &nbytes, NULL, NULL); |
558 | | if (result == SOCKET_ERROR) |
559 | | DEBUGMSGTL(("netsnmp_udp", "WSASendMsg() not found (errno %d)\n", |
560 | | WSAGetLastError())); |
561 | | |
562 | | closesocket(s); |
563 | | #endif |
564 | 0 | } |