blob: ada4781cb6629bae06d52bcfa0ee164a83583014 [file] [log] [blame]
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-features.h>
#include <net-snmp/types.h>
#include <net-snmp/library/snmp_transport.h>
#include <stdio.h>
#if HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#include <sys/types.h>
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <ctype.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_DMALLOC_H
#include <dmalloc.h>
#endif
#include <net-snmp/output_api.h>
#include <net-snmp/utilities.h>
#include <net-snmp/library/default_store.h>
#include <net-snmp/library/snmpUDPDomain.h>
#ifdef NETSNMP_TRANSPORT_TLSBASE_DOMAIN
#include <net-snmp/library/snmpTLSBaseDomain.h>
#endif
#ifdef NETSNMP_TRANSPORT_TLSTCP_DOMAIN
#include <net-snmp/library/snmpTLSTCPDomain.h>
#endif
#ifdef NETSNMP_TRANSPORT_STD_DOMAIN
#include <net-snmp/library/snmpSTDDomain.h>
#endif
#ifdef NETSNMP_TRANSPORT_TCP_DOMAIN
#include <net-snmp/library/snmpTCPDomain.h>
#endif
#ifdef NETSNMP_TRANSPORT_DTLSUDP_DOMAIN
#include <net-snmp/library/snmpDTLSUDPDomain.h>
#endif
#ifdef NETSNMP_TRANSPORT_SSH_DOMAIN
#include <net-snmp/library/snmpSSHDomain.h>
#endif
#ifdef NETSNMP_TRANSPORT_ALIAS_DOMAIN
#include <net-snmp/library/snmpAliasDomain.h>
#endif
#ifdef NETSNMP_TRANSPORT_IPX_DOMAIN
#include <net-snmp/library/snmpIPXDomain.h>
#endif
#ifdef NETSNMP_TRANSPORT_UNIX_DOMAIN
#include <net-snmp/library/snmpUnixDomain.h>
#endif
#ifdef NETSNMP_TRANSPORT_AAL5PVC_DOMAIN
#include <net-snmp/library/snmpAAL5PVCDomain.h>
#endif
#ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN
#include <net-snmp/library/snmpUDPIPv6Domain.h>
#endif
#ifdef NETSNMP_TRANSPORT_TCPIPV6_DOMAIN
#include <net-snmp/library/snmpTCPIPv6Domain.h>
#endif
#include <net-snmp/library/snmp_api.h>
#include <net-snmp/library/snmp_service.h>
#include <net-snmp/library/read_config.h>
netsnmp_feature_child_of(transport_all, libnetsnmp)
netsnmp_feature_child_of(tdomain_support, transport_all)
netsnmp_feature_child_of(tdomain_transport_oid, transport_all)
netsnmp_feature_child_of(sockaddr_size, transport_all)
/*
* Our list of supported transport domains.
*/
static netsnmp_tdomain *domain_list = NULL;
/*
* The standard SNMP domains.
*/
oid netsnmpUDPDomain[] = { 1, 3, 6, 1, 6, 1, 1 };
size_t netsnmpUDPDomain_len = OID_LENGTH(netsnmpUDPDomain);
oid netsnmpCLNSDomain[] = { 1, 3, 6, 1, 6, 1, 2 };
size_t netsnmpCLNSDomain_len = OID_LENGTH(netsnmpCLNSDomain);
oid netsnmpCONSDomain[] = { 1, 3, 6, 1, 6, 1, 3 };
size_t netsnmpCONSDomain_len = OID_LENGTH(netsnmpCONSDomain);
oid netsnmpDDPDomain[] = { 1, 3, 6, 1, 6, 1, 4 };
size_t netsnmpDDPDomain_len = OID_LENGTH(netsnmpDDPDomain);
oid netsnmpIPXDomain[] = { 1, 3, 6, 1, 6, 1, 5 };
size_t netsnmpIPXDomain_len = OID_LENGTH(netsnmpIPXDomain);
static void netsnmp_tdomain_dump(void);
void
init_snmp_transport(void)
{
netsnmp_ds_register_config(ASN_BOOLEAN,
"snmp", "dontLoadHostConfig",
NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_DONT_LOAD_HOST_FILES);
}
/*
* Make a deep copy of an netsnmp_transport.
*/
netsnmp_transport *
netsnmp_transport_copy(netsnmp_transport *t)
{
netsnmp_transport *n = NULL;
if (t == NULL) {
return NULL;
}
n = SNMP_MALLOC_TYPEDEF(netsnmp_transport);
if (n == NULL) {
return NULL;
}
if (t->domain != NULL) {
n->domain = t->domain;
n->domain_length = t->domain_length;
} else {
n->domain = NULL;
n->domain_length = 0;
}
if (t->local != NULL) {
n->local = (u_char *) malloc(t->local_length);
if (n->local == NULL) {
netsnmp_transport_free(n);
return NULL;
}
n->local_length = t->local_length;
memcpy(n->local, t->local, t->local_length);
} else {
n->local = NULL;
n->local_length = 0;
}
if (t->remote != NULL) {
n->remote = (u_char *) malloc(t->remote_length);
if (n->remote == NULL) {
netsnmp_transport_free(n);
return NULL;
}
n->remote_length = t->remote_length;
memcpy(n->remote, t->remote, t->remote_length);
} else {
n->remote = NULL;
n->remote_length = 0;
}
if (t->data != NULL && t->data_length > 0) {
n->data = malloc(t->data_length);
if (n->data == NULL) {
netsnmp_transport_free(n);
return NULL;
}
n->data_length = t->data_length;
memcpy(n->data, t->data, t->data_length);
} else {
n->data = NULL;
n->data_length = 0;
}
n->msgMaxSize = t->msgMaxSize;
n->f_accept = t->f_accept;
n->f_recv = t->f_recv;
n->f_send = t->f_send;
n->f_close = t->f_close;
n->f_copy = t->f_copy;
n->f_config = t->f_config;
n->f_fmtaddr = t->f_fmtaddr;
n->sock = t->sock;
n->flags = t->flags;
n->base_transport = netsnmp_transport_copy(t->base_transport);
/* give the transport a chance to do "special things" */
if (t->f_copy)
t->f_copy(t, n);
return n;
}
void
netsnmp_transport_free(netsnmp_transport *t)
{
if (NULL == t)
return;
SNMP_FREE(t->local);
SNMP_FREE(t->remote);
SNMP_FREE(t->data);
netsnmp_transport_free(t->base_transport);
SNMP_FREE(t);
}
/*
* netsnmp_transport_peer_string
*
* returns string representation of peer address.
*
* caller is responsible for freeing the allocated string.
*/
char *
netsnmp_transport_peer_string(netsnmp_transport *t, void *data, int len)
{
char *str;
if (NULL == t)
return NULL;
if (t->f_fmtaddr != NULL)
str = t->f_fmtaddr(t, data, len);
else
str = strdup("<UNKNOWN>");
return str;
}
#ifndef NETSNMP_FEATURE_REMOVE_SOCKADDR_SIZE
int
netsnmp_sockaddr_size(struct sockaddr *sa)
{
if (NULL == sa)
return 0;
switch (sa->sa_family) {
case AF_INET:
return sizeof(struct sockaddr_in);
break;
#ifdef NETSNMP_ENABLE_IPV6
case AF_INET6:
return sizeof(struct sockaddr_in6);
break;
#endif
}
return 0;
}
#endif /* NETSNMP_FEATURE_REMOVE_SOCKADDR_SIZE */
int
netsnmp_transport_send(netsnmp_transport *t, void *packet, int length,
void **opaque, int *olength)
{
int dumpPacket, debugLength;
if ((NULL == t) || (NULL == t->f_send)) {
DEBUGMSGTL(("transport:pkt:send", "NULL transport or send function\n"));
return SNMPERR_GENERR;
}
dumpPacket = netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_DUMP_PACKET);
debugLength = (SNMPERR_SUCCESS ==
debug_is_token_registered("transport:send"));
if (dumpPacket | debugLength) {
char *str = netsnmp_transport_peer_string(t,
opaque ? *opaque : NULL,
olength ? *olength : 0);
if (debugLength)
DEBUGMSGT_NC(("transport:send","%lu bytes to %s\n",
(unsigned long)length, str));
if (dumpPacket)
snmp_log(LOG_DEBUG, "\nSending %lu bytes to %s\n",
(unsigned long)length, str);
SNMP_FREE(str);
}
if (dumpPacket)
xdump(packet, length, "");
return t->f_send(t, packet, length, opaque, olength);
}
int
netsnmp_transport_recv(netsnmp_transport *t, void *packet, int length,
void **opaque, int *olength)
{
int debugLength;
if ((NULL == t) || (NULL == t->f_recv)) {
DEBUGMSGTL(("transport:recv", "NULL transport or recv function\n"));
return SNMPERR_GENERR;
}
length = t->f_recv(t, packet, length, opaque, olength);
if (length <=0)
return length; /* don't log timeouts/socket closed */
debugLength = (SNMPERR_SUCCESS ==
debug_is_token_registered("transport:recv"));
if (debugLength) {
char *str = netsnmp_transport_peer_string(t,
opaque ? *opaque : NULL,
olength ? *olength : 0);
if (debugLength)
DEBUGMSGT_NC(("transport:recv","%d bytes from %s\n",
length, str));
SNMP_FREE(str);
}
return length;
}
#ifndef NETSNMP_FEATURE_REMOVE_TDOMAIN_SUPPORT
int
netsnmp_tdomain_support(const oid * in_oid,
size_t in_len,
const oid ** out_oid, size_t * out_len)
{
netsnmp_tdomain *d = NULL;
for (d = domain_list; d != NULL; d = d->next) {
if (netsnmp_oid_equals(in_oid, in_len, d->name, d->name_length) == 0) {
if (out_oid != NULL && out_len != NULL) {
*out_oid = d->name;
*out_len = d->name_length;
}
return 1;
}
}
return 0;
}
#endif /* NETSNMP_FEATURE_REMOVE_TDOMAIN_SUPPORT */
void
netsnmp_tdomain_init(void)
{
DEBUGMSGTL(("tdomain", "netsnmp_tdomain_init() called\n"));
/* include the configure generated list of constructor calls */
#include "transports/snmp_transport_inits.h"
netsnmp_tdomain_dump();
}
void
netsnmp_clear_tdomain_list(void)
{
netsnmp_tdomain *list = domain_list, *next = NULL;
DEBUGMSGTL(("tdomain", "clear_tdomain_list() called\n"));
while (list != NULL) {
next = list->next;
SNMP_FREE(list->prefix);
/* attention!! list itself is not in the heap, so we must not free it! */
list = next;
}
domain_list = NULL;
}
static void
netsnmp_tdomain_dump(void)
{
netsnmp_tdomain *d;
int i = 0;
DEBUGMSGTL(("tdomain", "domain_list -> "));
for (d = domain_list; d != NULL; d = d->next) {
DEBUGMSG(("tdomain", "{ "));
DEBUGMSGOID(("tdomain", d->name, d->name_length));
DEBUGMSG(("tdomain", ", \""));
for (i = 0; d->prefix[i] != NULL; i++) {
DEBUGMSG(("tdomain", "%s%s", d->prefix[i],
(d->prefix[i + 1]) ? "/" : ""));
}
DEBUGMSG(("tdomain", "\" } -> "));
}
DEBUGMSG(("tdomain", "[NIL]\n"));
}
int
netsnmp_tdomain_register(netsnmp_tdomain *n)
{
netsnmp_tdomain **prevNext = &domain_list, *d;
if (n != NULL) {
for (d = domain_list; d != NULL; d = d->next) {
if (netsnmp_oid_equals(n->name, n->name_length,
d->name, d->name_length) == 0) {
/*
* Already registered.
*/
return 0;
}
prevNext = &(d->next);
}
n->next = NULL;
*prevNext = n;
return 1;
} else {
return 0;
}
}
netsnmp_feature_child_of(tdomain_unregister, netsnmp_unused)
#ifndef NETSNMP_FEATURE_REMOVE_TDOMAIN_UNREGISTER
int
netsnmp_tdomain_unregister(netsnmp_tdomain *n)
{
netsnmp_tdomain **prevNext = &domain_list, *d;
if (n != NULL) {
for (d = domain_list; d != NULL; d = d->next) {
if (netsnmp_oid_equals(n->name, n->name_length,
d->name, d->name_length) == 0) {
*prevNext = n->next;
SNMP_FREE(n->prefix);
return 1;
}
prevNext = &(d->next);
}
return 0;
} else {
return 0;
}
}
#endif /* NETSNMP_FEATURE_REMOVE_TDOMAIN_UNREGISTER */
static netsnmp_tdomain *
find_tdomain(const char* spec)
{
netsnmp_tdomain *d;
for (d = domain_list; d != NULL; d = d->next) {
int i;
for (i = 0; d->prefix[i] != NULL; i++)
if (strcasecmp(d->prefix[i], spec) == 0) {
DEBUGMSGTL(("tdomain",
"Found domain \"%s\" from specifier \"%s\"\n",
d->prefix[0], spec));
return d;
}
}
DEBUGMSGTL(("tdomain", "Found no domain from specifier \"%s\"\n", spec));
return NULL;
}
static int
netsnmp_is_fqdn(const char *thename)
{
if (!thename)
return 0;
while(*thename) {
if (*thename != '.' && !isupper((unsigned char)*thename) &&
!islower((unsigned char)*thename) &&
!isdigit((unsigned char)*thename) && *thename != '-') {
return 0;
}
thename++;
}
return 1;
}
/*
* Locate the appropriate transport domain and call the create function for
* it.
*/
netsnmp_transport *
netsnmp_tdomain_transport_full(const char *application,
const char *str, int local,
const char *default_domain,
const char *default_target)
{
netsnmp_tdomain *match = NULL;
const char *addr = NULL;
const char * const *spec = NULL;
int any_found = 0;
char buf[SNMP_MAXPATH];
char **lspec = 0;
DEBUGMSGTL(("tdomain",
"tdomain_transport_full(\"%s\", \"%s\", %d, \"%s\", \"%s\")\n",
application, str ? str : "[NIL]", local,
default_domain ? default_domain : "[NIL]",
default_target ? default_target : "[NIL]"));
/* see if we can load a host-name specific set of conf files */
if (!netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_DONT_LOAD_HOST_FILES) &&
netsnmp_is_fqdn(str)) {
static int have_added_handler = 0;
char *newhost;
struct config_line *config_handlers;
struct config_files file_names;
char *prev_hostname;
/* register a "transport" specifier */
if (!have_added_handler) {
have_added_handler = 1;
netsnmp_ds_register_config(ASN_OCTET_STR,
"snmp", "transport",
NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_HOSTNAME);
}
/* we save on specific setting that we don't allow to change
from one transport creation to the next; ie, we don't want
the "transport" specifier to be a default. It should be a
single invocation use only */
prev_hostname = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_HOSTNAME);
if (prev_hostname)
prev_hostname = strdup(prev_hostname);
/* read in the hosts/STRING.conf files */
config_handlers = read_config_get_handlers("snmp");
snprintf(buf, sizeof(buf)-1, "hosts/%s", str);
file_names.fileHeader = buf;
file_names.start = config_handlers;
file_names.next = NULL;
DEBUGMSGTL(("tdomain", "checking for host specific config %s\n",
buf));
read_config_files_of_type(EITHER_CONFIG, &file_names);
if (NULL !=
(newhost = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_HOSTNAME))) {
strlcpy(buf, newhost, sizeof(buf));
str = buf;
}
netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
NETSNMP_DS_LIB_HOSTNAME,
prev_hostname);
SNMP_FREE(prev_hostname);
}
/* First try - assume that there is a domain in str (domain:target) */
if (str != NULL) {
const char *cp;
if ((cp = strchr(str, ':')) != NULL) {
char* mystring = (char*)malloc(cp + 1 - str);
memcpy(mystring, str, cp - str);
mystring[cp - str] = '\0';
addr = cp + 1;
match = find_tdomain(mystring);
free(mystring);
}
}
/*
* Second try, if there is no domain in str (target), then try the
* default domain
*/
if (match == NULL) {
addr = str;
if (addr && *addr == '/') {
DEBUGMSGTL(("tdomain",
"Address starts with '/', so assume \"unix\" "
"domain\n"));
match = find_tdomain("unix");
} else if (default_domain) {
DEBUGMSGTL(("tdomain",
"Use user specified default domain \"%s\"\n",
default_domain));
if (!strchr(default_domain, ','))
match = find_tdomain(default_domain);
else {
int commas = 0;
const char *cp = default_domain;
char *dup = strdup(default_domain);
while (*++cp) if (*cp == ',') commas++;
lspec = calloc(commas+2, sizeof(char *));
commas = 1;
lspec[0] = strtok(dup, ",");
while ((lspec[commas++] = strtok(NULL, ",")))
;
spec = (const char * const *)lspec;
}
} else {
spec = netsnmp_lookup_default_domains(application);
if (spec == NULL) {
DEBUGMSGTL(("tdomain",
"No default domain found, assume \"udp\"\n"));
match = find_tdomain("udp");
} else {
const char * const * r = spec;
DEBUGMSGTL(("tdomain",
"Use application default domains"));
while(*r) {
DEBUGMSG(("tdomain", " \"%s\"", *r));
++r;
}
DEBUGMSG(("tdomain", "\n"));
}
}
}
for(;;) {
if (match) {
netsnmp_transport *t = NULL;
const char* addr2;
any_found = 1;
/*
* Ok, we know what domain to try, lets see what default data
* should be used with it
*/
if (default_target != NULL)
addr2 = default_target;
else
addr2 = netsnmp_lookup_default_target(application,
match->prefix[0]);
DEBUGMSGTL(("tdomain",
"trying domain \"%s\" address \"%s\" "
"default address \"%s\"\n",
match->prefix[0], addr ? addr : "[NIL]",
addr2 ? addr2 : "[NIL]"));
if (match->f_create_from_tstring) {
NETSNMP_LOGONCE((LOG_WARNING,
"transport domain %s uses deprecated f_create_from_tstring\n",
match->prefix[0]));
t = match->f_create_from_tstring(addr, local);
}
else
t = match->f_create_from_tstring_new(addr, local, addr2);
if (t) {
if (lspec) {
free(lspec[0]);
free(lspec);
}
return t;
}
}
addr = str;
if (spec && *spec)
match = find_tdomain(*spec++);
else
break;
}
if (!any_found)
snmp_log(LOG_ERR, "No support for any checked transport domain\n");
if (lspec) {
free(lspec[0]);
free(lspec);
}
return NULL;
}
netsnmp_transport *
netsnmp_tdomain_transport(const char *str, int local,
const char *default_domain)
{
return netsnmp_tdomain_transport_full("snmp", str, local, default_domain,
NULL);
}
#ifndef NETSNMP_FEATURE_REMOVE_TDOMAIN_TRANSPORT_OID
netsnmp_transport *
netsnmp_tdomain_transport_oid(const oid * dom,
size_t dom_len,
const u_char * o, size_t o_len, int local)
{
netsnmp_tdomain *d;
int i;
DEBUGMSGTL(("tdomain", "domain \""));
DEBUGMSGOID(("tdomain", dom, dom_len));
DEBUGMSG(("tdomain", "\"\n"));
for (d = domain_list; d != NULL; d = d->next) {
for (i = 0; d->prefix[i] != NULL; i++) {
if (netsnmp_oid_equals(dom, dom_len, d->name, d->name_length) ==
0) {
return d->f_create_from_ostring(o, o_len, local);
}
}
}
snmp_log(LOG_ERR, "No support for requested transport domain\n");
return NULL;
}
#endif /* NETSNMP_FEATURE_REMOVE_TDOMAIN_TRANSPORT_OID */
netsnmp_transport*
netsnmp_transport_open(const char* application, const char* str, int local)
{
return netsnmp_tdomain_transport_full(application, str, local, NULL, NULL);
}
netsnmp_transport*
netsnmp_transport_open_server(const char* application, const char* str)
{
return netsnmp_tdomain_transport_full(application, str, 1, NULL, NULL);
}
netsnmp_transport*
netsnmp_transport_open_client(const char* application, const char* str)
{
return netsnmp_tdomain_transport_full(application, str, 0, NULL, NULL);
}
/** adds a transport to a linked list of transports.
Returns 1 on failure, 0 on success */
int
netsnmp_transport_add_to_list(netsnmp_transport_list **transport_list,
netsnmp_transport *transport)
{
netsnmp_transport_list *newptr =
SNMP_MALLOC_TYPEDEF(netsnmp_transport_list);
if (!newptr)
return 1;
newptr->next = *transport_list;
newptr->transport = transport;
*transport_list = newptr;
return 0;
}
/** removes a transport from a linked list of transports.
Returns 1 on failure, 0 on success */
int
netsnmp_transport_remove_from_list(netsnmp_transport_list **transport_list,
netsnmp_transport *transport)
{
netsnmp_transport_list *ptr = *transport_list, *lastptr = NULL;
while (ptr && ptr->transport != transport) {
lastptr = ptr;
ptr = ptr->next;
}
if (!ptr)
return 1;
if (lastptr)
lastptr->next = ptr->next;
else
*transport_list = ptr->next;
SNMP_FREE(ptr);
return 0;
}
int
netsnmp_transport_config_compare(netsnmp_transport_config *left,
netsnmp_transport_config *right) {
return strcmp(left->key, right->key);
}
netsnmp_transport_config *
netsnmp_transport_create_config(char *key, char *value) {
netsnmp_transport_config *entry =
SNMP_MALLOC_TYPEDEF(netsnmp_transport_config);
entry->key = strdup(key);
entry->value = strdup(value);
return entry;
}