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