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