summaryrefslogtreecommitdiff
path: root/snmplib/snmp_transport.c
diff options
context:
space:
mode:
Diffstat (limited to 'snmplib/snmp_transport.c')
-rw-r--r--snmplib/snmp_transport.c768
1 files changed, 768 insertions, 0 deletions
diff --git a/snmplib/snmp_transport.c b/snmplib/snmp_transport.c
new file mode 100644
index 0000000..9b16245
--- /dev/null
+++ b/snmplib/snmp_transport.c
@@ -0,0 +1,768 @@
+#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];
+
+ 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));
+ match = find_tdomain(default_domain);
+ } 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) {
+ 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");
+ 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;
+}