summaryrefslogtreecommitdiff
path: root/agent/mibgroup/smux/smux.c
diff options
context:
space:
mode:
Diffstat (limited to 'agent/mibgroup/smux/smux.c')
-rw-r--r--agent/mibgroup/smux/smux.c2114
1 files changed, 2114 insertions, 0 deletions
diff --git a/agent/mibgroup/smux/smux.c b/agent/mibgroup/smux/smux.c
new file mode 100644
index 0000000..4598c2b
--- /dev/null
+++ b/agent/mibgroup/smux/smux.c
@@ -0,0 +1,2114 @@
+/*
+ * Smux module authored by Rohit Dube.
+ * Rewritten by Nick Amato <naamato@merit.net>.
+ */
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-features.h>
+#include <sys/types.h>
+#include <ctype.h>
+
+#if HAVE_IO_H /* win32 */
+#include <io.h>
+#endif
+#include <stdio.h>
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERR_H
+#include <err.h>
+#endif
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+#include <errno.h>
+#if HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#include <sys/stat.h>
+#if HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#if HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#if HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#include <net-snmp/net-snmp-includes.h>
+#include <net-snmp/agent/net-snmp-agent-includes.h>
+#include <net-snmp/library/tools.h>
+
+#include "smux.h"
+#include "mibdefs.h"
+#include "snmpd.h"
+
+netsnmp_feature_require(snprint_objid)
+
+long smux_long;
+u_long smux_ulong;
+struct sockaddr_in smux_sa;
+struct counter64 smux_counter64;
+oid smux_objid[MAX_OID_LEN];
+u_char smux_str[SMUXMAXSTRLEN];
+int smux_listen_sd = -1;
+
+static struct timeval smux_rcv_timeout;
+static long smux_reqid;
+
+void init_smux(void);
+static u_char *smux_open_process(int, u_char *, size_t *, int *);
+static u_char *smux_rreq_process(int, u_char *, size_t *);
+static u_char *smux_close_process(int, u_char *, size_t *);
+static u_char *smux_trap_process(u_char *, size_t *);
+static u_char *smux_parse(u_char *, oid *, size_t *, size_t *, u_char *);
+static u_char *smux_parse_var(u_char *, size_t *, oid *, size_t *,
+ size_t *, u_char *);
+static void smux_send_close(int, int);
+static void smux_list_detach(smux_reg **, smux_reg *);
+static void smux_replace_active(smux_reg *, smux_reg *);
+static void smux_peer_cleanup(int);
+static int smux_auth_peer(oid *, size_t, char *, int);
+static int smux_build(u_char, long, oid *,
+ size_t *, u_char, u_char *, size_t, u_char *,
+ size_t *);
+static int smux_list_add(smux_reg **, smux_reg *);
+static int smux_pdu_process(int, u_char *, size_t);
+static int smux_send_rrsp(int, int);
+static smux_reg *smux_find_match(smux_reg *, int, oid *, size_t, long);
+static smux_reg *smux_find_replacement(oid *, size_t);
+u_char *var_smux(struct variable *, oid *, size_t *, int, size_t *,
+ WriteMethod ** write_method);
+int var_smux_write(int, u_char *, u_char, size_t, u_char *,
+ oid *, size_t);
+
+static smux_reg *ActiveRegs; /* Active registrations */
+static smux_reg *PassiveRegs; /* Currently unused registrations */
+
+static smux_peer_auth *Auths[SMUX_MAX_PEERS]; /* Configured peers */
+static int nauths, npeers = 0;
+
+struct variable2 smux_variables[] = {
+ /*
+ * bogus entry, as in pass.c
+ */
+ {MIBINDEX, ASN_INTEGER, NETSNMP_OLDAPI_RWRITE,
+ var_smux, 0, {MIBINDEX}},
+};
+
+
+
+void
+smux_parse_smux_socket(const char *token, char *cptr)
+{
+ DEBUGMSGTL(("smux", "port spec: %s\n", cptr));
+ netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_SMUX_SOCKET, cptr);
+}
+
+void
+smux_parse_peer_auth(const char *token, char *cptr)
+{
+ smux_peer_auth *aptr;
+ char *password_cptr;
+ int rv;
+
+ if ((aptr =
+ (smux_peer_auth *) calloc(1, sizeof(smux_peer_auth))) == NULL) {
+ snmp_log_perror("smux_parse_peer_auth: malloc");
+ return;
+ }
+ if (nauths == SMUX_MAX_PEERS) {
+ config_perror("Too many smuxpeers");
+ free(aptr);
+ return;
+ }
+
+ password_cptr = strchr(cptr, ' ');
+ if (password_cptr)
+ *(password_cptr++) = '\0';
+
+ /*
+ * oid
+ */
+ aptr->sa_active_fd = -1;
+ aptr->sa_oid_len = MAX_OID_LEN;
+ rv = read_objid( cptr, aptr->sa_oid, &aptr->sa_oid_len );
+ DEBUGMSGTL(("smux_conf", "parsing registration for: %s\n", cptr));
+ if (!rv)
+ config_perror("Error parsing smux oid");
+
+ if (password_cptr != NULL) { /* Do we have a password or not? */
+ DEBUGMSGTL(("smux_conf", "password is: %s\n",
+ SNMP_STRORNULL(password_cptr)));
+
+ /*
+ * password
+ */
+ if (*password_cptr)
+ strlcpy(aptr->sa_passwd, password_cptr, sizeof(aptr->sa_passwd));
+ } else {
+ /*
+ * null passwords OK
+ */
+ DEBUGMSGTL(("smux_conf", "null password\n"));
+ }
+
+ Auths[nauths++] = aptr;
+ return;
+}
+
+void
+smux_free_peer_auth(void)
+{
+ int i;
+
+ for (i = 0; i < nauths; i++) {
+ free(Auths[i]);
+ Auths[i] = NULL;
+ }
+ nauths = 0;
+}
+
+void
+init_smux(void)
+{
+ snmpd_register_config_handler("smuxpeer", smux_parse_peer_auth,
+ smux_free_peer_auth,
+ "OID-IDENTITY PASSWORD");
+ snmpd_register_config_handler("smuxsocket",
+ smux_parse_smux_socket, NULL,
+ "SMUX bind address");
+}
+
+void
+real_init_smux(void)
+{
+ struct sockaddr_in lo_socket;
+ char *smux_socket;
+ int one = 1;
+
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE) == SUB_AGENT) {
+ smux_listen_sd = -1;
+ return;
+ }
+
+ /*
+ * Reqid
+ */
+ smux_reqid = 0;
+ smux_listen_sd = -1;
+
+ /*
+ * Receive timeout
+ */
+ smux_rcv_timeout.tv_sec = 0;
+ smux_rcv_timeout.tv_usec = 500000;
+
+ /*
+ * Get ready to listen on the SMUX port
+ */
+ memset(&lo_socket, (0), sizeof(lo_socket));
+ lo_socket.sin_family = AF_INET;
+
+ smux_socket = netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID,
+ NETSNMP_DS_SMUX_SOCKET);
+#ifdef NETSNMP_ENABLE_LOCAL_SMUX
+ if (!smux_socket)
+ smux_socket = "127.0.0.1"; /* By default, listen on localhost only */
+#endif
+ netsnmp_sockaddr_in( &lo_socket, smux_socket, SMUXPORT );
+
+ if ((smux_listen_sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ snmp_log_perror("[init_smux] socket failed");
+ return;
+ }
+#ifdef SO_REUSEADDR
+ /*
+ * At least on Linux, when the master agent terminates, any
+ * TCP connections for SMUX peers are put in the TIME_WAIT
+ * state for about 60 seconds. If the master agent is started
+ * during this time, the bind for the listening socket will
+ * fail because the SMUX port is in use.
+ */
+ if (setsockopt(smux_listen_sd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
+ sizeof(one)) < 0) {
+ snmp_log_perror("[init_smux] setsockopt(SO_REUSEADDR) failed");
+ }
+#endif /* SO_REUSEADDR */
+
+ if (bind(smux_listen_sd, (struct sockaddr *) &lo_socket,
+ sizeof(lo_socket)) < 0) {
+ snmp_log_perror("[init_smux] bind failed");
+ close(smux_listen_sd);
+ smux_listen_sd = -1;
+ return;
+ }
+#ifdef SO_KEEPALIVE
+ if (setsockopt(smux_listen_sd, SOL_SOCKET, SO_KEEPALIVE, (char *) &one,
+ sizeof(one)) < 0) {
+ snmp_log_perror("[init_smux] setsockopt(SO_KEEPALIVE) failed");
+ close(smux_listen_sd);
+ smux_listen_sd = -1;
+ return;
+ }
+#endif /* SO_KEEPALIVE */
+
+ if (listen(smux_listen_sd, SOMAXCONN) == -1) {
+ snmp_log_perror("[init_smux] listen failed");
+ close(smux_listen_sd);
+ smux_listen_sd = -1;
+ return;
+ }
+
+ DEBUGMSGTL(("smux_init",
+ "[smux_init] done; smux listen sd is %d, smux port is %d\n",
+ smux_listen_sd, ntohs(lo_socket.sin_port)));
+}
+
+u_char *
+var_smux(struct variable * vp,
+ oid * name,
+ size_t * length,
+ int exact, size_t * var_len, WriteMethod ** write_method)
+{
+ u_char *valptr, val_type;
+ smux_reg *rptr;
+
+ *write_method = var_smux_write;
+ /*
+ * search the active registration list
+ */
+ for (rptr = ActiveRegs; rptr; rptr = rptr->sr_next) {
+ if (0 >= snmp_oidtree_compare(vp->name, vp->namelen, rptr->sr_name,
+ rptr->sr_name_len))
+ break;
+ }
+ if (rptr == NULL)
+ return NULL;
+ else if (exact && (*length < rptr->sr_name_len))
+ return NULL;
+
+ valptr = smux_snmp_process(exact, name, length,
+ var_len, &val_type, rptr->sr_fd);
+
+ if (valptr == NULL)
+ return NULL;
+
+ if ((snmp_oidtree_compare(name, *length, rptr->sr_name,
+ rptr->sr_name_len)) != 0) {
+ /*
+ * the peer has returned a value outside
+ * * of the registered tree
+ */
+ return NULL;
+ } else {
+ /*
+ * set the type and return the value
+ */
+ vp->type = val_type;
+ return valptr;
+ }
+}
+
+int
+var_smux_write(int action,
+ u_char * var_val,
+ u_char var_val_type,
+ size_t var_val_len,
+ u_char * statP, oid * name, size_t name_len)
+{
+ smux_reg *rptr;
+ u_char buf[SMUXMAXPKTSIZE], *ptr, sout[3], type;
+ int reterr;
+ size_t var_len, datalen, name_length, packet_len;
+ size_t len;
+ ssize_t tmp_len;
+ long reqid, errsts, erridx;
+ u_char *dataptr;
+
+ DEBUGMSGTL(("smux", "[var_smux_write] entering var_smux_write\n"));
+
+ len = SMUXMAXPKTSIZE;
+ reterr = SNMP_ERR_NOERROR;
+ var_len = var_val_len;
+ name_length = name_len;
+
+ /*
+ * XXX find the descriptor again
+ */
+ for (rptr = ActiveRegs; rptr; rptr = rptr->sr_next) {
+ if (!snmp_oidtree_compare(name, name_len, rptr->sr_name,
+ rptr->sr_name_len))
+ break;
+ }
+
+ if (!rptr) {
+ DEBUGMSGTL(("smux", "[var_smux_write] unknown registration\n"));
+ return SNMP_ERR_GENERR;
+ }
+
+ switch (action) {
+ case RESERVE1:
+ DEBUGMSGTL(("smux", "[var_smux_write] entering RESERVE1\n"));
+
+ /*
+ * length might be long
+ */
+ var_len += (*(var_val + 1) & ASN_LONG_LEN) ?
+ var_len + ((*(var_val + 1) & 0x7F) + 2) : 2;
+
+ switch (var_val_type) {
+ case ASN_INTEGER:
+ case ASN_OCTET_STR:
+ case ASN_COUNTER:
+ case ASN_GAUGE:
+ case ASN_TIMETICKS:
+ case ASN_UINTEGER:
+ case ASN_COUNTER64:
+ case ASN_IPADDRESS:
+ case ASN_OPAQUE:
+ case ASN_NSAP:
+ case ASN_OBJECT_ID:
+ case ASN_BIT_STR:
+ datalen = var_val_len;
+ dataptr = var_val;
+ break;
+ case SNMP_NOSUCHOBJECT:
+ case SNMP_NOSUCHINSTANCE:
+ case SNMP_ENDOFMIBVIEW:
+ case ASN_NULL:
+ default:
+ DEBUGMSGTL(("smux",
+ "[var_smux_write] variable not supported\n"));
+ return SNMP_ERR_GENERR;
+ break;
+ }
+
+ if ((smux_build((u_char) SMUX_SET, smux_reqid,
+ name, &name_length, var_val_type, dataptr,
+ datalen, buf, &len)) < 0) {
+ DEBUGMSGTL(("smux", "[var_smux_write] smux build failed\n"));
+ return SNMP_ERR_GENERR;
+ }
+
+ if (sendto(rptr->sr_fd, (void *) buf, len, 0, NULL, 0) < 0) {
+ DEBUGMSGTL(("smux", "[var_smux_write] send failed\n"));
+ return SNMP_ERR_GENERR;
+ }
+
+ while (1) {
+ /*
+ * peek at what's received
+ */
+ if ((len = recvfrom(rptr->sr_fd, (void *) buf,
+ SMUXMAXPKTSIZE, MSG_PEEK, NULL, NULL)) <= 0) {
+ if ((len == -1) && ((errno == EINTR) || (errno == EAGAIN)))
+ {
+ continue;
+ }
+ DEBUGMSGTL(("smux",
+ "[var_smux_write] peek failed or timed out\n"));
+ /*
+ * do we need to do a peer cleanup in this case??
+ */
+ smux_peer_cleanup(rptr->sr_fd);
+ smux_snmp_select_list_del(rptr->sr_fd);
+ return SNMP_ERR_GENERR;
+ }
+
+ DEBUGMSGTL(("smux", "[var_smux_write] Peeked at %" NETSNMP_PRIz
+ "d bytes\n", len));
+ DEBUGDUMPSETUP("var_smux_write", buf, len);
+
+ /*
+ * determine if we received more than one packet
+ */
+ packet_len = len;
+ ptr = asn_parse_header(buf, &packet_len, &type);
+ packet_len += (ptr - buf);
+ if (len > (ssize_t)packet_len) {
+ /*
+ * set length to receive only the first packet
+ */
+ len = packet_len;
+ }
+
+ /*
+ * receive the first packet
+ */
+ tmp_len = len;
+ do
+ {
+ len = tmp_len;
+ len = recvfrom(rptr->sr_fd, (void *) buf, len, 0, NULL, NULL);
+ }
+ while((len == -1) && ((errno == EINTR) || (errno == EAGAIN)));
+
+ if (len <= 0) {
+ DEBUGMSGTL(("smux",
+ "[var_smux_write] recv failed or timed out\n"));
+ smux_peer_cleanup(rptr->sr_fd);
+ smux_snmp_select_list_del(rptr->sr_fd);
+ return SNMP_ERR_GENERR;
+ }
+
+ DEBUGMSGTL(("smux", "[var_smux_write] Received %" NETSNMP_PRIz
+ "d bytes\n", len));
+
+ if (buf[0] == SMUX_TRAP) {
+ DEBUGMSGTL(("smux", "[var_smux_write] Received trap\n"));
+ snmp_log(LOG_INFO, "Got trap from peer on fd %d\n",
+ rptr->sr_fd);
+ ptr = asn_parse_header(buf, &len, &type);
+ smux_trap_process(ptr, &len);
+
+
+ /*
+ * go and peek at received data again
+ */
+ /*
+ * we could receive the reply or another trap
+ */
+ } else {
+ ptr = buf;
+ ptr = asn_parse_header(ptr, &len, &type);
+ if ((ptr == NULL) || type != SNMP_MSG_RESPONSE)
+ return SNMP_ERR_GENERR;
+
+ ptr =
+ asn_parse_int(ptr, &len, &type, &reqid, sizeof(reqid));
+ if ((ptr == NULL) || type != ASN_INTEGER)
+ return SNMP_ERR_GENERR;
+
+ ptr =
+ asn_parse_int(ptr, &len, &type, &errsts,
+ sizeof(errsts));
+ if ((ptr == NULL) || type != ASN_INTEGER)
+ return SNMP_ERR_GENERR;
+
+ if (errsts) {
+ DEBUGMSGTL(("smux",
+ "[var_smux_write] errsts returned\n"));
+ return (errsts);
+ }
+
+ ptr =
+ asn_parse_int(ptr, &len, &type, &erridx,
+ sizeof(erridx));
+ if ((ptr == NULL) || type != ASN_INTEGER)
+ return SNMP_ERR_GENERR;
+
+ reterr = SNMP_ERR_NOERROR;
+ break;
+ }
+ } /* while (1) */
+ break; /* case Action == RESERVE1 */
+
+ case RESERVE2:
+ DEBUGMSGTL(("smux", "[var_smux_write] entering RESERVE2\n"));
+ reterr = SNMP_ERR_NOERROR;
+ break; /* case Action == RESERVE2 */
+
+ case FREE:
+ case COMMIT:
+ ptr = sout;
+ *(ptr++) = (u_char) SMUX_SOUT;
+ *(ptr++) = (u_char) 1;
+ if (action == FREE) {
+ *ptr = (u_char) 1; /* rollback */
+ DEBUGMSGTL(("smux",
+ "[var_smux_write] entering FREE - sending RollBack \n"));
+ } else {
+ *ptr = (u_char) 0; /* commit */
+ DEBUGMSGTL(("smux",
+ "[var_smux_write] entering FREE - sending Commit \n"));
+ }
+
+ if ((sendto(rptr->sr_fd, (void *) sout, 3, 0, NULL, 0)) < 0) {
+ DEBUGMSGTL(("smux",
+ "[var_smux_write] send rollback/commit failed\n"));
+ return SNMP_ERR_GENERR;
+ }
+
+ reterr = SNMP_ERR_NOERROR;
+ break; /* case Action == COMMIT */
+
+ default:
+ break;
+ }
+ return reterr;
+}
+
+
+int
+smux_accept(int sd)
+{
+ u_char data[SMUXMAXPKTSIZE], *ptr, type;
+ struct sockaddr_in in_socket;
+ struct timeval tv;
+ int fail, fd;
+ socklen_t alen;
+ int length;
+ size_t len;
+
+ alen = sizeof(struct sockaddr_in);
+ /*
+ * this may be too high
+ */
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+
+ /*
+ * connection request
+ */
+ DEBUGMSGTL(("smux", "[smux_accept] Calling accept()\n"));
+ errno = 0;
+ if ((fd = accept(sd, (struct sockaddr *) &in_socket, &alen)) < 0) {
+ snmp_log_perror("[smux_accept] accept failed");
+ return -1;
+ } else {
+ snmp_log(LOG_INFO, "[smux_accept] accepted fd %d from %s:%d\n",
+ fd, inet_ntoa(in_socket.sin_addr),
+ ntohs(in_socket.sin_port));
+ if (npeers + 1 == SMUXMAXPEERS) {
+ snmp_log(LOG_ERR,
+ "[smux_accept] denied peer on fd %d, limit %d reached",
+ fd, SMUXMAXPEERS);
+ close(sd);
+ return -1;
+ }
+
+ /*
+ * now block for an OpenPDU
+ */
+ do
+ {
+ length = recvfrom(fd, (char *) data, SMUXMAXPKTSIZE, 0, NULL, NULL);
+ }
+ while((length == -1) && ((errno == EINTR) || (errno == EAGAIN)));
+
+ if (length <= 0) {
+ DEBUGMSGTL(("smux",
+ "[smux_accept] peer on fd %d died or timed out\n",
+ fd));
+ close(fd);
+ return -1;
+ }
+ /*
+ * try to authorize him
+ */
+ ptr = data;
+ len = length;
+ if ((ptr = asn_parse_header(ptr, &len, &type)) == NULL) {
+ smux_send_close(fd, SMUXC_PACKETFORMAT);
+ close(fd);
+ DEBUGMSGTL(("smux", "[smux_accept] peer on %d sent bad open", fd));
+ return -1;
+ } else if (type != (u_char) SMUX_OPEN) {
+ smux_send_close(fd, SMUXC_PROTOCOLERROR);
+ close(fd);
+ DEBUGMSGTL(("smux",
+ "[smux_accept] peer on %d did not send open: (%d)\n",
+ fd, type));
+ return -1;
+ }
+ ptr = smux_open_process(fd, ptr, &len, &fail);
+ if (fail) {
+ smux_send_close(fd, SMUXC_AUTHENTICATIONFAILURE);
+ close(fd);
+ DEBUGMSGTL(("smux",
+ "[smux_accept] peer on %d failed authentication\n",
+ fd));
+ return -1;
+ }
+
+ /*
+ * he's OK
+ */
+#ifdef SO_RCVTIMEO
+ if (setsockopt
+ (fd, SOL_SOCKET, SO_RCVTIMEO, (void *) &tv, sizeof(tv)) < 0) {
+ DEBUGMSGTL(("smux",
+ "[smux_accept] setsockopt(SO_RCVTIMEO) failed fd %d\n",
+ fd));
+ snmp_log_perror("smux_accept: setsockopt SO_RCVTIMEO");
+ }
+#endif
+ npeers++;
+ DEBUGMSGTL(("smux", "[smux_accept] fd %d\n", fd));
+
+ /*
+ * Process other PDUs already read, e.g. a registerRequest.
+ */
+ len = length - (ptr - data);
+ if (smux_pdu_process(fd, ptr, len) < 0) {
+ /*
+ * Easy come, easy go. Clean-up is already done.
+ */
+ return -1;
+ }
+ }
+ return fd;
+}
+
+int
+smux_process(int fd)
+{
+ int length, tmp_length;
+ u_char data[SMUXMAXPKTSIZE];
+ u_char type, *ptr;
+ size_t packet_len;
+
+ do
+ {
+ length = recvfrom(fd, (char *) data, SMUXMAXPKTSIZE, MSG_PEEK, NULL,
+ NULL);
+ }
+ while((length == -1) && ((errno == EINTR) || (errno == EAGAIN)));
+
+ if (length <= 0)
+ {
+ snmp_log_perror("[smux_process] peek failed");
+ smux_peer_cleanup(fd);
+ return -1;
+ }
+
+ /*
+ * determine if we received more than one packet
+ */
+ packet_len = length;
+ ptr = asn_parse_header(data, &packet_len, &type);
+ packet_len += (ptr - data);
+ if (length > packet_len) {
+ /*
+ * set length to receive only the first packet
+ */
+ length = packet_len;
+ }
+
+ tmp_length = length;
+ do
+ {
+ length = tmp_length;
+ length = recvfrom(fd, (char *) data, length, 0, NULL, NULL);
+ }
+ while((length == -1) && ((errno == EINTR) || (errno == EAGAIN)));
+
+ if (length <= 0) {
+ /*
+ * the peer went away, close this descriptor
+ * * and delete it from the list
+ */
+ DEBUGMSGTL(("smux",
+ "[smux_process] peer on fd %d died or timed out\n",
+ fd));
+ smux_peer_cleanup(fd);
+ return -1;
+ }
+
+ return smux_pdu_process(fd, data, length);
+}
+
+static int
+smux_pdu_process(int fd, u_char * data, size_t length)
+{
+ int error;
+ size_t len;
+ u_char *ptr, type;
+
+ DEBUGMSGTL(("smux", "[smux_pdu_process] Processing %" NETSNMP_PRIz
+ "d bytes\n", length));
+
+ error = 0;
+ ptr = data;
+ while (error == 0 && ptr != NULL && ptr < data + length) {
+ len = length - (ptr - data);
+ ptr = asn_parse_header(ptr, &len, &type);
+ DEBUGMSGTL(("smux", "[smux_pdu_process] type is %d\n",
+ (int) type));
+ switch (type) {
+ case SMUX_OPEN:
+ smux_send_close(fd, SMUXC_PROTOCOLERROR);
+ DEBUGMSGTL(("smux",
+ "[smux_pdu_process] peer on fd %d sent duplicate open?\n",
+ fd));
+ smux_peer_cleanup(fd);
+ error = -1;
+ break;
+ case SMUX_CLOSE:
+ ptr = smux_close_process(fd, ptr, &len);
+ smux_peer_cleanup(fd);
+ error = -1;
+ break;
+ case SMUX_RREQ:
+ ptr = smux_rreq_process(fd, ptr, &len);
+ break;
+ case SMUX_RRSP:
+ error = -1;
+ smux_send_close(fd, SMUXC_PROTOCOLERROR);
+ smux_peer_cleanup(fd);
+ DEBUGMSGTL(("smux",
+ "[smux_pdu_process] peer on fd %d sent RRSP!\n",
+ fd));
+ break;
+ case SMUX_SOUT:
+ error = -1;
+ smux_send_close(fd, SMUXC_PROTOCOLERROR);
+ smux_peer_cleanup(fd);
+ DEBUGMSGTL(("smux", "This shouldn't have happened!\n"));
+ break;
+ case SMUX_TRAP:
+ snmp_log(LOG_INFO, "Got trap from peer on fd %d\n", fd);
+ if (ptr)
+ {
+ DEBUGMSGTL(("smux", "[smux_pdu_process] call smux_trap_process.\n"));
+ ptr = smux_trap_process(ptr, &len);
+ }
+ else
+ {
+ DEBUGMSGTL(("smux", "[smux_pdu_process] smux_trap_process not called: ptr=NULL.\n"));
+ DEBUGMSGTL(("smux", "[smux_pdu_process] Error: \n%s\n", snmp_api_errstring(0)));
+ }
+ /*
+ * watch out for close on top of this...should return correct end
+ */
+ break;
+ default:
+ smux_send_close(fd, SMUXC_PACKETFORMAT);
+ smux_peer_cleanup(fd);
+ DEBUGMSGTL(("smux", "[smux_pdu_process] Wrong type %d\n",
+ (int) type));
+ error = -1;
+ break;
+ }
+ }
+ return error;
+}
+
+static u_char *
+smux_open_process(int fd, u_char * ptr, size_t * len, int *fail)
+{
+ u_char type;
+ long version;
+ oid oid_name[MAX_OID_LEN];
+ char passwd[SMUXMAXSTRLEN];
+ char descr[SMUXMAXSTRLEN];
+ char oid_print[SMUXMAXSTRLEN];
+ int i;
+ size_t oid_name_len, string_len;
+
+ if (!(ptr = asn_parse_int(ptr, len, &type, &version, sizeof(version)))) {
+ DEBUGMSGTL(("smux", "[smux_open_process] version parse failed\n"));
+ *fail = TRUE;
+ return ((ptr += *len));
+ }
+ DEBUGMSGTL(("smux",
+ "[smux_open_process] version %ld, len %" NETSNMP_PRIz
+ "u, type %d\n", version, *len, (int) type));
+
+ oid_name_len = MAX_OID_LEN;
+ if ((ptr = asn_parse_objid(ptr, len, &type, oid_name,
+ &oid_name_len)) == NULL) {
+ DEBUGMSGTL(("smux", "[smux_open_process] oid parse failed\n"));
+ *fail = TRUE;
+ return ((ptr += *len));
+ }
+ snprint_objid(oid_print, sizeof(oid_print), oid_name, oid_name_len);
+
+ if (snmp_get_do_debugging()) {
+ DEBUGMSGTL(("smux", "[smux_open_process] smux peer: %s\n",
+ oid_print));
+ DEBUGMSGTL(("smux", "[smux_open_process] len %" NETSNMP_PRIz
+ "u, type %d\n", *len, (int) type));
+ }
+
+ string_len = SMUXMAXSTRLEN;
+ if ((ptr = asn_parse_string(ptr, len, &type, (u_char *) descr,
+ &string_len)) == NULL) {
+ DEBUGMSGTL(("smux", "[smux_open_process] descr parse failed\n"));
+ *fail = TRUE;
+ return ((ptr += *len));
+ }
+
+ if (snmp_get_do_debugging()) {
+ DEBUGMSGTL(("smux", "[smux_open_process] smux peer descr: "));
+ for (i = 0; i < (int) string_len; i++)
+ DEBUGMSG(("smux", "%c", descr[i]));
+ DEBUGMSG(("smux", "\n"));
+ DEBUGMSGTL(("smux", "[smux_open_process] len %" NETSNMP_PRIz
+ "u, type %d\n", *len, (int) type));
+ }
+ descr[string_len] = 0;
+
+ string_len = SMUXMAXSTRLEN;
+ if ((ptr = asn_parse_string(ptr, len, &type, (u_char *) passwd,
+ &string_len)) == NULL) {
+ DEBUGMSGTL(("smux", "[smux_open_process] passwd parse failed\n"));
+ *fail = TRUE;
+ return ((ptr += *len));
+ }
+
+ if (snmp_get_do_debugging()) {
+ DEBUGMSGTL(("smux", "[smux_open_process] smux peer passwd: "));
+ for (i = 0; i < (int) string_len; i++)
+ DEBUGMSG(("smux", "%c", passwd[i]));
+ DEBUGMSG(("smux", "\n"));
+ DEBUGMSGTL(("smux", "[smux_open_process] len %" NETSNMP_PRIz
+ "u, type %d\n", *len, (int) type));
+ }
+ passwd[string_len] = '\0';
+ if (!smux_auth_peer(oid_name, oid_name_len, passwd, fd)) {
+ snmp_log(LOG_WARNING,
+ "refused smux peer: oid %s, descr %s\n",
+ oid_print, descr);
+ *fail = TRUE;
+ return ptr;
+ }
+ snmp_log(LOG_INFO,
+ "accepted smux peer: oid %s, descr %s\n",
+ oid_print, descr);
+ *fail = FALSE;
+ return ptr;
+}
+
+static void
+smux_send_close(int fd, int reason)
+{
+ u_char outpacket[3], *ptr;
+
+ ptr = outpacket;
+
+ *(ptr++) = (u_char) SMUX_CLOSE;
+ *(ptr++) = (u_char) 1;
+ *ptr = (u_char) (reason & 0xFF);
+
+ if (snmp_get_do_debugging())
+ DEBUGMSGTL(("smux",
+ "[smux_close] sending close to fd %d, reason %d\n", fd,
+ reason));
+
+ /*
+ * send a response back
+ */
+ if (sendto(fd, (char *) outpacket, 3, 0, NULL, 0) < 0) {
+ snmp_log_perror("[smux_snmp_close] send failed");
+ }
+}
+
+
+static int
+smux_auth_peer(oid * name, size_t namelen, char *passwd, int fd)
+{
+ int i;
+ char oid_print[SMUXMAXSTRLEN];
+
+ if (snmp_get_do_debugging()) {
+ snprint_objid(oid_print, sizeof(oid_print), name, namelen);
+ DEBUGMSGTL(("smux:auth", "[smux_auth_peer] Authorizing: %s, %s\n",
+ oid_print, passwd));
+ }
+
+ for (i = 0; i < nauths; i++) {
+ if (snmp_get_do_debugging()) {
+ snprint_objid(oid_print, sizeof(oid_print),
+ Auths[i]->sa_oid, Auths[i]->sa_oid_len);
+ DEBUGMSGTL(("smux:auth", "[smux_auth_peer] Checking OID: %s (%d)\n",
+ oid_print, i));
+ }
+ if (snmp_oid_compare(Auths[i]->sa_oid, Auths[i]->sa_oid_len,
+ name, namelen) == 0) {
+ if (snmp_get_do_debugging()) {
+ DEBUGMSGTL(("smux:auth", "[smux_auth_peer] Checking P/W: %s (%d)\n",
+ Auths[i]->sa_passwd, Auths[i]->sa_active_fd));
+ }
+ if (!(strcmp(Auths[i]->sa_passwd, passwd)) &&
+ (Auths[i]->sa_active_fd == -1)) {
+ /*
+ * matched, mark the auth
+ */
+ Auths[i]->sa_active_fd = fd;
+ return 1;
+ }
+ }
+ }
+ /*
+ * did not match oid and passwd
+ */
+ return 0;
+}
+
+
+/*
+ * XXX - Bells and Whistles:
+ * Need to catch signal when snmpd goes down and send close pdu to gated
+ */
+static u_char *
+smux_close_process(int fd, u_char * ptr, size_t * len)
+{
+ long down = 0;
+ int length = *len;
+
+ /*
+ * This is the integer part of the close pdu
+ */
+ while (length--) {
+ down = (down << 8) | (long) *ptr;
+ ptr++;
+ }
+
+ DEBUGMSGTL(("smux",
+ "[smux_close_process] close from peer on fd %d reason %ld\n",
+ fd, down));
+ smux_peer_cleanup(fd);
+
+ return NULL;
+}
+
+static u_char *
+smux_rreq_process(int sd, u_char * ptr, size_t * len)
+{
+ long priority, rpriority;
+ long operation;
+ oid oid_name[MAX_OID_LEN];
+ size_t oid_name_len;
+ int i, result;
+ u_char type;
+ smux_reg *rptr, *nrptr;
+
+ oid_name_len = MAX_OID_LEN;
+ ptr = asn_parse_objid(ptr, len, &type, oid_name, &oid_name_len);
+
+ DEBUGMSGTL(("smux", "[smux_rreq_process] smux subtree: "));
+ DEBUGMSGOID(("smux", oid_name, oid_name_len));
+ DEBUGMSG(("smux", "\n"));
+
+ if ((ptr = asn_parse_int(ptr, len, &type, &priority,
+ sizeof(priority))) == NULL) {
+ DEBUGMSGTL(("smux",
+ "[smux_rreq_process] priority parse failed\n"));
+ smux_send_rrsp(sd, -1);
+ return NULL;
+ }
+ DEBUGMSGTL(("smux", "[smux_rreq_process] priority %ld\n", priority));
+
+ if ((ptr = asn_parse_int(ptr, len, &type, &operation,
+ sizeof(operation))) == NULL) {
+ DEBUGMSGTL(("smux",
+ "[smux_rreq_process] operation parse failed\n"));
+ smux_send_rrsp(sd, -1);
+ return NULL;
+ }
+ DEBUGMSGTL(("smux", "[smux_rreq_process] operation %ld\n", operation));
+
+ if (operation == SMUX_REGOP_DELETE) {
+ /*
+ * search the active list for this registration
+ */
+ rptr =
+ smux_find_match(ActiveRegs, sd, oid_name, oid_name_len,
+ priority);
+ if (rptr) {
+ rpriority = rptr->sr_priority;
+ /*
+ * unregister the mib
+ */
+ unregister_mib(rptr->sr_name, rptr->sr_name_len);
+ /*
+ * find a replacement
+ */
+ nrptr =
+ smux_find_replacement(rptr->sr_name, rptr->sr_name_len);
+ if (nrptr) {
+ /*
+ * found one
+ */
+ smux_replace_active(rptr, nrptr);
+ } else {
+ /*
+ * no replacement found
+ */
+ smux_list_detach(&ActiveRegs, rptr);
+ free(rptr);
+ }
+ smux_send_rrsp(sd, rpriority);
+ return ptr;
+ }
+ /*
+ * search the passive list for this registration
+ */
+ rptr =
+ smux_find_match(PassiveRegs, sd, oid_name, oid_name_len,
+ priority);
+ if (rptr) {
+ rpriority = rptr->sr_priority;
+ smux_list_detach(&PassiveRegs, rptr);
+ free(rptr);
+ smux_send_rrsp(sd, rpriority);
+ return ptr;
+ }
+ /*
+ * This peer cannot unregister the tree, it does not
+ * * belong to him. Send him an error.
+ */
+ smux_send_rrsp(sd, -1);
+ return ptr;
+
+ } else if ((operation == SMUX_REGOP_REGISTER_RO) ||
+ (operation == SMUX_REGOP_REGISTER_RW)) {
+ if (priority < -1) {
+ DEBUGMSGTL(("smux",
+ "[smux_rreq_process] peer fd %d invalid priority %ld",
+ sd, priority));
+ smux_send_rrsp(sd, -1);
+ return NULL;
+ }
+ if ((nrptr = malloc(sizeof(smux_reg))) == NULL) {
+ snmp_log_perror("[smux_rreq_process] malloc");
+ smux_send_rrsp(sd, -1);
+ return NULL;
+ }
+ nrptr->sr_priority = priority;
+ nrptr->sr_name_len = oid_name_len;
+ nrptr->sr_fd = sd;
+ for (i = 0; i < (int) oid_name_len; i++)
+ nrptr->sr_name[i] = oid_name[i];
+
+ /*
+ * See if this tree matches or scopes any of the
+ * * active trees.
+ */
+ for (rptr = ActiveRegs; rptr; rptr = rptr->sr_next) {
+ result =
+ snmp_oid_compare(oid_name, oid_name_len, rptr->sr_name,
+ rptr->sr_name_len);
+ if (result == 0) {
+ if (oid_name_len == rptr->sr_name_len) {
+ if (nrptr->sr_priority == -1) {
+ nrptr->sr_priority = rptr->sr_priority;
+ do {
+ nrptr->sr_priority++;
+ } while (smux_list_add(&PassiveRegs, nrptr));
+ goto done;
+ } else if (nrptr->sr_priority < rptr->sr_priority) {
+ /*
+ * Better priority. There are no better
+ * * priorities for this tree in the passive list,
+ * * so replace the current active tree.
+ */
+ smux_replace_active(rptr, nrptr);
+ goto done;
+ } else {
+ /*
+ * Equal or worse priority
+ */
+ do {
+ nrptr->sr_priority++;
+ } while (smux_list_add(&PassiveRegs, nrptr) == -1);
+ goto done;
+ }
+ } else if (oid_name_len < rptr->sr_name_len) {
+ /*
+ * This tree scopes a current active
+ * * tree. Replace the current active tree.
+ */
+ smux_replace_active(rptr, nrptr);
+ goto done;
+ } else { /* oid_name_len > rptr->sr_name_len */
+ /*
+ * This tree is scoped by a current
+ * * active tree.
+ */
+ do {
+ nrptr->sr_priority++;
+ } while (smux_list_add(&PassiveRegs, nrptr) == -1);
+ goto done;
+ }
+ }
+ }
+ /*
+ * We didn't find it in the active list. Add it at
+ * * the requested priority.
+ */
+ if (nrptr->sr_priority == -1)
+ nrptr->sr_priority = 0;
+ smux_list_add(&ActiveRegs, nrptr);
+ if (register_mib("smux", (struct variable *)
+ smux_variables, sizeof(struct variable2),
+ 1, nrptr->sr_name, nrptr->sr_name_len)
+ != SNMPERR_SUCCESS) {
+ DEBUGMSGTL(("smux", "[smux_rreq_process] Failed to register subtree\n"));
+ smux_list_detach(&ActiveRegs, nrptr);
+ free(nrptr);
+ smux_send_rrsp(sd, -1);
+ return NULL;
+ }
+
+ done:
+ smux_send_rrsp(sd, nrptr->sr_priority);
+ return ptr;
+ } else {
+ DEBUGMSGTL(("smux", "[smux_rreq_process] unknown operation\n"));
+ smux_send_rrsp(sd, -1);
+ return NULL;
+ }
+}
+
+/*
+ * Find the registration with a matching descriptor, OID and priority. If
+ * the priority is -1 then find a registration with a matching descriptor,
+ * a matching OID, and the highest priority.
+ */
+static smux_reg *
+smux_find_match(smux_reg * regs, int sd, oid * oid_name,
+ size_t oid_name_len, long priority)
+{
+ smux_reg *rptr, *bestrptr;
+
+ bestrptr = NULL;
+ for (rptr = regs; rptr; rptr = rptr->sr_next) {
+ if (rptr->sr_fd != sd)
+ continue;
+ if (snmp_oid_compare
+ (rptr->sr_name, rptr->sr_name_len, oid_name, oid_name_len))
+ continue;
+ if (rptr->sr_priority == priority)
+ return rptr;
+ if (priority != -1)
+ continue;
+ if (bestrptr) {
+ if (bestrptr->sr_priority > rptr->sr_priority)
+ bestrptr = rptr;
+ } else {
+ bestrptr = rptr;
+ }
+ }
+ return bestrptr;
+}
+
+static void
+smux_replace_active(smux_reg * actptr, smux_reg * pasptr)
+{
+ smux_list_detach(&ActiveRegs, actptr);
+ unregister_mib(actptr->sr_name, actptr->sr_name_len);
+
+ smux_list_detach(&PassiveRegs, pasptr);
+ (void) smux_list_add(&ActiveRegs, pasptr);
+
+ register_mib("smux", (struct variable *) smux_variables,
+ sizeof(struct variable2), 1, pasptr->sr_name,
+ pasptr->sr_name_len);
+ free(actptr);
+}
+
+static void
+smux_list_detach(smux_reg ** head, smux_reg * m_remove)
+{
+ smux_reg *rptr, *rptr2;
+
+ if (*head == NULL) {
+ DEBUGMSGTL(("smux", "[smux_list_detach] Ouch!"));
+ return;
+ }
+ if (*head == m_remove) {
+ *head = (*head)->sr_next;
+ return;
+ }
+ for (rptr = *head, rptr2 = rptr->sr_next; rptr2;
+ rptr2 = rptr2->sr_next, rptr = rptr->sr_next) {
+ if (rptr2 == m_remove) {
+ rptr->sr_next = rptr2->sr_next;
+ return;
+ }
+ }
+}
+
+/*
+ * Attempt to add a registration (in order) to a list. If the
+ * add fails (because of an existing registration with equal
+ * priority) return -1.
+ */
+static int
+smux_list_add(smux_reg ** head, smux_reg * add)
+{
+ smux_reg *rptr, *prev;
+ int result;
+
+ if (*head == NULL) {
+ *head = add;
+ (*head)->sr_next = NULL;
+ return 0;
+ }
+ prev = NULL;
+ for (rptr = *head; rptr; rptr = rptr->sr_next) {
+ result = snmp_oid_compare(add->sr_name, add->sr_name_len,
+ rptr->sr_name, rptr->sr_name_len);
+ if (result == 0) {
+ /*
+ * Same tree...
+ */
+ if (add->sr_priority == rptr->sr_priority) {
+ /*
+ * ... same pri : nope
+ */
+ return -1;
+ } else if (add->sr_priority < rptr->sr_priority) {
+ /*
+ * ... lower pri : insert and return
+ */
+ add->sr_next = rptr;
+ if ( prev ) { prev->sr_next = add; }
+ else { *head = add; }
+ return 0;
+#ifdef XXX
+ } else {
+ /*
+ * ... higher pri : put after
+ */
+ add->sr_next = rptr->sr_next;
+ rptr->sr_next = add;
+#endif
+ }
+ } else if (result < 0) {
+ /*
+ * Earlier tree : insert and return
+ */
+ add->sr_next = rptr;
+ if ( prev ) { prev->sr_next = add; }
+ else { *head = add; }
+ return 0;
+#ifdef XXX
+ } else {
+ /*
+ * Later tree : put after
+ */
+ add->sr_next = rptr->sr_next;
+ rptr->sr_next = add;
+ return 0;
+#endif
+ }
+ prev = rptr;
+ }
+ /*
+ * Otherwise, this entry must come last
+ */
+ if ( prev ) { prev->sr_next = add; }
+ else { *head = add; }
+ add->sr_next = NULL;
+ return 0;
+}
+
+/*
+ * Find a replacement for this registration. In order
+ * of preference:
+ *
+ * - Least difference in subtree length
+ * - Best (lowest) priority
+ *
+ * For example, if we need to replace .1.3.6.1.69,
+ * we would pick .1.3.6.1.69.1 instead of .1.3.6.69.1.1
+ *
+ */
+static smux_reg *
+smux_find_replacement(oid * name, size_t name_len)
+{
+ smux_reg *rptr, *bestptr;
+ int bestlen, difflen;
+
+ bestlen = SMUX_MAX_PRIORITY;
+ bestptr = NULL;
+
+ for (rptr = PassiveRegs; rptr; rptr = rptr->sr_next) {
+ if (!snmp_oidtree_compare(rptr->sr_name, rptr->sr_name_len,
+ name, name_len)) {
+ if ((difflen = rptr->sr_name_len - name_len)
+ < bestlen || !bestptr) {
+ bestlen = difflen;
+ bestptr = rptr;
+ } else if ((difflen == bestlen) &&
+ (rptr->sr_priority < bestptr->sr_priority))
+ bestptr = rptr;
+ }
+ }
+ return bestptr;
+}
+
+u_char *
+smux_snmp_process(int exact,
+ oid * objid,
+ size_t * len,
+ size_t * return_len, u_char * return_type, int sd)
+{
+ u_char packet[SMUXMAXPKTSIZE], *ptr, result[SMUXMAXPKTSIZE];
+ ssize_t length = SMUXMAXPKTSIZE;
+ int tmp_length;
+ u_char type;
+ size_t packet_len;
+
+ /*
+ * Send the query to the peer
+ */
+ smux_reqid++;
+
+ if (exact)
+ type = SMUX_GET;
+ else
+ type = SMUX_GETNEXT;
+
+ if (smux_build(type, smux_reqid, objid, len, 0, NULL,
+ *len, packet, (size_t *) &length) < 0) {
+ snmp_log(LOG_ERR, "[smux_snmp_process]: smux_build failed\n");
+ return NULL;
+ }
+ DEBUGMSGTL(("smux", "[smux_snmp_process] oid from build: "));
+ DEBUGMSGOID(("smux", objid, *len));
+ DEBUGMSG(("smux", "\n"));
+
+ if (sendto(sd, (char *) packet, length, 0, NULL, 0) < 0) {
+ snmp_log_perror("[smux_snmp_process] send failed");
+ }
+
+ DEBUGMSGTL(("smux",
+ "[smux_snmp_process] Sent %d request to peer; %" NETSNMP_PRIz "d bytes\n",
+ (int) type, length));
+
+ while (1) {
+ /*
+ * peek at what's received
+ */
+ length = recvfrom(sd, (char *) result, SMUXMAXPKTSIZE, MSG_PEEK, NULL,
+ NULL);
+ if (length <= 0) {
+ if ((length == -1) && ((errno == EINTR) || (errno == EAGAIN)))
+ {
+ continue;
+ }
+ else
+ {
+ snmp_log_perror("[smux_snmp_process] peek failed");
+ smux_peer_cleanup(sd);
+ smux_snmp_select_list_del(sd);
+ return NULL;
+ }
+ }
+
+ DEBUGMSGTL(("smux", "[smux_snmp_process] Peeked at %" NETSNMP_PRIz "d bytes\n",
+ length));
+ DEBUGDUMPSETUP("smux_snmp_process", result, length);
+
+ /*
+ * determine if we received more than one packet
+ */
+ packet_len = length;
+ ptr = asn_parse_header(result, &packet_len, &type);
+ packet_len += (ptr - result);
+ if (length > packet_len) {
+ /*
+ * set length to receive only the first packet
+ */
+ length = packet_len;
+ }
+
+ /*
+ * receive the first packet
+ */
+ tmp_length = length;
+ do
+ {
+ length = tmp_length;
+ length = recvfrom(sd, (char *) result, length, 0, NULL, NULL);
+ }
+ while((length == -1) && ((errno == EINTR) || (errno == EAGAIN)));
+
+ if (length <= 0) {
+ snmp_log_perror("[smux_snmp_process] recv failed");
+ smux_peer_cleanup(sd);
+ smux_snmp_select_list_del(sd);
+ return NULL;
+ }
+
+ DEBUGMSGTL(("smux", "[smux_snmp_process] Received %" NETSNMP_PRIz "d bytes\n",
+ length));
+
+ if (result[0] == SMUX_TRAP) {
+ DEBUGMSGTL(("smux", "[smux_snmp_process] Received trap\n"));
+ snmp_log(LOG_INFO, "Got trap from peer on fd %d\n", sd);
+ ptr = asn_parse_header(result, (size_t *) &length, &type);
+ smux_trap_process(ptr, (size_t *) &length);
+
+ /*
+ * go and peek at received data again
+ */
+ /*
+ * we could receive the reply or another trap
+ */
+ } else {
+ /*
+ * Interpret reply
+ */
+ ptr = smux_parse(result, objid, len, return_len, return_type);
+ /*
+ * ptr will point to query result or NULL if error
+ */
+ break;
+ }
+ } /* while (1) */
+
+ return ptr;
+}
+
+static u_char *
+smux_parse(u_char * rsp,
+ oid * objid,
+ size_t * oidlen, size_t * return_len, u_char * return_type)
+{
+ size_t length = SMUXMAXPKTSIZE;
+ u_char *ptr, type;
+ long reqid, errstat, errindex;
+
+ ptr = rsp;
+
+ /*
+ * Return pointer to the snmp/smux return value.
+ * return_len should contain the number of bytes in the value
+ * returned above.
+ * objid is the next object, with len for GETNEXT.
+ * objid and len are not changed for GET
+ */
+ ptr = asn_parse_header(ptr, &length, &type);
+ if (ptr == NULL || type != SNMP_MSG_RESPONSE)
+ return NULL;
+
+ if ((ptr = asn_parse_int(ptr, &length, &type, &reqid,
+ sizeof(reqid))) == NULL) {
+ DEBUGMSGTL(("smux", "[smux_parse] parse of reqid failed\n"));
+ return NULL;
+ }
+ if ((ptr = asn_parse_int(ptr, &length, &type, &errstat,
+ sizeof(errstat))) == NULL) {
+ DEBUGMSGTL(("smux",
+ "[smux_parse] parse of error status failed\n"));
+ return NULL;
+ }
+ if ((ptr = asn_parse_int(ptr, &length, &type, &errindex,
+ sizeof(errindex))) == NULL) {
+ DEBUGMSGTL(("smux", "[smux_parse] parse of error index failed\n"));
+ return NULL;
+ }
+
+ /*
+ * XXX How to send something intelligent back in case of an error
+ */
+ DEBUGMSGTL(("smux",
+ "[smux_parse] Message type %d, reqid %ld, errstat %ld, \n\terrindex %ld\n",
+ (int) type, reqid, errstat, errindex));
+ if (ptr == NULL || errstat != SNMP_ERR_NOERROR)
+ return NULL;
+
+ /*
+ * stuff to return
+ */
+ return (smux_parse_var
+ (ptr, &length, objid, oidlen, return_len, return_type));
+}
+
+
+static u_char *
+smux_parse_var(u_char * varbind,
+ size_t * varbindlength,
+ oid * objid,
+ size_t * oidlen, size_t * varlength, u_char * vartype)
+{
+ oid var_name[MAX_OID_LEN];
+ size_t var_name_len;
+ size_t var_val_len;
+ u_char *var_val;
+ size_t str_len, objid_len;
+ size_t len;
+ u_char *ptr;
+ u_char type;
+
+ ptr = varbind;
+ len = *varbindlength;
+
+ DEBUGMSGTL(("smux", "[smux_parse_var] before any processing: "));
+ DEBUGMSGOID(("smux", objid, *oidlen));
+ DEBUGMSG(("smux", "\n"));
+
+ ptr = asn_parse_header(ptr, &len, &type);
+ if (ptr == NULL || type != (ASN_SEQUENCE | ASN_CONSTRUCTOR)) {
+ snmp_log(LOG_NOTICE, "[smux_parse_var] Panic: type %d\n",
+ (int) type);
+ return NULL;
+ }
+
+ /*
+ * get hold of the objid and the asn1 coded value
+ */
+ var_name_len = MAX_OID_LEN;
+ ptr = snmp_parse_var_op(ptr, var_name, &var_name_len, vartype,
+ &var_val_len, &var_val, &len);
+
+ *oidlen = var_name_len;
+ memcpy(objid, var_name, var_name_len * sizeof(oid));
+
+ DEBUGMSGTL(("smux", "[smux_parse_var] returning oid : "));
+ DEBUGMSGOID(("smux", objid, *oidlen));
+ DEBUGMSG(("smux", "\n"));
+ /*
+ * XXX
+ */
+ len = SMUXMAXPKTSIZE;
+ DEBUGMSGTL(("smux",
+ "[smux_parse_var] Asn coded len of var %" NETSNMP_PRIz
+ "u, type %d\n", var_val_len, (int) *vartype));
+
+ switch ((short) *vartype) {
+ case ASN_INTEGER:
+ *varlength = sizeof(long);
+ asn_parse_int(var_val, &len, vartype,
+ (long *) &smux_long, *varlength);
+ return (u_char *) & smux_long;
+ break;
+ case ASN_COUNTER:
+ case ASN_GAUGE:
+ case ASN_TIMETICKS:
+ case ASN_UINTEGER:
+ *varlength = sizeof(u_long);
+ asn_parse_unsigned_int(var_val, &len, vartype,
+ (u_long *) & smux_ulong, *varlength);
+ return (u_char *) & smux_ulong;
+ break;
+ case ASN_COUNTER64:
+ *varlength = sizeof(smux_counter64);
+ asn_parse_unsigned_int64(var_val, &len, vartype,
+ (struct counter64 *) &smux_counter64,
+ *varlength);
+ return (u_char *) & smux_counter64;
+ break;
+ case ASN_IPADDRESS:
+ *varlength = 4;
+ /*
+ * consume the tag and length, but just copy here
+ * because we know it is an ip address
+ */
+ if ((var_val = asn_parse_header(var_val, &len, &type)) == NULL)
+ return NULL;
+ memcpy((u_char *) & (smux_sa.sin_addr.s_addr), var_val,
+ *varlength);
+ return (u_char *) & (smux_sa.sin_addr.s_addr);
+ break;
+ case ASN_OCTET_STR:
+ /*
+ * XXX
+ */
+ if (len == 0)
+ return NULL;
+ str_len = SMUXMAXSTRLEN;
+ asn_parse_string(var_val, &len, vartype, smux_str, &str_len);
+ *varlength = str_len;
+ return smux_str;
+ break;
+ case ASN_OPAQUE:
+ case ASN_NSAP:
+ case ASN_OBJECT_ID:
+ objid_len = MAX_OID_LEN;
+ asn_parse_objid(var_val, &len, vartype, smux_objid, &objid_len);
+ *varlength = objid_len * sizeof(oid);
+ return (u_char *) smux_objid;
+ break;
+ case SNMP_NOSUCHOBJECT:
+ case SNMP_NOSUCHINSTANCE:
+ case SNMP_ENDOFMIBVIEW:
+ case ASN_NULL:
+ return NULL;
+ break;
+ case ASN_BIT_STR:
+ /*
+ * XXX
+ */
+ if (len == 0)
+ return NULL;
+ str_len = SMUXMAXSTRLEN;
+ asn_parse_bitstring(var_val, &len, vartype, smux_str, &str_len);
+ *varlength = str_len;
+ return (u_char *) smux_str;
+ break;
+ default:
+ snmp_log(LOG_ERR, "bad type returned (%x)\n", *vartype);
+ return NULL;
+ break;
+ }
+}
+
+/*
+ * XXX This is a bad hack - do not want to muck with ucd code
+ */
+static int
+smux_build(u_char type,
+ long reqid,
+ oid * objid,
+ size_t * oidlen,
+ u_char val_type,
+ u_char * val, size_t val_len, u_char * packet, size_t * length)
+{
+ u_char *ptr, *save1, *save2;
+ size_t len;
+ long errstat = 0;
+ long errindex = 0;
+
+ /*
+ * leave space for Seq and length
+ */
+ save1 = packet;
+ ptr = packet + 4;
+ len = *length - 4;
+
+ /*
+ * build reqid
+ */
+ ptr = asn_build_int(ptr, &len,
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
+ ASN_INTEGER), &reqid,
+ sizeof(reqid));
+ if (ptr == NULL) {
+ return -1;
+ }
+
+ /*
+ * build err stat
+ */
+ ptr = asn_build_int(ptr, &len,
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
+ ASN_INTEGER), &errstat, sizeof(errstat));
+ if (ptr == NULL) {
+ return -1;
+ }
+
+ /*
+ * build err index
+ */
+ ptr = asn_build_int(ptr, &len,
+ (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE |
+ ASN_INTEGER), &errindex,
+ sizeof(errindex));
+ if (ptr == NULL) {
+ return -1;
+ }
+
+ save2 = ptr;
+ ptr += 4;
+ len -= 4;
+
+ if (type != SMUX_SET) {
+ val_type = ASN_NULL;
+ val_len = 0;
+ }
+
+ /*
+ * build var list : snmp_build_var_op not liked by gated XXX
+ */
+ ptr = snmp_build_var_op(ptr, objid, oidlen, val_type, val_len,
+ val, &len);
+ if (ptr == NULL) {
+ return -1;
+ }
+
+ len = ptr - save1;
+ asn_build_sequence(save1, &len, type, (ptr - save1 - 4));
+
+ len = ptr - save2;
+ asn_build_sequence(save2, &len,
+ (ASN_SEQUENCE | ASN_CONSTRUCTOR),
+ (ptr - save2 - 4));
+
+ *length = ptr - packet;
+
+ return 0;
+}
+
+static void
+smux_peer_cleanup(int sd)
+{
+ smux_reg *nrptr, *rptr, *rptr2;
+ int i;
+
+ /*
+ * close the descriptor
+ */
+ close(sd);
+
+ /*
+ * delete all of the passive registrations that this peer owns
+ */
+ for (rptr = PassiveRegs; rptr; rptr = nrptr) {
+ nrptr = rptr->sr_next;
+ if (rptr->sr_fd == sd) {
+ smux_list_detach(&PassiveRegs, rptr);
+ free(rptr);
+ }
+ rptr = nrptr;
+ }
+ /*
+ * find replacements for all of the active registrations found
+ */
+ for (rptr = ActiveRegs; rptr; rptr = rptr2) {
+ rptr2 = rptr->sr_next;
+ if (rptr->sr_fd == sd) {
+ smux_list_detach(&ActiveRegs, rptr);
+ unregister_mib(rptr->sr_name, rptr->sr_name_len);
+ if ((nrptr = smux_find_replacement(rptr->sr_name,
+ rptr->sr_name_len)) !=
+ NULL) {
+ smux_list_detach(&PassiveRegs, nrptr);
+ smux_list_add(&ActiveRegs, nrptr);
+ register_mib("smux", (struct variable *)
+ smux_variables, sizeof(struct variable2),
+ 1, nrptr->sr_name, nrptr->sr_name_len);
+ }
+ free(rptr);
+ }
+ }
+
+ /*
+ * decrement the peer count
+ */
+ npeers--;
+
+ /*
+ * make his auth available again
+ */
+ for (i = 0; i < nauths; i++) {
+ if (Auths[i]->sa_active_fd == sd) {
+ char oid_name[128];
+ Auths[i]->sa_active_fd = -1;
+ snprint_objid(oid_name, sizeof(oid_name), Auths[i]->sa_oid,
+ Auths[i]->sa_oid_len);
+ snmp_log(LOG_INFO, "peer disconnected: %s\n", oid_name);
+ }
+ }
+}
+
+int
+smux_send_rrsp(int sd, int pri)
+{
+ u_char outdata[2 + sizeof(int)];
+ u_char *ptr = outdata;
+ int intsize = sizeof(int);
+ u_int mask = ((u_int) 0xFF) << (8 * (sizeof(int) - 1));
+ /*
+ * e.g. mask is 0xFF000000 on a 32-bit machine
+ */
+ int sent;
+
+ /*
+ * This is kind of like calling asn_build_int(), but the
+ * encoding will always be the size of an integer on this
+ * machine, never shorter.
+ */
+ *ptr++ = (u_char) SMUX_RRSP;
+ *ptr++ = (u_char) intsize;
+
+ /*
+ * Copy each byte, most significant first.
+ */
+ while (intsize--) {
+ *ptr++ = (u_char) ((pri & mask) >> (8 * (sizeof(int) - 1)));
+ pri <<= 8;
+ }
+
+ sent = sendto(sd, (char *) outdata, sizeof outdata, 0, NULL, 0);
+ if (sent < 0) {
+ DEBUGMSGTL(("smux", "[smux_send_rrsp] send failed\n"));
+ }
+ return (sent);
+}
+
+static u_char *
+smux_trap_process(u_char * rsp, size_t * len)
+{
+ oid sa_enterpriseoid[MAX_OID_LEN], var_name[MAX_OID_LEN];
+ size_t datalen, var_name_len, var_val_len, maxlen;
+ size_t sa_enterpriseoid_len;
+ u_char vartype, *ptr, *var_val;
+
+ long trap, specific;
+ u_long timestamp;
+
+ netsnmp_variable_list *snmptrap_head, *snmptrap_ptr, *snmptrap_tmp;
+ snmptrap_head = NULL;
+ snmptrap_ptr = NULL;
+
+ ptr = rsp;
+
+ /*
+ * parse the sub-agent enterprise oid
+ */
+ sa_enterpriseoid_len = MAX_OID_LEN;
+ if ((ptr = asn_parse_objid(ptr, len,
+ &vartype, (oid *) & sa_enterpriseoid,
+ &sa_enterpriseoid_len)) == NULL) {
+ DEBUGMSGTL(("smux",
+ "[smux_trap_process] asn_parse_objid failed\n"));
+ return NULL;
+ }
+
+ /*
+ * parse the agent-addr ipAddress
+ */
+ datalen = SMUXMAXSTRLEN;
+ if (((ptr = asn_parse_string(ptr, len,
+ &vartype, smux_str,
+ &datalen)) == NULL) ||
+ (vartype != (u_char) ASN_IPADDRESS)) {
+ DEBUGMSGTL(("smux",
+ "[smux_trap_process] asn_parse_string failed\n"));
+ return NULL;
+ }
+
+ /*
+ * parse the generic trap int
+ */
+ datalen = sizeof(long);
+ if ((ptr = asn_parse_int(ptr, len, &vartype, &trap, datalen)) == NULL) {
+ DEBUGMSGTL(("smux",
+ "[smux_trap_process] asn_parse_int generic failed\n"));
+ return NULL;
+ }
+
+ /*
+ * parse the specific trap int
+ */
+ datalen = sizeof(long);
+ if ((ptr = asn_parse_int(ptr, len,
+ &vartype, &specific, datalen)) == NULL) {
+ DEBUGMSGTL(("smux",
+ "[smux_trap_process] asn_parse_int specific failed\n"));
+ return NULL;
+ }
+
+ /*
+ * parse the timeticks timestamp
+ */
+ datalen = sizeof(u_long);
+ if (((ptr = asn_parse_unsigned_int(ptr, len,
+ &vartype, (u_long *) & timestamp,
+ datalen)) == NULL) ||
+ (vartype != (u_char) ASN_TIMETICKS)) {
+ DEBUGMSGTL(("smux",
+ "[smux_trap_process] asn_parse_unsigned_int (timestamp) failed\n"));
+ return NULL;
+ }
+
+ /*
+ * parse out the overall sequence
+ */
+ ptr = asn_parse_header(ptr, len, &vartype);
+ if (ptr == NULL || vartype != (ASN_SEQUENCE | ASN_CONSTRUCTOR)) {
+ return NULL;
+ }
+
+ /*
+ * parse the variable bindings
+ */
+ while (ptr && *len) {
+
+ /*
+ * get the objid and the asn1 coded value
+ */
+ var_name_len = MAX_OID_LEN;
+ ptr = snmp_parse_var_op(ptr, var_name, &var_name_len, &vartype,
+ &var_val_len, (u_char **) & var_val, len);
+
+ if (ptr == NULL) {
+ return NULL;
+ }
+
+ maxlen = SMUXMAXPKTSIZE;
+ switch ((short) vartype) {
+ case ASN_INTEGER:
+ var_val_len = sizeof(long);
+ asn_parse_int(var_val, &maxlen, &vartype,
+ (long *) &smux_long, var_val_len);
+ var_val = (u_char *) & smux_long;
+ break;
+ case ASN_COUNTER:
+ case ASN_GAUGE:
+ case ASN_TIMETICKS:
+ case ASN_UINTEGER:
+ var_val_len = sizeof(u_long);
+ asn_parse_unsigned_int(var_val, &maxlen, &vartype,
+ (u_long *) & smux_ulong, var_val_len);
+ var_val = (u_char *) & smux_ulong;
+ break;
+ case ASN_COUNTER64:
+ var_val_len = sizeof(smux_counter64);
+ asn_parse_unsigned_int64(var_val, &maxlen, &vartype,
+ (struct counter64 *) &smux_counter64,
+ var_val_len);
+ var_val = (u_char *) & smux_counter64;
+ break;
+ case ASN_IPADDRESS:
+ var_val_len = 4;
+ /*
+ * consume the tag and length, but just copy here
+ * because we know it is an ip address
+ */
+ if ((var_val =
+ asn_parse_header(var_val, &maxlen, &vartype)) == NULL)
+ return NULL;
+ memcpy((u_char *) & (smux_sa.sin_addr.s_addr), var_val,
+ var_val_len);
+ var_val = (u_char *) & (smux_sa.sin_addr.s_addr);
+ break;
+ case ASN_OPAQUE:
+ case ASN_OCTET_STR:
+ /*
+ * XXX
+ */
+ if (len == NULL)
+ return NULL;
+ var_val_len = SMUXMAXSTRLEN;
+ asn_parse_string(var_val, &maxlen, &vartype,
+ smux_str, &var_val_len);
+ var_val = smux_str;
+ break;
+ case ASN_OBJECT_ID:
+ var_val_len = MAX_OID_LEN;
+ asn_parse_objid(var_val, &maxlen, &vartype,
+ smux_objid, &var_val_len);
+ var_val_len *= sizeof(oid);
+ var_val = (u_char *) smux_objid;
+ break;
+ case SNMP_NOSUCHOBJECT:
+ case SNMP_NOSUCHINSTANCE:
+ case SNMP_ENDOFMIBVIEW:
+ case ASN_NULL:
+ var_val = NULL;
+ break;
+ case ASN_BIT_STR:
+ /*
+ * XXX
+ */
+ if (len == NULL)
+ return NULL;
+ var_val_len = SMUXMAXSTRLEN;
+ asn_parse_bitstring(var_val, &maxlen, &vartype,
+ smux_str, &var_val_len);
+ var_val = (u_char *) smux_str;
+ break;
+ case ASN_NSAP:
+ default:
+ snmp_log(LOG_ERR, "bad type returned (%x)\n", vartype);
+ var_val = NULL;
+ break;
+ }
+
+ snmptrap_tmp =
+ (netsnmp_variable_list *)
+ malloc(sizeof(netsnmp_variable_list));
+ if (snmptrap_tmp == NULL)
+ return NULL;
+ memset(snmptrap_tmp, 0, sizeof(netsnmp_variable_list));
+ if (snmptrap_head == NULL) {
+ snmptrap_head = snmptrap_tmp;
+ snmptrap_ptr = snmptrap_head;
+ } else {
+ snmptrap_ptr->next_variable = snmptrap_tmp;
+ snmptrap_ptr = snmptrap_ptr->next_variable;
+ }
+
+ snmptrap_ptr->type = vartype;
+ snmptrap_ptr->next_variable = NULL;
+ snmp_set_var_objid(snmptrap_ptr, var_name, var_name_len);
+ snmp_set_var_value(snmptrap_ptr, (char *) var_val, var_val_len);
+
+ }
+
+ /*
+ * send the traps
+ */
+ send_enterprise_trap_vars(trap, specific, (oid *) & sa_enterpriseoid,
+ sa_enterpriseoid_len, snmptrap_head);
+
+ /*
+ * free trap variables
+ */
+ snmp_free_varbind(snmptrap_head);
+
+ return ptr;
+
+}
+
+#define NUM_SOCKETS 32
+static int sdlist[NUM_SOCKETS], sdlen = 0;
+
+int smux_snmp_select_list_add(int sd)
+{
+ if (sdlen < NUM_SOCKETS)
+ {
+ sdlist[sdlen++] = sd;
+ return(1);
+ }
+ return(0);
+}
+
+int smux_snmp_select_list_del(int sd)
+{
+ int i, found=0;
+
+ for (i = 0; i < (sdlen); i++) {
+ if (sdlist[i] == sd)
+ {
+ sdlist[i] = 0;
+ found = 1;
+ }
+ if ((found) &&(i < (sdlen - 1)))
+ sdlist[i] = sdlist[i + 1];
+ }
+ if (found)
+ {
+ sdlen--;
+ return(1);
+ }
+ return(0);
+}
+
+int smux_snmp_select_list_get_length(void)
+{
+ return(sdlen);
+}
+
+int smux_snmp_select_list_get_SD_from_List(int pos)
+{
+ if (pos < NUM_SOCKETS)
+ {
+ return(sdlist[pos]);
+ }
+ return(0);
+}