2223 lines
65 KiB
C
2223 lines
65 KiB
C
/*
|
|
* 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 "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_get(oid *, size_t, oid *, size_t *, int, size_t *,
|
|
u_char *);
|
|
int var_smux_write(int, u_char *, u_char, size_t, 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;
|
|
|
|
|
|
|
|
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 = (int) 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)));
|
|
}
|
|
|
|
static int
|
|
smux_handler(netsnmp_mib_handler *handler,
|
|
netsnmp_handler_registration *reginfo,
|
|
netsnmp_agent_request_info *reqinfo,
|
|
netsnmp_request_info *requests)
|
|
{
|
|
u_char *access = NULL;
|
|
size_t var_len;
|
|
int exact = 1;
|
|
int status = 0;
|
|
u_char var_type;
|
|
static long old_reqid = -1;
|
|
static long old_sessid = -1;
|
|
long new_reqid, new_sessid;
|
|
|
|
/* Increment the reqid of outgoing SMUX messages only when processing
|
|
* new incoming SNMP message, i.e. when reqid or session id chamges */
|
|
new_reqid = reqinfo->asp->pdu->reqid;
|
|
new_sessid = reqinfo->asp->session->sessid;
|
|
DEBUGMSGTL(("smux", "smux_handler: incoming reqid=%ld, sessid=%ld\n",
|
|
new_reqid, new_sessid));
|
|
if (old_reqid != new_reqid || old_sessid != new_sessid) {
|
|
smux_reqid++;
|
|
old_reqid = new_reqid;
|
|
old_sessid = new_sessid;
|
|
}
|
|
|
|
switch (reqinfo->mode) {
|
|
case MODE_GETNEXT:
|
|
case MODE_GETBULK:
|
|
exact = 0;
|
|
}
|
|
|
|
for (; requests; requests = requests->next) {
|
|
switch(reqinfo->mode) {
|
|
case MODE_GET:
|
|
case MODE_GETNEXT:
|
|
case MODE_SET_RESERVE1:
|
|
access = var_smux_get(reginfo->rootoid,
|
|
reginfo->rootoid_len,
|
|
requests->requestvb->name,
|
|
&requests->requestvb->name_length,
|
|
exact,
|
|
&var_len,
|
|
&var_type);
|
|
if (access)
|
|
if (reqinfo->mode != MODE_SET_RESERVE1)
|
|
snmp_set_var_typed_value(requests->requestvb,
|
|
var_type, access, var_len);
|
|
if (reqinfo->mode != MODE_SET_RESERVE1)
|
|
break;
|
|
/* fall through if MODE_SET_RESERVE1 */
|
|
/* FALL THROUGH */
|
|
|
|
default:
|
|
/* SET processing */
|
|
status = var_smux_write(reqinfo->mode,
|
|
requests->requestvb->val.string,
|
|
requests->requestvb->type,
|
|
requests->requestvb->val_len,
|
|
requests->requestvb->name,
|
|
requests->requestvb->name_length);
|
|
if (status != SNMP_ERR_NOERROR) {
|
|
netsnmp_set_request_error(reqinfo, requests, status);
|
|
}
|
|
}
|
|
}
|
|
return SNMP_ERR_NOERROR;
|
|
}
|
|
|
|
u_char *
|
|
var_smux_get(oid *root, size_t root_len,
|
|
oid * name, size_t * length,
|
|
int exact, size_t * var_len, u_char *var_type)
|
|
{
|
|
u_char *valptr;
|
|
smux_reg *rptr;
|
|
|
|
/*
|
|
* search the active registration list
|
|
*/
|
|
for (rptr = ActiveRegs; rptr; rptr = rptr->sr_next) {
|
|
if (0 >= snmp_oidtree_compare(root, root_len, 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, var_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 {
|
|
return valptr;
|
|
}
|
|
}
|
|
|
|
int
|
|
var_smux_write(int action,
|
|
u_char * var_val,
|
|
u_char var_val_type,
|
|
size_t var_val_len,
|
|
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);
|
|
if (ptr == NULL)
|
|
return SNMP_ERR_GENERR;
|
|
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"));
|
|
DEBUGMSGTL(("smux", "Got trap from peer on fd %d\n",
|
|
rptr->sr_fd));
|
|
ptr = asn_parse_header(buf, &len, &type);
|
|
if (ptr == NULL)
|
|
return SNMP_ERR_GENERR;
|
|
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 = (int) accept(sd, (struct sockaddr *) &in_socket, &alen)) < 0) {
|
|
snmp_log_perror("[smux_accept] accept failed");
|
|
return -1;
|
|
} else {
|
|
DEBUGMSGTL(("smux", "[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(fd);
|
|
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)
|
|
{
|
|
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);
|
|
if (ptr == NULL)
|
|
return -1;
|
|
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);
|
|
if (ptr == NULL) {
|
|
DEBUGMSGTL(("smux", "[smux_pdu_process] cannot parse header\n"));
|
|
break;
|
|
}
|
|
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:
|
|
DEBUGMSGTL(("smux", "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;
|
|
}
|
|
DEBUGMSGTL(("smux",
|
|
"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;
|
|
netsnmp_handler_registration *reg;
|
|
|
|
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;
|
|
|
|
reg = netsnmp_create_handler_registration("smux",
|
|
smux_handler,
|
|
nrptr->sr_name,
|
|
nrptr->sr_name_len,
|
|
HANDLER_CAN_RWRITE);
|
|
if (reg == NULL) {
|
|
snmp_log(LOG_ERR, "SMUX: cannot create new smux peer "
|
|
"registration\n");
|
|
smux_send_rrsp(sd, -1);
|
|
free(nrptr);
|
|
return NULL;
|
|
}
|
|
if (netsnmp_register_handler(reg) != MIB_REGISTERED_OK) {
|
|
snmp_log(LOG_ERR, "SMUX: cannot register new smux peer\n");
|
|
smux_send_rrsp(sd, -1);
|
|
free(nrptr);
|
|
return NULL;
|
|
}
|
|
nrptr->reginfo = reg;
|
|
smux_list_add(&ActiveRegs, nrptr);
|
|
|
|
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)
|
|
{
|
|
netsnmp_handler_registration *reg;
|
|
|
|
smux_list_detach(&ActiveRegs, actptr);
|
|
if (actptr->reginfo) {
|
|
netsnmp_unregister_handler(actptr->reginfo);
|
|
actptr->reginfo = NULL;
|
|
}
|
|
|
|
smux_list_detach(&PassiveRegs, pasptr);
|
|
|
|
(void) smux_list_add(&ActiveRegs, pasptr);
|
|
free(actptr);
|
|
|
|
reg = netsnmp_create_handler_registration("smux",
|
|
smux_handler,
|
|
pasptr->sr_name,
|
|
pasptr->sr_name_len,
|
|
HANDLER_CAN_RWRITE);
|
|
if (reg == NULL) {
|
|
snmp_log(LOG_ERR, "SMUX: cannot create new smux peer registration\n");
|
|
pasptr->reginfo = NULL;
|
|
return;
|
|
}
|
|
if (netsnmp_register_handler(reg) != MIB_REGISTERED_OK) {
|
|
snmp_log(LOG_ERR, "SMUX: cannot register new smux peer\n");
|
|
pasptr->reginfo = NULL;
|
|
return;
|
|
}
|
|
pasptr->reginfo = reg;
|
|
}
|
|
|
|
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
|
|
*/
|
|
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);
|
|
if (ptr == NULL)
|
|
return NULL;
|
|
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"));
|
|
DEBUGMSGTL(("smux", "Got trap from peer on fd %d\n", sd));
|
|
ptr = asn_parse_header(result, (size_t *) &length, &type);
|
|
if (ptr == NULL)
|
|
return NULL;
|
|
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;
|
|
netsnmp_handler_registration *reg;
|
|
|
|
/*
|
|
* 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);
|
|
if (rptr->reginfo) {
|
|
netsnmp_unregister_handler(rptr->reginfo);
|
|
rptr->reginfo = NULL;
|
|
}
|
|
if ((nrptr = smux_find_replacement(rptr->sr_name,
|
|
rptr->sr_name_len)) !=
|
|
NULL) {
|
|
smux_list_detach(&PassiveRegs, nrptr);
|
|
reg = netsnmp_create_handler_registration("smux",
|
|
smux_handler,
|
|
nrptr->sr_name,
|
|
nrptr->sr_name_len,
|
|
HANDLER_CAN_RWRITE);
|
|
if (reg == NULL) {
|
|
snmp_log(LOG_ERR, "SMUX: cannot create new smux peer "
|
|
"registration\n");
|
|
continue;
|
|
}
|
|
if (netsnmp_register_handler(reg) != MIB_REGISTERED_OK) {
|
|
snmp_log(LOG_ERR, "SMUX: cannot register new smux peer\n");
|
|
continue;
|
|
}
|
|
nrptr->reginfo = reg;
|
|
smux_list_add(&ActiveRegs, nrptr);
|
|
}
|
|
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);
|
|
DEBUGMSGTL(("smux", "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)
|
|
goto err;
|
|
|
|
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)
|
|
goto err;
|
|
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
|
|
*/
|
|
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
|
|
*/
|
|
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 = calloc(1, sizeof(netsnmp_variable_list));
|
|
if (snmptrap_tmp == NULL)
|
|
goto err;
|
|
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;
|
|
|
|
err:
|
|
snmp_free_varbind(snmptrap_head);
|
|
return NULL;
|
|
}
|
|
|
|
#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);
|
|
}
|