/src/net-snmp/snmplib/transports/snmpUDPsharedDomain.c
Line | Count | Source |
1 | | /* UDPshared transport support functions |
2 | | * |
3 | | * Portions of this file are copyrighted by: |
4 | | * Copyright (c) 2016 VMware, Inc. All rights reserved. |
5 | | * Use is subject to license terms specified in the COPYING file |
6 | | * distributed with the Net-SNMP package. |
7 | | */ |
8 | | |
9 | | #include <net-snmp/net-snmp-config.h> |
10 | | |
11 | | #include <net-snmp/types.h> |
12 | | #include "snmpIPBaseDomain.h" |
13 | | #include <net-snmp/library/snmpUDPsharedDomain.h> |
14 | | |
15 | | #include <stddef.h> |
16 | | #include <stdio.h> |
17 | | #include <sys/types.h> |
18 | | #include <ctype.h> |
19 | | #ifdef HAVE_STDLIB_H |
20 | | #include <stdlib.h> |
21 | | #endif |
22 | | #ifdef HAVE_STRING_H |
23 | | #include <string.h> |
24 | | #else |
25 | | #include <strings.h> |
26 | | #endif |
27 | | #ifdef HAVE_SYS_SOCKET_H |
28 | | #include <sys/socket.h> |
29 | | #endif |
30 | | #ifdef HAVE_NETINET_IN_H |
31 | | #include <netinet/in.h> |
32 | | #endif |
33 | | #ifdef HAVE_ARPA_INET_H |
34 | | #include <arpa/inet.h> |
35 | | #endif |
36 | | #ifdef HAVE_NETDB_H |
37 | | #include <netdb.h> |
38 | | #endif |
39 | | #include <errno.h> |
40 | | |
41 | | #include <net-snmp/types.h> |
42 | | #include <net-snmp/library/snmp_debug.h> |
43 | | #include <net-snmp/library/tools.h> |
44 | | #include <net-snmp/library/snmp_assert.h> |
45 | | |
46 | | #include <net-snmp/library/snmpSocketBaseDomain.h> |
47 | | #include <net-snmp/library/snmpUDPDomain.h> |
48 | | #include <net-snmp/library/snmpUDPIPv4BaseDomain.h> |
49 | | |
50 | | netsnmp_feature_require(transport_cache); |
51 | | |
52 | | const oid netsnmpUDPsharedDomain[] = { 1,3,6,1,2,1,100,1,999 }; /** made up */ |
53 | | size_t netsnmpUDPsharedDomain_len = OID_LENGTH(netsnmpUDPsharedDomain); |
54 | | |
55 | | /* *************************************************************************** |
56 | | */ |
57 | | static int |
58 | | _udpshared_close(netsnmp_transport *t) |
59 | 0 | { |
60 | 0 | netsnmp_transport *b = t? t->base_transport : NULL; |
61 | |
|
62 | 0 | DEBUGMSGTL(("udpshared:close", "%p/%p\n", t,b )); |
63 | | |
64 | | /** we don't really have a socket - base transport does */ |
65 | 0 | if (NULL == t || NULL == b) |
66 | 0 | return -1; |
67 | | |
68 | 0 | t->base_transport = NULL; |
69 | 0 | t->sock = -1; /* we just had a copy of base's fd */ |
70 | | |
71 | | /** remove base transport. if it is still in use, return. */ |
72 | 0 | if (netsnmp_transport_cache_remove(b) == 1) |
73 | 0 | DEBUGMSGTL(("udpshared:close", "keeping socket open for %d user(s)\n", |
74 | 0 | b->local_length)); |
75 | 0 | else |
76 | 0 | b->f_close(b); /** close base transport */ |
77 | |
|
78 | 0 | return 0; |
79 | 0 | } |
80 | | |
81 | | static int |
82 | | _udpshared_recv(netsnmp_transport *t, void *buf, int size, |
83 | | void **opaque, int *olength) |
84 | 0 | { |
85 | 0 | if (NULL == t || NULL == t->base_transport) |
86 | 0 | return -1; |
87 | | |
88 | 0 | return t->base_transport->f_recv(t->base_transport, buf, size, opaque, |
89 | 0 | olength); |
90 | 0 | } |
91 | | |
92 | | static int |
93 | | _udpshared_send(netsnmp_transport *t, const void *buf, int size, |
94 | | void **opaque, int *olength) |
95 | 0 | { |
96 | 0 | void *_opaque, **_opaque_p = &_opaque; |
97 | 0 | int _olength, *_olength_p = &_olength; |
98 | |
|
99 | 0 | if (NULL == t || NULL == t->base_transport) |
100 | 0 | return -1; |
101 | | |
102 | | /* |
103 | | * opaque points to an address pair to use to send, overriding the |
104 | | * address pair in the transport. So if no address was specified by |
105 | | * the caller, use the udpshared transport address, overriding the |
106 | | * empty base_transport address. |
107 | | */ |
108 | 0 | if (NULL == opaque || NULL == *opaque) { |
109 | 0 | _opaque = t->data; |
110 | 0 | _olength = t->data_length; |
111 | 0 | } else { |
112 | 0 | _opaque_p = opaque; |
113 | 0 | _olength_p = olength; |
114 | 0 | } |
115 | 0 | return t->base_transport->f_send(t->base_transport, buf, size, _opaque_p, |
116 | 0 | _olength_p); |
117 | 0 | } |
118 | | |
119 | | static char * |
120 | | _udpshared_fmtaddr(netsnmp_transport *t, const void *data, int len) |
121 | 0 | { |
122 | 0 | if (NULL == t || NULL == t->base_transport || |
123 | 0 | NULL == t->base_transport->f_fmtaddr) |
124 | 0 | return strdup("<UNKNOWN>"); |
125 | | |
126 | 0 | return t->base_transport->f_fmtaddr(t->base_transport, data, len); |
127 | 0 | } |
128 | | |
129 | | static int |
130 | | _setup_session(netsnmp_transport *t, netsnmp_session *session) |
131 | 0 | { |
132 | 0 | if (NULL == t || NULL == session) |
133 | 0 | return -1; |
134 | | |
135 | 0 | session->flags |= SNMP_FLAGS_SHARED_SOCKET; |
136 | |
|
137 | 0 | return 0; |
138 | 0 | } |
139 | | |
140 | | static netsnmp_transport * |
141 | | _transport_common(netsnmp_transport *t) |
142 | 0 | { |
143 | 0 | void *save_data; |
144 | 0 | int save_data_len; |
145 | |
|
146 | 0 | DEBUGTRACETOK("9:udpshared"); |
147 | |
|
148 | 0 | if (NULL == t) |
149 | 0 | return NULL; |
150 | | |
151 | | /* |
152 | | * t->data contains remote addr, which can/will vary, so don't |
153 | | * copy it to base transport |
154 | | */ |
155 | 0 | save_data = t->data; |
156 | 0 | save_data_len = t->data_length; |
157 | 0 | t->data = NULL; |
158 | 0 | t->data_length = 0; |
159 | | |
160 | | /** save base transport for clients; need in send/recv functions later */ |
161 | 0 | t->base_transport = netsnmp_transport_copy(t); |
162 | 0 | if (NULL == t->base_transport) { |
163 | 0 | free(save_data); |
164 | 0 | netsnmp_transport_free(t); |
165 | 0 | return NULL; |
166 | 0 | } |
167 | | |
168 | | /** restore remote addr */ |
169 | 0 | t->data = save_data; |
170 | 0 | t->data_length = save_data_len; |
171 | | |
172 | | /** Set UDPsharedDomain specifics */ |
173 | 0 | t->domain = netsnmpUDPsharedDomain; |
174 | 0 | t->domain_length = netsnmpUDPsharedDomain_len; |
175 | |
|
176 | 0 | t->f_recv = _udpshared_recv; |
177 | 0 | t->f_send = _udpshared_send; |
178 | 0 | t->f_close = _udpshared_close; |
179 | 0 | t->f_fmtaddr = _udpshared_fmtaddr; |
180 | 0 | t->f_setup_session = _setup_session; |
181 | 0 | t->flags = NETSNMP_TRANSPORT_FLAG_SHARED; |
182 | 0 | if (t->base_transport->domain == netsnmpUDPDomain) |
183 | 0 | t->f_get_taddr = netsnmp_ipv4_get_taddr; |
184 | 0 | #ifdef NETSNMP_ENABLE_IPV6 |
185 | 0 | else if (t->base_transport->domain == netsnmp_UDPIPv6Domain) |
186 | 0 | t->f_get_taddr = netsnmp_ipv6_get_taddr; |
187 | 0 | #endif |
188 | 0 | else |
189 | 0 | netsnmp_assert(0); |
190 | |
|
191 | 0 | return t; |
192 | 0 | } |
193 | | |
194 | | netsnmp_transport * |
195 | | netsnmp_udpshared_transport(const struct netsnmp_ep *ep, int local) |
196 | 0 | { |
197 | 0 | netsnmp_transport *t = NULL; |
198 | |
|
199 | 0 | t = netsnmp_udp_transport(ep, local); |
200 | 0 | if (NULL == t) |
201 | 0 | return NULL; |
202 | | |
203 | 0 | t = _transport_common(t); |
204 | |
|
205 | 0 | return t; |
206 | 0 | } |
207 | | |
208 | | netsnmp_transport * |
209 | | netsnmp_udpshared_transport_with_source(const struct netsnmp_ep *ep, |
210 | | int flags, |
211 | | const struct netsnmp_ep *src_addr) |
212 | 0 | { |
213 | 0 | netsnmp_transport *t = NULL, *b = NULL; |
214 | 0 | int local = flags & NETSNMP_TSPEC_LOCAL; |
215 | |
|
216 | 0 | DEBUGMSGTL(("udpshared:create", "from addr with source\n")); |
217 | | |
218 | | /** init common parts of parent transport */ |
219 | 0 | t = netsnmp_udpipv4base_transport_init(ep, local); |
220 | 0 | if (NULL == t) |
221 | 0 | return NULL; |
222 | | |
223 | 0 | if (!_transport_common(t)) |
224 | 0 | return NULL; |
225 | | |
226 | 0 | if (!local && src_addr) { |
227 | | /** check for existing base transport */ |
228 | 0 | b = netsnmp_transport_cache_get(PF_INET, SOCK_DGRAM, local, |
229 | 0 | (const void *)src_addr, |
230 | 0 | sizeof(*src_addr)); |
231 | 0 | if (NULL != b && NULL != b->local) { |
232 | | /* |
233 | | * uh-oh. we've assumed sharedudp is just for clients, and we're |
234 | | * using local_length as a reference count. |
235 | | */ |
236 | 0 | snmp_log(LOG_ERR, |
237 | 0 | "sharedudp transport is only for client/remote\n"); |
238 | 0 | netsnmp_transport_free(t); |
239 | 0 | return NULL; |
240 | 0 | } |
241 | 0 | } |
242 | | |
243 | | /** if no base transport found, create one */ |
244 | 0 | if (NULL == b) { |
245 | 0 | b = netsnmp_udp_transport_with_source(ep, local, src_addr); |
246 | 0 | if (NULL == b) { |
247 | 0 | netsnmp_transport_free(t); |
248 | 0 | return NULL; |
249 | 0 | } |
250 | 0 | } |
251 | 0 | ++b->local_length; /* reference count */ |
252 | 0 | t->base_transport = b; |
253 | 0 | t->msgMaxSize = b->msgMaxSize; |
254 | 0 | t->flags |= NETSNMP_TRANSPORT_FLAG_SHARED; |
255 | | |
256 | | /** get local socket address */ |
257 | 0 | if (!local) { |
258 | 0 | t->sock = b->sock; |
259 | 0 | netsnmp_udpipv4base_transport_get_bound_addr(t); |
260 | 0 | } |
261 | | |
262 | | /** cache base transport for future use */ |
263 | 0 | if (!local && src_addr && 1 == b->local_length) { |
264 | 0 | netsnmp_transport_cache_save(PF_INET, SOCK_DGRAM, local, |
265 | 0 | (const void *)src_addr, sizeof(*src_addr), |
266 | 0 | b); |
267 | 0 | } |
268 | |
|
269 | 0 | return t; |
270 | 0 | } |
271 | | |
272 | | #ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN |
273 | | |
274 | | /* |
275 | | * Open a shared UDP transport for SNMP. Local is TRUE if addr is the local |
276 | | * address to bind to (i.e. this is a server-type session); otherwise addr is |
277 | | * the remote address to send things to. |
278 | | */ |
279 | | netsnmp_transport * |
280 | | netsnmp_udpshared6_transport(const struct netsnmp_ep *ep, int local) |
281 | 0 | { |
282 | 0 | netsnmp_transport *t = NULL; |
283 | |
|
284 | 0 | t = netsnmp_udp6_transport(ep, local); |
285 | 0 | if (NULL != t) |
286 | 0 | t = _transport_common(t); |
287 | |
|
288 | 0 | return t; |
289 | 0 | } |
290 | | |
291 | | netsnmp_transport * |
292 | | netsnmp_udpshared6_transport_with_source(const struct netsnmp_ep *ep, |
293 | | int flags, |
294 | | const struct netsnmp_ep *src_addr6) |
295 | 0 | { |
296 | 0 | netsnmp_transport *t = NULL, *b = NULL; |
297 | 0 | int local = flags & NETSNMP_TSPEC_LOCAL; |
298 | |
|
299 | 0 | DEBUGMSGTL(("udpshared:create", "from addr6 with source\n")); |
300 | | |
301 | | /** init common parts of parent transport */ |
302 | 0 | t = netsnmp_udp6_transport_init(ep, local); |
303 | 0 | if (NULL == t) |
304 | 0 | return NULL; |
305 | | |
306 | 0 | if (!_transport_common(t)) |
307 | 0 | return NULL; |
308 | | |
309 | 0 | if (!local && src_addr6) { |
310 | | /** check for existing base transport */ |
311 | 0 | b = netsnmp_transport_cache_get(PF_INET6, SOCK_DGRAM, local, |
312 | 0 | (const void *)src_addr6, |
313 | 0 | sizeof(*src_addr6)); |
314 | 0 | if (NULL != b && NULL != b->local) { |
315 | | /* |
316 | | * uh-oh. we've assumed sharedudp is just for clients, and we're |
317 | | * using local_length as a reference count. |
318 | | */ |
319 | 0 | snmp_log(LOG_ERR, |
320 | 0 | "sharedudp transport is only for client/remote\n"); |
321 | 0 | netsnmp_transport_free(t); |
322 | 0 | return NULL; |
323 | 0 | } |
324 | 0 | } |
325 | | |
326 | | /** if no base transport found, create one */ |
327 | 0 | if (NULL == b) { |
328 | 0 | b = netsnmp_udp6_transport_with_source(ep, local, src_addr6); |
329 | 0 | if (NULL == b) { |
330 | 0 | netsnmp_transport_free(t); |
331 | 0 | return NULL; |
332 | 0 | } |
333 | 0 | } |
334 | 0 | ++b->local_length; /* reference count */ |
335 | 0 | t->base_transport = b; |
336 | 0 | t->flags |= NETSNMP_TRANSPORT_FLAG_SHARED; |
337 | | |
338 | | /** get local socket address */ |
339 | 0 | if (!local) { |
340 | 0 | t->sock = b->sock; |
341 | 0 | netsnmp_udp6_transport_get_bound_addr(t); |
342 | 0 | } |
343 | | |
344 | | /** cache base transport for future use */ |
345 | 0 | if (!local && src_addr6 && 1 == b->local_length) { |
346 | 0 | netsnmp_transport_cache_save(PF_INET6, SOCK_DGRAM, local, |
347 | 0 | (const void *)src_addr6, |
348 | 0 | sizeof(*src_addr6), b); |
349 | 0 | } |
350 | |
|
351 | 0 | return t; |
352 | 0 | } |
353 | | #endif /* NETSNMP_TRANSPORT_UDPIPV6_DOMAIN */ |
354 | | |
355 | | netsnmp_transport * |
356 | | netsnmp_udpshared_create_ostring(const void *o, size_t o_len, int local) |
357 | 0 | { |
358 | 0 | struct netsnmp_ep ep; |
359 | |
|
360 | 0 | DEBUGMSGTL(("udpshared:create", "from ostring\n")); |
361 | |
|
362 | 0 | memset(&ep, 0, sizeof(ep)); |
363 | 0 | if (netsnmp_ipv4_ostring_to_sockaddr(&ep.a.sin, o, o_len)) |
364 | 0 | return netsnmp_udpshared_transport(&ep, local); |
365 | 0 | #ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN |
366 | 0 | else if (netsnmp_ipv6_ostring_to_sockaddr(&ep.a.sin6, o, o_len)) |
367 | 0 | return netsnmp_udpshared6_transport(&ep, local); |
368 | 0 | #endif |
369 | 0 | return NULL; |
370 | 0 | } |
371 | | |
372 | | netsnmp_transport * |
373 | | netsnmp_udpshared_create_tstring(const char *str, int isserver, |
374 | | const char *default_target) |
375 | 0 | { |
376 | 0 | struct netsnmp_ep ep; |
377 | 0 | netsnmp_transport *t; |
378 | |
|
379 | 0 | DEBUGMSGTL(("udpshared:create", "from tstring %s\n", str)); |
380 | |
|
381 | 0 | if (netsnmp_sockaddr_in3(&ep, str, default_target)) |
382 | 0 | t = netsnmp_udpshared_transport(&ep, isserver); |
383 | 0 | #ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN |
384 | 0 | else if (netsnmp_sockaddr_in6_3(&ep, str, default_target)) |
385 | 0 | t = netsnmp_udpshared6_transport(&ep, isserver); |
386 | 0 | #endif |
387 | 0 | else |
388 | 0 | return NULL; |
389 | | |
390 | 0 | return t; |
391 | 0 | } |
392 | | |
393 | | static netsnmp_transport * |
394 | | _tspec_v4(const struct netsnmp_ep *ep, netsnmp_tdomain_spec *tspec) |
395 | 0 | { |
396 | 0 | int local = tspec->flags & NETSNMP_TSPEC_LOCAL; |
397 | |
|
398 | 0 | if (NULL != tspec->source) { |
399 | 0 | struct netsnmp_ep src_addr; |
400 | | |
401 | | /** get sockaddr from source */ |
402 | 0 | if (!netsnmp_sockaddr_in3(&src_addr, tspec->source, NULL)) |
403 | 0 | return NULL; |
404 | 0 | return netsnmp_udpshared_transport_with_source(ep, local, &src_addr); |
405 | 0 | } |
406 | | |
407 | | /** no source and default client address ok */ |
408 | 0 | return netsnmp_udpshared_transport(ep, local); |
409 | 0 | } |
410 | | |
411 | | #ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN |
412 | | static netsnmp_transport * |
413 | | _tspec_v6(const struct netsnmp_ep *ep, netsnmp_tdomain_spec *tspec) |
414 | 0 | { |
415 | 0 | int local = tspec->flags & NETSNMP_TSPEC_LOCAL; |
416 | |
|
417 | 0 | if (NULL != tspec->source) { |
418 | 0 | struct netsnmp_ep src_addr; |
419 | | |
420 | | /** get sockaddr from source */ |
421 | 0 | if (!netsnmp_sockaddr_in6_3(&src_addr, tspec->source, NULL)) |
422 | 0 | return NULL; |
423 | 0 | return netsnmp_udpshared6_transport_with_source(ep, local, &src_addr); |
424 | 0 | } |
425 | | |
426 | | /** no source and default client address ok */ |
427 | 0 | return netsnmp_udpshared6_transport(ep, local); |
428 | 0 | } |
429 | | #endif /* NETSNMP_TRANSPORT_UDPIPV6_DOMAIN */ |
430 | | |
431 | | netsnmp_transport * |
432 | | netsnmp_udpshared_create_tspec(netsnmp_tdomain_spec *tspec) |
433 | 0 | { |
434 | 0 | struct netsnmp_ep ep; |
435 | |
|
436 | 0 | DEBUGMSGTL(("udpshared:create", "from tspec\n")); |
437 | |
|
438 | 0 | if (NULL == tspec) |
439 | 0 | return NULL; |
440 | | |
441 | 0 | if (netsnmp_sockaddr_in3(&ep, tspec->target, tspec->default_target)) |
442 | 0 | return _tspec_v4(&ep, tspec); |
443 | 0 | #ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN |
444 | 0 | else if (netsnmp_sockaddr_in6_3(&ep, tspec->target, tspec->default_target)) |
445 | 0 | return _tspec_v6(&ep, tspec); |
446 | 0 | #endif |
447 | | |
448 | 0 | return NULL; |
449 | 0 | } |
450 | | |
451 | | void |
452 | | netsnmp_udpshared_ctor(void) |
453 | 3.98k | { |
454 | 3.98k | static netsnmp_tdomain domain; |
455 | 3.98k | static int done = 0; |
456 | | |
457 | 3.98k | if (done) |
458 | 3.98k | return; |
459 | 4 | done = 1; |
460 | | |
461 | 4 | domain.name = netsnmpUDPsharedDomain; |
462 | 4 | domain.name_length = netsnmpUDPsharedDomain_len; |
463 | | |
464 | 4 | domain.prefix = calloc(2, sizeof(char *)); |
465 | 4 | if (!domain.prefix) { |
466 | 0 | snmp_log(LOG_ERR, "calloc() failed - out of memory\n"); |
467 | 0 | return; |
468 | 0 | } |
469 | 4 | domain.prefix[0] = "udpshared"; |
470 | | |
471 | 4 | domain.f_create_from_tstring_new = netsnmp_udpshared_create_tstring; |
472 | 4 | domain.f_create_from_tspec = netsnmp_udpshared_create_tspec; |
473 | 4 | domain.f_create_from_ostring = netsnmp_udpshared_create_ostring; |
474 | | |
475 | 4 | netsnmp_tdomain_register(&domain); |
476 | 4 | } |