diff options
Diffstat (limited to 'agent/mibgroup/agentx/protocol.c')
-rw-r--r-- | agent/mibgroup/agentx/protocol.c | 2058 |
1 files changed, 2058 insertions, 0 deletions
diff --git a/agent/mibgroup/agentx/protocol.c b/agent/mibgroup/agentx/protocol.c new file mode 100644 index 0000000..0845cf1 --- /dev/null +++ b/agent/mibgroup/agentx/protocol.c @@ -0,0 +1,2058 @@ +/* Portions of this file are subject to the following copyright(s). See + * the Net-SNMP's COPYING file for more details and other copyrights + * that may apply: + */ +/* + * Portions of this file are copyrighted by: + * Copyright © 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms specified in the COPYING file + * distributed with the Net-SNMP package. + */ + +#include <net-snmp/net-snmp-config.h> + +#include <stdio.h> +#include <errno.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 +#include <sys/types.h> +#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 +#if HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#if HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#include <net-snmp/net-snmp-includes.h> + +#include "agentx/protocol.h" + +const char * +agentx_cmd(u_char code) +{ + switch (code) { + case AGENTX_MSG_OPEN: + return "Open"; + case AGENTX_MSG_CLOSE: + return "Close"; + case AGENTX_MSG_REGISTER: + return "Register"; + case AGENTX_MSG_UNREGISTER: + return "Unregister"; + case AGENTX_MSG_GET: + return "Get"; + case AGENTX_MSG_GETNEXT: + return "Get Next"; + case AGENTX_MSG_GETBULK: + return "Get Bulk"; + case AGENTX_MSG_TESTSET: + return "Test Set"; + case AGENTX_MSG_COMMITSET: + return "Commit Set"; + case AGENTX_MSG_UNDOSET: + return "Undo Set"; + case AGENTX_MSG_CLEANUPSET: + return "Cleanup Set"; + case AGENTX_MSG_NOTIFY: + return "Notify"; + case AGENTX_MSG_PING: + return "Ping"; + case AGENTX_MSG_INDEX_ALLOCATE: + return "Index Allocate"; + case AGENTX_MSG_INDEX_DEALLOCATE: + return "Index Deallocate"; + case AGENTX_MSG_ADD_AGENT_CAPS: + return "Add Agent Caps"; + case AGENTX_MSG_REMOVE_AGENT_CAPS: + return "Remove Agent Caps"; + case AGENTX_MSG_RESPONSE: + return "Response"; + default: + return "Unknown"; + } +} + +int +agentx_realloc_build_int(u_char ** buf, size_t * buf_len, size_t * out_len, + int allow_realloc, + unsigned int value, int network_order) +{ + unsigned int ivalue = value; + size_t ilen = *out_len; +#ifdef WORDS_BIGENDIAN + unsigned int i = 0; +#endif + + while ((*out_len + 4) >= *buf_len) { + if (!(allow_realloc && snmp_realloc(buf, buf_len))) { + return 0; + } + } + + if (network_order) { +#ifndef WORDS_BIGENDIAN + value = ntohl(value); +#endif + memmove((*buf + *out_len), &value, 4); + *out_len += 4; + } else { +#ifndef WORDS_BIGENDIAN + memmove((*buf + *out_len), &value, 4); + *out_len += 4; +#else + for (i = 0; i < 4; i++) { + *(*buf + *out_len) = (u_char) value & 0xff; + (*out_len)++; + value >>= 8; + } +#endif + } + DEBUGDUMPSETUP("send", (*buf + ilen), 4); + DEBUGMSG(("dumpv_send", " Integer:\t%u (0x%.2X)\n", ivalue, + ivalue)); + return 1; +} + +void +agentx_build_int(u_char * bufp, u_int value, int network_byte_order) +{ + u_char *orig_bufp = bufp; + u_int orig_val = value; + + if (network_byte_order) { +#ifndef WORDS_BIGENDIAN + value = ntohl(value); +#endif + memmove(bufp, &value, 4); + } else { +#ifndef WORDS_BIGENDIAN + memmove(bufp, &value, 4); +#else + *bufp = (u_char) value & 0xff; + value >>= 8; + bufp++; + *bufp = (u_char) value & 0xff; + value >>= 8; + bufp++; + *bufp = (u_char) value & 0xff; + value >>= 8; + bufp++; + *bufp = (u_char) value & 0xff; +#endif + } + DEBUGDUMPSETUP("send", orig_bufp, 4); + DEBUGMSG(("dumpv_send", " Integer:\t%u (0x%.2X)\n", orig_val, + orig_val)); +} + +int +agentx_realloc_build_short(u_char ** buf, size_t * buf_len, + size_t * out_len, int allow_realloc, + unsigned short value, int network_order) +{ + unsigned short ivalue = value; + size_t ilen = *out_len; +#ifdef WORDS_BIGENDIAN + unsigned short i = 0; +#endif + + while ((*out_len + 2) >= *buf_len) { + if (!(allow_realloc && snmp_realloc(buf, buf_len))) { + return 0; + } + } + + if (network_order) { +#ifndef WORDS_BIGENDIAN + value = ntohs(value); +#endif + memmove((*buf + *out_len), &value, 2); + *out_len += 2; + } else { +#ifndef WORDS_BIGENDIAN + memmove((*buf + *out_len), &value, 2); + *out_len += 2; +#else + for (i = 0; i < 2; i++) { + *(*buf + *out_len) = (u_char) value & 0xff; + (*out_len)++; + value >>= 8; + } +#endif + } + DEBUGDUMPSETUP("send", (*buf + ilen), 2); + DEBUGMSG(("dumpv_send", " Short:\t%hu (0x%.2hX)\n", ivalue, ivalue)); + return 1; +} + +int +agentx_realloc_build_oid(u_char ** buf, size_t * buf_len, size_t * out_len, + int allow_realloc, + int inclusive, oid * name, size_t name_len, + int network_order) +{ + size_t ilen = *out_len, i = 0; + int prefix = 0; + + DEBUGPRINTINDENT("dumpv_send"); + DEBUGMSG(("dumpv_send", "OID: ")); + DEBUGMSGOID(("dumpv_send", name, name_len)); + DEBUGMSG(("dumpv_send", "\n")); + + /* + * Updated clarification from the AgentX mailing list. + * The "null Object Identifier" mentioned in RFC 2471, + * section 5.1 is a special placeholder value, and + * should only be used when explicitly mentioned in + * this RFC. In particular, it does *not* mean {0, 0} + */ + if (name_len == 0) + inclusive = 0; + + /* + * 'Compact' internet OIDs + */ + if (name_len >= 5 && (name[0] == 1 && name[1] == 3 && + name[2] == 6 && name[3] == 1 && + name[4] > 0 && name[4] < 256)) { + prefix = name[4]; + name += 5; + name_len -= 5; + } + + while ((*out_len + 4 + (4 * name_len)) >= *buf_len) { + if (!(allow_realloc && snmp_realloc(buf, buf_len))) { + return 0; + } + } + + *(*buf + *out_len) = (u_char) name_len; + (*out_len)++; + *(*buf + *out_len) = (u_char) prefix; + (*out_len)++; + *(*buf + *out_len) = (u_char) inclusive; + (*out_len)++; + *(*buf + *out_len) = (u_char) 0x00; + (*out_len)++; + + DEBUGDUMPHEADER("send", "OID Header"); + DEBUGDUMPSETUP("send", (*buf + ilen), 4); + DEBUGMSG(("dumpv_send", " # subids:\t%d (0x%.2X)\n", (int)name_len, + (unsigned int)name_len)); + DEBUGPRINTINDENT("dumpv_send"); + DEBUGMSG(("dumpv_send", " prefix:\t%d (0x%.2X)\n", prefix, prefix)); + DEBUGPRINTINDENT("dumpv_send"); + DEBUGMSG(("dumpv_send", " inclusive:\t%d (0x%.2X)\n", inclusive, + inclusive)); + DEBUGINDENTLESS(); + DEBUGDUMPHEADER("send", "OID Segments"); + + for (i = 0; i < name_len; i++) { + if (!agentx_realloc_build_int(buf, buf_len, out_len, allow_realloc, + name[i], network_order)) { + DEBUGINDENTLESS(); + return 0; + } + } + DEBUGINDENTLESS(); + + return 1; +} + +int +agentx_realloc_build_string(u_char ** buf, size_t * buf_len, + size_t * out_len, int allow_realloc, + u_char * string, size_t string_len, + int network_order) +{ + size_t ilen = *out_len, i = 0; + + while ((*out_len + 4 + (4 * ((string_len + 3) / 4))) >= *buf_len) { + if (!(allow_realloc && snmp_realloc(buf, buf_len))) { + return 0; + } + } + + DEBUGDUMPHEADER("send", "Build String"); + DEBUGDUMPHEADER("send", "length"); + if (!agentx_realloc_build_int(buf, buf_len, out_len, allow_realloc, + string_len, network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + + if (string_len == 0) { + DEBUGMSG(("dumpv_send", " String: <empty>\n")); + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 1; + } + + memmove((*buf + *out_len), string, string_len); + *out_len += string_len; + + /* + * Pad to a multiple of 4 bytes if necessary (per RFC 2741). + */ + + if (string_len % 4 != 0) { + for (i = 0; i < 4 - (string_len % 4); i++) { + *(*buf + *out_len) = 0; + (*out_len)++; + } + } + + DEBUGDUMPSETUP("send", (*buf + ilen + 4), ((string_len + 3) / 4) * 4); + DEBUGMSG(("dumpv_send", " String:\t%s\n", string)); + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 1; +} + +#ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES +int +agentx_realloc_build_double(u_char ** buf, size_t * buf_len, + size_t * out_len, int allow_realloc, + double double_val, int network_order) +{ + union { + double doubleVal; + int intVal[2]; + char c[sizeof(double)]; + } du; + int tmp; + u_char opaque_buffer[3 + sizeof(double)]; + + opaque_buffer[0] = ASN_OPAQUE_TAG1; + opaque_buffer[1] = ASN_OPAQUE_DOUBLE; + opaque_buffer[2] = sizeof(double); + + du.doubleVal = double_val; + tmp = htonl(du.intVal[0]); + du.intVal[0] = htonl(du.intVal[1]); + du.intVal[1] = tmp; + memcpy(&opaque_buffer[3], &du.c[0], sizeof(double)); + + return agentx_realloc_build_string(buf, buf_len, out_len, + allow_realloc, opaque_buffer, + 3 + sizeof(double), network_order); +} + +int +agentx_realloc_build_float(u_char ** buf, size_t * buf_len, + size_t * out_len, int allow_realloc, + float float_val, int network_order) +{ + union { + float floatVal; + int intVal; + char c[sizeof(float)]; + } fu; + u_char opaque_buffer[3 + sizeof(float)]; + + opaque_buffer[0] = ASN_OPAQUE_TAG1; + opaque_buffer[1] = ASN_OPAQUE_FLOAT; + opaque_buffer[2] = sizeof(float); + + fu.floatVal = float_val; + fu.intVal = htonl(fu.intVal); + memcpy(&opaque_buffer[3], &fu.c[0], sizeof(float)); + + return agentx_realloc_build_string(buf, buf_len, out_len, + allow_realloc, opaque_buffer, + 3 + sizeof(float), network_order); +} +#endif + +int +agentx_realloc_build_varbind(u_char ** buf, size_t * buf_len, + size_t * out_len, int allow_realloc, + netsnmp_variable_list * vp, int network_order) +{ + DEBUGDUMPHEADER("send", "VarBind"); + DEBUGDUMPHEADER("send", "type"); +#ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES + if ((vp->type == ASN_OPAQUE_FLOAT) || (vp->type == ASN_OPAQUE_DOUBLE) + || (vp->type == ASN_OPAQUE_I64) || (vp->type == ASN_OPAQUE_U64) + || (vp->type == ASN_OPAQUE_COUNTER64)) { + if (!agentx_realloc_build_short + (buf, buf_len, out_len, allow_realloc, + (unsigned short) ASN_OPAQUE, network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + } else +#endif + if (vp->type == ASN_PRIV_INCL_RANGE || vp->type == ASN_PRIV_EXCL_RANGE) { + if (!agentx_realloc_build_short + (buf, buf_len, out_len, allow_realloc, + (unsigned short) ASN_OBJECT_ID, network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + } else { + if (!agentx_realloc_build_short + (buf, buf_len, out_len, allow_realloc, + (unsigned short) vp->type, network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + } + + while ((*out_len + 2) >= *buf_len) { + if (!(allow_realloc && snmp_realloc(buf, buf_len))) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + } + + *(*buf + *out_len) = 0; + (*out_len)++; + *(*buf + *out_len) = 0; + (*out_len)++; + DEBUGINDENTLESS(); + + DEBUGDUMPHEADER("send", "name"); + if (!agentx_realloc_build_oid(buf, buf_len, out_len, allow_realloc, 0, + vp->name, vp->name_length, + network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + DEBUGINDENTLESS(); + + DEBUGDUMPHEADER("send", "value"); + switch (vp->type) { + + case ASN_INTEGER: + case ASN_COUNTER: + case ASN_GAUGE: + case ASN_TIMETICKS: + case ASN_UINTEGER: + if (!agentx_realloc_build_int(buf, buf_len, out_len, allow_realloc, + *(vp->val.integer), network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + break; + +#ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES + case ASN_OPAQUE_FLOAT: + DEBUGDUMPHEADER("send", "Build Opaque Float"); + DEBUGPRINTINDENT("dumpv_send"); + DEBUGMSG(("dumpv_send", " Float:\t%f\n", *(vp->val.floatVal))); + if (!agentx_realloc_build_float + (buf, buf_len, out_len, allow_realloc, *(vp->val.floatVal), + network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + DEBUGINDENTLESS(); + break; + + case ASN_OPAQUE_DOUBLE: + DEBUGDUMPHEADER("send", "Build Opaque Double"); + DEBUGPRINTINDENT("dumpv_send"); + DEBUGMSG(("dumpv_send", " Double:\t%f\n", *(vp->val.doubleVal))); + if (!agentx_realloc_build_double + (buf, buf_len, out_len, allow_realloc, *(vp->val.doubleVal), + network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + DEBUGINDENTLESS(); + break; + + case ASN_OPAQUE_I64: + case ASN_OPAQUE_U64: + case ASN_OPAQUE_COUNTER64: + /* + * XXX - TODO - encode as raw OPAQUE for now (so fall through + * here). + */ +#endif + + case ASN_OCTET_STR: + case ASN_IPADDRESS: + case ASN_OPAQUE: + if (!agentx_realloc_build_string + (buf, buf_len, out_len, allow_realloc, vp->val.string, + vp->val_len, network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + break; + + case ASN_OBJECT_ID: + case ASN_PRIV_EXCL_RANGE: + case ASN_PRIV_INCL_RANGE: + if (!agentx_realloc_build_oid + (buf, buf_len, out_len, allow_realloc, 1, vp->val.objid, + vp->val_len / sizeof(oid), network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + break; + + case ASN_COUNTER64: + if (network_order) { + DEBUGDUMPHEADER("send", "Build Counter64 (high, low)"); + if (!agentx_realloc_build_int + (buf, buf_len, out_len, allow_realloc, + vp->val.counter64->high, network_order) + || !agentx_realloc_build_int(buf, buf_len, out_len, + allow_realloc, + vp->val.counter64->low, + network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + DEBUGINDENTLESS(); + } else { + DEBUGDUMPHEADER("send", "Build Counter64 (low, high)"); + if (!agentx_realloc_build_int + (buf, buf_len, out_len, allow_realloc, + vp->val.counter64->low, network_order) + || !agentx_realloc_build_int(buf, buf_len, out_len, + allow_realloc, + vp->val.counter64->high, + network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + DEBUGINDENTLESS(); + } + break; + + case ASN_NULL: + case SNMP_NOSUCHOBJECT: + case SNMP_NOSUCHINSTANCE: + case SNMP_ENDOFMIBVIEW: + break; + + default: + DEBUGMSGTL(("agentx_build_varbind", "unknown type %d (0x%02x)\n", + vp->type, vp->type)); + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 1; +} + +int +agentx_realloc_build_header(u_char ** buf, size_t * buf_len, + size_t * out_len, int allow_realloc, + netsnmp_pdu *pdu) +{ + size_t ilen = *out_len; + const int network_order = + pdu->flags & AGENTX_FLAGS_NETWORK_BYTE_ORDER; + + while ((*out_len + 4) >= *buf_len) { + if (!(allow_realloc && snmp_realloc(buf, buf_len))) { + return 0; + } + } + + /* + * First 4 bytes are version, pdu type, flags, and a 0 reserved byte. + */ + + *(*buf + *out_len) = 1; + (*out_len)++; + *(*buf + *out_len) = pdu->command; + (*out_len)++; + *(*buf + *out_len) = (u_char) (pdu->flags & AGENTX_MSG_FLAGS_MASK); + (*out_len)++; + *(*buf + *out_len) = 0; + (*out_len)++; + + DEBUGDUMPHEADER("send", "AgentX Header"); + DEBUGDUMPSETUP("send", (*buf + ilen), 4); + DEBUGMSG(("dumpv_send", " Version:\t%d\n", (int) *(*buf + ilen))); + DEBUGPRINTINDENT("dumpv_send"); + DEBUGMSG(("dumpv_send", " Command:\t%d (%s)\n", pdu->command, + agentx_cmd((u_char)pdu->command))); + DEBUGPRINTINDENT("dumpv_send"); + DEBUGMSG(("dumpv_send", " Flags:\t%02x\n", (int) *(*buf + ilen + 2))); + + DEBUGDUMPHEADER("send", "Session ID"); + if (!agentx_realloc_build_int(buf, buf_len, out_len, allow_realloc, + pdu->sessid, network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + DEBUGINDENTLESS(); + + DEBUGDUMPHEADER("send", "Transaction ID"); + if (!agentx_realloc_build_int(buf, buf_len, out_len, allow_realloc, + pdu->transid, network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + DEBUGINDENTLESS(); + + DEBUGDUMPHEADER("send", "Request ID"); + if (!agentx_realloc_build_int(buf, buf_len, out_len, allow_realloc, + pdu->reqid, network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + DEBUGINDENTLESS(); + + DEBUGDUMPHEADER("send", "Dummy Length :-("); + if (!agentx_realloc_build_int(buf, buf_len, out_len, allow_realloc, + 0, network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + DEBUGINDENTLESS(); + + if (pdu->flags & AGENTX_MSG_FLAG_NON_DEFAULT_CONTEXT) { + DEBUGDUMPHEADER("send", "Community"); + if (!agentx_realloc_build_string + (buf, buf_len, out_len, allow_realloc, pdu->community, + pdu->community_len, network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + DEBUGINDENTLESS(); + } + + DEBUGINDENTLESS(); + return 1; +} + +static int +_agentx_realloc_build(u_char ** buf, size_t * buf_len, size_t * out_len, + int allow_realloc, + netsnmp_session * session, netsnmp_pdu *pdu) +{ + size_t ilen = *out_len; + netsnmp_variable_list *vp; + int inc, i = 0; + const int network_order = + pdu->flags & AGENTX_FLAGS_NETWORK_BYTE_ORDER; + + session->s_snmp_errno = 0; + session->s_errno = 0; + + /* + * Various PDU types don't include context information (RFC 2741, p. 20). + */ + switch (pdu->command) { + case AGENTX_MSG_OPEN: + case AGENTX_MSG_CLOSE: + case AGENTX_MSG_RESPONSE: + case AGENTX_MSG_COMMITSET: + case AGENTX_MSG_UNDOSET: + case AGENTX_MSG_CLEANUPSET: + pdu->flags &= ~(AGENTX_MSG_FLAG_NON_DEFAULT_CONTEXT); + } + + /* We've received a PDU that has specified a context. NetSNMP however, uses + * the pdu->community field to specify context when using the AgentX + * protocol. Therefore we need to copy the context name and length into the + * pdu->community and pdu->community_len fields, respectively. */ + if (pdu->contextName != NULL && pdu->community == NULL) + { + pdu->community = (u_char *) strdup(pdu->contextName); + pdu->community_len = pdu->contextNameLen; + pdu->flags |= AGENTX_MSG_FLAG_NON_DEFAULT_CONTEXT; + } + + /* + * Build the header (and context if appropriate). + */ + if (!agentx_realloc_build_header + (buf, buf_len, out_len, allow_realloc, pdu)) { + return 0; + } + + /* + * Everything causes a response, except for agentx-Response-PDU and + * agentx-CleanupSet-PDU. + */ + + pdu->flags |= UCD_MSG_FLAG_EXPECT_RESPONSE; + + DEBUGDUMPHEADER("send", "AgentX Payload"); + switch (pdu->command) { + + case AGENTX_MSG_OPEN: + /* + * Timeout + */ + while ((*out_len + 4) >= *buf_len) { + if (!(allow_realloc && snmp_realloc(buf, buf_len))) { + DEBUGINDENTLESS(); + return 0; + } + } + *(*buf + *out_len) = (u_char) pdu->time; + (*out_len)++; + for (i = 0; i < 3; i++) { + *(*buf + *out_len) = 0; + (*out_len)++; + } + DEBUGDUMPHEADER("send", "Open Timeout"); + DEBUGDUMPSETUP("send", (*buf + *out_len - 4), 4); + DEBUGMSG(("dumpv_send", " Timeout:\t%d\n", + (int) *(*buf + *out_len - 4))); + DEBUGINDENTLESS(); + + DEBUGDUMPHEADER("send", "Open ID"); + if (!agentx_realloc_build_oid + (buf, buf_len, out_len, allow_realloc, 0, pdu->variables->name, + pdu->variables->name_length, network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + DEBUGINDENTLESS(); + DEBUGDUMPHEADER("send", "Open Description"); + if (!agentx_realloc_build_string + (buf, buf_len, out_len, allow_realloc, + pdu->variables->val.string, pdu->variables->val_len, + network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + DEBUGINDENTLESS(); + break; + + case AGENTX_MSG_CLOSE: + /* + * Reason + */ + while ((*out_len + 4) >= *buf_len) { + if (!(allow_realloc && snmp_realloc(buf, buf_len))) { + DEBUGINDENTLESS(); + return 0; + } + } + *(*buf + *out_len) = (u_char) pdu->errstat; + (*out_len)++; + for (i = 0; i < 3; i++) { + *(*buf + *out_len) = 0; + (*out_len)++; + } + DEBUGDUMPHEADER("send", "Close Reason"); + DEBUGDUMPSETUP("send", (*buf + *out_len - 4), 4); + DEBUGMSG(("dumpv_send", " Reason:\t%d\n", + (int) *(*buf + *out_len - 4))); + DEBUGINDENTLESS(); + break; + + case AGENTX_MSG_REGISTER: + case AGENTX_MSG_UNREGISTER: + while ((*out_len + 4) >= *buf_len) { + if (!(allow_realloc && snmp_realloc(buf, buf_len))) { + DEBUGINDENTLESS(); + return 0; + } + } + if (pdu->command == AGENTX_MSG_REGISTER) { + *(*buf + *out_len) = (u_char) pdu->time; + } else { + *(*buf + *out_len) = 0; + } + (*out_len)++; + *(*buf + *out_len) = (u_char) pdu->priority; + (*out_len)++; + *(*buf + *out_len) = (u_char) pdu->range_subid; + (*out_len)++; + *(*buf + *out_len) = (u_char) 0; + (*out_len)++; + + DEBUGDUMPHEADER("send", "(Un)Register Header"); + DEBUGDUMPSETUP("send", (*buf + *out_len - 4), 4); + if (pdu->command == AGENTX_MSG_REGISTER) { + DEBUGMSG(("dumpv_send", " Timeout:\t%d\n", + (int) *(*buf + *out_len - 4))); + DEBUGPRINTINDENT("dumpv_send"); + } + DEBUGMSG(("dumpv_send", " Priority:\t%d\n", + (int) *(*buf + *out_len - 3))); + DEBUGPRINTINDENT("dumpv_send"); + DEBUGMSG(("dumpv_send", " Range SubID:\t%d\n", + (int) *(*buf + *out_len - 2))); + DEBUGINDENTLESS(); + + vp = pdu->variables; + DEBUGDUMPHEADER("send", "(Un)Register Prefix"); + if (!agentx_realloc_build_oid + (buf, buf_len, out_len, allow_realloc, 0, vp->name, + vp->name_length, network_order)) { + + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + DEBUGINDENTLESS(); + + if (pdu->range_subid) { + DEBUGDUMPHEADER("send", "(Un)Register Range"); + if (!agentx_realloc_build_int + (buf, buf_len, out_len, allow_realloc, + vp->val.objid[pdu->range_subid - 1], network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + DEBUGINDENTLESS(); + } + break; + + case AGENTX_MSG_GETBULK: + DEBUGDUMPHEADER("send", "GetBulk Non-Repeaters"); + if (!agentx_realloc_build_short + (buf, buf_len, out_len, allow_realloc, + (u_short)pdu->non_repeaters, + network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + DEBUGINDENTLESS(); + + DEBUGDUMPHEADER("send", "GetBulk Max-Repetitions"); + if (!agentx_realloc_build_short + (buf, buf_len, out_len, allow_realloc, + (u_short)pdu->max_repetitions, + network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + DEBUGINDENTLESS(); + + /* + * Fallthrough + */ + + case AGENTX_MSG_GET: + case AGENTX_MSG_GETNEXT: + DEBUGDUMPHEADER("send", "Get* Variable List"); + for (vp = pdu->variables; vp != NULL; vp = vp->next_variable) { + inc = (vp->type == ASN_PRIV_INCL_RANGE); + if (!agentx_realloc_build_oid + (buf, buf_len, out_len, allow_realloc, inc, vp->name, + vp->name_length, network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + if (!agentx_realloc_build_oid + (buf, buf_len, out_len, allow_realloc, 0, vp->val.objid, + vp->val_len / sizeof(oid), network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + } + DEBUGINDENTLESS(); + break; + + case AGENTX_MSG_RESPONSE: + pdu->flags &= ~(UCD_MSG_FLAG_EXPECT_RESPONSE); + if (!agentx_realloc_build_int(buf, buf_len, out_len, allow_realloc, + pdu->time, network_order)) { + DEBUGINDENTLESS(); + return 0; + } + DEBUGDUMPHEADER("send", "Response"); + DEBUGDUMPSETUP("send", (*buf + *out_len - 4), 4); + DEBUGMSG(("dumpv_send", " sysUpTime:\t%lu\n", pdu->time)); + DEBUGINDENTLESS(); + + if (!agentx_realloc_build_short + (buf, buf_len, out_len, allow_realloc, + (u_short)pdu->errstat, + network_order) + || !agentx_realloc_build_short(buf, buf_len, out_len, + allow_realloc, + (u_short)pdu->errindex, + network_order)) { + DEBUGINDENTLESS(); + return 0; + } + DEBUGDUMPHEADER("send", "Response errors"); + DEBUGDUMPSETUP("send", (*buf + *out_len - 4), 4); + DEBUGMSG(("dumpv_send", " errstat:\t%ld\n", pdu->errstat)); + DEBUGPRINTINDENT("dumpv_send"); + DEBUGMSG(("dumpv_send", " errindex:\t%ld\n", pdu->errindex)); + DEBUGINDENTLESS(); + + /* + * Fallthrough + */ + + case AGENTX_MSG_INDEX_ALLOCATE: + case AGENTX_MSG_INDEX_DEALLOCATE: + case AGENTX_MSG_NOTIFY: + case AGENTX_MSG_TESTSET: + DEBUGDUMPHEADER("send", "Get* Variable List"); + for (vp = pdu->variables; vp != NULL; vp = vp->next_variable) { + if (!agentx_realloc_build_varbind + (buf, buf_len, out_len, allow_realloc, vp, + network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + } + DEBUGINDENTLESS(); + break; + + case AGENTX_MSG_COMMITSET: + case AGENTX_MSG_UNDOSET: + case AGENTX_MSG_PING: + /* + * "Empty" packet. + */ + break; + + case AGENTX_MSG_CLEANUPSET: + pdu->flags &= ~(UCD_MSG_FLAG_EXPECT_RESPONSE); + break; + + case AGENTX_MSG_ADD_AGENT_CAPS: + DEBUGDUMPHEADER("send", "AgentCaps OID"); + if (!agentx_realloc_build_oid + (buf, buf_len, out_len, allow_realloc, 0, pdu->variables->name, + pdu->variables->name_length, network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + DEBUGINDENTLESS(); + + DEBUGDUMPHEADER("send", "AgentCaps Description"); + if (!agentx_realloc_build_string + (buf, buf_len, out_len, allow_realloc, + pdu->variables->val.string, pdu->variables->val_len, + network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + DEBUGINDENTLESS(); + break; + + case AGENTX_MSG_REMOVE_AGENT_CAPS: + DEBUGDUMPHEADER("send", "AgentCaps OID"); + if (!agentx_realloc_build_oid + (buf, buf_len, out_len, allow_realloc, 0, pdu->variables->name, + pdu->variables->name_length, network_order)) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return 0; + } + DEBUGINDENTLESS(); + break; + + default: + session->s_snmp_errno = SNMPERR_UNKNOWN_PDU; + return 0; + } + DEBUGINDENTLESS(); + + /* + * Fix the payload length (ignoring the 20-byte header). + */ + + agentx_build_int((*buf + 16), (*out_len - ilen) - 20, network_order); + + DEBUGMSGTL(("agentx_build", "packet built okay\n")); + return 1; +} + +int +agentx_realloc_build(netsnmp_session * session, netsnmp_pdu *pdu, + u_char ** buf, size_t * buf_len, size_t * out_len) +{ + if (session == NULL || buf_len == NULL || + out_len == NULL || pdu == NULL || buf == NULL) { + return -1; + } + if (!_agentx_realloc_build(buf, buf_len, out_len, 1, session, pdu)) { + if (session->s_snmp_errno == 0) { + session->s_snmp_errno = SNMPERR_BAD_ASN1_BUILD; + } + return -1; + } + + return 0; +} + + /*********************** + * + * Utility functions for parsing an AgentX packet + * + ***********************/ + +int +agentx_parse_int(u_char * data, u_int network_byte_order) +{ + u_int value = 0; + + /* + * Note - this doesn't handle 'PDP_ENDIAN' systems + * If anyone needs this added, contact the coders list + */ + DEBUGDUMPSETUP("recv", data, 4); + if (network_byte_order) { + memmove(&value, data, 4); +#ifndef WORDS_BIGENDIAN + value = ntohl(value); +#endif + } else { +#ifndef WORDS_BIGENDIAN + memmove(&value, data, 4); +#else + /* + * The equivalent of the 'ntohl()' macro, + * except this macro is null on big-endian systems + */ + value += data[3]; + value <<= 8; + value += data[2]; + value <<= 8; + value += data[1]; + value <<= 8; + value += data[0]; +#endif + } + DEBUGMSG(("dumpv_recv", " Integer:\t%u (0x%.2X)\n", value, value)); + + return value; +} + + +int +agentx_parse_short(u_char * data, u_int network_byte_order) +{ + u_short value = 0; + + if (network_byte_order) { + memmove(&value, data, 2); +#ifndef WORDS_BIGENDIAN + value = ntohs(value); +#endif + } else { +#ifndef WORDS_BIGENDIAN + memmove(&value, data, 2); +#else + /* + * The equivalent of the 'ntohs()' macro, + * except this macro is null on big-endian systems + */ + value += data[1]; + value <<= 8; + value += data[0]; +#endif + } + + DEBUGDUMPSETUP("recv", data, 2); + DEBUGMSG(("dumpv_recv", " Short:\t%hu (0x%.2X)\n", value, value)); + return value; +} + + +u_char * +agentx_parse_oid(u_char * data, size_t * length, int *inc, + oid * oid_buf, size_t * oid_len, u_int network_byte_order) +{ + u_int n_subid; + u_int prefix; + u_int tmp_oid_len; + int i; + int int_offset; + u_int *int_ptr = (u_int *)oid_buf; + u_char *buf_ptr = data; + + if (*length < 4) { + DEBUGMSGTL(("agentx", "Incomplete Object ID\n")); + return NULL; + } + + DEBUGDUMPHEADER("recv", "OID Header"); + DEBUGDUMPSETUP("recv", data, 4); + DEBUGMSG(("dumpv_recv", " # subids:\t%d (0x%.2X)\n", data[0], + data[0])); + DEBUGPRINTINDENT("dumpv_recv"); + DEBUGMSG(("dumpv_recv", " prefix: \t%d (0x%.2X)\n", data[1], + data[1])); + DEBUGPRINTINDENT("dumpv_recv"); + DEBUGMSG(("dumpv_recv", " inclusive:\t%d (0x%.2X)\n", data[2], + data[2])); + + DEBUGINDENTLESS(); + DEBUGDUMPHEADER("recv", "OID Segments"); + + n_subid = data[0]; + prefix = data[1]; + if (inc) + *inc = data[2]; + int_offset = sizeof(oid)/4; + + buf_ptr += 4; + *length -= 4; + + DEBUGMSG(("djp", " parse_oid\n")); + DEBUGMSG(("djp", " sizeof(oid) = %d\n", (int)sizeof(oid))); + if (n_subid == 0 && prefix == 0) { + /* + * Null OID + */ + memset(int_ptr, 0, 2 * sizeof(oid)); + *oid_len = 2; + DEBUGPRINTINDENT("dumpv_recv"); + DEBUGMSG(("dumpv_recv", "OID: NULL (0.0)\n")); + DEBUGINDENTLESS(); + return buf_ptr; + } + + /* + * Check that the expanded OID will fit in the buffer provided + */ + tmp_oid_len = (prefix ? n_subid + 5 : n_subid); + if (*oid_len < tmp_oid_len) { + DEBUGMSGTL(("agentx", "Oversized Object ID (buf=%" NETSNMP_PRIz "d" + " pdu=%d)\n", *oid_len, tmp_oid_len)); + DEBUGINDENTLESS(); + return NULL; + } + +#ifdef WORDS_BIGENDIAN +# define endianoff 1 +#else +# define endianoff 0 +#endif + if (*length < 4 * n_subid) { + DEBUGMSGTL(("agentx", "Incomplete Object ID\n")); + DEBUGINDENTLESS(); + return NULL; + } + + if (prefix) { + if (int_offset == 2) { /* align OID values in 64 bit agent */ + memset(int_ptr, 0, 10*sizeof(int_ptr[0])); + int_ptr[0+endianoff] = 1; + int_ptr[2+endianoff] = 3; + int_ptr[4+endianoff] = 6; + int_ptr[6+endianoff] = 1; + int_ptr[8+endianoff] = prefix; + } else { /* assume int_offset == 1 */ + int_ptr[0] = 1; + int_ptr[1] = 3; + int_ptr[2] = 6; + int_ptr[3] = 1; + int_ptr[4] = prefix; + } + int_ptr = int_ptr + (int_offset * 5); + } + + for (i = 0; i < (int) (int_offset * n_subid); i = i + int_offset) { + int x; + + x = agentx_parse_int(buf_ptr, network_byte_order); + if (int_offset == 2) { + int_ptr[i+0] = 0; + int_ptr[i+1] = 0; + int_ptr[i+endianoff]=x; + } else { + int_ptr[i] = x; + } + buf_ptr += 4; + *length -= 4; + } + + *oid_len = tmp_oid_len; + + DEBUGINDENTLESS(); + DEBUGPRINTINDENT("dumpv_recv"); + DEBUGMSG(("dumpv_recv", "OID: ")); + DEBUGMSGOID(("dumpv_recv", oid_buf, *oid_len)); + DEBUGMSG(("dumpv_recv", "\n")); + + return buf_ptr; +} + + + +u_char * +agentx_parse_string(u_char * data, size_t * length, + u_char * string, size_t * str_len, + u_int network_byte_order) +{ + u_int len; + + if (*length < 4) { + DEBUGMSGTL(("agentx", "Incomplete string (too short: %d)\n", + (int)*length)); + return NULL; + } + + len = agentx_parse_int(data, network_byte_order); + if (*length < len + 4) { + DEBUGMSGTL(("agentx", "Incomplete string (still too short: %d)\n", + (int)*length)); + return NULL; + } + if (len > *str_len) { + DEBUGMSGTL(("agentx", "String too long (too long)\n")); + return NULL; + } + memmove(string, data + 4, len); + string[len] = '\0'; + *str_len = len; + + len += 3; /* Extend the string length to include the padding */ + len >>= 2; + len <<= 2; + + *length -= (len + 4); + DEBUGDUMPSETUP("recv", data, (len + 4)); + DEBUGIF("dumpv_recv") { + u_char *buf = NULL; + size_t buf_len = 0, out_len = 0; + + if (sprint_realloc_asciistring(&buf, &buf_len, &out_len, 1, + string, len)) { + DEBUGMSG(("dumpv_recv", "String: %s\n", buf)); + } else { + DEBUGMSG(("dumpv_recv", "String: %s [TRUNCATED]\n", buf)); + } + if (buf != NULL) { + free(buf); + } + } + return data + (len + 4); +} + +u_char * +agentx_parse_opaque(u_char * data, size_t * length, int *type, + u_char * opaque_buf, size_t * opaque_len, + u_int network_byte_order) +{ +#ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES + union { + float floatVal; + double doubleVal; + int intVal[2]; + char c[sizeof(double)]; + } fu; + int tmp; + u_char *buf; +#endif + u_char *const cp = + agentx_parse_string(data, length, + opaque_buf, opaque_len, network_byte_order); + +#ifdef NETSNMP_WITH_OPAQUE_SPECIAL_TYPES + if (cp == NULL) + return NULL; + + buf = opaque_buf; + + if ((*opaque_len <= 3) || (buf[0] != ASN_OPAQUE_TAG1)) + return cp; /* Unrecognised opaque type */ + + switch (buf[1]) { + case ASN_OPAQUE_FLOAT: + if ((*opaque_len != (3 + sizeof(float))) || + (buf[2] != sizeof(float))) + return cp; /* Encoding isn't right for FLOAT */ + + memcpy(&fu.c[0], &buf[3], sizeof(float)); + fu.intVal[0] = ntohl(fu.intVal[0]); + *opaque_len = sizeof(float); + memcpy(opaque_buf, &fu.c[0], sizeof(float)); + *type = ASN_OPAQUE_FLOAT; + DEBUGMSG(("dumpv_recv", "Float: %f\n", fu.floatVal)); + return cp; + + case ASN_OPAQUE_DOUBLE: + if ((*opaque_len != (3 + sizeof(double))) || + (buf[2] != sizeof(double))) + return cp; /* Encoding isn't right for DOUBLE */ + + memcpy(&fu.c[0], &buf[3], sizeof(double)); + tmp = ntohl(fu.intVal[1]); + fu.intVal[1] = ntohl(fu.intVal[0]); + fu.intVal[0] = tmp; + *opaque_len = sizeof(double); + memcpy(opaque_buf, &fu.c[0], sizeof(double)); + *type = ASN_OPAQUE_DOUBLE; + DEBUGMSG(("dumpv_recv", "Double: %f\n", fu.doubleVal)); + return cp; + + case ASN_OPAQUE_I64: + case ASN_OPAQUE_U64: + case ASN_OPAQUE_COUNTER64: + default: + return cp; /* Unrecognised opaque sub-type */ + } +#else + return cp; +#endif +} + + +u_char * +agentx_parse_varbind(u_char * data, size_t * length, int *type, + oid * oid_buf, size_t * oid_len, + u_char * data_buf, size_t * data_len, + u_int network_byte_order) +{ + u_char *bufp = data; + u_int int_val; + struct counter64 tmp64; + + DEBUGDUMPHEADER("recv", "VarBind:"); + DEBUGDUMPHEADER("recv", "Type"); + *type = agentx_parse_short(bufp, network_byte_order); + DEBUGINDENTLESS(); + bufp += 4; + *length -= 4; + + bufp = agentx_parse_oid(bufp, length, NULL, oid_buf, oid_len, + network_byte_order); + if (bufp == NULL) { + DEBUGINDENTLESS(); + return NULL; + } + + switch (*type) { + case ASN_INTEGER: + case ASN_COUNTER: + case ASN_GAUGE: + case ASN_TIMETICKS: + case ASN_UINTEGER: + int_val = agentx_parse_int(bufp, network_byte_order); + memmove(data_buf, &int_val, 4); + *data_len = 4; + bufp += 4; + *length -= 4; + break; + + case ASN_OCTET_STR: + case ASN_IPADDRESS: + bufp = agentx_parse_string(bufp, length, data_buf, data_len, + network_byte_order); + break; + + case ASN_OPAQUE: + bufp = agentx_parse_opaque(bufp, length, type, data_buf, data_len, + network_byte_order); + break; + + case ASN_PRIV_INCL_RANGE: + case ASN_PRIV_EXCL_RANGE: + case ASN_OBJECT_ID: + bufp = + agentx_parse_oid(bufp, length, NULL, (oid *) data_buf, + data_len, network_byte_order); + *data_len *= sizeof(oid); + /* + * 'agentx_parse_oid()' returns the number of sub_ids + */ + break; + + case ASN_COUNTER64: + memset(&tmp64, 0, sizeof(tmp64)); + if (network_byte_order) { + tmp64.high = agentx_parse_int(bufp, network_byte_order); + tmp64.low = agentx_parse_int(bufp+4, network_byte_order); + } else { + tmp64.high = agentx_parse_int(bufp+4, network_byte_order); + tmp64.low = agentx_parse_int(bufp, network_byte_order); + } + + memcpy(data_buf, &tmp64, sizeof(tmp64)); + *data_len = sizeof(tmp64); + bufp += 8; + *length -= 8; + break; + + case ASN_NULL: + case SNMP_NOSUCHOBJECT: + case SNMP_NOSUCHINSTANCE: + case SNMP_ENDOFMIBVIEW: + /* + * No data associated with these types. + */ + *data_len = 0; + break; + + default: + DEBUGMSG(("recv", "Can not parse type %x", *type)); + DEBUGINDENTLESS(); + return NULL; + } + DEBUGINDENTLESS(); + return bufp; +} + +/* + * AgentX header: + * + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | h.version | h.type | h.flags | <reserved> | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | h.sessionID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | h.transactionID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | h.packetID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | h.payload_length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Total length = 20 bytes + * + * If we don't seem to have the full packet, return NULL + * and let the driving code go back for the rest. + * Don't report this as an error, as it's quite "normal" + * with a connection-oriented service. + * + * Note that once the header has been successfully processed + * (and hence we should have the full packet), any subsequent + * "running out of room" is indeed an error. + */ +u_char * +agentx_parse_header(netsnmp_pdu *pdu, u_char * data, size_t * length) +{ + register u_char *bufp = data; + size_t payload; + + if (*length < 20) { /* Incomplete header */ + return NULL; + } + + DEBUGDUMPHEADER("recv", "AgentX Header"); + DEBUGDUMPHEADER("recv", "Version"); + DEBUGDUMPSETUP("recv", bufp, 1); + pdu->version = AGENTX_VERSION_BASE | *bufp; + DEBUGMSG(("dumpv_recv", " Version:\t%d\n", *bufp)); + DEBUGINDENTLESS(); + bufp++; + + DEBUGDUMPHEADER("recv", "Command"); + DEBUGDUMPSETUP("recv", bufp, 1); + pdu->command = *bufp; + DEBUGMSG(("dumpv_recv", " Command:\t%d (%s)\n", *bufp, + agentx_cmd(*bufp))); + DEBUGINDENTLESS(); + bufp++; + + DEBUGDUMPHEADER("recv", "Flags"); + DEBUGDUMPSETUP("recv", bufp, 1); + pdu->flags |= *bufp; + DEBUGMSG(("dumpv_recv", " Flags:\t0x%x\n", *bufp)); + DEBUGINDENTLESS(); + bufp++; + + DEBUGDUMPHEADER("recv", "Reserved Byte"); + DEBUGDUMPSETUP("recv", bufp, 1); + DEBUGMSG(("dumpv_recv", " Reserved:\t0x%x\n", *bufp)); + DEBUGINDENTLESS(); + bufp++; + + DEBUGDUMPHEADER("recv", "Session ID"); + pdu->sessid = agentx_parse_int(bufp, + pdu-> + flags & + AGENTX_FLAGS_NETWORK_BYTE_ORDER); + DEBUGINDENTLESS(); + bufp += 4; + + DEBUGDUMPHEADER("recv", "Transaction ID"); + pdu->transid = agentx_parse_int(bufp, + pdu-> + flags & + AGENTX_FLAGS_NETWORK_BYTE_ORDER); + DEBUGINDENTLESS(); + bufp += 4; + + DEBUGDUMPHEADER("recv", "Packet ID"); + pdu->reqid = agentx_parse_int(bufp, + pdu-> + flags & AGENTX_FLAGS_NETWORK_BYTE_ORDER); + DEBUGINDENTLESS(); + bufp += 4; + + DEBUGDUMPHEADER("recv", "Payload Length"); + payload = agentx_parse_int(bufp, + pdu-> + flags & AGENTX_FLAGS_NETWORK_BYTE_ORDER); + DEBUGINDENTLESS(); + bufp += 4; + + DEBUGINDENTLESS(); + *length -= 20; + if (*length != payload) { /* Short payload */ + return NULL; + } + return bufp; +} + + +int +agentx_parse(netsnmp_session * session, netsnmp_pdu *pdu, u_char * data, + size_t len) +{ + register u_char *bufp = data; + u_char buffer[SNMP_MAX_MSG_SIZE]; + oid oid_buffer[MAX_OID_LEN], end_oid_buf[MAX_OID_LEN]; + size_t buf_len = sizeof(buffer); + size_t oid_buf_len = MAX_OID_LEN; + size_t end_oid_buf_len = MAX_OID_LEN; + + int range_bound; /* OID-range upper bound */ + int inc; /* Inclusive SearchRange flag */ + int type; /* VarBind data type */ + size_t *length = &len; + + if (pdu == NULL) + return (0); + + if (!IS_AGENTX_VERSION(session->version)) + return SNMPERR_BAD_VERSION; + +#ifndef SNMPERR_INCOMPLETE_PACKET + /* + * Ideally, "short" packets on stream connections should + * be handled specially, and the driving code set up to + * keep reading until the full packet is received. + * + * For now, lets assume that all packets are read in one go. + * I've probably inflicted enough damage on the UCD library + * for one week! + * + * I'll come back to this once Wes is speaking to me again. + */ +#define SNMPERR_INCOMPLETE_PACKET SNMPERR_ASN_PARSE_ERR +#endif + + + /* + * Handle (common) header .... + */ + bufp = agentx_parse_header(pdu, bufp, length); + if (bufp == NULL) + return SNMPERR_INCOMPLETE_PACKET; /* i.e. wait for the rest */ + + /* + * Control PDU handling + */ + pdu->flags |= UCD_MSG_FLAG_ALWAYS_IN_VIEW; + pdu->flags |= UCD_MSG_FLAG_FORCE_PDU_COPY; + pdu->flags &= (~UCD_MSG_FLAG_RESPONSE_PDU); + + /* + * ... and (not-un-common) context + */ + if (pdu->flags & AGENTX_MSG_FLAG_NON_DEFAULT_CONTEXT) { + DEBUGDUMPHEADER("recv", "Context"); + bufp = agentx_parse_string(bufp, length, buffer, &buf_len, + pdu->flags & + AGENTX_FLAGS_NETWORK_BYTE_ORDER); + DEBUGINDENTLESS(); + if (bufp == NULL) + return SNMPERR_ASN_PARSE_ERR; + + pdu->community_len = buf_len; + snmp_clone_mem((void **) &pdu->community, + (void *) buffer, (unsigned) buf_len); + + /* The NetSNMP API stuffs the context into the PDU's community string + * field, when using the AgentX Protocol. The rest of the code however, + * expects to find the context in the PDU's context field. Therefore we + * need to copy the context into the PDU's context fields. */ + if (pdu->community_len > 0 && pdu->contextName == NULL) + { + pdu->contextName = strdup((char *) pdu->community); + pdu->contextNameLen = pdu->community_len; + } + + buf_len = sizeof(buffer); + } + + DEBUGDUMPHEADER("recv", "PDU"); + switch (pdu->command) { + case AGENTX_MSG_OPEN: + pdu->time = *bufp; /* Timeout */ + bufp += 4; + *length -= 4; + + /* + * Store subagent OID & description in a VarBind + */ + DEBUGDUMPHEADER("recv", "Subagent OID"); + bufp = agentx_parse_oid(bufp, length, NULL, + oid_buffer, &oid_buf_len, + pdu-> + flags & AGENTX_FLAGS_NETWORK_BYTE_ORDER); + DEBUGINDENTLESS(); + if (bufp == NULL) { + DEBUGINDENTLESS(); + return SNMPERR_ASN_PARSE_ERR; + } + DEBUGDUMPHEADER("recv", "Subagent Description"); + bufp = agentx_parse_string(bufp, length, buffer, &buf_len, + pdu-> + flags & + AGENTX_FLAGS_NETWORK_BYTE_ORDER); + DEBUGINDENTLESS(); + if (bufp == NULL) { + DEBUGINDENTLESS(); + return SNMPERR_ASN_PARSE_ERR; + } + snmp_pdu_add_variable(pdu, oid_buffer, oid_buf_len, + ASN_OCTET_STR, buffer, buf_len); + + oid_buf_len = MAX_OID_LEN; + buf_len = sizeof(buffer); + break; + + case AGENTX_MSG_CLOSE: + pdu->errstat = *bufp; /* Reason */ + bufp += 4; + *length -= 4; + + break; + + case AGENTX_MSG_UNREGISTER: + case AGENTX_MSG_REGISTER: + DEBUGDUMPHEADER("recv", "Registration Header"); + if (pdu->command == AGENTX_MSG_REGISTER) { + pdu->time = *bufp; /* Timeout (Register only) */ + DEBUGDUMPSETUP("recv", bufp, 1); + DEBUGMSG(("dumpv_recv", " Timeout: \t%d\n", *bufp)); + } + bufp++; + pdu->priority = *bufp; + DEBUGDUMPSETUP("recv", bufp, 1); + DEBUGMSG(("dumpv_recv", " Priority: \t%d\n", *bufp)); + bufp++; + pdu->range_subid = *bufp; + DEBUGDUMPSETUP("recv", bufp, 1); + DEBUGMSG(("dumpv_recv", " Range Sub-Id:\t%d\n", *bufp)); + bufp++; + bufp++; + *length -= 4; + DEBUGINDENTLESS(); + + DEBUGDUMPHEADER("recv", "Registration OID"); + bufp = agentx_parse_oid(bufp, length, NULL, + oid_buffer, &oid_buf_len, + pdu->flags & AGENTX_FLAGS_NETWORK_BYTE_ORDER); + DEBUGINDENTLESS(); + if (bufp == NULL) { + DEBUGINDENTLESS(); + return SNMPERR_ASN_PARSE_ERR; + } + + if (pdu->range_subid) { + range_bound = agentx_parse_int(bufp, pdu->flags & + AGENTX_FLAGS_NETWORK_BYTE_ORDER); + bufp += 4; + *length -= 4; + + /* + * Construct the end-OID. + */ + end_oid_buf_len = oid_buf_len * sizeof(oid); + memcpy(end_oid_buf, oid_buffer, end_oid_buf_len); + end_oid_buf[pdu->range_subid - 1] = range_bound; + + snmp_pdu_add_variable(pdu, oid_buffer, oid_buf_len, + ASN_PRIV_INCL_RANGE, + (u_char *) end_oid_buf, end_oid_buf_len); + } else { + snmp_add_null_var(pdu, oid_buffer, oid_buf_len); + } + + oid_buf_len = MAX_OID_LEN; + break; + + case AGENTX_MSG_GETBULK: + DEBUGDUMPHEADER("recv", "Non-repeaters"); + pdu->non_repeaters = agentx_parse_short(bufp, pdu->flags & + AGENTX_FLAGS_NETWORK_BYTE_ORDER); + DEBUGINDENTLESS(); + DEBUGDUMPHEADER("recv", "Max-repeaters"); + pdu->max_repetitions = agentx_parse_short(bufp + 2, pdu->flags & + AGENTX_FLAGS_NETWORK_BYTE_ORDER); + DEBUGINDENTLESS(); + bufp += 4; + *length -= 4; + /* + * Fallthrough - SearchRange handling is the same + */ + + case AGENTX_MSG_GETNEXT: + case AGENTX_MSG_GET: + + /* + * * SearchRange List + * * Keep going while we have data left + */ + DEBUGDUMPHEADER("recv", "Search Range"); + while (*length > 0) { + bufp = agentx_parse_oid(bufp, length, &inc, + oid_buffer, &oid_buf_len, + pdu->flags & + AGENTX_FLAGS_NETWORK_BYTE_ORDER); + if (bufp == NULL) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return SNMPERR_ASN_PARSE_ERR; + } + bufp = agentx_parse_oid(bufp, length, NULL, + end_oid_buf, &end_oid_buf_len, + pdu->flags & + AGENTX_FLAGS_NETWORK_BYTE_ORDER); + if (bufp == NULL) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return SNMPERR_ASN_PARSE_ERR; + } + end_oid_buf_len *= sizeof(oid); + /* + * 'agentx_parse_oid()' returns the number of sub_ids + */ + + if (inc) { + snmp_pdu_add_variable(pdu, oid_buffer, oid_buf_len, + ASN_PRIV_INCL_RANGE, + (u_char *) end_oid_buf, + end_oid_buf_len); + } else { + snmp_pdu_add_variable(pdu, oid_buffer, oid_buf_len, + ASN_PRIV_EXCL_RANGE, + (u_char *) end_oid_buf, + end_oid_buf_len); + } + oid_buf_len = MAX_OID_LEN; + end_oid_buf_len = MAX_OID_LEN; + } + + DEBUGINDENTLESS(); + break; + + + case AGENTX_MSG_RESPONSE: + + pdu->flags |= UCD_MSG_FLAG_RESPONSE_PDU; + + /* + * sysUpTime + */ + pdu->time = agentx_parse_int(bufp, pdu->flags & + AGENTX_FLAGS_NETWORK_BYTE_ORDER); + bufp += 4; + *length -= 4; + + pdu->errstat = agentx_parse_short(bufp, pdu->flags & + AGENTX_FLAGS_NETWORK_BYTE_ORDER); + pdu->errindex = + agentx_parse_short(bufp + 2, + pdu->flags & AGENTX_FLAGS_NETWORK_BYTE_ORDER); + bufp += 4; + *length -= 4; + /* + * Fallthrough - VarBind handling is the same + */ + + case AGENTX_MSG_INDEX_ALLOCATE: + case AGENTX_MSG_INDEX_DEALLOCATE: + case AGENTX_MSG_NOTIFY: + case AGENTX_MSG_TESTSET: + + /* + * * VarBind List + * * Keep going while we have data left + */ + + DEBUGDUMPHEADER("recv", "VarBindList"); + while (*length > 0) { + bufp = agentx_parse_varbind(bufp, length, &type, + oid_buffer, &oid_buf_len, + buffer, &buf_len, + pdu->flags & + AGENTX_FLAGS_NETWORK_BYTE_ORDER); + if (bufp == NULL) { + DEBUGINDENTLESS(); + DEBUGINDENTLESS(); + return SNMPERR_ASN_PARSE_ERR; + } + snmp_pdu_add_variable(pdu, oid_buffer, oid_buf_len, + (u_char) type, buffer, buf_len); + + oid_buf_len = MAX_OID_LEN; + buf_len = sizeof(buffer); + } + DEBUGINDENTLESS(); + break; + + case AGENTX_MSG_COMMITSET: + case AGENTX_MSG_UNDOSET: + case AGENTX_MSG_CLEANUPSET: + case AGENTX_MSG_PING: + + /* + * "Empty" packet + */ + break; + + + case AGENTX_MSG_ADD_AGENT_CAPS: + /* + * Store AgentCap OID & description in a VarBind + */ + bufp = agentx_parse_oid(bufp, length, NULL, + oid_buffer, &oid_buf_len, + pdu->flags & AGENTX_FLAGS_NETWORK_BYTE_ORDER); + if (bufp == NULL) + return SNMPERR_ASN_PARSE_ERR; + bufp = agentx_parse_string(bufp, length, buffer, &buf_len, + pdu->flags & + AGENTX_FLAGS_NETWORK_BYTE_ORDER); + if (bufp == NULL) + return SNMPERR_ASN_PARSE_ERR; + snmp_pdu_add_variable(pdu, oid_buffer, oid_buf_len, + ASN_OCTET_STR, buffer, buf_len); + + oid_buf_len = MAX_OID_LEN; + buf_len = sizeof(buffer); + break; + + case AGENTX_MSG_REMOVE_AGENT_CAPS: + /* + * Store AgentCap OID & description in a VarBind + */ + bufp = agentx_parse_oid(bufp, length, NULL, + oid_buffer, &oid_buf_len, + pdu->flags & AGENTX_FLAGS_NETWORK_BYTE_ORDER); + if (bufp == NULL) + return SNMPERR_ASN_PARSE_ERR; + snmp_add_null_var(pdu, oid_buffer, oid_buf_len); + + oid_buf_len = MAX_OID_LEN; + break; + + default: + DEBUGINDENTLESS(); + DEBUGMSGTL(("agentx", "Unrecognised PDU type: %d\n", + pdu->command)); + return SNMPERR_UNKNOWN_PDU; + } + DEBUGINDENTLESS(); + return SNMP_ERR_NOERROR; +} + + + + +#ifdef TESTING + +testit(netsnmp_pdu *pdu1) +{ + char packet1[BUFSIZ]; + char packet2[BUFSIZ]; + int len1, len2; + netsnmp_pdu pdu2; + netsnmp_session sess; + + memset(&pdu2, 0, sizeof(netsnmp_pdu)); + memset(packet1, 0, BUFSIZ); + memset(packet2, 0, BUFSIZ); + + /* + * Encode this into a "packet" + */ + len1 = BUFSIZ; + if (agentx_build(&sess, pdu1, packet1, &len1) < 0) { + DEBUGMSGTL(("agentx", "First build failed\n")); + exit(1); + } + + DEBUGMSGTL(("agentx", "First build succeeded:\n")); + xdump(packet1, len1, "Ax1> "); + + /* + * Unpack this into a PDU + */ + len2 = len1; + if (agentx_parse(&pdu2, packet1, &len2, (u_char **) NULL) < 0) { + DEBUGMSGTL(("agentx", "First parse failed\n")); + exit(1); + } + DEBUGMSGTL(("agentx", "First parse succeeded:\n")); + if (len2 != 0) + DEBUGMSGTL(("agentx", + "Warning - parsed packet has %d bytes left\n", len2)); + + /* + * Encode this into another "packet" + */ + len2 = BUFSIZ; + if (agentx_build(&sess, &pdu2, packet2, &len2) < 0) { + DEBUGMSGTL(("agentx", "Second build failed\n")); + exit(1); + } + + DEBUGMSGTL(("agentx", "Second build succeeded:\n")); + xdump(packet2, len2, "Ax2> "); + + /* + * Compare the results + */ + if (len1 != len2) { + DEBUGMSGTL(("agentx", + "Error: first build (%d) is different to second (%d)\n", + len1, len2)); + exit(1); + } + if (memcmp(packet1, packet2, len1) != 0) { + DEBUGMSGTL(("agentx", + "Error: first build data is different to second\n")); + exit(1); + } + + DEBUGMSGTL(("agentx", "OK\n")); +} + + + +main() +{ + netsnmp_pdu pdu1; + oid oid_buf[] = { 1, 3, 6, 1, 2, 1, 10 }; + oid oid_buf2[] = { 1, 3, 6, 1, 2, 1, 20 }; + oid null_oid[] = { 0, 0 }; + char *string = "Example string"; + char *context = "LUCS"; + + + /* + * Create an example AgentX pdu structure + */ + + memset(&pdu1, 0, sizeof(netsnmp_pdu)); + pdu1.command = AGENTX_MSG_TESTSET; + pdu1.flags = 0; + pdu1.sessid = 16; + pdu1.transid = 24; + pdu1.reqid = 132; + + pdu1.time = 10; + pdu1.non_repeaters = 3; + pdu1.max_repetitions = 32; + pdu1.priority = 5; + pdu1.range_subid = 0; + + snmp_pdu_add_variable(&pdu1, oid_buf, sizeof(oid_buf) / sizeof(oid), + ASN_OBJECT_ID, (char *) oid_buf2, + sizeof(oid_buf2)); + snmp_pdu_add_variable(&pdu1, oid_buf, sizeof(oid_buf) / sizeof(oid), + ASN_INTEGER, (char *) &pdu1.reqid, + sizeof(pdu1.reqid)); + snmp_pdu_add_variable(&pdu1, oid_buf, sizeof(oid_buf) / sizeof(oid), + ASN_OCTET_STR, (char *) string, strlen(string)); + + printf("Test with non-network order.....\n"); + testit(&pdu1); + + printf("\nTest with network order.....\n"); + pdu1.flags |= AGENTX_FLAGS_NETWORK_BYTE_ORDER; + testit(&pdu1); + + pdu1.community = context; + pdu1.community_len = strlen(context); + pdu1.flags |= AGENTX_FLAGS_NON_DEFAULT_CONTEXT; + printf("Test with non-default context.....\n"); + testit(&pdu1); + + +} +#endif + +/* + * returns the proper length of an incoming agentx packet. + */ +/* + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | h.version | h.type | h.flags | <reserved> | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | h.sessionID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | h.transactionID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | h.packetID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | h.payload_length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * 20 bytes in header + */ + +int +agentx_check_packet(u_char * packet, size_t packet_len) +{ + + if (packet_len < 20) + return 0; /* minimum header length == 20 */ + + return agentx_parse_int(packet + 16, + *(packet + + 2) & AGENTX_FLAGS_NETWORK_BYTE_ORDER) + 20; +} |