| /* |
| * 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); |
| } |