/* * Smux module authored by Rohit Dube. * Rewritten by Nick Amato . */ #include #include #include #include #if HAVE_IO_H /* win32 */ #include #endif #include #if HAVE_STDLIB_H #include #endif #if HAVE_STRING_H #include #else #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_ERR_H #include #endif #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #include #if HAVE_NETDB_H #include #endif #include #if HAVE_SYS_SOCKET_H #include #endif #if HAVE_SYS_FILIO_H #include #endif #if HAVE_NETINET_IN_H #include #endif #if HAVE_ARPA_INET_H #include #endif #if HAVE_SYS_IOCTL_H #include #endif #include #include #include #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); }