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