blob: d4fc1056cb1dd4aec4d1f7c3bf6042c0eaceadd9 [file] [log] [blame]
/*
* Smux module authored by Rohit Dube.
* Rewritten by Nick Amato <naamato@merit.net>.
*/
#include <config.h>
#include <sys/types.h>
#include <ctype.h>
#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>
#include <netdb.h>
#include <sys/stat.h>
#include <sys/socket.h>
#if HAVE_SYS_FILIO_H
#include <sys/filio.h>
#endif
#if HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#if HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#include "../../../snmplib/system.h"
#include "asn1.h"
#include "mibincl.h"
#include "mib.h"
#include "read_config.h"
#include "snmp.h"
#include "snmp_api.h"
#include "snmp_impl.h"
#include "smux.h"
#include "var_struct.h"
#include "util_funcs.h"
#include "mibdefs.h"
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];
#ifdef USING_SD_HANDLERS
extern int sdlist[];
extern int sdlen;
extern int (*sd_handlers[])(int);
#endif
static struct timeval smux_rcv_timeout;
static u_long smux_reqid;
int 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_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, u_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_send_rrsp (int, int);
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, RWRITE, var_smux, 0, {MIBINDEX}},
};
void
smux_parse_peer_auth(char *token, char *cptr)
{
smux_peer_auth *aptr;
if ((aptr = (smux_peer_auth *)calloc(1, sizeof(smux_peer_auth))) == NULL) {
perror("smux_parse_peer_auth: malloc");
return;
}
aptr->sa_active_fd = -1;
if (!cptr) {
/* null passwords OK */
Auths[nauths++] = aptr;
DEBUGMSGTL(("smux_conf", "null password\n"));
return;
}
if(*cptr == '.')
cptr++;
if (!isdigit(*cptr)) {
config_perror("second token is not an OID");
free((char *)aptr);
return;
}
/* oid */
aptr->sa_oid_len = parse_miboid(cptr, aptr->sa_oid);
DEBUGMSGTL(("smux_conf", "parsing registration for: %s\n", cptr));
while (isdigit(*cptr) || *cptr == '.')
cptr++;
cptr = skip_white(cptr);
/* password */
if (cptr)
strcpy(aptr->sa_passwd, cptr);
Auths[nauths++] = aptr;
}
void
smux_free_peer_auth(void)
{
int i;
for(i = 0; i < nauths; i++) {
free(Auths[i]);
Auths[i] = NULL;
}
}
int
init_smux(void)
{
struct sockaddr_in lo_socket;
int smux_sd;
int one = 1;
snmpd_register_config_handler("smuxpeer", smux_parse_peer_auth,
smux_free_peer_auth,
"OID-IDENTITY PASSWORD");
/* Reqid */
smux_reqid = 0;
/* 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;
lo_socket.sin_port = htons((u_short) SMUXPORT);
if ((smux_sd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
perror("[init_smux] socket failed\n");
return SMUXNOTOK;
}
if (bind (smux_sd, (struct sockaddr *) &lo_socket,
sizeof (lo_socket)) < 0) {
perror("[init_smux] bind failed\n");
close(smux_sd);
return SMUXNOTOK;
}
if (setsockopt (smux_sd, SOL_SOCKET, SO_KEEPALIVE, (char *)&one,
sizeof (one)) < 0) {
perror("[init_smux] setsockopt(SO_KEEPALIVE) failed\n");
close(smux_sd);
return SMUXNOTOK;
}
if(listen(smux_sd, SOMAXCONN) == -1) {
perror("[init_smux] listen failed\n");
close(smux_sd);
return SMUXNOTOK;
}
#ifdef USING_SD_HANDLERS
sdlist[sdlen] = smux_sd;
sd_handlers[sdlen++] = smux_accept;
fprintf(stderr, "sdlen in smux_init: %d\n", sdlen);
#endif
fprintf(stderr, "[smux_init] done; smux_sd is %d, smux_port is %d\n", smux_sd,
ntohs(lo_socket.sin_port));
return SMUXOK;
}
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 = NULL;
/* search the active registration list */
for (rptr = ActiveRegs; rptr; rptr = rptr->sr_next) {
if (!compare_tree(name, *length, rptr->sr_name,
rptr->sr_name_len))
break;
}
if (rptr == NULL)
return NULL;
else if (exact && (*length <= rptr->sr_name_len))
return NULL;
*write_method = var_smux_write;
valptr = smux_snmp_process(exact, name, length,
var_len, &val_type, rptr->sr_fd);
if ((compare_tree(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[6], type;
size_t len;
int reterr;
long reqid, errsts, erridx;
len = SMUXMAXPKTSIZE;
/* XXX find the descriptor again */
for (rptr = ActiveRegs; rptr; rptr = rptr->sr_next) {
if(!compare_tree(name, name_len, rptr->sr_name,
rptr->sr_name_len))
break;
}
if (action == COMMIT) {
if ((smux_build((u_char)SMUX_SET, smux_reqid, name,
&name_len, var_val_type, statP, var_val_len,
buf, &len)) == 0) {
DEBUGMSGTL (("smux","[var_smux_write] smux build failed\n"));
return SNMP_ERR_GENERR; /* ? */
}
}
if (send(rptr->sr_fd, (char *)buf, len, 0) < 0) {
DEBUGMSGTL (("smux","[var_smux_write] send failed\n"));
return SNMP_ERR_GENERR; /* ? */
}
if ((len = recv(rptr->sr_fd, (char *)buf, SMUXMAXPKTSIZE, 0)) <= 0) {
DEBUGMSGTL (("smux","[var_smux_write] recv failed or timed out\n"));
smux_peer_cleanup(rptr->sr_fd);
return SNMP_ERR_GENERR; /* ? */
}
ptr = buf;
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;
ptr = asn_parse_int(ptr, &len, &type, &erridx, sizeof(erridx));
if ((ptr == NULL) || type != ASN_INTEGER)
return SNMP_ERR_GENERR;
ptr = sout;
len = 6;
if ((ptr = asn_build_sequence(ptr, &len,
(u_char)SMUX_SOUT, 2)) == NULL)
return SNMP_ERR_GENERR;
*(ptr++) = (u_char)1;
if((errsts == 0) && (erridx == 0)) {
*ptr = (u_char)0;
reterr = SNMP_ERR_NOERROR;
} else {
*ptr = (u_char)1;
reterr = SNMP_ERR_COMMITFAILED;
}
if ((send(rptr->sr_fd, (char *)sout, 6, 0)) < 0) {
DEBUGMSGTL (("smux","[var_smux_write] send sout failed\n"));
return SNMP_ERR_GENERR;
}
return reterr;
}
int
smux_accept(int sd)
{
u_char data[SMUXMAXPKTSIZE], *ptr, type;
struct sockaddr_in in_socket;
struct timeval tv;
int fail, fd, alen;
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) {
perror("[smux_accept] accept failed\n");
return SMUXNOTOK;
} else {
fprintf(stderr, "[smux_accept] accepted fd %d - errno %d\n", fd, errno);
if (npeers + 1 == SMUXMAXPEERS) {
DEBUGMSGTL (("smux","[smux_accept] denied peer on fd %d, limit reached", fd));
close(sd);
return SMUXNOTOK;
}
/* now block for an OpenPDU */
if ((len = recv(fd, (char *)data, SMUXMAXPKTSIZE, 0)) <= 0) {
DEBUGMSGTL (("smux","[smux_accept] peer on fd %d died or timed out\n", fd));
close(fd);
return SMUXNOTOK;
}
/* try to authorize him */
ptr = data;
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"));
return SMUXNOTOK;
} 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", type));
return SMUXNOTOK;
}
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 SMUXNOTOK;
}
/* 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));
perror("smux/setsockopt");
}
#endif
npeers++;
#ifdef USING_SD_HANDLERS
sdlist[sdlen] = fd;
sd_handlers[sdlen++] = smux_process;
DEBUGMSGTL (("smux","[smux_accept] fd %d, sdlen %d\n", fd, sdlen));
#endif
}
return SMUXOK;
}
int
smux_process(int fd)
{
int error;
size_t len, length;
u_char data[SMUXMAXPKTSIZE], *ptr, type;
length = recv(fd, (char *)data, SMUXMAXPKTSIZE, 0);
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 SMUXNOTOK; /* return value ignored */
}
DEBUGMSGTL (("smux","[smux_process] Processing %d bytes\n", length));
error = SMUXOK;
ptr = data;
len = length;
while (ptr != NULL && ptr < data + length) {
len = length;
ptr = asn_parse_header(ptr, &len, &type);
DEBUGMSGTL (("smux","[smux_process] type is %d\n", (int) type));
switch (type) {
case SMUX_OPEN:
smux_send_close(fd, SMUXC_PROTOCOLERROR);
DEBUGMSGTL (("smux","[smux_process] peer on fd %d sent duplicate open?\n", fd));
smux_peer_cleanup(fd);
break;
case SMUX_CLOSE:
ptr = smux_close_process(fd, ptr, &len);
smux_peer_cleanup(fd);
break;
case SMUX_RREQ:
ptr = smux_rreq_process(fd, ptr, &len);
break;
case SMUX_RRSP:
error = SMUXNOTOK;
ptr = NULL;
smux_send_close(fd, SMUXC_PROTOCOLERROR);
smux_peer_cleanup(fd);
DEBUGMSGTL (("smux","[smux_process] peer on fd %d sent RRSP!\n", fd));
break;
case SMUX_SOUT:
error = SMUXNOTOK;
ptr = NULL;
smux_send_close(fd, SMUXC_PROTOCOLERROR);
smux_peer_cleanup(fd);
DEBUGMSGTL (("smux","This shouldn't have happened!\n"));
break;
default:
smux_send_close(fd, SMUXC_PACKETFORMAT);
smux_peer_cleanup(fd);
DEBUGMSGTL (("smux","[smux_process] Wrong type %d\n", (int)type));
error = SMUXNOTOK;
break;
}
}
return error; /* return value ignored */
}
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 string[SMUXMAXSTRLEN];
int i;
size_t oid_name_len, string_len;
if ((ptr = asn_parse_int(ptr, len, &type, &version,
sizeof(version))) == NULL) {
DEBUGMSGTL (("smux","[smux_open_process] version parse failed\n"));
*fail = TRUE;
return((ptr += *len));
}
DEBUGMSGTL(("smux",
"[smux_open_process] version %d, len %d, 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));
}
if (snmp_get_do_debugging()) {
DEBUGMSGTL (("smux","[smux_open_process] smux peer:"));
for (i=0; i<oid_name_len; i++)
DEBUGMSG (("smux",".%d", oid_name[i]));
DEBUGMSG (("smux"," \n"));
DEBUGMSGTL (("smux","[smux_open_process] len %d, type %d\n", *len, (int)type));
}
string_len = SMUXMAXSTRLEN;
if ((ptr = asn_parse_string(ptr, len, &type, (u_char *)string,
&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<string_len; i++)
DEBUGMSG (("smux","%c", string[i]));
DEBUGMSG (("smux"," \n"));
DEBUGMSGTL (("smux","[smux_open_process] len %d, type %d\n", *len, (int)type));
}
string_len = SMUXMAXSTRLEN;
if ((ptr = asn_parse_string(ptr, len, &type, (u_char *)string,
&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<string_len; i++)
DEBUGMSG (("smux","%c", string[i]));
DEBUGMSG (("smux"," \n"));
DEBUGMSGTL (("smux","[smux_open_process] len %d, type %d\n", *len, (int)type));
}
string[string_len] = '\0';
if(!smux_auth_peer(oid_name, oid_name_len, string, fd)) {
if(snmp_get_do_debugging()) {
DEBUGMSGTL (("smux","[smux_open_process] peer authentication failed for oid\n"));
for (i = 0; i < oid_name_len; i++)
DEBUGMSG (("smux","\t.%d", oid_name[i]));
DEBUGMSG (("smux"," password %s\n", string));
}
*fail = TRUE;
return ptr;
}
*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 (send (fd, (char *)outpacket, 3, 0) < 0) {
perror("[smux_send_close] send failed\n");
}
}
static int
smux_auth_peer(oid *name, size_t namelen, char *passwd, int fd)
{
int i;
for (i = 0; i < nauths; i++) {
if (snmp_oid_compare(Auths[i]->sa_oid, Auths[i]->sa_oid_len,
name, namelen) == 0) {
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;
}
else
return 0;
}
}
/* 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 %d\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;
long operation;
oid oid_name[MAX_OID_LEN];
size_t oid_name_len;
int i, result;
u_char type;
char c_oid[SPRINT_MAX_LEN];
smux_reg *rptr, *nrptr;
oid_name_len = MAX_OID_LEN;
ptr = asn_parse_objid(ptr, len, &type, oid_name, &oid_name_len);
if (snmp_get_do_debugging()) {
sprint_objid (c_oid, oid_name, oid_name_len);
DEBUGMSGTL (("smux","[smux_rreq_process] smux subtree: %s\n", c_oid));
}
if ((ptr = asn_parse_int(ptr, len, &type, &priority,
sizeof(priority))) == NULL) {
DEBUGMSGTL (("smux","[smux_rreq_process] priority parse failed\n"));
return NULL;
}
DEBUGMSGTL (("smux","[smux_rreq_process] priority %d\n", priority));
if ((ptr = asn_parse_int(ptr, len, &type, &operation,
sizeof(operation))) == NULL) {
DEBUGMSGTL (("smux","[smux_rreq_process] operation parse failed\n"));
return NULL;
}
DEBUGMSGTL (("smux","[smux_rreq_process] operation %d\n", operation));
if(operation == SMUX_REGOP_DELETE) {
/* search the active list for this registration */
for (rptr = ActiveRegs; rptr; rptr = rptr->sr_next) {
if ((rptr->sr_fd == sd) && !(snmp_oid_compare(rptr->sr_name,
rptr->sr_name_len, oid_name, oid_name_len)) &&
(rptr->sr_priority == priority)) {
/* unregister the mib */
unregister_mib(rptr->sr_name, rptr->sr_name_len);
/* find a replacement */
if ((nrptr = smux_find_replacement(rptr->sr_name,
rptr->sr_name_len)) == NULL) {
/* no replacement found */
smux_list_detach(&ActiveRegs, &nrptr);
free(nrptr);
} else {
/* found one */
smux_replace_active(rptr, nrptr);
}
return ptr;
}
}
/* search the passive list for this registration */
for (rptr = PassiveRegs; rptr; rptr = rptr->sr_next) {
if ((rptr->sr_fd == sd) && !(snmp_oid_compare(rptr->sr_name,
rptr->sr_name_len, oid_name, oid_name_len)) &&
(rptr->sr_priority == priority)) {
smux_list_detach(&PassiveRegs, &nrptr);
free(nrptr);
}
}
/* this peer cannot unregister the tree, it does not
* belong to him. XXX for now, ignore it.
*/
return ptr;
}
if (operation == SMUX_REGOP_REGISTER) {
if (priority < -1) {
DEBUGMSGTL (("smux","[smux_rreq_process] peer fd %d invalid priority", sd, priority));
return NULL;
}
if((nrptr = malloc(sizeof(smux_reg))) == NULL) {
perror("[smux_rreq_process] malloc");
return NULL;
}
nrptr->sr_priority = priority;
nrptr->sr_name_len = oid_name_len;
nrptr->sr_fd = sd;
for(i = 0; i < 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);
register_mib("smux", (struct variable *)
smux_variables, sizeof(struct variable2),
1, nrptr->sr_name, nrptr->sr_name_len);
done:
if (smux_send_rrsp(sd, nrptr->sr_priority))
DEBUGMSGTL (("smux","[smux_rreq_process] send failed\n"));
return ptr;
}
DEBUGMSGTL (("smux","[smux_rreq_process] unknown operation\n"));
return NULL;
}
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) {
*m_remove = *head;
*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) {
*m_remove = rptr2;
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;
int result;
if(*head == NULL) {
*head = add;
(*head)->sr_next = NULL;
return 0;
}
for (rptr = *head; rptr->sr_next; 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) && (add->sr_priority == rptr->sr_priority)) {
/* same tree, same pri, nope */
return -1;
} else if (result < 0) {
/* this can only happen if we go before the head */
add->sr_next = *head;
*head = add;
return 0;
} else if ((snmp_oid_compare(add->sr_name, add->sr_name_len,
rptr->sr_next->sr_name, rptr->sr_next->sr_name_len)) < 0) {
/* insert here */
add->sr_next = rptr->sr_next;
rptr->sr_next = add;
return 0;
}
}
/* compare the last one */
if ((snmp_oid_compare(add->sr_name, add->sr_name_len, rptr->sr_name,
rptr->sr_name_len) == 0) && add->sr_priority == rptr->sr_priority)
return -1;
else {
rptr->sr_next = 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 (!compare_tree(rptr->sr_name, rptr->sr_name_len,
name, name_len)) {
if ((difflen = rptr->sr_name_len - name_len)
< bestlen) {
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];
size_t length = SMUXMAXPKTSIZE;
u_char type;
char c_oid[SPRINT_MAX_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, &length) != SMUXOK) {
printf("[smux_snmp_process]: smux_build failed\n");
return NULL;
}
if (snmp_get_do_debugging()) {
sprint_objid (c_oid, objid, *len);
DEBUGMSGTL (("smux","[smux_snmp_process] oid from build: %s\n",c_oid));
}
if (send(sd, (char *)packet, length, 0) < 0) {
perror("[smux_snmp_process] send failed\n");
}
DEBUGMSGTL(("smux",
"[smux_snmp_process] Sent %d request to peer; %d bytes\n",
(int)type, length));
/*
* receive
* XXX the RCVTIMEO could return a short result.
*/
length = recv(sd, (char *)result, SMUXMAXPKTSIZE, 0);
if (length < 0) {
perror("[smux_snmp_process] recv failed\n");
smux_peer_cleanup(sd);
return NULL;
}
DEBUGMSGTL (("smux","[smux_snmp_process] Recived %d bytes from gated\n", length));
/* Interpret reply */
if ((ptr = smux_parse(result, objid, len, return_len, return_type)) == NULL) {
smux_send_close(sd, SMUXC_PACKETFORMAT);
return NULL;
}
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 %d, errstat %d, \n\terrindex %d\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;
char c_oid[SPRINT_MAX_LEN];
ptr = varbind;
len = *varbindlength;
if (snmp_get_do_debugging()) {
sprint_objid (c_oid, objid, *oidlen);
DEBUGMSGTL (("smux","[smux_parse_var] before any processing: %s\n", c_oid));
}
ptr = asn_parse_header(ptr, &len, &type);
if (ptr == NULL || type != (ASN_SEQUENCE | ASN_CONSTRUCTOR)) {
printf ("[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));
if (snmp_get_do_debugging()) {
sprint_objid (c_oid, objid, *oidlen);
DEBUGMSGTL (("smux","[smux_parse_var] returning oid : %s\n", c_oid));
}
/* XXX */
len = SMUXMAXPKTSIZE;
DEBUGMSGTL(("smux",
"[smux_parse_var] Asn coded len of var %d, 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;
/*
* XXX - skip tag and length. We already know this is an ip
* address
*/
memcpy((u_char *)&(smux_sa.sin_addr.s_addr), var_val+2,
*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;
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:
fprintf(stderr, "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,
u_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_unsigned_int(ptr, &len,
(u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
&reqid, sizeof(reqid));
/* build err stat */
ptr = asn_build_int(ptr, &len,
(u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
&errstat, sizeof(errstat));
/* build err index */
ptr = asn_build_int(ptr, &len,
(u_char)(ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER),
&errindex, sizeof(errindex));
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);
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 SMUXOK;
}
static void
smux_peer_cleanup(int sd)
{
smux_reg *nrptr, *rptr, *rptr2;
int nfound, i;
nfound = 0;
/* 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);
}
}
#ifdef USING_SD_HANDLERS
/* XXX stop paying attention to his socket */
for (i = 0; i < sdlen; i++) {
if (sdlist[i] == sd) {
for (; i < (sdlen-1); i++) {
sdlist[i] = sdlist[i+1];
sd_handlers[i] = sd_handlers[i+1];
}
}
}
sdlen--;
#endif
/* decrement the peer count */
npeers--;
/* make his auth available again */
for (i = 0; i < nauths; i++) {
if (Auths[i]->sa_active_fd == sd) {
Auths[i]->sa_active_fd = -1;
}
}
}
int
smux_send_rrsp(int sd, int pri)
{
u_char outdata[6], *ptr;
int i, mask;
ptr = outdata;
/* "mask is 0xFF000000 on a big-endian machine" */
mask = 0xFF;
*(ptr++) = (u_char) SMUX_RRSP;
*ptr = (u_char) 4;
for(i = 0; i < 4; i++, mask >>= 8)
*(++ptr) = (u_char)(pri & mask);
if((send(sd, (char *)outdata, 6, 0)) < 0)
return SMUXNOTOK;
else
return SMUXOK;
}