diff options
Diffstat (limited to 'bsnmp')
| -rw-r--r-- | bsnmp/Makefile.am | 4 | ||||
| -rw-r--r-- | bsnmp/asn1.c | 1000 | ||||
| -rw-r--r-- | bsnmp/asn1.h | 183 | ||||
| -rw-r--r-- | bsnmp/snmp.c | 1081 | ||||
| -rw-r--r-- | bsnmp/snmp.h | 174 | ||||
| -rw-r--r-- | bsnmp/snmppriv.h | 45 | 
6 files changed, 2487 insertions, 0 deletions
| diff --git a/bsnmp/Makefile.am b/bsnmp/Makefile.am new file mode 100644 index 0000000..7a7a739 --- /dev/null +++ b/bsnmp/Makefile.am @@ -0,0 +1,4 @@ + +noinst_LIBRARIES = libbsnmp-custom.a +libbsnmp_custom_a_SOURCES = asn1.c asn1.h snmp.c snmp.h snmppriv.h + diff --git a/bsnmp/asn1.c b/bsnmp/asn1.c new file mode 100644 index 0000000..8a206ee --- /dev/null +++ b/bsnmp/asn1.c @@ -0,0 +1,1000 @@ +/* + * Copyright (c) 2001-2003 + *	Fraunhofer Institute for Open Communication Systems (FhG Fokus). + *	All rights reserved. + * + * Author: Harti Brandt <harti@freebsd.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Begemot: bsnmp/lib/asn1.c,v 1.28 2004/08/06 08:46:49 brandt Exp $ + * + * ASN.1 for SNMP. + */ +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <stdint.h> +#include <assert.h> +#include "asn1.h" + +static void asn_error_func(const struct asn_buf *, const char *, ...); + +void (*asn_error)(const struct asn_buf *, const char *, ...) = asn_error_func; + +/* + * Read the next header. This reads the tag (note, that only single + * byte tags are supported for now) and the length field. The length field + * is restricted to a 32-bit value. + * All errors of this function stop the decoding. + */ +enum asn_err +asn_get_header(struct asn_buf *b, u_char *type, asn_len_t *len) +{ +	u_int length; + +	if (b->asn_len == 0) { +		asn_error(b, "no identifier for header"); +		return (ASN_ERR_EOBUF); +	} +	*type = *b->asn_cptr; +	if ((*type & ASN_TYPE_MASK) > 0x30) { +		asn_error(b, "types > 0x30 not supported (%u)", +		    *type & ASN_TYPE_MASK); +		return (ASN_ERR_FAILED); +	} +	b->asn_cptr++; +	b->asn_len--; +	if (b->asn_len == 0) { +		asn_error(b, "no length field"); +		return (ASN_ERR_EOBUF); +	} +	if (*b->asn_cptr & 0x80) { +		length = *b->asn_cptr++ & 0x7f; +		b->asn_len--; +		if (length == 0) { +			asn_error(b, "indefinite length not supported"); +			return (ASN_ERR_FAILED); +		} +		if (length > ASN_MAXLENLEN) { +			asn_error(b, "long length too long (%u)", length); +			return (ASN_ERR_FAILED); +		} +		if (length > b->asn_len) { +			asn_error(b, "long length truncated"); +			return (ASN_ERR_EOBUF); +		} +		*len = 0; +		while (length--) { +			*len = (*len << 8) | *b->asn_cptr++; +			b->asn_len--; +		} +	} else { +		*len = *b->asn_cptr++; +		b->asn_len--; +	} +	return (ASN_ERR_OK); +} + +/* + * Write a length field (restricted to values < 2^32-1) and return the + * number of bytes this field takes. If ptr is NULL, the length is computed + * but nothing is written. If the length would be too large return 0. + */ +static u_int +asn_put_len(u_char *ptr, asn_len_t len) +{ +	u_int lenlen, lenlen1; +	asn_len_t tmp; + +	if (len > ASN_MAXLEN) { +		asn_error(NULL, "encoding length too long: (%u)", len); +		return (0); +	} + +	if (len <= 127) { +		if (ptr) +			*ptr++ = (u_char)len; +		return (1); +	} else { +		lenlen = 0; +		/* compute number of bytes for value (is at least 1) */ +		for (tmp = len; tmp != 0; tmp >>= 8) +			lenlen++; +		if (ptr != NULL) { +			*ptr++ = (u_char)lenlen | 0x80; +			lenlen1 = lenlen; +			while (lenlen1-- > 0) { +				ptr[lenlen1] = len & 0xff; +				len >>= 8; +			} +		} +		return (lenlen + 1); +	} +} + +/* + * Write a header (tag and length fields). + * Tags are restricted to one byte tags (value <= 0x30) and the + * lenght field to 16-bit. All errors stop the encoding. + */ +enum asn_err +asn_put_header(struct asn_buf *b, u_char type, asn_len_t len) +{ +	u_int lenlen; + +	/* tag field */ +	if ((type & ASN_TYPE_MASK) > 0x30) { +		asn_error(NULL, "types > 0x30 not supported (%u)", +		    type & ASN_TYPE_MASK); +		return (ASN_ERR_FAILED); +	} +	if (b->asn_len == 0) +		return (ASN_ERR_EOBUF); + +	*b->asn_ptr++ = type; +	b->asn_len--; + +	/* length field */ +	if ((lenlen = asn_put_len(NULL, len)) == 0) +		return (ASN_ERR_FAILED); +	if (b->asn_len < lenlen) +		return (ASN_ERR_EOBUF); + +	(void)asn_put_len(b->asn_ptr, len); +	b->asn_ptr += lenlen; +	b->asn_len -= lenlen; +	return (ASN_ERR_OK); +} + + +/* + * This constructs a temporary sequence header with space for the maximum + * length field (three byte). Set the pointer that ptr points to to the + * start of the encoded header. This is used for a later call to + * asn_commit_header which will fix-up the length field and move the + * value if needed. All errors should stop the encoding. + */ +#define	TEMP_LEN (1 + ASN_MAXLENLEN + 1) +enum asn_err +asn_put_temp_header(struct asn_buf *b, u_char type, u_char **ptr) +{ +	int ret; + +	if (b->asn_len < TEMP_LEN) +		return (ASN_ERR_EOBUF); +	*ptr = b->asn_ptr; +	if ((ret = asn_put_header(b, type, ASN_MAXLEN)) == ASN_ERR_OK) +		assert(b->asn_ptr == *ptr + TEMP_LEN); +	return (ret); +} +enum asn_err +asn_commit_header(struct asn_buf *b, u_char *ptr) +{ +	asn_len_t len; +	u_int lenlen, shift; + +	/* compute length of encoded value without header */ +	len = b->asn_ptr - (ptr + TEMP_LEN); + +	/* insert length. may not fail. */ +	lenlen = asn_put_len(ptr + 1, len); +	if (lenlen > TEMP_LEN - 1) +		return (ASN_ERR_FAILED); + +	if (lenlen < TEMP_LEN - 1) { +		/* shift value down */ +		shift = (TEMP_LEN - 1) - lenlen; +		memmove(ptr + 1 + lenlen, ptr + TEMP_LEN, len); +		b->asn_ptr -= shift; +		b->asn_len += shift; +	} +	return (ASN_ERR_OK); +} +#undef TEMP_LEN + +/* + * BER integer. This may be used to get a signed 64 bit integer at maximum. + * The maximum length should be checked by the caller. This cannot overflow + * if the caller ensures that len is at maximum 8. + * + * <bytes> + */ +static enum asn_err +asn_get_real_integer(struct asn_buf *b, asn_len_t len, int64_t *vp) +{ +	uint64_t val; +	int neg = 0; +	enum asn_err err; + +	if (b->asn_len < len) { +		asn_error(b, "truncated integer"); +		return (ASN_ERR_EOBUF); +	} +	if (len == 0) { +		asn_error(b, "zero-length integer"); +		*vp = 0; +		return (ASN_ERR_BADLEN); +	} +	err = ASN_ERR_OK; +	if (len > 8) +		err = ASN_ERR_RANGE; +	else if (len > 1 && +	    ((*b->asn_cptr == 0x00 && (b->asn_cptr[1] & 0x80) == 0) || +	    (*b->asn_cptr == 0xff && (b->asn_cptr[1] & 0x80) == 0x80))) { +		asn_error(b, "non-minimal integer"); +		err = ASN_ERR_BADLEN; +	} + +	if (*b->asn_cptr & 0x80) +		neg = 1; +	val = 0; +	while (len--) { +		val <<= 8; +		val |= neg ? (u_char)~*b->asn_cptr : *b->asn_cptr; +		b->asn_len--; +		b->asn_cptr++; +	} +	if (neg) { +		*vp = -(int64_t)val - 1; +	} else +		*vp = (int64_t)val; +	return (err); +} + +/* + * Write a signed integer with the given type. The caller has to ensure + * that the actual value is ok for this type. + */ +static enum asn_err +asn_put_real_integer(struct asn_buf *b, u_char type, int64_t ival) +{ +	int i, neg = 0; +# define OCTETS 8 +	u_char buf[OCTETS]; +	uint64_t val; +	enum asn_err ret; + +	if (ival < 0) { +		/* this may fail if |INT64_MIN| > |INT64_MAX| and +		 * the value is between * INT64_MIN <= ival < -(INT64_MAX+1) */ +		val = (uint64_t)-(ival + 1); +		neg = 1; +	} else +		val = (uint64_t)ival; + +	/* split the value into octets */ +	for (i = OCTETS - 1; i >= 0; i--) { +		buf[i] = val & 0xff; +		if (neg) +			buf[i] = ~buf[i]; +		val >>= 8; +	} +	/* no leading 9 zeroes or ones */ +	for (i = 0; i < OCTETS - 1; i++) +		if (!((buf[i] == 0xff && (buf[i + 1] & 0x80) != 0) || +		    (buf[i] == 0x00 && (buf[i + 1] & 0x80) == 0))) +			break; +	if ((ret = asn_put_header(b, type, OCTETS - i))) +		return (ret); +	if (OCTETS - (u_int)i > b->asn_len) +		return (ASN_ERR_EOBUF); + +	while (i < OCTETS) { +		*b->asn_ptr++ = buf[i++]; +		b->asn_len--; +	} +	return (ASN_ERR_OK); +# undef OCTETS +} + + +/* + * The same for unsigned 64-bitters. Here we have the problem, that overflow + * can happen, because the value maybe 9 bytes long. In this case the + * first byte must be 0. + */ +static enum asn_err +asn_get_real_unsigned(struct asn_buf *b, asn_len_t len, uint64_t *vp) +{ +	enum asn_err err; + +	if (b->asn_len < len) { +		asn_error(b, "truncated integer"); +		return (ASN_ERR_EOBUF); +	} +	if (len == 0) { +		asn_error(b, "zero-length integer"); +		*vp = 0; +		return (ASN_ERR_BADLEN); +	} +	err = ASN_ERR_OK; +	*vp = 0; +	if ((*b->asn_cptr & 0x80) || (len == 9 && *b->asn_cptr != 0)) { +		/* negative integer or too larger */ +		*vp = 0xffffffffffffffffULL; +		err = ASN_ERR_RANGE; +	} else if (len > 1 && +	    *b->asn_cptr == 0x00 && (b->asn_cptr[1] & 0x80) == 0) { +		asn_error(b, "non-minimal unsigned"); +		err = ASN_ERR_BADLEN; +	} + +	while (len--) { +		*vp = (*vp << 8) | *b->asn_cptr++; +		b->asn_len--; +	} +	return (err); +} + + +/* + * Values with the msb on need 9 octets. + */ +static int +asn_put_real_unsigned(struct asn_buf *b, u_char type, uint64_t val) +{ +	int i; +# define OCTETS 9 +	u_char buf[OCTETS]; +	enum asn_err ret; + +	/* split the value into octets */ +	for (i = OCTETS - 1; i >= 0; i--) { +		buf[i] = val & 0xff; +		val >>= 8; +	} +	/* no leading 9 zeroes */ +	for (i = 0; i < OCTETS - 1; i++) +		if (!(buf[i] == 0x00 && (buf[i + 1] & 0x80) == 0)) +			break; +	if ((ret = asn_put_header(b, type, OCTETS - i))) +		return (ret); +	if (OCTETS - (u_int)i > b->asn_len) +		return (ASN_ERR_EOBUF); + +	while (i < OCTETS) { +		*b->asn_ptr++ = buf[i++]; +		b->asn_len--; +	} +#undef OCTETS +	return (ASN_ERR_OK); +} + +/* + * The ASN.1 INTEGER type is restricted to 32-bit signed by the SMI. + */ +enum asn_err +asn_get_integer_raw(struct asn_buf *b, asn_len_t len, int32_t *vp) +{ +	int64_t val; +	enum asn_err ret; + +	if ((ret = asn_get_real_integer(b, len, &val)) == ASN_ERR_OK) { +		if (len > 4) +			ret = ASN_ERR_BADLEN; +		else if (val > INT32_MAX || val < INT32_MIN) +			/* may not happen */ +			ret = ASN_ERR_RANGE; +		*vp = (int32_t)val; +	} +	return (ret); +} + +enum asn_err +asn_get_integer(struct asn_buf *b, int32_t *vp) +{ +	asn_len_t len; +	u_char type; +	enum asn_err err; + +	if ((err = asn_get_header(b, &type, &len)) != ASN_ERR_OK) +		return (err); +	if (type != ASN_TYPE_INTEGER) { +		asn_error(b, "bad type for integer (%u)", type); +		return (ASN_ERR_TAG); +	} + +	return (asn_get_integer_raw(b, len, vp)); +} + +enum asn_err +asn_put_integer(struct asn_buf *b, int32_t val) +{ +	return (asn_put_real_integer(b, ASN_TYPE_INTEGER, val)); +} + +/* + * OCTETSTRING + * + * <0x04> <len> <data ...> + * + * Get an octetstring. noctets must point to the buffer size and on + * return will contain the size of the octetstring, regardless of the + * buffer size. + */ +enum asn_err +asn_get_octetstring_raw(struct asn_buf *b, asn_len_t len, u_char *octets, +    u_int *noctets) +{ +	enum asn_err err = ASN_ERR_OK; + +	if (*noctets < len) { +		asn_error(b, "octetstring truncated"); +		err = ASN_ERR_RANGE; +	} +	if (b->asn_len < len) { +		asn_error(b, "truncatet octetstring"); +		return (ASN_ERR_EOBUF); +	} +	if (*noctets < len) +		memcpy(octets, b->asn_cptr, *noctets); +	else +		memcpy(octets, b->asn_cptr, len); +	*noctets = len; +	b->asn_cptr += len; +	b->asn_len -= len; +	return (err); +} + +enum asn_err +asn_get_octetstring(struct asn_buf *b, u_char *octets, u_int *noctets) +{ +	enum asn_err err; +	u_char type; +	asn_len_t len; + +	if ((err = asn_get_header(b, &type, &len)) != ASN_ERR_OK) +		return (err); +	if (type != ASN_TYPE_OCTETSTRING) { +		asn_error(b, "bad type for octetstring (%u)", type); +		return (ASN_ERR_TAG); +	} +	return (asn_get_octetstring_raw(b, len, octets, noctets)); +} + +enum asn_err +asn_put_octetstring(struct asn_buf *b, const u_char *octets, u_int noctets) +{ +	enum asn_err ret; + +	if ((ret = asn_put_header(b, ASN_TYPE_OCTETSTRING, noctets)) != ASN_ERR_OK) +		return (ret); +	if (b->asn_len < noctets) +		return (ASN_ERR_EOBUF); + +	memcpy(b->asn_ptr, octets, noctets); +	b->asn_ptr += noctets; +	b->asn_len -= noctets; +	return (ASN_ERR_OK); +} + +/* + * NULL + * + * <0x05> <0x00> + */ +enum asn_err +asn_get_null_raw(struct asn_buf *b, asn_len_t len) +{ +	if (len != 0) { +		if (b->asn_len < len) { +			asn_error(b, "truncated NULL"); +			return (ASN_ERR_EOBUF); +		} +		asn_error(b, "bad length for NULL (%u)", len); +		b->asn_len -= len; +		b->asn_ptr += len; +		return (ASN_ERR_BADLEN); +	} +	return (ASN_ERR_OK); +} + +enum asn_err +asn_get_null(struct asn_buf *b) +{ +	u_char type; +	asn_len_t len; +	enum asn_err err; + +	if ((err = asn_get_header(b, &type, &len)) != ASN_ERR_OK) +		return (err); +	if (type != ASN_TYPE_NULL) { +		asn_error(b, "bad type for NULL (%u)", type); +		return (ASN_ERR_TAG); +	} +	return (asn_get_null_raw(b, len)); +} + +enum asn_err +asn_put_null(struct asn_buf *b) +{ +	return (asn_put_header(b, ASN_TYPE_NULL, 0)); +} + +enum asn_err +asn_put_exception(struct asn_buf *b, u_int except) +{ +	return (asn_put_header(b, ASN_CLASS_CONTEXT | except, 0)); +} + +/* + * OBJID + * + * <0x06> <len> <subid...> + */ +enum asn_err +asn_get_objid_raw(struct asn_buf *b, asn_len_t len, struct asn_oid *oid) +{ +	asn_subid_t subid; +	enum asn_err err; + +	if (b->asn_len < len) { +		asn_error(b, "truncated OBJID"); +		return (ASN_ERR_EOBUF); +	} +	oid->len = 0; +	if (len == 0) { +		asn_error(b, "short OBJID"); +		oid->subs[oid->len++] = 0; +		oid->subs[oid->len++] = 0; +		return (ASN_ERR_BADLEN); +	} +	err = ASN_ERR_OK; +	while (len != 0) { +		if (oid->len == ASN_MAXOIDLEN) { +			asn_error(b, "OID too long (%u)", oid->len); +			b->asn_cptr += len; +			b->asn_len -= len; +			return (ASN_ERR_BADLEN); +		} +		subid = 0; +		do { +			if (len == 0) { +				asn_error(b, "unterminated subid"); +				return (ASN_ERR_EOBUF); +			} +			if (subid > (ASN_MAXID >> 7)) { +				asn_error(b, "OBID subid too larger"); +				err = ASN_ERR_RANGE; +			} +			subid = (subid << 7) | (*b->asn_cptr & 0x7f); +			len--; +			b->asn_len--; +		} while (*b->asn_cptr++ & 0x80); +		if (oid->len == 0) { +			if (subid < 80) { +				oid->subs[oid->len++] = subid / 40; +				oid->subs[oid->len++] = subid % 40; +			} else { +				oid->subs[oid->len++] = 2; +				oid->subs[oid->len++] = subid - 80; +			} +		} else { +			oid->subs[oid->len++] = subid; +		} +	} +	return (err); + +} + +enum asn_err +asn_get_objid(struct asn_buf *b, struct asn_oid *oid) +{ +	u_char type; +	asn_len_t len; +	enum asn_err err; + +	if ((err = asn_get_header(b, &type, &len)) != ASN_ERR_OK) +		return (err); +	if (type != ASN_TYPE_OBJID) { +		asn_error(b, "bad type for OBJID (%u)", type); +		return (ASN_ERR_TAG); +	} +	return (asn_get_objid_raw(b, len, oid)); +} + +enum asn_err +asn_put_objid(struct asn_buf *b, const struct asn_oid *oid) +{ +	asn_subid_t first, sub; +	enum asn_err err, err1; +	u_int i, oidlen; +	asn_len_t len; + +	err = ASN_ERR_OK; +	if (oid->len == 0) { +		/* illegal */ +		asn_error(NULL, "short oid"); +		err = ASN_ERR_RANGE; +		first = 0; +		oidlen = 2; +	} else if (oid->len == 1) { +		/* illegal */ +		asn_error(b, "short oid"); +		if (oid->subs[0] > 2) +			asn_error(NULL, "oid[0] too large (%u)", oid->subs[0]); +		err = ASN_ERR_RANGE; +		first = oid->subs[0] * 40; +		oidlen = 2; +	} else { +		if (oid->len > ASN_MAXOIDLEN) { +			asn_error(NULL, "oid too long %u", oid->len); +			err = ASN_ERR_RANGE; +		} +		if (oid->subs[0] > 2 || +		    (oid->subs[0] < 2 && oid->subs[0] >= 40)) { +			asn_error(NULL, "oid out of range (%u,%u)", +			    oid->subs[0], oid->subs[1]); +			err = ASN_ERR_RANGE; +		} +		first = 40 * oid->subs[0] + oid->subs[1]; +		oidlen = oid->len; +	} +	len = 0; +	for (i = 1; i < oidlen; i++) { +		sub = (i == 1) ? first : oid->subs[i]; +		if (sub > ASN_MAXID) { +			asn_error(NULL, "oid subid too large"); +			err = ASN_ERR_RANGE; +		} +		len += (sub <= 0x7f) ? 1 +		    : (sub <= 0x3fff) ? 2 +		    : (sub <= 0x1fffff) ? 3 +		    : (sub <= 0xfffffff) ? 4 +		    : 5; +	} +	if ((err1 = asn_put_header(b, ASN_TYPE_OBJID, len)) != ASN_ERR_OK) +		return (err1); +	if (b->asn_len < len) +		return (ASN_ERR_EOBUF); + +	for (i = 1; i < oidlen; i++) { +		sub = (i == 1) ? first : oid->subs[i]; +		if (sub <= 0x7f) { +			*b->asn_ptr++ = sub; +			b->asn_len--; +		} else if (sub <= 0x3fff) { +			*b->asn_ptr++ = (sub >> 7) | 0x80; +			*b->asn_ptr++ = sub & 0x7f; +			b->asn_len -= 2; +		} else if (sub <= 0x1fffff) { +			*b->asn_ptr++ = (sub >> 14) | 0x80; +			*b->asn_ptr++ = ((sub >> 7) & 0x7f) | 0x80; +			*b->asn_ptr++ = sub & 0x7f; +			b->asn_len -= 3; +		} else if (sub <= 0xfffffff) { +			*b->asn_ptr++ = (sub >> 21) | 0x80; +			*b->asn_ptr++ = ((sub >> 14) & 0x7f) | 0x80; +			*b->asn_ptr++ = ((sub >> 7) & 0x7f) | 0x80; +			*b->asn_ptr++ = sub & 0x7f; +			b->asn_len -= 4; +		} else { +			*b->asn_ptr++ = (sub >> 28) | 0x80; +			*b->asn_ptr++ = ((sub >> 21) & 0x7f) | 0x80; +			*b->asn_ptr++ = ((sub >> 14) & 0x7f) | 0x80; +			*b->asn_ptr++ = ((sub >> 7) & 0x7f) | 0x80; +			*b->asn_ptr++ = sub & 0x7f; +			b->asn_len -= 5; +		} +	} +	return (err); +} +/* + * SEQUENCE header + * + * <0x10|0x20> <len> <data...> + */ +enum asn_err +asn_get_sequence(struct asn_buf *b, asn_len_t *len) +{ +	u_char type; +	enum asn_err err; + +	if ((err = asn_get_header(b, &type, len)) != ASN_ERR_OK) +		return (err); +	if (type != (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED)) { +		asn_error(b, "bad sequence type %u", type); +		return (ASN_ERR_TAG); +	} +	if (*len > b->asn_len) { +		asn_error(b, "truncated sequence"); +		return (ASN_ERR_EOBUF); +	} +	return (ASN_ERR_OK); +} + +/* + * Application types + * + * 0x40 4 MSB 2MSB 2LSB LSB + */ +enum asn_err +asn_get_ipaddress_raw(struct asn_buf *b, asn_len_t len, u_char *addr) +{ +	u_int i; + +	if (b->asn_len < len) { +		asn_error(b, "truncated ip-address"); +		return (ASN_ERR_EOBUF); +	} +	if (len < 4) { +		asn_error(b, "short length for ip-Address %u", len); +		for (i = 0; i < len; i++) +			*addr++ = *b->asn_cptr++; +		while (i++ < len) +			*addr++ = 0; +		b->asn_len -= len; +		return (ASN_ERR_BADLEN); +	} +	for (i = 0; i < 4; i++) +		*addr++ = *b->asn_cptr++; +	b->asn_cptr += len - 4; +	b->asn_len -= len; +	return (ASN_ERR_OK); +} + +enum asn_err +asn_get_ipaddress(struct asn_buf *b, u_char *addr) +{ +	u_char type; +	asn_len_t len; +	enum asn_err err; + +	if ((err = asn_get_header(b, &type, &len)) != ASN_ERR_OK) +		return (err); +	if (type != (ASN_CLASS_APPLICATION|ASN_APP_IPADDRESS)) { +		asn_error(b, "bad type for ip-address %u", type); +		return (ASN_ERR_TAG); +	} +	return (asn_get_ipaddress_raw(b, len, addr)); +} + +enum asn_err +asn_put_ipaddress(struct asn_buf *b, const u_char *addr) +{ +	enum asn_err err; + +	if ((err = asn_put_header(b, ASN_CLASS_APPLICATION|ASN_APP_IPADDRESS, +	    4)) != ASN_ERR_OK) +		return (err); +	if (b->asn_len < 4) +		return (ASN_ERR_EOBUF); + +	memcpy(b->asn_ptr, addr, 4); +	b->asn_ptr += 4; +	b->asn_len -= 4; +	return (ASN_ERR_OK); +} + + +/* + * UNSIGNED32 + * + * 0x42|0x41 <len> ... + */ +enum asn_err +asn_get_uint32_raw(struct asn_buf *b, asn_len_t len, uint32_t *vp) +{ +	uint64_t v; +	enum asn_err err; + +	if ((err = asn_get_real_unsigned(b, len, &v)) == ASN_ERR_OK) { +		if (len > 5) { +			asn_error(b, "uint32 too long %u", len); +			err = ASN_ERR_BADLEN; +		} else if (v > UINT32_MAX) { +			asn_error(b, "uint32 too large %llu", v); +			err = ASN_ERR_RANGE; +		} +		*vp = (uint32_t)v; +	} +	return (err); +} + +enum asn_err +asn_put_uint32(struct asn_buf *b, u_char type, uint32_t val) +{ +	uint64_t v = val; + +	return (asn_put_real_unsigned(b, ASN_CLASS_APPLICATION|type, v)); +} + +/* + * COUNTER64 + * 0x46 <len> ... + */ +enum asn_err +asn_get_counter64_raw(struct asn_buf *b, asn_len_t len, uint64_t *vp) +{ +	return (asn_get_real_unsigned(b, len, vp)); +} + +enum asn_err +asn_put_counter64(struct asn_buf *b, uint64_t val) +{ +	return (asn_put_real_unsigned(b, +	    ASN_CLASS_APPLICATION | ASN_APP_COUNTER64, val)); +} + +/* + * TimeTicks + * 0x43 <len> ... + */ +enum asn_err +asn_get_timeticks(struct asn_buf *b, uint32_t *vp) +{ +	asn_len_t len; +	u_char type; +	enum asn_err err; + +	if ((err = asn_get_header(b, &type, &len)) != ASN_ERR_OK) +		return (err); +	if (type != (ASN_CLASS_APPLICATION|ASN_APP_TIMETICKS)) { +		asn_error(b, "bad type for timeticks %u", type); +		return (ASN_ERR_TAG); +	} +	return (asn_get_uint32_raw(b, len, vp)); +} + +enum asn_err +asn_put_timeticks(struct asn_buf *b, uint32_t val) +{ +	uint64_t v = val; + +	return (asn_put_real_unsigned(b, +	    ASN_CLASS_APPLICATION | ASN_APP_TIMETICKS, v)); +} + +/* + * Construct a new OID by taking a range of sub ids of the original oid. + */ +void +asn_slice_oid(struct asn_oid *dest, const struct asn_oid *src, +    u_int from, u_int to) +{ +	if (from >= to) { +		dest->len = 0; +		return; +	} +	dest->len = to - from; +	memcpy(dest->subs, &src->subs[from], dest->len * sizeof(dest->subs[0])); +} + +/* + * Append from to to + */ +void +asn_append_oid(struct asn_oid *to, const struct asn_oid *from) +{ +	memcpy(&to->subs[to->len], &from->subs[0], +	    from->len * sizeof(from->subs[0])); +	to->len += from->len; +} + +/* + * Skip a value + */ +enum asn_err +asn_skip(struct asn_buf *b, asn_len_t len) +{ +	if (b->asn_len < len) +		return (ASN_ERR_EOBUF); +	b->asn_cptr += len; +	b->asn_len -= len; +	return (ASN_ERR_OK); +} + +/* + * Compare two OIDs. + * + * o1 < o2 : -1 + * o1 > o2 : +1 + * o1 = o2 :  0 + */ +int +asn_compare_oid(const struct asn_oid *o1, const struct asn_oid *o2) +{ +	u_long i; + +	for (i = 0; i < o1->len && i < o2->len; i++) { +		if (o1->subs[i] < o2->subs[i]) +			return (-1); +		if (o1->subs[i] > o2->subs[i]) +			return (+1); +	} +	if (o1->len < o2->len) +		return (-1); +	if (o1->len > o2->len) +		return (+1); +	return (0); +} + +/* + * Check whether an OID is a sub-string of another OID. + */ +int +asn_is_suboid(const struct asn_oid *o1, const struct asn_oid *o2) +{ +	u_long i; + +	for (i = 0; i < o1->len; i++) +		if (i >= o2->len || o1->subs[i] != o2->subs[i]) +			return (0); +	return (1); +} + +/* + * Put a string representation of an oid into a user buffer. This buffer + * is assumed to be at least ASN_OIDSTRLEN characters long. + * + * sprintf is assumed not to fail here. + */ +char * +asn_oid2str_r(const struct asn_oid *oid, char *buf) +{ +	u_int len, i; +	char *ptr; + +	if ((len = oid->len) > ASN_MAXOIDLEN) +		len = ASN_MAXOIDLEN; +	buf[0] = '\0'; +	for (i = 0, ptr = buf; i < len; i++) { +		if (i > 0) +			*ptr++ = '.'; +		ptr += sprintf(ptr, "%u", oid->subs[i]); +	} +	return (buf); +} + +/* + * Make a string from an OID in a private buffer. + */ +char * +asn_oid2str(const struct asn_oid *oid) +{ +	static char str[ASN_OIDSTRLEN]; + +	return (asn_oid2str_r(oid, str)); +} + + +static void +asn_error_func(const struct asn_buf *b, const char *err, ...) +{ +	va_list ap; +	u_long i; + +	fprintf(stderr, "ASN.1: "); +	va_start(ap, err); +	vfprintf(stderr, err, ap); +	va_end(ap); + +	if (b != NULL) { +		fprintf(stderr, " at"); +		for (i = 0; b->asn_len > i; i++) +			fprintf(stderr, " %02x", b->asn_cptr[i]); +	} +	fprintf(stderr, "\n"); +} diff --git a/bsnmp/asn1.h b/bsnmp/asn1.h new file mode 100644 index 0000000..23b7814 --- /dev/null +++ b/bsnmp/asn1.h @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2001-2003 + *	Fraunhofer Institute for Open Communication Systems (FhG Fokus). + *	All rights reserved. + * + * Author: Harti Brandt <harti@freebsd.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Begemot: bsnmp/lib/asn1.h,v 1.18 2004/08/06 08:46:50 brandt Exp $ + * + * ASN.1 for SNMP + */ +#ifndef asn1_h_ +#define asn1_h_ + +#include <sys/types.h> +#include <stdint.h> + +struct asn_buf { +	union { +		u_char	*ptr; +		const u_char *cptr; +	}	asn_u; +	size_t	asn_len; +}; +#define asn_cptr	asn_u.cptr +#define asn_ptr	asn_u.ptr + +/* these restrictions are in the SMI */ +#define ASN_MAXID	0xffffffff +#define ASN_MAXOIDLEN	128 + +/* the string needed for this (with trailing zero) */ +#define ASN_OIDSTRLEN	(ASN_MAXOIDLEN * (10 + 1) - 1 + 1) + +/* type of subidentifiers */ +typedef uint32_t asn_subid_t; + +struct asn_oid { +	u_int	len; +	asn_subid_t subs[ASN_MAXOIDLEN]; +}; + +enum asn_err { +	/* conversion was ok */ +	ASN_ERR_OK	= 0, +	/* conversion failed and stopped */ +	ASN_ERR_FAILED	= 1 | 0x1000, +	/* length field bad, value skipped */ +	ASN_ERR_BADLEN	= 2, +	/* out of buffer, stopped */ +	ASN_ERR_EOBUF	= 3 | 0x1000, +	/* length ok, but value is out of range */ +	ASN_ERR_RANGE	= 4, +	/* not the expected tag, stopped */ +	ASN_ERR_TAG	= 5 | 0x1000, +}; +#define ASN_ERR_STOPPED(E) (((E) & 0x1000) != 0) + +/* type for the length field of encoded values. The length is restricted + * to 65535, but using uint16_t would give conversion warnings on gcc */ +typedef uint32_t asn_len_t;	/* could be also uint16_t */ + +/* maximal length of a long length field without the length of the length */ +#define ASN_MAXLEN	65535 +#define ASN_MAXLENLEN	2	/* number of bytes in a length */ + +/* maximum size of an octet string as per SMIv2 */ +#define ASN_MAXOCTETSTRING 65535 + +extern void (*asn_error)(const struct asn_buf *, const char *, ...); + +enum asn_err asn_get_header(struct asn_buf *, u_char *, asn_len_t *); +enum asn_err asn_put_header(struct asn_buf *, u_char, asn_len_t); + +enum asn_err asn_put_temp_header(struct asn_buf *, u_char, u_char **); +enum asn_err asn_commit_header(struct asn_buf *, u_char *); + +enum asn_err asn_get_integer_raw(struct asn_buf *, asn_len_t, int32_t *); +enum asn_err asn_get_integer(struct asn_buf *, int32_t *); +enum asn_err asn_put_integer(struct asn_buf *, int32_t); + +enum asn_err asn_get_octetstring_raw(struct asn_buf *, asn_len_t, u_char *, u_int *); +enum asn_err asn_get_octetstring(struct asn_buf *, u_char *, u_int *); +enum asn_err asn_put_octetstring(struct asn_buf *, const u_char *, u_int); + +enum asn_err asn_get_null_raw(struct asn_buf *b, asn_len_t); +enum asn_err asn_get_null(struct asn_buf *); +enum asn_err asn_put_null(struct asn_buf *); + +enum asn_err asn_put_exception(struct asn_buf *, u_int); + +enum asn_err asn_get_objid_raw(struct asn_buf *, asn_len_t, struct asn_oid *); +enum asn_err asn_get_objid(struct asn_buf *, struct asn_oid *); +enum asn_err asn_put_objid(struct asn_buf *, const struct asn_oid *); + +enum asn_err asn_get_sequence(struct asn_buf *, asn_len_t *); + +enum asn_err asn_get_ipaddress_raw(struct asn_buf *, asn_len_t, u_char *); +enum asn_err asn_get_ipaddress(struct asn_buf *, u_char *); +enum asn_err asn_put_ipaddress(struct asn_buf *, const u_char *); + +enum asn_err asn_get_uint32_raw(struct asn_buf *, asn_len_t, uint32_t *); +enum asn_err asn_put_uint32(struct asn_buf *, u_char, uint32_t); + +enum asn_err asn_get_counter64_raw(struct asn_buf *, asn_len_t, uint64_t *); +enum asn_err asn_put_counter64(struct asn_buf *, uint64_t); + +enum asn_err asn_get_timeticks(struct asn_buf *, uint32_t *); +enum asn_err asn_put_timeticks(struct asn_buf *, uint32_t); + +enum asn_err asn_skip(struct asn_buf *, asn_len_t); + +/* + * Utility functions for OIDs + */ +/* get a sub-OID from the middle of another OID */ +void asn_slice_oid(struct asn_oid *, const struct asn_oid *, u_int, u_int); + +/* append an OID to another one */ +void asn_append_oid(struct asn_oid *, const struct asn_oid *); + +/* compare two OIDs */ +int asn_compare_oid(const struct asn_oid *, const struct asn_oid *); + +/* check whether the first is a suboid of the second one */ +int asn_is_suboid(const struct asn_oid *, const struct asn_oid *); + +/* format an OID into a user buffer of size ASN_OIDSTRLEN */ +char *asn_oid2str_r(const struct asn_oid *, char *); + +/* format an OID into a private static buffer */ +char *asn_oid2str(const struct asn_oid *); + +enum { +	ASN_TYPE_BOOLEAN	= 0x01, +	ASN_TYPE_INTEGER	= 0x02, +	ASN_TYPE_BITSTRING	= 0x03, +	ASN_TYPE_OCTETSTRING	= 0x04, +	ASN_TYPE_NULL		= 0x05, +	ASN_TYPE_OBJID		= 0x06, +	ASN_TYPE_SEQUENCE	= 0x10, + +	ASN_TYPE_CONSTRUCTED	= 0x20, +	ASN_CLASS_UNIVERSAL	= 0x00, +	ASN_CLASS_APPLICATION	= 0x40, +	ASN_CLASS_CONTEXT	= 0x80, +	ASN_CLASS_PRIVATE	= 0xc0, +	ASN_TYPE_MASK		= 0x1f, + +	ASN_APP_IPADDRESS	= 0x00, +	ASN_APP_COUNTER		= 0x01, +	ASN_APP_GAUGE		= 0x02, +	ASN_APP_TIMETICKS	= 0x03, +	ASN_APP_OPAQUE		= 0x04,	/* not implemented */ +	ASN_APP_COUNTER64	= 0x06, + +	ASN_EXCEPT_NOSUCHOBJECT	= 0x00, +	ASN_EXCEPT_NOSUCHINSTANCE = 0x01, +	ASN_EXCEPT_ENDOFMIBVIEW	= 0x02, +}; + +#endif diff --git a/bsnmp/snmp.c b/bsnmp/snmp.c new file mode 100644 index 0000000..f758262 --- /dev/null +++ b/bsnmp/snmp.c @@ -0,0 +1,1081 @@ +/* + * Copyright (c) 2001-2003 + *	Fraunhofer Institute for Open Communication Systems (FhG Fokus). + *	All rights reserved. + * + * Author: Harti Brandt <harti@freebsd.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Begemot: bsnmp/lib/snmp.c,v 1.38 2004/08/06 08:46:53 brandt Exp $ + * + * SNMP + */ +#include <sys/types.h> +#include <sys/socket.h> +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdarg.h> +#include <stdint.h> +#include <string.h> +#include <ctype.h> +#include <netdb.h> +#include <errno.h> + +#include "asn1.h" +#include "snmp.h" +#include "snmppriv.h" + +static void snmp_error_func(const char *, ...); +static void snmp_printf_func(const char *, ...); + +void (*snmp_error)(const char *, ...) = snmp_error_func; +void (*snmp_printf)(const char *, ...) = snmp_printf_func; + +/* + * Get the next variable binding from the list. + * ASN errors on the sequence or the OID are always fatal. + */ +static enum asn_err +get_var_binding(struct asn_buf *b, struct snmp_value *binding) +{ +	u_char type; +	asn_len_t len, trailer; +	enum asn_err err; + +	if (asn_get_sequence(b, &len) != ASN_ERR_OK) { +		snmp_error("cannot parse varbind header"); +		return (ASN_ERR_FAILED); +	} + +	/* temporary truncate the length so that the parser does not +	 * eat up bytes behind the sequence in the case the encoding is +	 * wrong of inner elements. */ +	trailer = b->asn_len - len; +	b->asn_len = len; + +	if (asn_get_objid(b, &binding->var) != ASN_ERR_OK) { +		snmp_error("cannot parse binding objid"); +		return (ASN_ERR_FAILED); +	} +	if (asn_get_header(b, &type, &len) != ASN_ERR_OK) { +		snmp_error("cannot parse binding value header"); +		return (ASN_ERR_FAILED); +	} + +	switch (type) { + +	  case ASN_TYPE_NULL: +		binding->syntax = SNMP_SYNTAX_NULL; +		err = asn_get_null_raw(b, len); +		break; + +	  case ASN_TYPE_INTEGER: +		binding->syntax = SNMP_SYNTAX_INTEGER; +		err = asn_get_integer_raw(b, len, &binding->v.integer); +		break; + +	  case ASN_TYPE_OCTETSTRING: +		binding->syntax = SNMP_SYNTAX_OCTETSTRING; +		binding->v.octetstring.octets = malloc(len); +		if (binding->v.octetstring.octets == NULL) { +			snmp_error("%s", strerror(errno)); +			return (ASN_ERR_FAILED); +		} +		binding->v.octetstring.len = len; +		err = asn_get_octetstring_raw(b, len, +		    binding->v.octetstring.octets, +		    &binding->v.octetstring.len); +		if (ASN_ERR_STOPPED(err)) { +			free(binding->v.octetstring.octets); +			binding->v.octetstring.octets = NULL; +		} +		break; + +	  case ASN_TYPE_OBJID: +		binding->syntax = SNMP_SYNTAX_OID; +		err = asn_get_objid_raw(b, len, &binding->v.oid); +		break; + +	  case ASN_CLASS_APPLICATION|ASN_APP_IPADDRESS: +		binding->syntax = SNMP_SYNTAX_IPADDRESS; +		err = asn_get_ipaddress_raw(b, len, binding->v.ipaddress); +		break; + +	  case ASN_CLASS_APPLICATION|ASN_APP_TIMETICKS: +		binding->syntax = SNMP_SYNTAX_TIMETICKS; +		err = asn_get_uint32_raw(b, len, &binding->v.uint32); +		break; + +	  case ASN_CLASS_APPLICATION|ASN_APP_COUNTER: +		binding->syntax = SNMP_SYNTAX_COUNTER; +		err = asn_get_uint32_raw(b, len, &binding->v.uint32); +		break; + +	  case ASN_CLASS_APPLICATION|ASN_APP_GAUGE: +		binding->syntax = SNMP_SYNTAX_GAUGE; +		err = asn_get_uint32_raw(b, len, &binding->v.uint32); +		break; + +	  case ASN_CLASS_APPLICATION|ASN_APP_COUNTER64: +		binding->syntax = SNMP_SYNTAX_COUNTER64; +		err = asn_get_counter64_raw(b, len, &binding->v.counter64); +		break; + +	  case ASN_CLASS_CONTEXT | ASN_EXCEPT_NOSUCHOBJECT: +		binding->syntax = SNMP_SYNTAX_NOSUCHOBJECT; +		err = asn_get_null_raw(b, len); +		break; + +	  case ASN_CLASS_CONTEXT | ASN_EXCEPT_NOSUCHINSTANCE: +		binding->syntax = SNMP_SYNTAX_NOSUCHINSTANCE; +		err = asn_get_null_raw(b, len); +		break; + +	  case ASN_CLASS_CONTEXT | ASN_EXCEPT_ENDOFMIBVIEW: +		binding->syntax = SNMP_SYNTAX_ENDOFMIBVIEW; +		err = asn_get_null_raw(b, len); +		break; + +	  default: +		if ((err = asn_skip(b, len)) == ASN_ERR_OK) +			err = ASN_ERR_TAG; +		snmp_error("bad binding value type 0x%x", type); +		break; +	} + +	if (ASN_ERR_STOPPED(err)) { +		snmp_error("cannot parse binding value"); +		return (err); +	} + +	if (b->asn_len != 0) +		snmp_error("ignoring junk at end of binding"); + +	b->asn_len = trailer; + +	return (err); +} + +/* + * Parse the different PDUs contents. Any ASN error in the outer components + * are fatal. Only errors in variable values may be tolerated. If all + * components can be parsed it returns either ASN_ERR_OK or the first + * error that was found. + */ +enum asn_err +snmp_parse_pdus_hdr(struct asn_buf *b, struct snmp_pdu *pdu, asn_len_t *lenp) +{ +	if (pdu->type == SNMP_PDU_TRAP) { +		if (asn_get_objid(b, &pdu->enterprise) != ASN_ERR_OK) { +			snmp_error("cannot parse trap enterprise"); +			return (ASN_ERR_FAILED); +		} +		if (asn_get_ipaddress(b, pdu->agent_addr) != ASN_ERR_OK) { +			snmp_error("cannot parse trap agent address"); +			return (ASN_ERR_FAILED); +		} +		if (asn_get_integer(b, &pdu->generic_trap) != ASN_ERR_OK) { +			snmp_error("cannot parse 'generic-trap'"); +			return (ASN_ERR_FAILED); +		} +		if (asn_get_integer(b, &pdu->specific_trap) != ASN_ERR_OK) { +			snmp_error("cannot parse 'specific-trap'"); +			return (ASN_ERR_FAILED); +		} +		if (asn_get_timeticks(b, &pdu->time_stamp) != ASN_ERR_OK) { +			snmp_error("cannot parse trap 'time-stamp'"); +			return (ASN_ERR_FAILED); +		} +	} else { +		if (asn_get_integer(b, &pdu->request_id) != ASN_ERR_OK) { +			snmp_error("cannot parse 'request-id'"); +			return (ASN_ERR_FAILED); +		} +		if (asn_get_integer(b, &pdu->error_status) != ASN_ERR_OK) { +			snmp_error("cannot parse 'error_status'"); +			return (ASN_ERR_FAILED); +		} +		if (asn_get_integer(b, &pdu->error_index) != ASN_ERR_OK) { +			snmp_error("cannot parse 'error_index'"); +			return (ASN_ERR_FAILED); +		} +	} + +	if (asn_get_sequence(b, lenp) != ASN_ERR_OK) { +		snmp_error("cannot get varlist header"); +		return (ASN_ERR_FAILED); +	} + +	return (ASN_ERR_OK); +} + +static enum asn_err +parse_pdus(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip) +{ +	asn_len_t len, trailer; +	struct snmp_value *v; +	enum asn_err err, err1; + +	err = snmp_parse_pdus_hdr(b, pdu, &len); +	if (ASN_ERR_STOPPED(err)) +		return (err); + +	trailer = b->asn_len - len; + +	v = pdu->bindings; +	err = ASN_ERR_OK; +	while (b->asn_len != 0) { +		if (pdu->nbindings == SNMP_MAX_BINDINGS) { +			snmp_error("too many bindings (> %u) in PDU", +			    SNMP_MAX_BINDINGS); +			return (ASN_ERR_FAILED); +		} +		err1 = get_var_binding(b, v); +		if (ASN_ERR_STOPPED(err1)) +			return (ASN_ERR_FAILED); +		if (err1 != ASN_ERR_OK && err == ASN_ERR_OK) { +			err = err1; +			*ip = pdu->nbindings + 1; +		} +		pdu->nbindings++; +		v++; +	} + +	b->asn_len = trailer; + +	return (err); +} + +/* + * Parse the outer SEQUENCE value. ASN_ERR_TAG means 'bad version'. + */ +enum asn_err +snmp_parse_message_hdr(struct asn_buf *b, struct snmp_pdu *pdu, asn_len_t *lenp) +{ +	int32_t version; +	u_char type; +	u_int comm_len; + +	if (asn_get_integer(b, &version) != ASN_ERR_OK) { +		snmp_error("cannot decode version"); +		return (ASN_ERR_FAILED); +	} + +	if (version == 0) { +		pdu->version = SNMP_V1; +	} else if (version == 1) { +		pdu->version = SNMP_V2c; +	} else { +		pdu->version = SNMP_Verr; +		snmp_error("unsupported SNMP version"); +		return (ASN_ERR_TAG); +	} + +	comm_len = SNMP_COMMUNITY_MAXLEN; +	if (asn_get_octetstring(b, (u_char *)pdu->community, +	    &comm_len) != ASN_ERR_OK) { +		snmp_error("cannot decode community"); +		return (ASN_ERR_FAILED); +	} +	pdu->community[comm_len] = '\0'; + +	if (asn_get_header(b, &type, lenp) != ASN_ERR_OK) { +		snmp_error("cannot get pdu header"); +		return (ASN_ERR_FAILED); +	} +	if ((type & ~ASN_TYPE_MASK) != +	    (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT)) { +		snmp_error("bad pdu header tag"); +		return (ASN_ERR_FAILED); +	} +	pdu->type = type & ASN_TYPE_MASK; + +	switch (pdu->type) { + +	  case SNMP_PDU_GET: +	  case SNMP_PDU_GETNEXT: +	  case SNMP_PDU_RESPONSE: +	  case SNMP_PDU_SET: +		break; + +	  case SNMP_PDU_TRAP: +		if (pdu->version != SNMP_V1) { +			snmp_error("bad pdu type %u", pdu->type); +			return (ASN_ERR_FAILED); +		} +		break; + +	  case SNMP_PDU_GETBULK: +	  case SNMP_PDU_INFORM: +	  case SNMP_PDU_TRAP2: +	  case SNMP_PDU_REPORT: +		if (pdu->version == SNMP_V1) { +			snmp_error("bad pdu type %u", pdu->type); +			return (ASN_ERR_FAILED); +		} +		break; + +	  default: +		snmp_error("bad pdu type %u", pdu->type); +		return (ASN_ERR_FAILED); +	} + + +	if (*lenp > b->asn_len) { +		snmp_error("pdu length too long"); +		return (ASN_ERR_FAILED); +	} + +	return (ASN_ERR_OK); +} + +static enum asn_err +parse_message(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip) +{ +	enum asn_err err; +	asn_len_t len, trailer; + +	err = snmp_parse_message_hdr(b, pdu, &len); +	if (ASN_ERR_STOPPED(err)) +		return (err); + +	trailer = b->asn_len - len; +	b->asn_len = len; + +	err = parse_pdus(b, pdu, ip); +	if (ASN_ERR_STOPPED(err)) +		return (ASN_ERR_FAILED); + +	if (b->asn_len != 0) +		snmp_error("ignoring trailing junk after pdu"); + +	b->asn_len = trailer; + +	return (err); +} + +/* + * Decode the PDU except for the variable bindings itself. + * If decoding fails because of a bad binding, but the rest can be + * decoded, ip points to the index of the failed variable (errors + * OORANGE, BADLEN or BADVERS). + */ +enum snmp_code +snmp_pdu_decode(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip) +{ +	asn_len_t len; + +	memset(pdu, 0, sizeof(*pdu)); + +	if (asn_get_sequence(b, &len) != ASN_ERR_OK) { +		snmp_error("cannot decode pdu header"); +		return (SNMP_CODE_FAILED); +	} +	if (b->asn_len < len) { +		snmp_error("outer sequence value too short"); +		return (SNMP_CODE_FAILED); +	} +	if (b->asn_len != len) { +		snmp_error("ignoring trailing junk in message"); +		b->asn_len = len; +	} + +	switch (parse_message(b, pdu, ip)) { + +	  case ASN_ERR_OK: +		return (SNMP_CODE_OK); + +	  case ASN_ERR_FAILED: +	  case ASN_ERR_EOBUF: +		snmp_pdu_free(pdu); +		return (SNMP_CODE_FAILED); + +	  case ASN_ERR_BADLEN: +		return (SNMP_CODE_BADLEN); + +	  case ASN_ERR_RANGE: +		return (SNMP_CODE_OORANGE); + +	  case ASN_ERR_TAG: +		if (pdu->version == SNMP_Verr) +			return (SNMP_CODE_BADVERS); +		else +			return (SNMP_CODE_BADENC); +	} + +	return (SNMP_CODE_OK); +} + +/* + * Check whether what we have is the complete PDU by snooping at the + * enclosing structure header. This returns: + *   -1		if there are ASN.1 errors + *    0		if we need more data + *  > 0		the length of this PDU + */ +int +snmp_pdu_snoop(const struct asn_buf *b0) +{ +	u_int length; +	asn_len_t len; +	struct asn_buf b = *b0; + +	/* <0x10|0x20> <len> <data...> */ + +	if (b.asn_len == 0) +		return (0); +	if (b.asn_cptr[0] != (ASN_TYPE_SEQUENCE | ASN_TYPE_CONSTRUCTED)) { +		asn_error(&b, "bad sequence type %u", b.asn_cptr[0]); +		return (-1); +	} +	b.asn_len--; +	b.asn_cptr++; + +	if (b.asn_len == 0) +		return (0); + +	if (*b.asn_cptr & 0x80) { +		/* long length */ +		length = *b.asn_cptr++ & 0x7f; +		b.asn_len--; +		if (length == 0) { +			asn_error(&b, "indefinite length not supported"); +			return (-1); +		} +		if (length > ASN_MAXLENLEN) { +			asn_error(&b, "long length too long (%u)", length); +			return (-1); +		} +		if (length > b.asn_len) +			return (0); +		len = 0; +		while (length--) { +			len = (len << 8) | *b.asn_cptr++; +			b.asn_len--; +		} +	} else { +		len = *b.asn_cptr++; +		b.asn_len--; +	} + +	if (len > b.asn_len) +		return (0); + +	return (len + b.asn_cptr - b0->asn_cptr); +} + +/* + * Encode the SNMP PDU without the variable bindings field. + * We do this the rather uneffective way by + * moving things around and assuming that the length field will never + * use more than 2 bytes. + * We need a number of pointers to apply the fixes afterwards. + */ +enum snmp_code +snmp_pdu_encode_header(struct asn_buf *b, struct snmp_pdu *pdu) +{ +	enum asn_err err; + +	if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED), +	    &pdu->outer_ptr) != ASN_ERR_OK) +		return (SNMP_CODE_FAILED); + +	if (pdu->version == SNMP_V1) +		err = asn_put_integer(b, 0); +	else if (pdu->version == SNMP_V2c) +		err = asn_put_integer(b, 1); +	else +		return (SNMP_CODE_BADVERS); +	if (err != ASN_ERR_OK) +		return (SNMP_CODE_FAILED); + +	if (asn_put_octetstring(b, (u_char *)pdu->community, +	    strlen(pdu->community)) != ASN_ERR_OK) +		return (SNMP_CODE_FAILED); + +	if (asn_put_temp_header(b, (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT | +	    pdu->type), &pdu->pdu_ptr) != ASN_ERR_OK) +		return (SNMP_CODE_FAILED); + +	if (pdu->type == SNMP_PDU_TRAP) { +		if (pdu->version != SNMP_V1 || +		    asn_put_objid(b, &pdu->enterprise) != ASN_ERR_OK || +		    asn_put_ipaddress(b, pdu->agent_addr) != ASN_ERR_OK || +		    asn_put_integer(b, pdu->generic_trap) != ASN_ERR_OK || +		    asn_put_integer(b, pdu->specific_trap) != ASN_ERR_OK || +		    asn_put_timeticks(b, pdu->time_stamp) != ASN_ERR_OK) +			return (SNMP_CODE_FAILED); +	} else { +		if (pdu->version == SNMP_V1 && (pdu->type == SNMP_PDU_GETBULK || +		    pdu->type == SNMP_PDU_INFORM || +		    pdu->type == SNMP_PDU_TRAP2 || +		    pdu->type == SNMP_PDU_REPORT)) +			return (SNMP_CODE_FAILED); + +		if (asn_put_integer(b, pdu->request_id) != ASN_ERR_OK || +		    asn_put_integer(b, pdu->error_status) != ASN_ERR_OK || +		    asn_put_integer(b, pdu->error_index) != ASN_ERR_OK) +			return (SNMP_CODE_FAILED); +	} + +	if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED), +	    &pdu->vars_ptr) != ASN_ERR_OK) +		return (SNMP_CODE_FAILED); + +	return (SNMP_CODE_OK); +} + +enum snmp_code +snmp_fix_encoding(struct asn_buf *b, const struct snmp_pdu *pdu) +{ +	if (asn_commit_header(b, pdu->vars_ptr) != ASN_ERR_OK || +	    asn_commit_header(b, pdu->pdu_ptr) != ASN_ERR_OK || +	    asn_commit_header(b, pdu->outer_ptr) != ASN_ERR_OK) +		return (SNMP_CODE_FAILED); +	return (SNMP_CODE_OK); +} + +/* + * Encode a binding. Caller must ensure, that the syntax is ok for that version. + * Be sure not to cobber b, when something fails. + */ +enum asn_err +snmp_binding_encode(struct asn_buf *b, const struct snmp_value *binding) +{ +	u_char *ptr; +	enum asn_err err; +	struct asn_buf save = *b; + +	if ((err = asn_put_temp_header(b, (ASN_TYPE_SEQUENCE | +	    ASN_TYPE_CONSTRUCTED), &ptr)) != ASN_ERR_OK) { +		*b = save; +		return (err); +	} + +	if ((err = asn_put_objid(b, &binding->var)) != ASN_ERR_OK) { +		*b = save; +		return (err); +	} + +	switch (binding->syntax) { + +	  case SNMP_SYNTAX_NULL: +		err = asn_put_null(b); +		break; + +	  case SNMP_SYNTAX_INTEGER: +		err = asn_put_integer(b, binding->v.integer); +		break; + +	  case SNMP_SYNTAX_OCTETSTRING: +		err = asn_put_octetstring(b, binding->v.octetstring.octets, +		    binding->v.octetstring.len); +		break; + +	  case SNMP_SYNTAX_OID: +		err = asn_put_objid(b, &binding->v.oid); +		break; + +	  case SNMP_SYNTAX_IPADDRESS: +		err = asn_put_ipaddress(b, binding->v.ipaddress); +		break; + +	  case SNMP_SYNTAX_TIMETICKS: +		err = asn_put_uint32(b, ASN_APP_TIMETICKS, binding->v.uint32); +		break; + +	  case SNMP_SYNTAX_COUNTER: +		err = asn_put_uint32(b, ASN_APP_COUNTER, binding->v.uint32); +		break; + +	  case SNMP_SYNTAX_GAUGE: +		err = asn_put_uint32(b, ASN_APP_GAUGE, binding->v.uint32); +		break; + +	  case SNMP_SYNTAX_COUNTER64: +		err = asn_put_counter64(b, binding->v.counter64); +		break; + +	  case SNMP_SYNTAX_NOSUCHOBJECT: +		err = asn_put_exception(b, ASN_EXCEPT_NOSUCHOBJECT); +		break; + +	  case SNMP_SYNTAX_NOSUCHINSTANCE: +		err = asn_put_exception(b, ASN_EXCEPT_NOSUCHINSTANCE); +		break; + +	  case SNMP_SYNTAX_ENDOFMIBVIEW: +		err = asn_put_exception(b, ASN_EXCEPT_ENDOFMIBVIEW); +		break; +	} + +	if (err != ASN_ERR_OK) { +		*b = save; +		return (err); +	} + +	err = asn_commit_header(b, ptr); +	if (err != ASN_ERR_OK) { +		*b = save; +		return (err); +	} + +	return (ASN_ERR_OK); +} + +/* + * Encode an PDU. + */ +enum snmp_code +snmp_pdu_encode(struct snmp_pdu *pdu, struct asn_buf *resp_b) +{ +	u_int idx; +	enum snmp_code err; + +	if ((err = snmp_pdu_encode_header(resp_b, pdu)) != SNMP_CODE_OK) +		return (err); +	for (idx = 0; idx < pdu->nbindings; idx++) +		if ((err = snmp_binding_encode(resp_b, &pdu->bindings[idx])) +		    != ASN_ERR_OK) +			return (SNMP_CODE_FAILED); + +	return (snmp_fix_encoding(resp_b, pdu)); +} + +static void +dump_binding(const struct snmp_value *b) +{ +	u_int i; +	char buf[ASN_OIDSTRLEN]; + +	snmp_printf("%s=", asn_oid2str_r(&b->var, buf)); +	switch (b->syntax) { + +	  case SNMP_SYNTAX_NULL: +		snmp_printf("NULL"); +		break; + +	  case SNMP_SYNTAX_INTEGER: +		snmp_printf("INTEGER %d", b->v.integer); +		break; + +	  case SNMP_SYNTAX_OCTETSTRING: +		snmp_printf("OCTET STRING %lu:", b->v.octetstring.len); +		for (i = 0; i < b->v.octetstring.len; i++) +			snmp_printf(" %02x", b->v.octetstring.octets[i]); +		break; + +	  case SNMP_SYNTAX_OID: +		snmp_printf("OID %s", asn_oid2str_r(&b->v.oid, buf)); +		break; + +	  case SNMP_SYNTAX_IPADDRESS: +		snmp_printf("IPADDRESS %u.%u.%u.%u", b->v.ipaddress[0], +		    b->v.ipaddress[1], b->v.ipaddress[2], b->v.ipaddress[3]); +		break; + +	  case SNMP_SYNTAX_COUNTER: +		snmp_printf("COUNTER %u", b->v.uint32); +		break; + +	  case SNMP_SYNTAX_GAUGE: +		snmp_printf("GAUGE %u", b->v.uint32); +		break; + +	  case SNMP_SYNTAX_TIMETICKS: +		snmp_printf("TIMETICKS %u", b->v.uint32); +		break; + +	  case SNMP_SYNTAX_COUNTER64: +		snmp_printf("COUNTER64 %lld", b->v.counter64); +		break; + +	  case SNMP_SYNTAX_NOSUCHOBJECT: +		snmp_printf("NoSuchObject"); +		break; + +	  case SNMP_SYNTAX_NOSUCHINSTANCE: +		snmp_printf("NoSuchInstance"); +		break; + +	  case SNMP_SYNTAX_ENDOFMIBVIEW: +		snmp_printf("EndOfMibView"); +		break; + +	  default: +		snmp_printf("UNKNOWN SYNTAX %u", b->syntax); +		break; +	} +} + +static __inline void +dump_bindings(const struct snmp_pdu *pdu) +{ +	u_int i; + +	for (i = 0; i < pdu->nbindings; i++) { +		snmp_printf(" [%u]: ", i); +		dump_binding(&pdu->bindings[i]); +		snmp_printf("\n"); +	} +} + +static __inline void +dump_notrap(const struct snmp_pdu *pdu) +{ +	snmp_printf(" request_id=%d", pdu->request_id); +	snmp_printf(" error_status=%d", pdu->error_status); +	snmp_printf(" error_index=%d\n", pdu->error_index); +	dump_bindings(pdu); +} + +void +snmp_pdu_dump(const struct snmp_pdu *pdu) +{ +	char buf[ASN_OIDSTRLEN]; +	const char *vers; +	static const char *types[] = { +		[SNMP_PDU_GET] =	"GET", +		[SNMP_PDU_GETNEXT] =	"GETNEXT", +		[SNMP_PDU_RESPONSE] =	"RESPONSE", +		[SNMP_PDU_SET] =	"SET", +		[SNMP_PDU_TRAP] =	"TRAPv1", +		[SNMP_PDU_GETBULK] =	"GETBULK", +		[SNMP_PDU_INFORM] =	"INFORM", +		[SNMP_PDU_TRAP2] =	"TRAPv2", +		[SNMP_PDU_REPORT] =	"REPORT", +	}; + +	if (pdu->version == SNMP_V1) +		vers = "SNMPv1"; +	else if (pdu->version == SNMP_V2c) +		vers = "SNMPv2c"; +	else +		vers = "v?"; + +	switch (pdu->type) { +	  case SNMP_PDU_TRAP: +		snmp_printf("%s %s '%s'", types[pdu->type], vers, pdu->community); +		snmp_printf(" enterprise=%s", asn_oid2str_r(&pdu->enterprise, buf)); +		snmp_printf(" agent_addr=%u.%u.%u.%u", pdu->agent_addr[0], +		    pdu->agent_addr[1], pdu->agent_addr[2], pdu->agent_addr[3]); +		snmp_printf(" generic_trap=%d", pdu->generic_trap); +		snmp_printf(" specific_trap=%d", pdu->specific_trap); +		snmp_printf(" time-stamp=%u\n", pdu->time_stamp); +		dump_bindings(pdu); +		break; + +	  case SNMP_PDU_GET: +	  case SNMP_PDU_GETNEXT: +	  case SNMP_PDU_RESPONSE: +	  case SNMP_PDU_SET: +	  case SNMP_PDU_GETBULK: +	  case SNMP_PDU_INFORM: +	  case SNMP_PDU_TRAP2: +	  case SNMP_PDU_REPORT: +		snmp_printf("%s %s '%s'", types[pdu->type], vers, pdu->community); +		dump_notrap(pdu); +		break; + +	  default: +		snmp_printf("bad pdu type %u\n", pdu->type); +		break; +	} +} + +void +snmp_value_free(struct snmp_value *value) +{ +	if (value->syntax == SNMP_SYNTAX_OCTETSTRING) +		free(value->v.octetstring.octets); +	value->syntax = SNMP_SYNTAX_NULL; +} + +int +snmp_value_copy(struct snmp_value *to, const struct snmp_value *from) +{ +	to->var = from->var; +	to->syntax = from->syntax; + +	if (from->syntax == SNMP_SYNTAX_OCTETSTRING) { +		if ((to->v.octetstring.len = from->v.octetstring.len) == 0) +			to->v.octetstring.octets = NULL; +		else { +			to->v.octetstring.octets = malloc(to->v.octetstring.len); +			if (to->v.octetstring.octets == NULL) +				return (-1); +			(void)memcpy(to->v.octetstring.octets, +			    from->v.octetstring.octets, to->v.octetstring.len); +		} +	} else +		to->v = from->v; +	return (0); +} + +void +snmp_pdu_free(struct snmp_pdu *pdu) +{ +	u_int i; + +	for (i = 0; i < pdu->nbindings; i++) +		snmp_value_free(&pdu->bindings[i]); +} + +/* + * Parse an ASCII SNMP value into the binary form + */ +int +snmp_value_parse(const char *str, enum snmp_syntax syntax, union snmp_values *v) +{ +	char *end; + +	switch (syntax) { + +	  case SNMP_SYNTAX_NULL: +	  case SNMP_SYNTAX_NOSUCHOBJECT: +	  case SNMP_SYNTAX_NOSUCHINSTANCE: +	  case SNMP_SYNTAX_ENDOFMIBVIEW: +		if (*str != '\0') +			return (-1); +		return (0); + +	  case SNMP_SYNTAX_INTEGER: +		v->integer = strtoll(str, &end, 0); +		if (*end != '\0') +			return (-1); +		return (0); + +	  case SNMP_SYNTAX_OCTETSTRING: +	    { +		u_long len;	/* actual length of string */ +		u_long alloc;	/* allocate length of string */ +		u_char *octs;	/* actual octets */ +		u_long oct;	/* actual octet */ +		u_char *nocts;	/* to avoid memory leak */ +		u_char c;	/* actual character */ + +# define STUFFC(C)							\ +		if (alloc == len) {					\ +			alloc += 100;					\ +			if ((nocts = realloc(octs, alloc)) == NULL) {	\ +				free(octs);				\ +				return (-1);				\ +			}						\ +			octs = nocts;					\ +		}							\ +		octs[len++] = (C); + +		len = alloc = 0; +		octs = NULL; + +		if (*str == '"') { +			str++; +			while((c = *str++) != '\0') { +				if (c == '"') { +					if (*str != '\0') { +						free(octs); +						return (-1); +					} +					break; +				} +				if (c == '\\') { +					switch (c = *str++) { + +					  case '\\': +						break; +					  case 'a': +						c = '\a'; +						break; +					  case 'b': +						c = '\b'; +						break; +					  case 'f': +						c = '\f'; +						break; +					  case 'n': +						c = '\n'; +						break; +					  case 'r': +						c = '\r'; +						break; +					  case 't': +						c = '\t'; +						break; +					  case 'v': +						c = '\v'; +						break; +					  case 'x': +						c = 0; +						if (!isxdigit(*str)) +							break; +						if (isdigit(*str)) +							c = *str++ - '0'; +						else if (isupper(*str)) +							c = *str++ - 'A' + 10; +						else +							c = *str++ - 'a' + 10; +						if (!isxdigit(*str)) +							break; +						if (isdigit(*str)) +							c += *str++ - '0'; +						else if (isupper(*str)) +							c += *str++ - 'A' + 10; +						else +							c += *str++ - 'a' + 10; +						break; +					  case '0': case '1': case '2': +					  case '3': case '4': case '5': +					  case '6': case '7': +						c = *str++ - '0'; +						if (*str < '0' || *str > '7') +							break; +						c = *str++ - '0'; +						if (*str < '0' || *str > '7') +							break; +						c = *str++ - '0'; +						break; +					  default: +						break; +					} +				} +				STUFFC(c); +			} +		} else { +			while (*str != '\0') { +				oct = strtoul(str, &end, 16); +				str = end; +				if (oct > 0xff) { +					free(octs); +					return (-1); +				} +				STUFFC(oct); +				if (*str == ':') +					str++; +				else if(*str != '\0') { +					free(octs); +					return (-1); +				} +			} +		} +		v->octetstring.octets = octs; +		v->octetstring.len = len; +		return (0); +# undef STUFFC +	    } + +	  case SNMP_SYNTAX_OID: +	    { +		u_long subid; + +		v->oid.len = 0; + +		for (;;) { +			if (v->oid.len == ASN_MAXOIDLEN) +				return (-1); +			subid = strtoul(str, &end, 10); +			str = end; +			if (subid > ASN_MAXID) +				return (-1); +			v->oid.subs[v->oid.len++] = (asn_subid_t)subid; +			if (*str == '\0') +				break; +			if (*str != '.') +				return (-1); +			str++; +		} +		return (0); +	    } + +	  case SNMP_SYNTAX_IPADDRESS: +	    { +		struct hostent *he; +		u_long ip[4]; +		int n; + +		if (sscanf(str, "%lu.%lu.%lu.%lu%n", &ip[0], &ip[1], &ip[2], +		    &ip[3], &n) == 4 && (size_t)n == strlen(str) && +		    ip[0] <= 0xff && ip[1] <= 0xff && +		    ip[2] <= 0xff && ip[3] <= 0xff) { +			v->ipaddress[0] = (u_char)ip[0]; +			v->ipaddress[1] = (u_char)ip[1]; +			v->ipaddress[2] = (u_char)ip[2]; +			v->ipaddress[3] = (u_char)ip[3]; +			return (0); +		} + +		if ((he = gethostbyname(str)) == NULL) +			return (-1); +		if (he->h_addrtype != AF_INET) +			return (-1); + +		v->ipaddress[0] = he->h_addr[0]; +		v->ipaddress[1] = he->h_addr[1]; +		v->ipaddress[2] = he->h_addr[2]; +		v->ipaddress[3] = he->h_addr[3]; +		return (0); +	    } + +	  case SNMP_SYNTAX_COUNTER: +	  case SNMP_SYNTAX_GAUGE: +	  case SNMP_SYNTAX_TIMETICKS: +	    { +		uint64_t sub; + +		sub = strtoull(str, &end, 0); +		if (*end != '\0' || sub > 0xffffffff) +			return (-1); +		v->uint32 = (uint32_t)sub; +		return (0); +	    } + +	  case SNMP_SYNTAX_COUNTER64: +		v->counter64 = strtoull(str, &end, 0); +		if (*end != '\0') +			return (-1); +		return (0); +	} +	abort(); +} + +static void +snmp_error_func(const char *fmt, ...) +{ +	va_list ap; + +	va_start(ap, fmt); +	fprintf(stderr, "SNMP: "); +	vfprintf(stderr, fmt, ap); +	fprintf(stderr, "\n"); +	va_end(ap); +} + +static void +snmp_printf_func(const char *fmt, ...) +{ +	va_list ap; + +	va_start(ap, fmt); +	vfprintf(stderr, fmt, ap); +	va_end(ap); +} diff --git a/bsnmp/snmp.h b/bsnmp/snmp.h new file mode 100644 index 0000000..1ae0775 --- /dev/null +++ b/bsnmp/snmp.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2001-2003 + *	Fraunhofer Institute for Open Communication Systems (FhG Fokus). + *	All rights reserved. + * + * Author: Harti Brandt <harti@freebsd.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Begemot: bsnmp/lib/snmp.h,v 1.30 2004/08/06 08:46:54 brandt Exp $ + * + * Header file for SNMP functions. + */ +#ifndef snmp_h_ +#define snmp_h_ + +#include <sys/types.h> + +#define SNMP_COMMUNITY_MAXLEN	128 +#define SNMP_MAX_BINDINGS	100 + +enum snmp_syntax { +	SNMP_SYNTAX_NULL	= 0, +	SNMP_SYNTAX_INTEGER,		/* == INTEGER32 */ +	SNMP_SYNTAX_OCTETSTRING, +	SNMP_SYNTAX_OID, +	SNMP_SYNTAX_IPADDRESS, +	SNMP_SYNTAX_COUNTER, +	SNMP_SYNTAX_GAUGE,		/* == UNSIGNED32 */ +	SNMP_SYNTAX_TIMETICKS, + +	/* v2 additions */ +	SNMP_SYNTAX_COUNTER64, +	SNMP_SYNTAX_NOSUCHOBJECT,	/* exception */ +	SNMP_SYNTAX_NOSUCHINSTANCE,	/* exception */ +	SNMP_SYNTAX_ENDOFMIBVIEW,	/* exception */ +}; + +struct snmp_value { +	struct asn_oid		var; +	enum snmp_syntax	syntax; +	union snmp_values { +	  int32_t		integer;	/* also integer32 */ +	  struct { +	    u_int		len; +	    u_char		*octets; +	  }			octetstring; +	  struct asn_oid	oid; +	  u_char		ipaddress[4]; +	  uint32_t		uint32;		/* also gauge32, counter32, +						   unsigned32, timeticks */ +	  uint64_t		counter64; +	}			v; +}; + +enum snmp_version { +	SNMP_Verr = 0, +	SNMP_V1 = 1, +	SNMP_V2c, +}; + +struct snmp_pdu { +	char		community[SNMP_COMMUNITY_MAXLEN + 1]; +	enum snmp_version version; +	u_int		type; + +	/* trap only */ +	struct asn_oid	enterprise; +	u_char		agent_addr[4]; +	int32_t		generic_trap; +	int32_t		specific_trap; +	uint32_t	time_stamp; + +	/* others */ +	int32_t		request_id; +	int32_t		error_status; +	int32_t		error_index; + +	/* fixes for encoding */ +	u_char		*outer_ptr; +	u_char		*pdu_ptr; +	u_char		*vars_ptr; + +	struct snmp_value bindings[SNMP_MAX_BINDINGS]; +	u_int		nbindings; +}; +#define snmp_v1_pdu snmp_pdu + +#define SNMP_PDU_GET		0 +#define SNMP_PDU_GETNEXT	1 +#define SNMP_PDU_RESPONSE	2 +#define SNMP_PDU_SET		3 +#define SNMP_PDU_TRAP		4	/* v1 */ +#define SNMP_PDU_GETBULK	5	/* v2 */ +#define SNMP_PDU_INFORM		6	/* v2 */ +#define SNMP_PDU_TRAP2		7	/* v2 */ +#define SNMP_PDU_REPORT		8	/* v2 */ + +#define SNMP_ERR_NOERROR	0 +#define SNMP_ERR_TOOBIG		1 +#define SNMP_ERR_NOSUCHNAME	2	/* v1 */ +#define SNMP_ERR_BADVALUE	3	/* v1 */ +#define SNMP_ERR_READONLY	4	/* v1 */ +#define SNMP_ERR_GENERR		5 +#define SNMP_ERR_NO_ACCESS	6	/* v2 */ +#define SNMP_ERR_WRONG_TYPE	7	/* v2 */ +#define SNMP_ERR_WRONG_LENGTH	8	/* v2 */ +#define SNMP_ERR_WRONG_ENCODING	9	/* v2 */ +#define SNMP_ERR_WRONG_VALUE	10	/* v2 */ +#define SNMP_ERR_NO_CREATION	11	/* v2 */ +#define SNMP_ERR_INCONS_VALUE	12	/* v2 */ +#define SNMP_ERR_RES_UNAVAIL	13	/* v2 */ +#define SNMP_ERR_COMMIT_FAILED	14	/* v2 */ +#define SNMP_ERR_UNDO_FAILED	15	/* v2 */ +#define SNMP_ERR_AUTH_ERR	16	/* v2 */ +#define SNMP_ERR_NOT_WRITEABLE	17	/* v2 */ +#define SNMP_ERR_INCONS_NAME	18	/* v2 */ + +#define SNMP_TRAP_COLDSTART	0 +#define SNMP_TRAP_WARMSTART	1 +#define SNMP_TRAP_LINKDOWN	2 +#define SNMP_TRAP_LINKUP	3 +#define SNMP_TRAP_AUTHENTICATION_FAILURE	4 +#define SNMP_TRAP_EGP_NEIGHBOR_LOSS	5 +#define SNMP_TRAP_ENTERPRISE	6 + +enum snmp_code { +	SNMP_CODE_OK = 0, +	SNMP_CODE_FAILED, +	SNMP_CODE_BADVERS, +	SNMP_CODE_BADLEN, +	SNMP_CODE_BADENC, +	SNMP_CODE_OORANGE, +}; + +void snmp_value_free(struct snmp_value *); +int snmp_value_parse(const char *, enum snmp_syntax, union snmp_values *); +int snmp_value_copy(struct snmp_value *, const struct snmp_value *); + +void snmp_pdu_free(struct snmp_pdu *); +enum snmp_code snmp_pdu_decode(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *); +enum snmp_code snmp_pdu_encode(struct snmp_pdu *pdu, struct asn_buf *resp_b); + +int snmp_pdu_snoop(const struct asn_buf *); + +void snmp_pdu_dump(const struct snmp_pdu *pdu); + +extern void (*snmp_error)(const char *, ...); +extern void (*snmp_printf)(const char *, ...); + +#define TRUTH_MK(F) ((F) ? 1 : 2) +#define TRUTH_GET(T) (((T) == 1) ? 1 : 0) +#define TRUTH_OK(T)  ((T) == 1 || (T) == 2) + +#endif diff --git a/bsnmp/snmppriv.h b/bsnmp/snmppriv.h new file mode 100644 index 0000000..87ef60e --- /dev/null +++ b/bsnmp/snmppriv.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2001-2003 + *	Fraunhofer Institute for Open Communication Systems (FhG Fokus). + *	All rights reserved. + * + * Author: Harti Brandt <harti@freebsd.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Begemot: bsnmp/lib/snmppriv.h,v 1.9 2004/08/06 08:46:58 brandt Exp $ + * + * Private functions. + */ +#include <sys/cdefs.h> + +enum asn_err snmp_binding_encode(struct asn_buf *, const struct snmp_value *); +enum snmp_code snmp_pdu_encode_header(struct asn_buf *, struct snmp_pdu *); +enum snmp_code snmp_fix_encoding(struct asn_buf *, const struct snmp_pdu *); +enum asn_err snmp_parse_message_hdr(struct asn_buf *b, struct snmp_pdu *pdu, +    asn_len_t *lenp); +enum asn_err snmp_parse_pdus_hdr(struct asn_buf *b, struct snmp_pdu *pdu, +    asn_len_t *lenp); + +#define DEFAULT_HOST "localhost" +#define DEFAULT_PORT "snmp" +#define DEFAULT_LOCAL "/var/run/snmp.sock" | 
