diff options
Diffstat (limited to 'snmplib/snmpCallbackDomain.c')
-rw-r--r-- | snmplib/snmpCallbackDomain.c | 623 |
1 files changed, 623 insertions, 0 deletions
diff --git a/snmplib/snmpCallbackDomain.c b/snmplib/snmpCallbackDomain.c new file mode 100644 index 0000000..8ad5bf7 --- /dev/null +++ b/snmplib/snmpCallbackDomain.c @@ -0,0 +1,623 @@ +#include <net-snmp/net-snmp-config.h> + +#include <stdio.h> +#include <sys/types.h> +#include <ctype.h> +#include <errno.h> + +#ifdef WIN32 +#include <net-snmp/library/winpipe.h> +#endif +#if HAVE_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif +#if HAVE_STDLIB_H +#include <stdlib.h> +#endif +#if HAVE_UNISTD_H +#include <unistd.h> +#endif +#if HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#if HAVE_SYS_UN_H +#include <sys/un.h> +#endif +#if HAVE_IO_H +#include <io.h> +#endif +#if HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#if HAVE_DMALLOC_H +#include <dmalloc.h> +#endif + +#include <net-snmp/types.h> +#include <net-snmp/output_api.h> +#include <net-snmp/config_api.h> +#include <net-snmp/utilities.h> + +#include <net-snmp/library/snmp_transport.h> +#include <net-snmp/library/snmpUnixDomain.h> +#include <net-snmp/library/snmp_api.h> +#include <net-snmp/library/snmp_client.h> +#include <net-snmp/library/snmpCallbackDomain.h> + +#ifndef NETSNMP_STREAM_QUEUE_LEN +#define NETSNMP_STREAM_QUEUE_LEN 5 +#endif + +#ifdef NETSNMP_TRANSPORT_CALLBACK_DOMAIN + +static netsnmp_transport_list *trlist = NULL; + +static int callback_count = 0; + +typedef struct callback_hack_s { + void *orig_transport_data; + netsnmp_pdu *pdu; +} callback_hack; + +typedef struct callback_queue_s { + int callback_num; + netsnmp_callback_pass *item; + struct callback_queue_s *next, *prev; +} callback_queue; + +callback_queue *thequeue; + +static netsnmp_transport * +find_transport_from_callback_num(int num) +{ + static netsnmp_transport_list *ptr; + for (ptr = trlist; ptr; ptr = ptr->next) + if (((netsnmp_callback_info *) ptr->transport->data)-> + callback_num == num) + return ptr->transport; + return NULL; +} + +static void +callback_debug_pdu(const char *ourstring, netsnmp_pdu *pdu) +{ + netsnmp_variable_list *vb; + int i = 1; + DEBUGMSGTL((ourstring, + "PDU: command = %d, errstat = %d, errindex = %d\n", + pdu->command, pdu->errstat, pdu->errindex)); + for (vb = pdu->variables; vb; vb = vb->next_variable) { + DEBUGMSGTL((ourstring, " var %d:", i++)); + DEBUGMSGVAR((ourstring, vb)); + DEBUGMSG((ourstring, "\n")); + } +} + +void +callback_push_queue(int num, netsnmp_callback_pass *item) +{ + callback_queue *newitem = SNMP_MALLOC_TYPEDEF(callback_queue); + callback_queue *ptr; + + newitem->callback_num = num; + newitem->item = item; + if (thequeue) { + for (ptr = thequeue; ptr && ptr->next; ptr = ptr->next) { + } + ptr->next = newitem; + newitem->prev = ptr; + } else { + thequeue = newitem; + } + DEBUGIF("dump_send_callback_transport") { + callback_debug_pdu("dump_send_callback_transport", item->pdu); + } +} + +netsnmp_callback_pass * +callback_pop_queue(int num) +{ + netsnmp_callback_pass *cp; + callback_queue *ptr; + + for (ptr = thequeue; ptr; ptr = ptr->next) { + if (ptr->callback_num == num) { + if (ptr->prev) { + ptr->prev->next = ptr->next; + } else { + thequeue = ptr->next; + } + if (ptr->next) { + ptr->next->prev = ptr->prev; + } + cp = ptr->item; + SNMP_FREE(ptr); + DEBUGIF("dump_recv_callback_transport") { + callback_debug_pdu("dump_recv_callback_transport", + cp->pdu); + } + return cp; + } + } + return NULL; +} + +/* + * Return a string representing the address in data, or else the "far end" + * address if data is NULL. + */ + +char * +netsnmp_callback_fmtaddr(netsnmp_transport *t, void *data, int len) +{ + char buf[SPRINT_MAX_LEN]; + netsnmp_callback_info *mystuff; + + if (!t) + return strdup("callback: unknown"); + + mystuff = (netsnmp_callback_info *) t->data; + + if (!mystuff) + return strdup("callback: unknown"); + + snprintf(buf, SPRINT_MAX_LEN, "callback: %d on fd %d", + mystuff->callback_num, mystuff->pipefds[0]); + return strdup(buf); +} + + + +/* + * You can write something into opaque that will subsequently get passed back + * to your send function if you like. For instance, you might want to + * remember where a PDU came from, so that you can send a reply there... + */ + +int +netsnmp_callback_recv(netsnmp_transport *t, void *buf, int size, + void **opaque, int *olength) +{ + int rc = -1; + char newbuf[1]; + netsnmp_callback_info *mystuff = (netsnmp_callback_info *) t->data; + + DEBUGMSGTL(("transport_callback", "hook_recv enter\n")); + + while (rc < 0) { +#ifdef WIN32 + rc = recvfrom(mystuff->pipefds[0], newbuf, 1, 0, NULL, 0); +#else + rc = read(mystuff->pipefds[0], newbuf, 1); +#endif + if (rc < 0 && errno != EINTR) { + break; + } + } + + if (mystuff->linkedto) { + /* + * we're the client. We don't need to do anything. + */ + } else { + /* + * malloc the space here, but it's filled in by + * snmp_callback_created_pdu() below + */ + int *returnnum = (int *) calloc(1, sizeof(int)); + *opaque = returnnum; + *olength = sizeof(int); + } + DEBUGMSGTL(("transport_callback", "hook_recv exit\n")); + return rc; +} + + + +int +netsnmp_callback_send(netsnmp_transport *t, void *buf, int size, + void **opaque, int *olength) +{ + int from, rc = -1; + netsnmp_callback_info *mystuff = (netsnmp_callback_info *) t->data; + netsnmp_callback_pass *cp; + + /* + * extract the pdu from the hacked buffer + */ + netsnmp_transport *other_side; + callback_hack *ch = (callback_hack *) * opaque; + netsnmp_pdu *pdu = ch->pdu; + *opaque = ch->orig_transport_data; + SNMP_FREE(ch); + + DEBUGMSGTL(("transport_callback", "hook_send enter\n")); + + cp = SNMP_MALLOC_TYPEDEF(netsnmp_callback_pass); + if (!cp) + return -1; + + cp->pdu = snmp_clone_pdu(pdu); + if (cp->pdu->transport_data) { + /* + * not needed and not properly freed later + */ + SNMP_FREE(cp->pdu->transport_data); + } + + if (cp->pdu->flags & UCD_MSG_FLAG_EXPECT_RESPONSE) + cp->pdu->flags ^= UCD_MSG_FLAG_EXPECT_RESPONSE; + + /* + * push the sent pdu onto the stack + */ + /* + * AND send a bogus byte to the remote callback receiver's pipe + */ + if (mystuff->linkedto) { + /* + * we're the client, send it to the parent + */ + cp->return_transport_num = mystuff->callback_num; + + other_side = find_transport_from_callback_num(mystuff->linkedto); + if (!other_side) { + snmp_free_pdu(cp->pdu); + SNMP_FREE(cp); + return -1; + } + + while (rc < 0) { +#ifdef WIN32 + rc = sendto(((netsnmp_callback_info*) other_side->data)->pipefds[1], " ", 1, 0, NULL, 0); +#else + rc = write(((netsnmp_callback_info *)other_side->data)->pipefds[1], + " ", 1); +#endif + if (rc < 0 && errno != EINTR) { + break; + } + } + callback_push_queue(mystuff->linkedto, cp); + /* + * we don't need the transport data any more + */ + if (*opaque) { + SNMP_FREE(*opaque); + *opaque = NULL; + } + } else { + /* + * we're the server, send it to the person that sent us the request + */ + from = **((int **) opaque); + /* + * we don't need the transport data any more + */ + if (*opaque) { + SNMP_FREE(*opaque); + *opaque = NULL; + } + other_side = find_transport_from_callback_num(from); + if (!other_side) { + snmp_free_pdu(cp->pdu); + SNMP_FREE(cp); + return -1; + } + while (rc < 0) { +#ifdef WIN32 + rc = sendto(((netsnmp_callback_info*) other_side->data)->pipefds[1], " ", 1, 0, NULL, 0); +#else + rc = write(((netsnmp_callback_info *)other_side->data)->pipefds[1], + " ", 1); +#endif + if (rc < 0 && errno != EINTR) { + break; + } + } + callback_push_queue(from, cp); + } + + DEBUGMSGTL(("transport_callback", "hook_send exit\n")); + return 0; +} + + + +int +netsnmp_callback_close(netsnmp_transport *t) +{ + int rc; + netsnmp_callback_info *mystuff = (netsnmp_callback_info *) t->data; + DEBUGMSGTL(("transport_callback", "hook_close enter\n")); + +#ifdef WIN32 + rc = closesocket(mystuff->pipefds[0]); + rc |= closesocket(mystuff->pipefds[1]); +#else + rc = close(mystuff->pipefds[0]); + rc |= close(mystuff->pipefds[1]); +#endif + + rc |= netsnmp_transport_remove_from_list(&trlist, t); + + DEBUGMSGTL(("transport_callback", "hook_close exit\n")); + return rc; +} + + + +int +netsnmp_callback_accept(netsnmp_transport *t) +{ + DEBUGMSGTL(("transport_callback", "hook_accept enter\n")); + DEBUGMSGTL(("transport_callback", "hook_accept exit\n")); + return 0; +} + + + +/* + * Open a Callback-domain transport for SNMP. Local is TRUE if addr + * is the local address to bind to (i.e. this is a server-type + * session); otherwise addr is the remote address to send things to + * (and we make up a temporary name for the local end of the + * connection). + */ + +netsnmp_transport * +netsnmp_callback_transport(int to) +{ + + netsnmp_transport *t = NULL; + netsnmp_callback_info *mydata; + int rc; + + /* + * transport + */ + t = SNMP_MALLOC_TYPEDEF(netsnmp_transport); + if (!t) + return NULL; + + /* + * our stuff + */ + mydata = SNMP_MALLOC_TYPEDEF(netsnmp_callback_info); + mydata->linkedto = to; + mydata->callback_num = ++callback_count; + mydata->data = NULL; + t->data = mydata; + +#ifdef WIN32 + rc = create_winpipe_transport(mydata->pipefds); +#else + rc = pipe(mydata->pipefds); +#endif + t->sock = mydata->pipefds[0]; + + if (rc) { + SNMP_FREE(mydata); + SNMP_FREE(t); + return NULL; + } + + t->f_recv = netsnmp_callback_recv; + t->f_send = netsnmp_callback_send; + t->f_close = netsnmp_callback_close; + t->f_accept = netsnmp_callback_accept; + t->f_fmtaddr = netsnmp_callback_fmtaddr; + + netsnmp_transport_add_to_list(&trlist, t); + + if (to) + DEBUGMSGTL(("transport_callback", "initialized %d linked to %d\n", + mydata->callback_num, to)); + else + DEBUGMSGTL(("transport_callback", + "initialized master listening on %d\n", + mydata->callback_num)); + return t; +} + +int +netsnmp_callback_hook_parse(netsnmp_session * sp, + netsnmp_pdu *pdu, + u_char * packetptr, size_t len) +{ + if (SNMP_MSG_RESPONSE == pdu->command || + SNMP_MSG_REPORT == pdu->command) + pdu->flags |= UCD_MSG_FLAG_RESPONSE_PDU; + else + pdu->flags &= (~UCD_MSG_FLAG_RESPONSE_PDU); + + return SNMP_ERR_NOERROR; +} + +int +netsnmp_callback_hook_build(netsnmp_session * sp, + netsnmp_pdu *pdu, u_char * ptk, size_t * len) +{ + /* + * very gross hack, as this is passed later to the transport_send + * function + */ + callback_hack *ch = SNMP_MALLOC_TYPEDEF(callback_hack); + DEBUGMSGTL(("transport_callback", "hook_build enter\n")); + ch->pdu = pdu; + ch->orig_transport_data = pdu->transport_data; + pdu->transport_data = ch; + switch (pdu->command) { + case SNMP_MSG_GETBULK: + if (pdu->max_repetitions < 0) { + sp->s_snmp_errno = SNMPERR_BAD_REPETITIONS; + return -1; + } + if (pdu->non_repeaters < 0) { + sp->s_snmp_errno = SNMPERR_BAD_REPEATERS; + return -1; + } + break; + case SNMP_MSG_RESPONSE: + case SNMP_MSG_TRAP: + case SNMP_MSG_TRAP2: + pdu->flags &= (~UCD_MSG_FLAG_EXPECT_RESPONSE); + /* + * Fallthrough + */ + default: + if (pdu->errstat == SNMP_DEFAULT_ERRSTAT) + pdu->errstat = 0; + if (pdu->errindex == SNMP_DEFAULT_ERRINDEX) + pdu->errindex = 0; + break; + } + + /* + * Copy missing values from session defaults + */ + switch (pdu->version) { +#ifndef NETSNMP_DISABLE_SNMPV1 + case SNMP_VERSION_1: +#endif +#ifndef NETSNMP_DISABLE_SNMPV2C + case SNMP_VERSION_2c: +#endif +#if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) + if (pdu->community_len == 0) { + if (sp->community_len == 0) { + sp->s_snmp_errno = SNMPERR_BAD_COMMUNITY; + return -1; + } + pdu->community = (u_char *) malloc(sp->community_len); + if (pdu->community == NULL) { + sp->s_snmp_errno = SNMPERR_MALLOC; + return -1; + } + memmove(pdu->community, + sp->community, sp->community_len); + pdu->community_len = sp->community_len; + } + break; +#endif + case SNMP_VERSION_3: + if (pdu->securityNameLen == 0) { + pdu->securityName = (char *)malloc(sp->securityNameLen); + if (pdu->securityName == NULL) { + sp->s_snmp_errno = SNMPERR_MALLOC; + return -1; + } + memmove(pdu->securityName, + sp->securityName, sp->securityNameLen); + pdu->securityNameLen = sp->securityNameLen; + } + if (pdu->securityModel == -1) + pdu->securityModel = sp->securityModel; + if (pdu->securityLevel == 0) + pdu->securityLevel = sp->securityLevel; + /* WHAT ELSE ?? */ + } + *len = 1; + DEBUGMSGTL(("transport_callback", "hook_build exit\n")); + return 1; +} + +int +netsnmp_callback_check_packet(u_char * pkt, size_t len) +{ + return 1; +} + +netsnmp_pdu * +netsnmp_callback_create_pdu(netsnmp_transport *transport, + void *opaque, size_t olength) +{ + netsnmp_pdu *pdu; + netsnmp_callback_pass *cp = + callback_pop_queue(((netsnmp_callback_info *) transport->data)-> + callback_num); + if (!cp) + return NULL; + pdu = cp->pdu; + pdu->transport_data = opaque; + pdu->transport_data_length = olength; + if (opaque) /* if created, we're the server */ + *((int *) opaque) = cp->return_transport_num; + SNMP_FREE(cp); + return pdu; +} + +netsnmp_session * +netsnmp_callback_open(int attach_to, + int (*return_func) (int op, + netsnmp_session * session, + int reqid, netsnmp_pdu *pdu, + void *magic), + int (*fpre_parse) (netsnmp_session *, + struct netsnmp_transport_s *, + void *, int), + int (*fpost_parse) (netsnmp_session *, netsnmp_pdu *, + int)) +{ + netsnmp_session callback_sess, *callback_ss; + netsnmp_transport *callback_tr; + + callback_tr = netsnmp_callback_transport(attach_to); + snmp_sess_init(&callback_sess); + callback_sess.callback = return_func; + if (attach_to) { + /* + * client + */ + /* + * trysess.community = (u_char *) callback_ss; + */ + } else { + callback_sess.isAuthoritative = SNMP_SESS_AUTHORITATIVE; + } + callback_sess.remote_port = 0; + callback_sess.retries = 0; + callback_sess.timeout = 30000000; + callback_sess.version = SNMP_DEFAULT_VERSION; /* (mostly) bogus */ + callback_ss = snmp_add_full(&callback_sess, callback_tr, + fpre_parse, + netsnmp_callback_hook_parse, fpost_parse, + netsnmp_callback_hook_build, + NULL, + netsnmp_callback_check_packet, + netsnmp_callback_create_pdu); + if (callback_ss) + callback_ss->local_port = + ((netsnmp_callback_info *) callback_tr->data)->callback_num; + return callback_ss; +} + + + +void +netsnmp_clear_callback_list(void) +{ + + netsnmp_transport_list *list = trlist, *next = NULL; + netsnmp_transport *tr = NULL; + + DEBUGMSGTL(("callback_clear", "called netsnmp_callback_clear_list()\n")); + while (list != NULL) { + next = list->next; + tr = list->transport; + + if (tr != NULL) { + tr->f_close(tr); + netsnmp_transport_remove_from_list(&trlist, tr); + netsnmp_transport_free(tr); + } + list = next; + } + trlist = NULL; + +} + +#endif /* NETSNMP_TRANSPORT_CALLBACK_DOMAIN */ |