/src/net-snmp/snmplib/snmp_transport.c
Line | Count | Source |
1 | | /* |
2 | | * Portions of this file are copyrighted by: |
3 | | * Copyright (c) 2016 VMware, Inc. All rights reserved. |
4 | | * Use is subject to license terms specified in the COPYING file |
5 | | * distributed with the Net-SNMP package. |
6 | | */ |
7 | | #include <net-snmp/net-snmp-config.h> |
8 | | #include <net-snmp/net-snmp-features.h> |
9 | | |
10 | | #include <net-snmp/types.h> |
11 | | #include <net-snmp/library/snmp_transport.h> |
12 | | |
13 | | #include <stdio.h> |
14 | | #ifdef HAVE_STRING_H |
15 | | #include <string.h> |
16 | | #else |
17 | | #include <strings.h> |
18 | | #endif |
19 | | #include <sys/types.h> |
20 | | |
21 | | #ifdef HAVE_STDLIB_H |
22 | | #include <stdlib.h> |
23 | | #endif |
24 | | |
25 | | #include <ctype.h> |
26 | | |
27 | | #ifdef HAVE_UNISTD_H |
28 | | #include <unistd.h> |
29 | | #endif |
30 | | |
31 | | #include <net-snmp/output_api.h> |
32 | | #include <net-snmp/utilities.h> |
33 | | |
34 | | #include <net-snmp/library/default_store.h> |
35 | | |
36 | | #include <net-snmp/library/snmpUDPDomain.h> |
37 | | #ifdef NETSNMP_TRANSPORT_TLSBASE_DOMAIN |
38 | | #include <net-snmp/library/snmpTLSBaseDomain.h> |
39 | | #endif |
40 | | #ifdef NETSNMP_TRANSPORT_TLSTCP_DOMAIN |
41 | | #include <net-snmp/library/snmpTLSTCPDomain.h> |
42 | | #endif |
43 | | #ifdef NETSNMP_TRANSPORT_STD_DOMAIN |
44 | | #include <net-snmp/library/snmpSTDDomain.h> |
45 | | #endif |
46 | | #ifdef NETSNMP_TRANSPORT_TCP_DOMAIN |
47 | | #include <net-snmp/library/snmpTCPDomain.h> |
48 | | #endif |
49 | | #ifdef NETSNMP_TRANSPORT_DTLSUDP_DOMAIN |
50 | | #include <net-snmp/library/snmpDTLSUDPDomain.h> |
51 | | #endif |
52 | | #ifdef NETSNMP_TRANSPORT_SSH_DOMAIN |
53 | | #include <net-snmp/library/snmpSSHDomain.h> |
54 | | #endif |
55 | | #ifdef NETSNMP_TRANSPORT_ALIAS_DOMAIN |
56 | | #include <net-snmp/library/snmpAliasDomain.h> |
57 | | #endif |
58 | | #ifdef NETSNMP_TRANSPORT_IPX_DOMAIN |
59 | | #include <net-snmp/library/snmpIPXDomain.h> |
60 | | #endif |
61 | | #ifdef NETSNMP_TRANSPORT_UNIX_DOMAIN |
62 | | #include <net-snmp/library/snmpUnixDomain.h> |
63 | | #endif |
64 | | #ifdef NETSNMP_TRANSPORT_AAL5PVC_DOMAIN |
65 | | #include <net-snmp/library/snmpAAL5PVCDomain.h> |
66 | | #endif |
67 | | #ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN |
68 | | #include <net-snmp/library/snmpUDPIPv6Domain.h> |
69 | | #endif |
70 | | #ifdef NETSNMP_TRANSPORT_TCPIPV6_DOMAIN |
71 | | #include <net-snmp/library/snmpTCPIPv6Domain.h> |
72 | | #endif |
73 | | #ifdef NETSNMP_TRANSPORT_UDPSHARED_DOMAIN |
74 | | #include <net-snmp/library/snmpUDPsharedDomain.h> |
75 | | #endif |
76 | | #include <net-snmp/library/snmp_api.h> |
77 | | #include <net-snmp/library/snmp_service.h> |
78 | | #include <net-snmp/library/read_config.h> |
79 | | |
80 | | netsnmp_feature_child_of(transport_all, libnetsnmp); |
81 | | |
82 | | netsnmp_feature_child_of(tdomain_support, transport_all); |
83 | | netsnmp_feature_child_of(tdomain_transport_oid, transport_all); |
84 | | netsnmp_feature_child_of(sockaddr_size, transport_all); |
85 | | netsnmp_feature_child_of(transport_cache, transport_all); |
86 | | |
87 | | /* |
88 | | * Our list of supported transport domains. |
89 | | */ |
90 | | |
91 | | static netsnmp_tdomain *domain_list = NULL; |
92 | | |
93 | | |
94 | | |
95 | | /* |
96 | | * The standard SNMP domains. |
97 | | */ |
98 | | |
99 | | const oid netsnmpUDPDomain[] = { 1, 3, 6, 1, 6, 1, 1 }; |
100 | | size_t netsnmpUDPDomain_len = OID_LENGTH(netsnmpUDPDomain); |
101 | | const oid netsnmpCLNSDomain[] = { 1, 3, 6, 1, 6, 1, 2 }; |
102 | | size_t netsnmpCLNSDomain_len = OID_LENGTH(netsnmpCLNSDomain); |
103 | | const oid netsnmpCONSDomain[] = { 1, 3, 6, 1, 6, 1, 3 }; |
104 | | size_t netsnmpCONSDomain_len = OID_LENGTH(netsnmpCONSDomain); |
105 | | const oid netsnmpDDPDomain[] = { 1, 3, 6, 1, 6, 1, 4 }; |
106 | | size_t netsnmpDDPDomain_len = OID_LENGTH(netsnmpDDPDomain); |
107 | | const oid netsnmpIPXDomain[] = { 1, 3, 6, 1, 6, 1, 5 }; |
108 | | size_t netsnmpIPXDomain_len = OID_LENGTH(netsnmpIPXDomain); |
109 | | |
110 | | static netsnmp_container *_container = NULL; |
111 | | |
112 | | |
113 | | static void netsnmp_tdomain_dump(void); |
114 | | |
115 | | |
116 | | #if !defined(NETSNMP_FEATURE_REMOVE_FILTER_SOURCE) |
117 | | static netsnmp_container * filtered = NULL; |
118 | | |
119 | | void netsnmp_transport_parse_filter(const char *word, char *cptr); |
120 | | #endif /* NETSNMP_FEATURE_REMOVE_FILTER_SOURCE */ |
121 | | |
122 | | void |
123 | | init_snmp_transport(void) |
124 | 4.25k | { |
125 | 4.25k | netsnmp_ds_register_config(ASN_BOOLEAN, |
126 | 4.25k | "snmp", "dontLoadHostConfig", |
127 | 4.25k | NETSNMP_DS_LIBRARY_ID, |
128 | 4.25k | NETSNMP_DS_LIB_DONT_LOAD_HOST_FILES); |
129 | 4.25k | #ifndef NETSNMP_FEATURE_REMOVE_FILTER_SOURCE |
130 | 4.25k | register_app_config_handler("sourceFilterType", |
131 | 4.25k | netsnmp_transport_parse_filterType, |
132 | 4.25k | NULL, "none|whitelist|blacklist"); |
133 | 4.25k | register_app_config_handler("sourceFilterAddress", |
134 | 4.25k | netsnmp_transport_parse_filter, |
135 | 4.25k | netsnmp_transport_filter_cleanup, |
136 | 4.25k | "host"); |
137 | 4.25k | #endif /* NETSNMP_FEATURE_REMOVE_FILTER_SOURCE */ |
138 | 4.25k | } |
139 | | |
140 | | void |
141 | | shutdown_snmp_transport(void) |
142 | 4.25k | { |
143 | 4.25k | #ifndef NETSNMP_FEATURE_REMOVE_FILTER_SOURCE |
144 | 4.25k | netsnmp_transport_filter_cleanup(); |
145 | 4.25k | #endif |
146 | 4.25k | } |
147 | | |
148 | | /* |
149 | | * Make a deep copy of an netsnmp_transport. |
150 | | */ |
151 | | netsnmp_transport * |
152 | | netsnmp_transport_copy(const netsnmp_transport *t) |
153 | 0 | { |
154 | 0 | netsnmp_transport *n = NULL; |
155 | |
|
156 | 0 | if (t == NULL) { |
157 | 0 | return NULL; |
158 | 0 | } |
159 | | |
160 | 0 | n = netsnmp_transport_alloc(); |
161 | 0 | if (n == NULL) { |
162 | 0 | return NULL; |
163 | 0 | } |
164 | | |
165 | 0 | if (t->domain != NULL) { |
166 | 0 | n->domain = t->domain; |
167 | 0 | n->domain_length = t->domain_length; |
168 | 0 | } else { |
169 | 0 | n->domain = NULL; |
170 | 0 | n->domain_length = 0; |
171 | 0 | } |
172 | |
|
173 | 0 | if (t->local != NULL) { |
174 | 0 | n->local = netsnmp_memdup(t->local, t->local_length); |
175 | 0 | if (n->local == NULL) { |
176 | 0 | netsnmp_transport_free(n); |
177 | 0 | return NULL; |
178 | 0 | } |
179 | 0 | n->local_length = t->local_length; |
180 | 0 | } else { |
181 | 0 | n->local = NULL; |
182 | 0 | n->local_length = 0; |
183 | 0 | } |
184 | | |
185 | 0 | if (t->remote != NULL) { |
186 | 0 | n->remote = netsnmp_memdup(t->remote, t->remote_length); |
187 | 0 | if (n->remote == NULL) { |
188 | 0 | netsnmp_transport_free(n); |
189 | 0 | return NULL; |
190 | 0 | } |
191 | 0 | n->remote_length = t->remote_length; |
192 | 0 | } else { |
193 | 0 | n->remote = NULL; |
194 | 0 | n->remote_length = 0; |
195 | 0 | } |
196 | | |
197 | 0 | if (t->data != NULL && t->data_length > 0) { |
198 | 0 | n->data = netsnmp_memdup(t->data, t->data_length); |
199 | 0 | if (n->data == NULL) { |
200 | 0 | netsnmp_transport_free(n); |
201 | 0 | return NULL; |
202 | 0 | } |
203 | 0 | n->data_length = t->data_length; |
204 | 0 | } else { |
205 | 0 | n->data = NULL; |
206 | 0 | n->data_length = 0; |
207 | 0 | } |
208 | | |
209 | 0 | n->msgMaxSize = t->msgMaxSize; |
210 | 0 | n->f_accept = t->f_accept; |
211 | 0 | n->f_recv = t->f_recv; |
212 | 0 | n->f_send = t->f_send; |
213 | 0 | n->f_close = t->f_close; |
214 | 0 | n->f_pending = t->f_pending; |
215 | 0 | n->f_copy = t->f_copy; |
216 | 0 | n->f_config = t->f_config; |
217 | 0 | n->f_fmtaddr = t->f_fmtaddr; |
218 | 0 | n->sock = t->sock; |
219 | 0 | n->flags = t->flags; |
220 | 0 | n->base_transport = netsnmp_transport_copy(t->base_transport); |
221 | | |
222 | | /* give the transport a chance to do "special things" */ |
223 | 0 | if (t->f_copy) |
224 | 0 | t->f_copy(t, n); |
225 | | |
226 | 0 | return n; |
227 | 0 | } |
228 | | |
229 | | netsnmp_transport * |
230 | | netsnmp_transport_alloc(void) |
231 | 6.99k | { |
232 | 6.99k | netsnmp_transport *transport; |
233 | | |
234 | 6.99k | transport = SNMP_MALLOC_TYPEDEF(netsnmp_transport); |
235 | 6.99k | if (!transport) |
236 | 0 | return NULL; |
237 | 6.99k | transport->sock = NETSNMP_INVALID_SOCKET; |
238 | 6.99k | return transport; |
239 | 6.99k | } |
240 | | |
241 | | void |
242 | | netsnmp_transport_free(netsnmp_transport *t) |
243 | 13.8k | { |
244 | 13.8k | if (NULL == t) |
245 | 6.91k | return; |
246 | | |
247 | 6.91k | #ifndef FEATURE_REMOVE_TRANSPORT_CACHE |
248 | | /** don't free a transport that is currently shared */ |
249 | 6.91k | if (netsnmp_transport_cache_remove(t) == 1) |
250 | 0 | return; |
251 | 6.91k | #endif |
252 | | |
253 | 6.91k | SNMP_FREE(t->local); |
254 | 6.91k | SNMP_FREE(t->remote); |
255 | 6.91k | SNMP_FREE(t->data); |
256 | 6.91k | netsnmp_transport_free(t->base_transport); |
257 | | |
258 | 6.91k | SNMP_FREE(t); |
259 | 6.91k | } |
260 | | |
261 | | /* |
262 | | * netsnmp_transport_peer_string |
263 | | * |
264 | | * returns string representation of peer address. |
265 | | * |
266 | | * caller is responsible for freeing the allocated string. |
267 | | */ |
268 | | char * |
269 | | netsnmp_transport_peer_string(netsnmp_transport *t, const void *data, int len) |
270 | 2.85k | { |
271 | 2.85k | char *str; |
272 | | |
273 | 2.85k | if (NULL == t) |
274 | 0 | return NULL; |
275 | | |
276 | 2.85k | if (t->f_fmtaddr != NULL) |
277 | 0 | str = t->f_fmtaddr(t, data, len); |
278 | 2.85k | else |
279 | 2.85k | str = strdup("<UNKNOWN>"); |
280 | | |
281 | 2.85k | return str; |
282 | 2.85k | } |
283 | | |
284 | | #if !defined(NETSNMP_FEATURE_REMOVE_FILTER_SOURCE) |
285 | | static int _transport_filter_init(void) |
286 | 1 | { |
287 | 1 | if (filtered) |
288 | 0 | return 0; |
289 | | |
290 | 1 | filtered = netsnmp_container_find("transport_filter:cstring"); |
291 | 1 | if (NULL == filtered) { |
292 | 0 | NETSNMP_LOGONCE((LOG_WARNING, |
293 | 0 | "couldn't allocate container for transport_filter list\n")); |
294 | 0 | return -1; |
295 | 0 | } |
296 | 1 | filtered->container_name = strdup("transport_filter list"); |
297 | | |
298 | 1 | return 0; |
299 | 1 | } |
300 | | |
301 | | int |
302 | | netsnmp_transport_filter_add(const char *addrtxt) |
303 | 442 | { |
304 | 442 | char *tmp; |
305 | 442 | int res; |
306 | | |
307 | | /* |
308 | | * create the container, if needed |
309 | | */ |
310 | 442 | if (!filtered && _transport_filter_init()) { |
311 | 0 | snmp_log(LOG_ERR,"netsnmp_transport_filter_add %s failed\n", |
312 | 0 | addrtxt); |
313 | 0 | return (-1); |
314 | 0 | } |
315 | 442 | tmp = strdup(addrtxt); |
316 | 442 | if (NULL == tmp) { |
317 | 0 | snmp_log(LOG_ERR,"netsnmp_transport_filter_add strdup failed\n"); |
318 | 0 | return(-1); |
319 | 0 | } |
320 | 442 | res = CONTAINER_INSERT(filtered, tmp); |
321 | 442 | if (res) |
322 | 148 | free(tmp); |
323 | 442 | return res; |
324 | 442 | } |
325 | | |
326 | | int |
327 | | netsnmp_transport_filter_remove(const char *addrtxt) |
328 | 0 | { |
329 | | /* |
330 | | * create the container, if needed |
331 | | */ |
332 | 0 | if (NULL == filtered) |
333 | 0 | return -1; |
334 | 0 | return CONTAINER_REMOVE(filtered, addrtxt); |
335 | 0 | } |
336 | | |
337 | | /* |
338 | | * netsnmp_transport_filter_check |
339 | | * |
340 | | * returns 1 if the specified address string is in the filter list |
341 | | */ |
342 | | int |
343 | | netsnmp_transport_filter_check(const char *addrtxt) |
344 | 0 | { |
345 | 0 | char *addr; |
346 | 0 | if (NULL == filtered) |
347 | 0 | return 0; |
348 | 0 | addr = CONTAINER_FIND(filtered, addrtxt); |
349 | 0 | return addr ? 1 : 0; |
350 | 0 | } |
351 | | |
352 | | void |
353 | | netsnmp_transport_parse_filterType(const char *word, char *cptr) |
354 | 284 | { |
355 | 284 | int type = 42; |
356 | 284 | if (strcmp(cptr,"whitelist") == 0) |
357 | 46 | type = 1; |
358 | 238 | else if (strcmp(cptr,"blacklist") == 0) |
359 | 10 | type = -1; |
360 | 228 | else if (strcmp(cptr,"none") == 0) |
361 | 10 | type = 0; |
362 | 218 | else |
363 | 218 | netsnmp_config_error("unknown source filter type: %s", cptr); |
364 | | |
365 | 284 | if (type != 42) { |
366 | 66 | DEBUGMSGTL(("transport:filterType", "set to %d\n", type)); |
367 | 66 | netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, |
368 | 66 | NETSNMP_DS_LIB_FILTER_TYPE, type); |
369 | 66 | } |
370 | 284 | } |
371 | | |
372 | | void |
373 | | netsnmp_transport_parse_filter(const char *word, char *cptr) |
374 | 442 | { |
375 | 442 | if (netsnmp_transport_filter_add(cptr)) |
376 | 148 | netsnmp_config_error("cannot create source filter: %s", cptr); |
377 | 442 | } |
378 | | |
379 | | void |
380 | | netsnmp_transport_filter_cleanup(void) |
381 | 14.7k | { |
382 | 14.7k | if (NULL == filtered) |
383 | 14.7k | return; |
384 | 0 | CONTAINER_CLEAR(filtered, filtered->free_item, NULL); |
385 | 0 | CONTAINER_FREE(filtered); |
386 | 0 | filtered = NULL; |
387 | 0 | } |
388 | | #endif /* NETSNMP_FEATURE_REMOVE_FILTER_SOURCE */ |
389 | | |
390 | | |
391 | | #ifndef NETSNMP_FEATURE_REMOVE_SOCKADDR_SIZE |
392 | | int |
393 | | netsnmp_sockaddr_size(const struct sockaddr *sa) |
394 | 0 | { |
395 | 0 | if (NULL == sa) |
396 | 0 | return 0; |
397 | | |
398 | 0 | switch (sa->sa_family) { |
399 | 0 | case AF_INET: |
400 | 0 | return sizeof(struct sockaddr_in); |
401 | 0 | break; |
402 | 0 | #ifdef NETSNMP_ENABLE_IPV6 |
403 | 0 | case AF_INET6: |
404 | 0 | return sizeof(struct sockaddr_in6); |
405 | 0 | break; |
406 | 0 | #endif |
407 | 0 | } |
408 | | |
409 | 0 | return 0; |
410 | 0 | } |
411 | | #endif /* NETSNMP_FEATURE_REMOVE_SOCKADDR_SIZE */ |
412 | | |
413 | | int |
414 | | netsnmp_transport_send(netsnmp_transport *t, const void *packet, int length, |
415 | | void **opaque, int *olength) |
416 | 20 | { |
417 | 20 | int dumpPacket, debugLength; |
418 | | |
419 | 20 | if ((NULL == t) || (NULL == t->f_send)) { |
420 | 0 | DEBUGMSGTL(("transport:pkt:send", "NULL transport or send function\n")); |
421 | 0 | return SNMPERR_GENERR; |
422 | 0 | } |
423 | | |
424 | 20 | dumpPacket = netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, |
425 | 20 | NETSNMP_DS_LIB_DUMP_PACKET); |
426 | 20 | debugLength = (SNMPERR_SUCCESS == |
427 | 20 | debug_is_token_registered("transport:send")); |
428 | | |
429 | 20 | if (dumpPacket | debugLength) { |
430 | 0 | char *str = netsnmp_transport_peer_string(t, |
431 | 0 | opaque ? *opaque : NULL, |
432 | 0 | olength ? *olength : 0); |
433 | 0 | if (debugLength) |
434 | 0 | DEBUGMSGT_NC(("transport:send","%lu bytes to %s\n", |
435 | 0 | (unsigned long)length, str)); |
436 | 0 | if (dumpPacket) |
437 | 0 | snmp_log(LOG_DEBUG, "\nSending %lu bytes to %s\n", |
438 | 0 | (unsigned long)length, str); |
439 | 0 | SNMP_FREE(str); |
440 | 0 | } |
441 | 20 | if (dumpPacket) |
442 | 0 | xdump(packet, length, ""); |
443 | | |
444 | 20 | return t->f_send(t, packet, length, opaque, olength); |
445 | 20 | } |
446 | | |
447 | | int |
448 | | netsnmp_transport_recv(netsnmp_transport *t, void *packet, int length, |
449 | | void **opaque, int *olength) |
450 | 3.36k | { |
451 | 3.36k | int debugLength; |
452 | | |
453 | 3.36k | if ((NULL == t) || (NULL == t->f_recv)) { |
454 | 0 | DEBUGMSGTL(("transport:recv", "NULL transport or recv function\n")); |
455 | 0 | return SNMPERR_GENERR; |
456 | 0 | } |
457 | | |
458 | 3.36k | length = t->f_recv(t, packet, length, opaque, olength); |
459 | | |
460 | 3.36k | if (length <=0) |
461 | 471 | return length; /* don't log timeouts/socket closed */ |
462 | | |
463 | 2.89k | debugLength = (SNMPERR_SUCCESS == |
464 | 2.89k | debug_is_token_registered("transport:recv")); |
465 | | |
466 | 2.89k | if (debugLength) { |
467 | 2.85k | char *str = netsnmp_transport_peer_string(t, |
468 | 2.85k | opaque ? *opaque : NULL, |
469 | 2.85k | olength ? *olength : 0); |
470 | 2.85k | DEBUGMSGT_NC(("transport:recv","%d bytes from %s\n", length, str)); |
471 | 2.85k | SNMP_FREE(str); |
472 | 2.85k | } |
473 | | |
474 | 2.89k | return length; |
475 | 3.36k | } |
476 | | |
477 | | |
478 | | |
479 | | #ifndef NETSNMP_FEATURE_REMOVE_TDOMAIN_SUPPORT |
480 | | int |
481 | | netsnmp_tdomain_support(const oid * in_oid, |
482 | | size_t in_len, |
483 | | const oid ** out_oid, size_t * out_len) |
484 | 0 | { |
485 | 0 | netsnmp_tdomain *d = NULL; |
486 | |
|
487 | 0 | for (d = domain_list; d != NULL; d = d->next) { |
488 | 0 | if (netsnmp_oid_equals(in_oid, in_len, d->name, d->name_length) == 0) { |
489 | 0 | if (out_oid != NULL && out_len != NULL) { |
490 | 0 | *out_oid = d->name; |
491 | 0 | *out_len = d->name_length; |
492 | 0 | } |
493 | 0 | return 1; |
494 | 0 | } |
495 | 0 | } |
496 | 0 | return 0; |
497 | 0 | } |
498 | | #endif /* NETSNMP_FEATURE_REMOVE_TDOMAIN_SUPPORT */ |
499 | | |
500 | | |
501 | | void |
502 | | netsnmp_tdomain_init(void) |
503 | 4.25k | { |
504 | 4.25k | DEBUGMSGTL(("tdomain", "netsnmp_tdomain_init() called\n")); |
505 | | |
506 | | /* include the configure generated list of constructor calls */ |
507 | 4.25k | #include "transports/snmp_transport_inits.h" |
508 | | |
509 | 4.25k | netsnmp_tdomain_dump(); |
510 | | |
511 | | |
512 | 4.25k | } |
513 | | |
514 | | void |
515 | | netsnmp_clear_tdomain_list(void) |
516 | 7.69k | { |
517 | 7.69k | netsnmp_tdomain *list = domain_list, *next = NULL; |
518 | 7.69k | DEBUGMSGTL(("tdomain", "clear_tdomain_list() called\n")); |
519 | | |
520 | 54.5k | while (list != NULL) { |
521 | 46.8k | next = list->next; |
522 | 46.8k | SNMP_FREE(list->prefix); |
523 | | /* attention!! list itself is not in the heap, so we must not free it! */ |
524 | 46.8k | list = next; |
525 | 46.8k | } |
526 | 7.69k | domain_list = NULL; |
527 | 7.69k | } |
528 | | |
529 | | |
530 | | static void |
531 | | netsnmp_tdomain_dump(void) |
532 | 4.25k | { |
533 | 4.25k | netsnmp_tdomain *d; |
534 | 4.25k | int i = 0; |
535 | | |
536 | 4.25k | DEBUGMSGTL(("tdomain", "domain_list -> ")); |
537 | 51.1k | for (d = domain_list; d != NULL; d = d->next) { |
538 | 46.8k | DEBUGMSG(("tdomain", "{ ")); |
539 | 46.8k | DEBUGMSGOID(("tdomain", d->name, d->name_length)); |
540 | 46.8k | DEBUGMSG(("tdomain", ", \"")); |
541 | 136k | for (i = 0; d->prefix[i] != NULL; i++) { |
542 | 89.4k | DEBUGMSG(("tdomain", "%s%s", d->prefix[i], |
543 | 89.4k | (d->prefix[i + 1]) ? "/" : "")); |
544 | 89.4k | } |
545 | 46.8k | DEBUGMSG(("tdomain", "\" } -> ")); |
546 | 46.8k | } |
547 | 4.25k | DEBUGMSG(("tdomain", "[NIL]\n")); |
548 | 4.25k | } |
549 | | |
550 | | |
551 | | |
552 | | int |
553 | | netsnmp_tdomain_register(netsnmp_tdomain *n) |
554 | 46.8k | { |
555 | 46.8k | netsnmp_tdomain **prevNext = &domain_list, *d; |
556 | | |
557 | 46.8k | if (n != NULL) { |
558 | 281k | for (d = domain_list; d != NULL; d = d->next) { |
559 | 234k | if (netsnmp_oid_equals(n->name, n->name_length, |
560 | 234k | d->name, d->name_length) == 0) { |
561 | | /* |
562 | | * Already registered. |
563 | | */ |
564 | 0 | return 0; |
565 | 0 | } |
566 | 234k | prevNext = &(d->next); |
567 | 234k | } |
568 | 46.8k | n->next = NULL; |
569 | 46.8k | *prevNext = n; |
570 | 46.8k | return 1; |
571 | 46.8k | } else { |
572 | 0 | return 0; |
573 | 0 | } |
574 | 46.8k | } |
575 | | |
576 | | |
577 | | |
578 | | netsnmp_feature_child_of(tdomain_unregister, netsnmp_unused); |
579 | | #ifndef NETSNMP_FEATURE_REMOVE_TDOMAIN_UNREGISTER |
580 | | int |
581 | | netsnmp_tdomain_unregister(netsnmp_tdomain *n) |
582 | 0 | { |
583 | 0 | netsnmp_tdomain **prevNext = &domain_list, *d; |
584 | |
|
585 | 0 | if (n != NULL) { |
586 | 0 | for (d = domain_list; d != NULL; d = d->next) { |
587 | 0 | if (netsnmp_oid_equals(n->name, n->name_length, |
588 | 0 | d->name, d->name_length) == 0) { |
589 | 0 | *prevNext = n->next; |
590 | 0 | SNMP_FREE(n->prefix); |
591 | 0 | return 1; |
592 | 0 | } |
593 | 0 | prevNext = &(d->next); |
594 | 0 | } |
595 | 0 | return 0; |
596 | 0 | } else { |
597 | 0 | return 0; |
598 | 0 | } |
599 | 0 | } |
600 | | #endif /* NETSNMP_FEATURE_REMOVE_TDOMAIN_UNREGISTER */ |
601 | | |
602 | | |
603 | | static netsnmp_tdomain * |
604 | | find_tdomain(const char* spec) |
605 | 3.52k | { |
606 | 3.52k | netsnmp_tdomain *d; |
607 | 28.4k | for (d = domain_list; d != NULL; d = d->next) { |
608 | 28.3k | int i; |
609 | 88.5k | for (i = 0; d->prefix[i] != NULL; i++) |
610 | 63.6k | if (strcasecmp(d->prefix[i], spec) == 0) { |
611 | 3.48k | DEBUGMSGTL(("tdomain", |
612 | 3.48k | "Found domain \"%s\" from specifier \"%s\"\n", |
613 | 3.48k | d->prefix[0], spec)); |
614 | 3.48k | return d; |
615 | 3.48k | } |
616 | 28.3k | } |
617 | 47 | DEBUGMSGTL(("tdomain", "Found no domain from specifier \"%s\"\n", spec)); |
618 | 47 | return NULL; |
619 | 3.52k | } |
620 | | |
621 | | static int |
622 | | netsnmp_is_fqdn(const char *thename) |
623 | 3.48k | { |
624 | 3.48k | if (!thename) |
625 | 0 | return 0; |
626 | 3.90k | while(*thename) { |
627 | 470 | if (*thename != '.' && !isupper((unsigned char)*thename) && |
628 | 329 | !islower((unsigned char)*thename) && |
629 | 329 | !isdigit((unsigned char)*thename) && *thename != '-') { |
630 | 47 | return 0; |
631 | 47 | } |
632 | 423 | thename++; |
633 | 423 | } |
634 | 3.43k | return 1; |
635 | 3.48k | } |
636 | | |
637 | | /* |
638 | | * Locate the appropriate transport domain and call the create function for |
639 | | * it. |
640 | | */ |
641 | | netsnmp_transport * |
642 | | netsnmp_tdomain_transport_tspec(netsnmp_tdomain_spec *tspec) |
643 | 3.48k | { |
644 | 3.48k | const char *application, *str, *default_domain, *default_target, *source; |
645 | 3.48k | int local; |
646 | 3.48k | netsnmp_tdomain *match = NULL; |
647 | 3.48k | const char *addr = NULL; |
648 | 3.48k | const char * const *spec = NULL; |
649 | 3.48k | int any_found = 0; |
650 | 3.48k | char buf[SNMP_MAXPATH]; |
651 | 3.48k | const char **lspec = NULL; |
652 | 3.48k | char *tokenized_domain = NULL; |
653 | | |
654 | 3.48k | application = tspec->application; |
655 | 3.48k | str = tspec->target; |
656 | 3.48k | local = tspec->flags & NETSNMP_TSPEC_LOCAL; |
657 | 3.48k | default_domain = tspec->default_domain; |
658 | 3.48k | default_target = tspec->default_target; |
659 | 3.48k | source = tspec->source; |
660 | | /** transport_config = tspec->transport_config; not used yet */ |
661 | | |
662 | 3.48k | DEBUGMSGTL(("tdomain", |
663 | 3.48k | "tdomain_transport_spec(\"%s\", \"%s\", %d, \"%s\", \"%s\", \"%s\")\n", |
664 | 3.48k | application, str ? str : "[NIL]", local, |
665 | 3.48k | default_domain ? default_domain : "[NIL]", |
666 | 3.48k | default_target ? default_target : "[NIL]", |
667 | 3.48k | source ? source : "[NIL]")); |
668 | | |
669 | | /* see if we can load a host-name specific set of conf files */ |
670 | 3.48k | if (!netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, |
671 | 3.48k | NETSNMP_DS_LIB_DONT_LOAD_HOST_FILES) && |
672 | 3.48k | netsnmp_is_fqdn(str)) { |
673 | 3.43k | static int have_added_handler = 0; |
674 | 3.43k | char *newhost; |
675 | 3.43k | struct config_line *config_handlers; |
676 | 3.43k | struct config_files file_names; |
677 | 3.43k | char *prev_hostname; |
678 | | |
679 | | /* register a "transport" specifier */ |
680 | 3.43k | if (!have_added_handler) { |
681 | 1 | have_added_handler = 1; |
682 | 1 | netsnmp_ds_register_config(ASN_OCTET_STR, |
683 | 1 | "snmp", "transport", |
684 | 1 | NETSNMP_DS_LIBRARY_ID, |
685 | 1 | NETSNMP_DS_LIB_HOSTNAME); |
686 | 1 | } |
687 | | |
688 | | /* we save on specific setting that we don't allow to change |
689 | | from one transport creation to the next; ie, we don't want |
690 | | the "transport" specifier to be a default. It should be a |
691 | | single invocation use only */ |
692 | 3.43k | prev_hostname = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
693 | 3.43k | NETSNMP_DS_LIB_HOSTNAME); |
694 | 3.43k | if (prev_hostname) |
695 | 0 | prev_hostname = strdup(prev_hostname); |
696 | | |
697 | | /* read in the hosts/STRING.conf files */ |
698 | 3.43k | config_handlers = read_config_get_handlers("snmp"); |
699 | 3.43k | snprintf(buf, sizeof(buf)-1, "hosts/%s", str); |
700 | 3.43k | file_names.fileHeader = buf; |
701 | 3.43k | file_names.start = config_handlers; |
702 | 3.43k | file_names.next = NULL; |
703 | 3.43k | DEBUGMSGTL(("tdomain", "checking for host specific config %s\n", |
704 | 3.43k | buf)); |
705 | 3.43k | read_config_files_of_type(EITHER_CONFIG, &file_names); |
706 | | |
707 | 3.43k | if (NULL != |
708 | 3.43k | (newhost = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
709 | 3.43k | NETSNMP_DS_LIB_HOSTNAME))) { |
710 | 0 | strlcpy(buf, newhost, sizeof(buf)); |
711 | 0 | str = buf; |
712 | 0 | } |
713 | | |
714 | 3.43k | netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, |
715 | 3.43k | NETSNMP_DS_LIB_HOSTNAME, |
716 | 3.43k | prev_hostname); |
717 | 3.43k | SNMP_FREE(prev_hostname); |
718 | 3.43k | } |
719 | | |
720 | | /* First try - assume that there is a domain in str (domain:target) */ |
721 | | |
722 | 3.48k | if (str != NULL) { |
723 | 3.48k | const char *cp; |
724 | 3.48k | if ((cp = strchr(str, ':')) != NULL) { |
725 | 47 | char* mystring = (char*)malloc(cp + 1 - str); |
726 | 47 | if (mystring == NULL) |
727 | 0 | return NULL; |
728 | 47 | memcpy(mystring, str, cp - str); |
729 | 47 | mystring[cp - str] = '\0'; |
730 | 47 | addr = cp + 1; |
731 | | |
732 | 47 | match = find_tdomain(mystring); |
733 | 47 | free(mystring); |
734 | 47 | } |
735 | 3.48k | } |
736 | | |
737 | | /* |
738 | | * Second try, if there is no domain in str (target), then try the |
739 | | * default domain |
740 | | */ |
741 | | |
742 | 3.48k | if (match == NULL) { |
743 | 3.48k | addr = str; |
744 | 3.48k | if (addr && *addr == '/') { |
745 | 0 | DEBUGMSGTL(("tdomain", |
746 | 0 | "Address starts with '/', so assume \"unix\" " |
747 | 0 | "domain\n")); |
748 | 0 | match = find_tdomain("unix"); |
749 | 3.48k | } else if (default_domain) { |
750 | 0 | DEBUGMSGTL(("tdomain", |
751 | 0 | "Use user specified default domain \"%s\"\n", |
752 | 0 | default_domain)); |
753 | 0 | if (!strchr(default_domain, ',')) |
754 | 0 | match = find_tdomain(default_domain); |
755 | 0 | else { |
756 | 0 | int commas = 0; |
757 | 0 | const char *cp = default_domain; |
758 | 0 | char *ptr = NULL; |
759 | 0 | tokenized_domain = strdup(default_domain); |
760 | 0 | if (!tokenized_domain) |
761 | 0 | return NULL; |
762 | | |
763 | 0 | while (*++cp) if (*cp == ',') commas++; |
764 | 0 | lspec = calloc(commas+2, sizeof(char *)); |
765 | 0 | if (!lspec) { |
766 | 0 | free(tokenized_domain); |
767 | 0 | return NULL; |
768 | 0 | } |
769 | 0 | commas = 1; |
770 | 0 | lspec[0] = strtok_r(tokenized_domain, ",", &ptr); |
771 | 0 | while ((lspec[commas++] = strtok_r(NULL, ",", &ptr))) |
772 | 0 | ; |
773 | 0 | spec = lspec; |
774 | 0 | } |
775 | 3.48k | } else { |
776 | 3.48k | spec = netsnmp_lookup_default_domains(application); |
777 | 3.48k | if (spec == NULL) { |
778 | 47 | DEBUGMSGTL(("tdomain", |
779 | 47 | "No default domain found, assume \"udp\"\n")); |
780 | 47 | match = find_tdomain("udp"); |
781 | 3.43k | } else { |
782 | 3.43k | const char * const * r = spec; |
783 | 3.43k | DEBUGMSGTL(("tdomain", |
784 | 3.43k | "Use application default domains")); |
785 | 10.3k | while(*r) { |
786 | 6.87k | DEBUGMSG(("tdomain", " \"%s\"", *r)); |
787 | 6.87k | ++r; |
788 | 6.87k | } |
789 | 3.43k | DEBUGMSG(("tdomain", "\n")); |
790 | 3.43k | } |
791 | 3.48k | } |
792 | 3.48k | } |
793 | | |
794 | 6.91k | for(;;) { |
795 | 6.91k | if (match) { |
796 | 3.48k | netsnmp_transport *t = NULL; |
797 | 3.48k | const char* addr2; |
798 | | |
799 | 3.48k | any_found = 1; |
800 | | /* |
801 | | * Ok, we know what domain to try, lets see what default data |
802 | | * should be used with it |
803 | | */ |
804 | 3.48k | if (default_target != NULL) |
805 | 0 | addr2 = default_target; |
806 | 3.48k | else |
807 | 3.48k | addr2 = netsnmp_lookup_default_target(application, |
808 | 3.48k | match->prefix[0]); |
809 | 3.48k | DEBUGMSGTL(("tdomain", |
810 | 3.48k | "trying domain \"%s\" address \"%s\" " |
811 | 3.48k | "default address \"%s\"\n", |
812 | 3.48k | match->prefix[0], addr ? addr : "[NIL]", |
813 | 3.48k | addr2 ? addr2 : "[NIL]")); |
814 | 3.48k | if (match->f_create_from_tspec) { |
815 | 3.48k | netsnmp_tdomain_spec tspec_tmp; |
816 | 3.48k | memcpy(&tspec_tmp, tspec, sizeof(tspec_tmp)); |
817 | | /** if we didn't have a default target but looked one up, |
818 | | * copy the spec and use the found default. */ |
819 | 3.48k | if ((default_target == NULL) && (addr2 != NULL)) |
820 | 3.43k | tspec_tmp.default_target = addr2; |
821 | 3.48k | if (addr != tspec_tmp.target) |
822 | 0 | tspec_tmp.target = addr; |
823 | 3.48k | t = match->f_create_from_tspec(&tspec_tmp); |
824 | 3.48k | } |
825 | 0 | else { |
826 | | #if 0 /** remove warning until all transports implement tspec */ |
827 | | NETSNMP_LOGONCE((LOG_WARNING, |
828 | | "transport domain %s uses deprecated f_create function\n", |
829 | | match->prefix[0])); |
830 | | #endif |
831 | 0 | if (match->f_create_from_tstring) { |
832 | 0 | t = match->f_create_from_tstring(addr, local); |
833 | 0 | } |
834 | 0 | else |
835 | 0 | t = match->f_create_from_tstring_new(addr, local, addr2); |
836 | 0 | } |
837 | 3.48k | if (t) { |
838 | 3.48k | if (lspec) { |
839 | 0 | free(tokenized_domain); |
840 | 0 | free(lspec); |
841 | 0 | } |
842 | 3.48k | return t; |
843 | 3.48k | } |
844 | 3.48k | } |
845 | 3.43k | addr = str; |
846 | 3.43k | if (spec && *spec) |
847 | 3.43k | match = find_tdomain(*spec++); |
848 | 0 | else |
849 | 0 | break; |
850 | 3.43k | } |
851 | 0 | if (!any_found) |
852 | 0 | snmp_log(LOG_ERR, "No support for any checked transport domain\n"); |
853 | 0 | if (lspec) { |
854 | 0 | free(tokenized_domain); |
855 | 0 | free(lspec); |
856 | 0 | } |
857 | 0 | return NULL; |
858 | 3.48k | } |
859 | | |
860 | | netsnmp_transport * |
861 | | netsnmp_tdomain_transport_full(const char *application, |
862 | | const char *str, int local, |
863 | | const char *default_domain, |
864 | | const char *default_target) |
865 | 3.48k | { |
866 | 3.48k | netsnmp_tdomain_spec tspec; |
867 | 3.48k | memset(&tspec, 0x0, sizeof(tspec)); |
868 | 3.48k | tspec.application = application; |
869 | 3.48k | tspec.target = str; |
870 | 3.48k | if (local) |
871 | 3.48k | tspec.flags |= NETSNMP_TSPEC_LOCAL; |
872 | 3.48k | tspec.default_domain = default_domain; |
873 | 3.48k | tspec.default_target = default_target; |
874 | 3.48k | tspec.source = NULL; |
875 | 3.48k | tspec.transport_config = NULL; |
876 | 3.48k | return netsnmp_tdomain_transport_tspec(&tspec); |
877 | 3.48k | } |
878 | | |
879 | | netsnmp_transport * |
880 | | netsnmp_tdomain_transport(const char *str, int local, |
881 | | const char *default_domain) |
882 | 0 | { |
883 | 0 | netsnmp_tdomain_spec tspec; |
884 | 0 | memset(&tspec, 0x0, sizeof(tspec)); |
885 | 0 | tspec.application = "snmp"; |
886 | 0 | tspec.target = str; |
887 | 0 | if (local) |
888 | 0 | tspec.flags |= NETSNMP_TSPEC_LOCAL; |
889 | 0 | tspec.default_domain = default_domain; |
890 | 0 | tspec.default_target = NULL; |
891 | 0 | tspec.source = NULL; |
892 | 0 | tspec.transport_config = NULL; |
893 | 0 | return netsnmp_tdomain_transport_tspec(&tspec); |
894 | 0 | } |
895 | | |
896 | | #ifndef NETSNMP_FEATURE_REMOVE_TDOMAIN_TRANSPORT_OID |
897 | | /* |
898 | | * The format of @dom and @o follows the TDomain and TAddress textual |
899 | | * conventions from RFC 2579. For the actual TAddress format definitions, |
900 | | * see e.g. SnmpUDPAddress in RFC 1906. |
901 | | */ |
902 | | netsnmp_transport * |
903 | | netsnmp_tdomain_transport_oid(const oid * dom, |
904 | | size_t dom_len, |
905 | | const u_char * o, size_t o_len, int local) |
906 | 0 | { |
907 | 0 | netsnmp_tdomain *d; |
908 | 0 | int i; |
909 | |
|
910 | 0 | DEBUGMSGTL(("tdomain", "domain \"")); |
911 | 0 | DEBUGMSGOID(("tdomain", dom, dom_len)); |
912 | 0 | DEBUGMSG(("tdomain", "\"\n")); |
913 | |
|
914 | 0 | for (d = domain_list; d != NULL; d = d->next) { |
915 | 0 | for (i = 0; d->prefix[i] != NULL; i++) { |
916 | 0 | if (netsnmp_oid_equals(dom, dom_len, d->name, d->name_length) == |
917 | 0 | 0) { |
918 | 0 | return d->f_create_from_ostring(o, o_len, local); |
919 | 0 | } |
920 | 0 | } |
921 | 0 | } |
922 | | |
923 | 0 | snmp_log(LOG_ERR, "No support for requested transport domain\n"); |
924 | 0 | return NULL; |
925 | 0 | } |
926 | | #endif /* NETSNMP_FEATURE_REMOVE_TDOMAIN_TRANSPORT_OID */ |
927 | | |
928 | | netsnmp_transport* |
929 | | netsnmp_transport_open(const char* application, const char* str, int local) |
930 | 0 | { |
931 | 0 | return netsnmp_tdomain_transport_full(application, str, local, NULL, NULL); |
932 | 0 | } |
933 | | |
934 | | netsnmp_transport* |
935 | | netsnmp_transport_open_server(const char* application, const char* str) |
936 | 3.48k | { |
937 | 3.48k | return netsnmp_tdomain_transport_full(application, str, 1, NULL, NULL); |
938 | 3.48k | } |
939 | | |
940 | | netsnmp_transport* |
941 | | netsnmp_transport_open_client(const char* application, const char* str) |
942 | 0 | { |
943 | 0 | return netsnmp_tdomain_transport_full(application, str, 0, NULL, NULL); |
944 | 0 | } |
945 | | |
946 | | /** adds a transport to a linked list of transports. |
947 | | Returns 1 on failure, 0 on success */ |
948 | | int |
949 | | netsnmp_transport_add_to_list(netsnmp_transport_list **transport_list, |
950 | | netsnmp_transport *transport) |
951 | 3.43k | { |
952 | 3.43k | netsnmp_transport_list *newptr = |
953 | 3.43k | SNMP_MALLOC_TYPEDEF(netsnmp_transport_list); |
954 | | |
955 | 3.43k | if (!newptr) |
956 | 0 | return 1; |
957 | | |
958 | 3.43k | newptr->next = *transport_list; |
959 | 3.43k | newptr->transport = transport; |
960 | | |
961 | 3.43k | *transport_list = newptr; |
962 | | |
963 | 3.43k | return 0; |
964 | 3.43k | } |
965 | | |
966 | | |
967 | | /** removes a transport from a linked list of transports. |
968 | | Returns 1 on failure, 0 on success */ |
969 | | int |
970 | | netsnmp_transport_remove_from_list(netsnmp_transport_list **transport_list, |
971 | | netsnmp_transport *transport) |
972 | 3.43k | { |
973 | 3.43k | netsnmp_transport_list *ptr = *transport_list, *lastptr = NULL; |
974 | | |
975 | 3.43k | while (ptr && ptr->transport != transport) { |
976 | 0 | lastptr = ptr; |
977 | 0 | ptr = ptr->next; |
978 | 0 | } |
979 | | |
980 | 3.43k | if (!ptr) |
981 | 0 | return 1; |
982 | | |
983 | 3.43k | if (lastptr) |
984 | 0 | lastptr->next = ptr->next; |
985 | 3.43k | else |
986 | 3.43k | *transport_list = ptr->next; |
987 | | |
988 | 3.43k | SNMP_FREE(ptr); |
989 | | |
990 | 3.43k | return 0; |
991 | 3.43k | } |
992 | | |
993 | | int |
994 | | netsnmp_transport_config_compare(const void *p, const void *q) |
995 | 0 | { |
996 | 0 | const netsnmp_transport_config *left = p, *right = q; |
997 | |
|
998 | 0 | return strcmp(left->key, right->key); |
999 | 0 | } |
1000 | | |
1001 | | netsnmp_transport_config * |
1002 | | netsnmp_transport_create_config(const char *key, const char *value) |
1003 | 104 | { |
1004 | 104 | netsnmp_transport_config *entry = |
1005 | 104 | SNMP_MALLOC_TYPEDEF(netsnmp_transport_config); |
1006 | 104 | if (!entry) |
1007 | 0 | return NULL; |
1008 | 104 | entry->key = strdup(key); |
1009 | 104 | entry->value = strdup(value); |
1010 | 104 | if (!entry->key || !entry->value) { |
1011 | 0 | free(entry->key); |
1012 | 0 | free(entry->value); |
1013 | 0 | free(entry); |
1014 | 0 | return NULL; |
1015 | 0 | } |
1016 | 104 | return entry; |
1017 | 104 | } |
1018 | | |
1019 | | #ifndef FEATURE_REMOVE_TRANSPORT_CACHE |
1020 | | |
1021 | | /* ************************************************************************* |
1022 | | * transport caching by address family, type and use |
1023 | | */ |
1024 | | typedef struct trans_cache_s { |
1025 | | netsnmp_transport *t; |
1026 | | int af; |
1027 | | int type; |
1028 | | int local; |
1029 | | netsnmp_sockaddr_storage bind_addr; |
1030 | | int count; /* number of times this transport has been returned */ |
1031 | | } trans_cache; |
1032 | | |
1033 | | static void _tc_free_item(void *tc, void *context); |
1034 | | static int _tc_compare(const void *p, const void *q); |
1035 | | |
1036 | | /** initialize transport cache */ |
1037 | | static int |
1038 | | _tc_init(void) |
1039 | 0 | { |
1040 | 0 | DEBUGMSGTL(("transport:cache:init", "%p\n", _container)); |
1041 | | |
1042 | | /** prevent double init */ |
1043 | 0 | if (NULL != _container) |
1044 | 0 | return 0; |
1045 | | |
1046 | 0 | _container = netsnmp_container_find("trans_cache:binary_array"); |
1047 | 0 | if (NULL == _container) { |
1048 | 0 | snmp_log(LOG_ERR, "failed to allocate trans_cache container\n"); |
1049 | 0 | return 1; |
1050 | 0 | } |
1051 | | |
1052 | 0 | _container->container_name = strdup("trans_cache"); |
1053 | 0 | _container->free_item = _tc_free_item; |
1054 | 0 | _container->compare = _tc_compare; |
1055 | |
|
1056 | 0 | return 0; |
1057 | 0 | } |
1058 | | |
1059 | | /* |
1060 | | * container compare function |
1061 | | * |
1062 | | * sort by af, type, local |
1063 | | */ |
1064 | | static int |
1065 | | _tc_compare(const void *p, const void *q) |
1066 | 0 | { |
1067 | 0 | const trans_cache *lhs = p, *rhs = q; |
1068 | |
|
1069 | 0 | netsnmp_assert((lhs != NULL) && (rhs != NULL)); |
1070 | |
|
1071 | 0 | DEBUGMSGTL(("9:transport:cache:compare", "%p/%p\n", lhs, rhs)); |
1072 | |
|
1073 | 0 | if (lhs->af < rhs->af) |
1074 | 0 | return -1; |
1075 | 0 | else if (lhs->af > rhs->af) |
1076 | 0 | return 1; |
1077 | | |
1078 | 0 | if (lhs->type < rhs->type) |
1079 | 0 | return -1; |
1080 | 0 | else if (lhs->type > rhs->type) |
1081 | 0 | return 1; |
1082 | | |
1083 | 0 | if (lhs->local < rhs->local) |
1084 | 0 | return -1; |
1085 | 0 | else if (lhs->local > rhs->local) |
1086 | 0 | return 1; |
1087 | | |
1088 | 0 | if (AF_INET == lhs->af) { |
1089 | 0 | const struct sockaddr_in *lha = &lhs->bind_addr.sin, |
1090 | 0 | *rha = &rhs->bind_addr.sin; |
1091 | 0 | if (lha->sin_addr.s_addr < rha->sin_addr.s_addr) |
1092 | 0 | return -1; |
1093 | 0 | else if (lha->sin_addr.s_addr > rha->sin_addr.s_addr) |
1094 | 0 | return 1; |
1095 | | |
1096 | 0 | if (lha->sin_port < rha->sin_port) |
1097 | 0 | return -1; |
1098 | 0 | else if (lha->sin_port > rha->sin_port) |
1099 | 0 | return 1; |
1100 | 0 | } |
1101 | 0 | #ifdef NETSNMP_ENABLE_IPV6 |
1102 | 0 | else if (AF_INET6 == lhs->af) { |
1103 | 0 | const struct sockaddr_in6 *lha = &lhs->bind_addr.sin6, |
1104 | 0 | *rha = &rhs->bind_addr.sin6; |
1105 | 0 | int rc = memcmp(lha->sin6_addr.s6_addr, rha->sin6_addr.s6_addr, |
1106 | 0 | sizeof(rha->sin6_addr.s6_addr)); |
1107 | 0 | if (rc) |
1108 | 0 | return rc; |
1109 | | |
1110 | 0 | if (lha->sin6_port < rha->sin6_port) |
1111 | 0 | return -1; |
1112 | 0 | else if (lha->sin6_port > rha->sin6_port) |
1113 | 0 | return 1; |
1114 | | |
1115 | 0 | if (lha->sin6_flowinfo < rha->sin6_flowinfo) |
1116 | 0 | return -1; |
1117 | 0 | else if (lha->sin6_flowinfo > rha->sin6_flowinfo) |
1118 | 0 | return 1; |
1119 | | |
1120 | 0 | if (lha->sin6_scope_id < rha->sin6_scope_id) |
1121 | 0 | return -1; |
1122 | 0 | else if (lha->sin6_scope_id > rha->sin6_scope_id) |
1123 | 0 | return 1; |
1124 | 0 | } |
1125 | 0 | #endif |
1126 | 0 | return 0; |
1127 | 0 | } |
1128 | | |
1129 | | static void |
1130 | | _tc_free(trans_cache *tc) |
1131 | 0 | { |
1132 | 0 | if (NULL == tc) |
1133 | 0 | return; |
1134 | | |
1135 | 0 | DEBUGMSGTL(("transport:cache:free", "%p %d/%d/%d/%p %d\n", tc, tc->af, |
1136 | 0 | tc->type, tc->local, tc->t, tc->count)); |
1137 | 0 | netsnmp_transport_free(tc->t); |
1138 | 0 | memset(tc, 0x0, sizeof(*tc)); |
1139 | 0 | free(tc); |
1140 | 0 | } |
1141 | | |
1142 | | static void |
1143 | | _tc_free_item(void *tc, void *context) |
1144 | 0 | { |
1145 | 0 | _tc_free(tc); |
1146 | 0 | } |
1147 | | |
1148 | | static void |
1149 | | _tc_remove(trans_cache *tc) |
1150 | 0 | { |
1151 | 0 | if (NULL == tc || NULL == _container) |
1152 | 0 | return; |
1153 | | |
1154 | 0 | DEBUGMSGTL(("transport:cache:remove", "%p\n", tc)); |
1155 | |
|
1156 | 0 | CONTAINER_REMOVE(_container, tc); |
1157 | 0 | } |
1158 | | |
1159 | | static trans_cache * |
1160 | | _tc_create(int af, int type, int local, const netsnmp_sockaddr_storage *addr, |
1161 | | unsigned addr_size, netsnmp_transport *t) |
1162 | 0 | { |
1163 | 0 | trans_cache *tc = SNMP_MALLOC_TYPEDEF(trans_cache); |
1164 | |
|
1165 | 0 | if (NULL == tc) { |
1166 | 0 | snmp_log(LOG_ERR, "failed to allocate trans_cache\n"); |
1167 | 0 | return NULL; |
1168 | 0 | } |
1169 | 0 | DEBUGMSGTL(("transport:cache:create", "%p\n", tc)); |
1170 | 0 | tc->af = af; |
1171 | 0 | tc->type = type; |
1172 | 0 | tc->local = local; |
1173 | 0 | tc->t = t; |
1174 | 0 | if (addr) |
1175 | 0 | memcpy(&tc->bind_addr, addr, addr_size); |
1176 | | /** we only understand ipv6 and ipv6 sockaddrs in compare */ |
1177 | 0 | if (AF_INET != tc->af && AF_INET6 != tc->af) |
1178 | 0 | NETSNMP_LOGONCE((LOG_WARNING, "transport cache not tested for af %d\n", |
1179 | 0 | tc->af)); |
1180 | 0 | return tc; |
1181 | 0 | } |
1182 | | |
1183 | | static trans_cache * |
1184 | | _tc_add(int af, int type, int local, const netsnmp_sockaddr_storage *addr, |
1185 | | unsigned addr_size, netsnmp_transport *t) |
1186 | 0 | { |
1187 | 0 | trans_cache *tc; |
1188 | 0 | int rc; |
1189 | |
|
1190 | 0 | DEBUGMSGTL(("transport:cache:add", "%d/%d/%d/%p\n", af, type, local, t)); |
1191 | |
|
1192 | 0 | if (NULL == _container) { |
1193 | 0 | _tc_init(); |
1194 | 0 | if (NULL == _container) |
1195 | 0 | return NULL; |
1196 | 0 | } |
1197 | | |
1198 | 0 | tc = _tc_create(af, type, local, addr, addr_size, t); |
1199 | 0 | if (NULL == tc) { |
1200 | 0 | DEBUGMSGTL(("transport:cache:add", |
1201 | 0 | "could not create transport cache\n")); |
1202 | 0 | return NULL; |
1203 | 0 | } |
1204 | | |
1205 | 0 | rc = CONTAINER_INSERT(_container, tc); |
1206 | 0 | if (rc) { |
1207 | 0 | DEBUGMSGTL(("transport:cache:add", "container insert failed\n")); |
1208 | 0 | _tc_free(tc); |
1209 | 0 | return NULL; |
1210 | 0 | } |
1211 | | |
1212 | 0 | return tc; |
1213 | 0 | } |
1214 | | |
1215 | | trans_cache * |
1216 | | _tc_find(int af, int type, int local, const netsnmp_sockaddr_storage *addr, |
1217 | | unsigned addr_size) |
1218 | 0 | { |
1219 | 0 | trans_cache tc, *rtn; |
1220 | |
|
1221 | 0 | DEBUGMSGTL(("transport:cache:find", "%d/%d/%d\n", af, type, local)); |
1222 | |
|
1223 | 0 | if (NULL == _container) |
1224 | 0 | return NULL; |
1225 | | |
1226 | 0 | memset(&tc, 0x00, sizeof(tc)); |
1227 | 0 | tc.af = af; |
1228 | 0 | tc.type = type; |
1229 | 0 | tc.local = local; |
1230 | 0 | if (addr) |
1231 | 0 | memcpy(&tc.bind_addr, addr, addr_size); |
1232 | |
|
1233 | 0 | rtn = CONTAINER_FIND(_container, &tc); |
1234 | 0 | DEBUGMSGTL(("transport:cache:find", "%p\n", rtn)); |
1235 | 0 | return rtn; |
1236 | 0 | } |
1237 | | |
1238 | | trans_cache * |
1239 | | _tc_find_transport(netsnmp_transport *t) |
1240 | 6.91k | { |
1241 | | /* |
1242 | | * we shouldn't really have that many transports, so instead of |
1243 | | * using an additional key, just iterate over the whole container. |
1244 | | */ |
1245 | 6.91k | netsnmp_iterator *itr; |
1246 | 6.91k | trans_cache *tc; |
1247 | | |
1248 | 6.91k | DEBUGMSGTL(("transport:cache:find_transport", "%p\n", t)); |
1249 | | |
1250 | 6.91k | if (NULL == _container) |
1251 | 6.91k | return NULL; |
1252 | | |
1253 | 0 | itr = CONTAINER_ITERATOR(_container); |
1254 | 0 | if (NULL == itr) { |
1255 | 0 | snmp_log(LOG_ERR, "could not get iterator for transport cache\n"); |
1256 | 0 | return NULL; |
1257 | 0 | } |
1258 | | |
1259 | 0 | tc = ITERATOR_FIRST(itr); |
1260 | 0 | for( ; tc; tc = ITERATOR_NEXT(itr)) |
1261 | 0 | if (tc->t == t) |
1262 | 0 | break; |
1263 | 0 | ITERATOR_RELEASE(itr); |
1264 | |
|
1265 | 0 | DEBUGMSGT(("transport:cache:find_transport","found %p\n", tc)); |
1266 | |
|
1267 | 0 | return tc; |
1268 | 0 | } |
1269 | | |
1270 | | int |
1271 | | netsnmp_transport_cache_remove(netsnmp_transport *t) |
1272 | 6.91k | { |
1273 | 6.91k | trans_cache *tc; |
1274 | | |
1275 | 6.91k | DEBUGMSGTL(("transport:cache:close", "%p\n", t)); |
1276 | | |
1277 | 6.91k | if (NULL == t) |
1278 | 0 | return 0; |
1279 | | |
1280 | | /** transport in cache? */ |
1281 | 6.91k | tc = _tc_find_transport(t); |
1282 | 6.91k | if (NULL == tc) { |
1283 | 6.91k | DEBUGMSGTL(("transport:cache:close", "%p not found in cache\n", t)); |
1284 | 6.91k | return 0; |
1285 | 6.91k | } |
1286 | | |
1287 | 0 | --tc->count; |
1288 | | |
1289 | | /** still in use? */ |
1290 | 0 | if (tc->count > 0) { |
1291 | 0 | DEBUGMSGTL(("transport:cache:close", "still %d user(s) of %p\n", |
1292 | 0 | tc->count, t)); |
1293 | 0 | return 1; |
1294 | 0 | } |
1295 | | |
1296 | | /** unbalanced get/close? */ |
1297 | 0 | if (tc->count < 0) |
1298 | 0 | snmp_log(LOG_WARNING, "transport cache get/close mismatch\n"); |
1299 | |
|
1300 | 0 | _tc_remove(tc); |
1301 | 0 | _tc_free(tc); /* also does close */ |
1302 | |
|
1303 | 0 | return 0; |
1304 | 0 | } |
1305 | | |
1306 | | /* |
1307 | | * netsnmp_transport_get: get a (possibly duplicate, cached) transport |
1308 | | */ |
1309 | | netsnmp_transport * |
1310 | | netsnmp_transport_cache_get(int af, int type, int local, |
1311 | | const netsnmp_sockaddr_storage *bind_addr, |
1312 | | unsigned addr_size) |
1313 | 0 | { |
1314 | 0 | trans_cache *tc; |
1315 | 0 | netsnmp_transport *t; |
1316 | |
|
1317 | 0 | DEBUGMSGTL(("transport:cache:get", "%d/%d/%d\n", af, type, local)); |
1318 | |
|
1319 | 0 | #define USE_CACHE 1 |
1320 | |
|
1321 | 0 | #ifdef USE_CACHE |
1322 | | /** check for existing transport */ |
1323 | 0 | tc = _tc_find(af, type, local, bind_addr, addr_size); |
1324 | 0 | if (tc) { |
1325 | 0 | DEBUGMSGTL(("transport:cache:get", "using existing transport %p\n", |
1326 | 0 | tc->t)); |
1327 | 0 | ++tc->count; |
1328 | 0 | return tc->t; |
1329 | 0 | } |
1330 | 0 | #endif |
1331 | | /** get transport */ |
1332 | 0 | t = NULL; /* _transport(af, type, 0);*/ |
1333 | 0 | if (NULL == t) { |
1334 | 0 | snmp_log(LOG_ERR, "could not get new transport for %d/%d/%d\n", af, |
1335 | 0 | type, local); |
1336 | 0 | return NULL; |
1337 | 0 | } |
1338 | 0 | DEBUGMSGTL(("transport:cache:get", "new transport %p\n", t)); |
1339 | |
|
1340 | 0 | #ifdef USE_CACHE |
1341 | | /** create transport cache for new transport */ |
1342 | 0 | tc = _tc_add(af, type, local, bind_addr, addr_size, t); |
1343 | 0 | if (NULL == tc) { |
1344 | 0 | DEBUGMSGTL(("transport:cache:get", "could not create transport cache entry\n")); |
1345 | | /* |
1346 | | * We have a transport, just no cache for it. Let's continue on and |
1347 | | * hope for the best. |
1348 | | */ |
1349 | 0 | return t; |
1350 | 0 | } |
1351 | 0 | tc->count = 1; |
1352 | 0 | #endif |
1353 | |
|
1354 | 0 | return t; |
1355 | 0 | } |
1356 | | |
1357 | | int |
1358 | | netsnmp_transport_cache_save(int af, int type, int local, |
1359 | | const netsnmp_sockaddr_storage *addr, |
1360 | | unsigned addr_size, |
1361 | | netsnmp_transport *t) |
1362 | 0 | { |
1363 | 0 | if (NULL == t) |
1364 | 0 | return 1; |
1365 | | |
1366 | 0 | if (NULL == _tc_add(af, type, local, addr, addr_size, t)) |
1367 | 0 | return 1; |
1368 | | |
1369 | 0 | return 0; |
1370 | 0 | } |
1371 | | #endif /* FEATURE_REMOVE_TRANSPORT_CACHE */ |