160484Sobrien/*
260484Sobrien * Copyright (c) 2001-2003
360484Sobrien *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
460484Sobrien *	All rights reserved.
560484Sobrien *
660484Sobrien * Author: Harti Brandt <harti@freebsd.org>
760484Sobrien *
860484Sobrien * Copyright (c) 2010 The FreeBSD Foundation
960484Sobrien * All rights reserved.
1060484Sobrien *
1160484Sobrien * Portions of this software were developed by Shteryana Sotirova Shopova
1260484Sobrien * under sponsorship from the FreeBSD Foundation.
1360484Sobrien *
1460484Sobrien * Redistribution and use in source and binary forms, with or without
1560484Sobrien * modification, are permitted provided that the following conditions
1660484Sobrien * are met:
1760484Sobrien * 1. Redistributions of source code must retain the above copyright
1860484Sobrien *    notice, this list of conditions and the following disclaimer.
1960484Sobrien * 2. Redistributions in binary form must reproduce the above copyright
2060484Sobrien *    notice, this list of conditions and the following disclaimer in the
2160484Sobrien *    documentation and/or other materials provided with the distribution.
2260484Sobrien *
2360484Sobrien * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2460484Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2560484Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2660484Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
2760484Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2860484Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2960484Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3060484Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3160484Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3260484Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3360484Sobrien * SUCH DAMAGE.
3460484Sobrien *
3560484Sobrien * $Begemot: bsnmp/lib/snmp.c,v 1.40 2005/10/04 14:32:42 brandt_h Exp $
3660484Sobrien *
3760484Sobrien * SNMP
3860484Sobrien */
3960484Sobrien#include <sys/types.h>
4060484Sobrien#include <sys/socket.h>
4160484Sobrien#include <stdio.h>
4260484Sobrien#include <stdlib.h>
4360484Sobrien#include <stddef.h>
4460484Sobrien#include <stdarg.h>
4560484Sobrien#ifdef HAVE_STDINT_H
4660484Sobrien#include <stdint.h>
4760484Sobrien#elif defined(HAVE_INTTYPES_H)
4860484Sobrien#include <inttypes.h>
4960484Sobrien#endif
5060484Sobrien#include <string.h>
5160484Sobrien#include <ctype.h>
5260484Sobrien#include <netdb.h>
5360484Sobrien#include <errno.h>
5460484Sobrien
5560484Sobrien#include "asn1.h"
5660484Sobrien#include "snmp.h"
5760484Sobrien#include "snmppriv.h"
5860484Sobrien
5960484Sobrienstatic void snmp_error_func(const char *, ...);
6060484Sobrienstatic void snmp_printf_func(const char *, ...);
6160484Sobrien
6260484Sobrienvoid (*snmp_error)(const char *, ...) = snmp_error_func;
6360484Sobrienvoid (*snmp_printf)(const char *, ...) = snmp_printf_func;
6460484Sobrien
6560484Sobrien/*
6660484Sobrien * Get the next variable binding from the list.
6760484Sobrien * ASN errors on the sequence or the OID are always fatal.
6860484Sobrien */
6960484Sobrienstatic enum asn_err
7060484Sobrienget_var_binding(struct asn_buf *b, struct snmp_value *binding)
7160484Sobrien{
7260484Sobrien	u_char type;
7360484Sobrien	asn_len_t len, trailer;
7460484Sobrien	enum asn_err err;
7560484Sobrien
7660484Sobrien	if (asn_get_sequence(b, &len) != ASN_ERR_OK) {
7760484Sobrien		snmp_error("cannot parse varbind header");
7860484Sobrien		return (ASN_ERR_FAILED);
7960484Sobrien	}
8060484Sobrien
8160484Sobrien	/* temporary truncate the length so that the parser does not
8260484Sobrien	 * eat up bytes behind the sequence in the case the encoding is
8360484Sobrien	 * wrong of inner elements. */
8460484Sobrien	trailer = b->asn_len - len;
8560484Sobrien	b->asn_len = len;
8660484Sobrien
8760484Sobrien	if (asn_get_objid(b, &binding->var) != ASN_ERR_OK) {
8860484Sobrien		snmp_error("cannot parse binding objid");
8960484Sobrien		return (ASN_ERR_FAILED);
9060484Sobrien	}
9160484Sobrien	if (asn_get_header(b, &type, &len) != ASN_ERR_OK) {
9260484Sobrien		snmp_error("cannot parse binding value header");
9360484Sobrien		return (ASN_ERR_FAILED);
9460484Sobrien	}
9560484Sobrien
9660484Sobrien	switch (type) {
9760484Sobrien
9860484Sobrien	  case ASN_TYPE_NULL:
9960484Sobrien		binding->syntax = SNMP_SYNTAX_NULL;
10060484Sobrien		err = asn_get_null_raw(b, len);
10160484Sobrien		break;
10260484Sobrien
10360484Sobrien	  case ASN_TYPE_INTEGER:
10460484Sobrien		binding->syntax = SNMP_SYNTAX_INTEGER;
10560484Sobrien		err = asn_get_integer_raw(b, len, &binding->v.integer);
10660484Sobrien		break;
10760484Sobrien
10860484Sobrien	  case ASN_TYPE_OCTETSTRING:
10960484Sobrien		binding->syntax = SNMP_SYNTAX_OCTETSTRING;
11060484Sobrien		binding->v.octetstring.octets = malloc(len);
11160484Sobrien		if (binding->v.octetstring.octets == NULL) {
11260484Sobrien			snmp_error("%s", strerror(errno));
11360484Sobrien			return (ASN_ERR_FAILED);
11460484Sobrien		}
11560484Sobrien		binding->v.octetstring.len = len;
11660484Sobrien		err = asn_get_octetstring_raw(b, len,
11760484Sobrien		    binding->v.octetstring.octets,
11860484Sobrien		    &binding->v.octetstring.len);
11960484Sobrien		if (ASN_ERR_STOPPED(err)) {
12060484Sobrien			free(binding->v.octetstring.octets);
12160484Sobrien			binding->v.octetstring.octets = NULL;
12260484Sobrien		}
12360484Sobrien		break;
12460484Sobrien
12560484Sobrien	  case ASN_TYPE_OBJID:
12660484Sobrien		binding->syntax = SNMP_SYNTAX_OID;
12760484Sobrien		err = asn_get_objid_raw(b, len, &binding->v.oid);
12860484Sobrien		break;
12960484Sobrien
13060484Sobrien	  case ASN_CLASS_APPLICATION|ASN_APP_IPADDRESS:
13160484Sobrien		binding->syntax = SNMP_SYNTAX_IPADDRESS;
13260484Sobrien		err = asn_get_ipaddress_raw(b, len, binding->v.ipaddress);
13360484Sobrien		break;
13460484Sobrien
13560484Sobrien	  case ASN_CLASS_APPLICATION|ASN_APP_TIMETICKS:
13660484Sobrien		binding->syntax = SNMP_SYNTAX_TIMETICKS;
13760484Sobrien		err = asn_get_uint32_raw(b, len, &binding->v.uint32);
13860484Sobrien		break;
13960484Sobrien
14060484Sobrien	  case ASN_CLASS_APPLICATION|ASN_APP_COUNTER:
14160484Sobrien		binding->syntax = SNMP_SYNTAX_COUNTER;
14260484Sobrien		err = asn_get_uint32_raw(b, len, &binding->v.uint32);
14360484Sobrien		break;
14460484Sobrien
14560484Sobrien	  case ASN_CLASS_APPLICATION|ASN_APP_GAUGE:
14660484Sobrien		binding->syntax = SNMP_SYNTAX_GAUGE;
14760484Sobrien		err = asn_get_uint32_raw(b, len, &binding->v.uint32);
14860484Sobrien		break;
14960484Sobrien
15060484Sobrien	  case ASN_CLASS_APPLICATION|ASN_APP_COUNTER64:
15160484Sobrien		binding->syntax = SNMP_SYNTAX_COUNTER64;
15260484Sobrien		err = asn_get_counter64_raw(b, len, &binding->v.counter64);
15360484Sobrien		break;
15460484Sobrien
15560484Sobrien	  case ASN_CLASS_CONTEXT | ASN_EXCEPT_NOSUCHOBJECT:
15660484Sobrien		binding->syntax = SNMP_SYNTAX_NOSUCHOBJECT;
15760484Sobrien		err = asn_get_null_raw(b, len);
15860484Sobrien		break;
15960484Sobrien
16060484Sobrien	  case ASN_CLASS_CONTEXT | ASN_EXCEPT_NOSUCHINSTANCE:
16160484Sobrien		binding->syntax = SNMP_SYNTAX_NOSUCHINSTANCE;
16260484Sobrien		err = asn_get_null_raw(b, len);
16360484Sobrien		break;
16460484Sobrien
16560484Sobrien	  case ASN_CLASS_CONTEXT | ASN_EXCEPT_ENDOFMIBVIEW:
16660484Sobrien		binding->syntax = SNMP_SYNTAX_ENDOFMIBVIEW;
16760484Sobrien		err = asn_get_null_raw(b, len);
16860484Sobrien		break;
16960484Sobrien
17060484Sobrien	  default:
17160484Sobrien		if ((err = asn_skip(b, len)) == ASN_ERR_OK)
17260484Sobrien			err = ASN_ERR_TAG;
17360484Sobrien		snmp_error("bad binding value type 0x%x", type);
17460484Sobrien		break;
17560484Sobrien	}
17660484Sobrien
17760484Sobrien	if (ASN_ERR_STOPPED(err)) {
17860484Sobrien		snmp_error("cannot parse binding value");
17960484Sobrien		return (err);
18060484Sobrien	}
18189857Sobrien
18260484Sobrien	if (b->asn_len != 0)
18360484Sobrien		snmp_error("ignoring junk at end of binding");
18460484Sobrien
18560484Sobrien	b->asn_len = trailer;
18660484Sobrien
18760484Sobrien	return (err);
18860484Sobrien}
18960484Sobrien
19060484Sobrien/*
19160484Sobrien * Parse the different PDUs contents. Any ASN error in the outer components
19260484Sobrien * are fatal. Only errors in variable values may be tolerated. If all
19360484Sobrien * components can be parsed it returns either ASN_ERR_OK or the first
19460484Sobrien * error that was found.
19560484Sobrien */
19660484Sobrienenum asn_err
19760484Sobriensnmp_parse_pdus_hdr(struct asn_buf *b, struct snmp_pdu *pdu, asn_len_t *lenp)
19860484Sobrien{
19960484Sobrien	if (pdu->type == SNMP_PDU_TRAP) {
20060484Sobrien		if (asn_get_objid(b, &pdu->enterprise) != ASN_ERR_OK) {
20160484Sobrien			snmp_error("cannot parse trap enterprise");
20260484Sobrien			return (ASN_ERR_FAILED);
20360484Sobrien		}
20460484Sobrien		if (asn_get_ipaddress(b, pdu->agent_addr) != ASN_ERR_OK) {
20560484Sobrien			snmp_error("cannot parse trap agent address");
20660484Sobrien			return (ASN_ERR_FAILED);
20760484Sobrien		}
20860484Sobrien		if (asn_get_integer(b, &pdu->generic_trap) != ASN_ERR_OK) {
20960484Sobrien			snmp_error("cannot parse 'generic-trap'");
21060484Sobrien			return (ASN_ERR_FAILED);
21160484Sobrien		}
21260484Sobrien		if (asn_get_integer(b, &pdu->specific_trap) != ASN_ERR_OK) {
21360484Sobrien			snmp_error("cannot parse 'specific-trap'");
21460484Sobrien			return (ASN_ERR_FAILED);
21560484Sobrien		}
21660484Sobrien		if (asn_get_timeticks(b, &pdu->time_stamp) != ASN_ERR_OK) {
21760484Sobrien			snmp_error("cannot parse trap 'time-stamp'");
21860484Sobrien			return (ASN_ERR_FAILED);
21960484Sobrien		}
22060484Sobrien	} else {
22160484Sobrien		if (asn_get_integer(b, &pdu->request_id) != ASN_ERR_OK) {
22260484Sobrien			snmp_error("cannot parse 'request-id'");
22360484Sobrien			return (ASN_ERR_FAILED);
22460484Sobrien		}
22560484Sobrien		if (asn_get_integer(b, &pdu->error_status) != ASN_ERR_OK) {
22660484Sobrien			snmp_error("cannot parse 'error_status'");
22760484Sobrien			return (ASN_ERR_FAILED);
22860484Sobrien		}
22960484Sobrien		if (asn_get_integer(b, &pdu->error_index) != ASN_ERR_OK) {
23060484Sobrien			snmp_error("cannot parse 'error_index'");
23160484Sobrien			return (ASN_ERR_FAILED);
23260484Sobrien		}
23360484Sobrien	}
23460484Sobrien
23560484Sobrien	if (asn_get_sequence(b, lenp) != ASN_ERR_OK) {
23660484Sobrien		snmp_error("cannot get varlist header");
23760484Sobrien		return (ASN_ERR_FAILED);
23860484Sobrien	}
23960484Sobrien
24060484Sobrien	return (ASN_ERR_OK);
24160484Sobrien}
24260484Sobrien
24360484Sobrienstatic enum asn_err
24460484Sobrienparse_pdus(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip)
24560484Sobrien{
24660484Sobrien	asn_len_t len, trailer;
24760484Sobrien	struct snmp_value *v;
24860484Sobrien	enum asn_err err, err1;
24960484Sobrien
25060484Sobrien	err = snmp_parse_pdus_hdr(b, pdu, &len);
25160484Sobrien	if (ASN_ERR_STOPPED(err))
25260484Sobrien		return (err);
25360484Sobrien
25460484Sobrien	trailer = b->asn_len - len;
25560484Sobrien
25660484Sobrien	v = pdu->bindings;
25760484Sobrien	err = ASN_ERR_OK;
25860484Sobrien	while (b->asn_len != 0) {
25960484Sobrien		if (pdu->nbindings == SNMP_MAX_BINDINGS) {
26060484Sobrien			snmp_error("too many bindings (> %u) in PDU",
26160484Sobrien			    SNMP_MAX_BINDINGS);
26260484Sobrien			return (ASN_ERR_FAILED);
26360484Sobrien		}
26460484Sobrien		err1 = get_var_binding(b, v);
26560484Sobrien		if (ASN_ERR_STOPPED(err1))
26660484Sobrien			return (ASN_ERR_FAILED);
26760484Sobrien		if (err1 != ASN_ERR_OK && err == ASN_ERR_OK) {
26860484Sobrien			err = err1;
26960484Sobrien			*ip = pdu->nbindings + 1;
27060484Sobrien		}
27160484Sobrien		pdu->nbindings++;
27260484Sobrien		v++;
27360484Sobrien	}
27460484Sobrien
27560484Sobrien	b->asn_len = trailer;
27660484Sobrien
27760484Sobrien	return (err);
27860484Sobrien}
27960484Sobrien
28060484Sobrien
28160484Sobrienstatic enum asn_err
28260484Sobrienparse_secparams(struct asn_buf *b, struct snmp_pdu *pdu)
28360484Sobrien{
28460484Sobrien	asn_len_t octs_len;
28560484Sobrien	u_char buf[256]; /* XXX: calc max possible size here */
28660484Sobrien	struct asn_buf tb;
28760484Sobrien
28860484Sobrien	memset(buf, 0, 256);
28960484Sobrien	tb.asn_ptr = buf;
29060484Sobrien	tb.asn_len = 256;
29160484Sobrien
29260484Sobrien	if (asn_get_octetstring(b, buf, &tb.asn_len) != ASN_ERR_OK) {
29360484Sobrien		snmp_error("cannot parse usm header");
29460484Sobrien		return (ASN_ERR_FAILED);
29560484Sobrien	}
29660484Sobrien
29760484Sobrien	if (asn_get_sequence(&tb, &octs_len) != ASN_ERR_OK) {
29860484Sobrien		snmp_error("cannot decode usm header");
29960484Sobrien		return (ASN_ERR_FAILED);
30060484Sobrien	}
30160484Sobrien
30260484Sobrien	octs_len = SNMP_ENGINE_ID_SIZ;
30360484Sobrien	if (asn_get_octetstring(&tb, (u_char *)&pdu->engine.engine_id,
30460484Sobrien	    &octs_len) != ASN_ERR_OK) {
30560484Sobrien		snmp_error("cannot decode msg engine id");
30660484Sobrien		return (ASN_ERR_FAILED);
30760484Sobrien	}
30860484Sobrien	pdu->engine.engine_len = octs_len;
30960484Sobrien
31060484Sobrien	if (asn_get_integer(&tb, &pdu->engine.engine_boots) != ASN_ERR_OK) {
31160484Sobrien		snmp_error("cannot decode msg engine boots");
31260484Sobrien		return (ASN_ERR_FAILED);
31360484Sobrien	}
31460484Sobrien
31560484Sobrien	if (asn_get_integer(&tb, &pdu->engine.engine_time) != ASN_ERR_OK) {
31660484Sobrien		snmp_error("cannot decode msg engine time");
31760484Sobrien		return (ASN_ERR_FAILED);
31860484Sobrien	}
31960484Sobrien
32060484Sobrien	octs_len = SNMP_ADM_STR32_SIZ - 1;
32160484Sobrien	if (asn_get_octetstring(&tb, (u_char *)&pdu->user.sec_name, &octs_len)
32260484Sobrien	    != ASN_ERR_OK) {
32360484Sobrien		snmp_error("cannot decode msg user name");
32460484Sobrien		return (ASN_ERR_FAILED);
32560484Sobrien	}
32660484Sobrien	pdu->user.sec_name[octs_len] = '\0';
32760484Sobrien
32860484Sobrien	octs_len = sizeof(pdu->msg_digest);
32960484Sobrien	if (asn_get_octetstring(&tb, (u_char *)&pdu->msg_digest, &octs_len) !=
33060484Sobrien	    ASN_ERR_OK || ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0 &&
33160484Sobrien	    octs_len != sizeof(pdu->msg_digest))) {
33260484Sobrien		snmp_error("cannot decode msg authentication param");
33360484Sobrien		return (ASN_ERR_FAILED);
33460484Sobrien	}
33560484Sobrien
33660484Sobrien	octs_len = sizeof(pdu->msg_salt);
33760484Sobrien	if (asn_get_octetstring(&tb, (u_char *)&pdu->msg_salt, &octs_len) !=
33860484Sobrien	    ASN_ERR_OK ||((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0 &&
33960484Sobrien	    octs_len != sizeof(pdu->msg_salt))) {
34060484Sobrien		snmp_error("cannot decode msg authentication param");
34160484Sobrien		return (ASN_ERR_FAILED);
34260484Sobrien	}
34360484Sobrien
34460484Sobrien	if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) {
34560484Sobrien		pdu->digest_ptr = b->asn_ptr - SNMP_USM_AUTH_SIZE;
34660484Sobrien		pdu->digest_ptr -= octs_len + ASN_MAXLENLEN;
34760484Sobrien	}
34860484Sobrien
34960484Sobrien	return (ASN_ERR_OK);
35060484Sobrien}
35160484Sobrien
35260484Sobrienstatic enum snmp_code
35360484Sobrienpdu_encode_secparams(struct asn_buf *b, struct snmp_pdu *pdu)
35460484Sobrien{
35560484Sobrien	u_char buf[256], *sptr;
35660484Sobrien        struct asn_buf tb;
35760484Sobrien        size_t auth_off, moved = 0;
35860484Sobrien
35960484Sobrien	auth_off = 0;
36060484Sobrien	memset(buf, 0, 256);
36160484Sobrien	tb.asn_ptr = buf;
36260484Sobrien	tb.asn_len = 256;
36360484Sobrien
36460484Sobrien	if (asn_put_temp_header(&tb, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED),
36560484Sobrien	    &sptr) != ASN_ERR_OK)
36660484Sobrien		return (SNMP_CODE_FAILED);
36760484Sobrien
36860484Sobrien	if (asn_put_octetstring(&tb, (u_char *)pdu->engine.engine_id,
36960484Sobrien	    pdu->engine.engine_len) != ASN_ERR_OK)
37060484Sobrien		return (SNMP_CODE_FAILED);
37160484Sobrien
37260484Sobrien	if (asn_put_integer(&tb, pdu->engine.engine_boots) != ASN_ERR_OK)
37360484Sobrien		return (SNMP_CODE_FAILED);
37460484Sobrien
37560484Sobrien	if (asn_put_integer(&tb, pdu->engine.engine_time) != ASN_ERR_OK)
37660484Sobrien		return (SNMP_CODE_FAILED);
37760484Sobrien
37860484Sobrien	if (asn_put_octetstring(&tb, (u_char *)pdu->user.sec_name,
37960484Sobrien	    strlen(pdu->user.sec_name)) != ASN_ERR_OK)
38060484Sobrien		return (SNMP_CODE_FAILED);
38160484Sobrien
38260484Sobrien	if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) {
38360484Sobrien		auth_off = sizeof(buf) - tb.asn_len + ASN_MAXLENLEN;
38460484Sobrien		if (asn_put_octetstring(&tb, (u_char *)pdu->msg_digest,
38560484Sobrien		    sizeof(pdu->msg_digest)) != ASN_ERR_OK)
38660484Sobrien			return (SNMP_CODE_FAILED);
38760484Sobrien	} else {
38860484Sobrien		if (asn_put_octetstring(&tb, (u_char *)pdu->msg_digest, 0)
38960484Sobrien		    != ASN_ERR_OK)
39060484Sobrien			return (SNMP_CODE_FAILED);
39160484Sobrien	}
39260484Sobrien
39360484Sobrien	if ((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0) {
39460484Sobrien		if (asn_put_octetstring(&tb, (u_char *)pdu->msg_salt,
39560484Sobrien		    sizeof(pdu->msg_salt)) != ASN_ERR_OK)
39660484Sobrien			return (SNMP_CODE_FAILED);
39760484Sobrien	} else {
39860484Sobrien		if (asn_put_octetstring(&tb, (u_char *)pdu->msg_salt, 0)
39960484Sobrien		    != ASN_ERR_OK)
40060484Sobrien			return (SNMP_CODE_FAILED);
40160484Sobrien	}
40260484Sobrien
40360484Sobrien	if (asn_commit_header(&tb, sptr, &moved) != ASN_ERR_OK)
40460484Sobrien		return (SNMP_CODE_FAILED);
40560484Sobrien
40660484Sobrien	if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0)
40760484Sobrien		pdu->digest_ptr = b->asn_ptr + auth_off - moved;
40860484Sobrien
40960484Sobrien	if (asn_put_octetstring(b, buf, sizeof(buf) - tb.asn_len) != ASN_ERR_OK)
41089857Sobrien		return (SNMP_CODE_FAILED);
41160484Sobrien	pdu->digest_ptr += ASN_MAXLENLEN;
41260484Sobrien
41360484Sobrien	if ((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0 && asn_put_temp_header(b,
41460484Sobrien	    ASN_TYPE_OCTETSTRING, &pdu->encrypted_ptr) != ASN_ERR_OK)
41560484Sobrien			return (SNMP_CODE_FAILED);
41660484Sobrien
41760484Sobrien	return (SNMP_CODE_OK);
41860484Sobrien}
41960484Sobrien
42060484Sobrien/*
42160484Sobrien * Decode the PDU except for the variable bindings itself.
42260484Sobrien * If decoding fails because of a bad binding, but the rest can be
42360484Sobrien * decoded, ip points to the index of the failed variable (errors
42460484Sobrien * OORANGE, BADLEN or BADVERS).
42560484Sobrien */
42660484Sobrienenum snmp_code
42760484Sobriensnmp_pdu_decode(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip)
42860484Sobrien{
42960484Sobrien	enum snmp_code code;
43060484Sobrien
43160484Sobrien	if ((code = snmp_pdu_decode_header(b, pdu)) != SNMP_CODE_OK)
43260484Sobrien		return (code);
43360484Sobrien
43460484Sobrien	if (pdu->version == SNMP_V3) {
43560484Sobrien		if (pdu->security_model != SNMP_SECMODEL_USM)
43660484Sobrien			return (SNMP_CODE_FAILED);
43760484Sobrien		if ((code = snmp_pdu_decode_secmode(b, pdu)) != SNMP_CODE_OK)
43860484Sobrien			return (code);
43960484Sobrien	}
44060484Sobrien
44160484Sobrien	code = snmp_pdu_decode_scoped(b, pdu, ip);
44260484Sobrien
44360484Sobrien	switch (code) {
44460484Sobrien	  case SNMP_CODE_FAILED:
44560484Sobrien		snmp_pdu_free(pdu);
44660484Sobrien		break;
44760484Sobrien
44860484Sobrien	  case SNMP_CODE_BADENC:
44960484Sobrien		if (pdu->version == SNMP_Verr)
45060484Sobrien			return (SNMP_CODE_BADVERS);
45160484Sobrien
45260484Sobrien	  default:
45360484Sobrien		break;
45460484Sobrien	}
45560484Sobrien
45660484Sobrien	return (code);
45760484Sobrien}
45860484Sobrien
45960484Sobrienenum snmp_code
46060484Sobriensnmp_pdu_decode_header(struct asn_buf *b, struct snmp_pdu *pdu)
46160484Sobrien{
46260484Sobrien	int32_t version;
46360484Sobrien	u_int octs_len;
46460484Sobrien	asn_len_t len;
46560484Sobrien
46660484Sobrien	pdu->outer_ptr = b->asn_ptr;
46760484Sobrien	pdu->outer_len = b->asn_len;
46860484Sobrien
46960484Sobrien	if (asn_get_sequence(b, &len) != ASN_ERR_OK) {
47060484Sobrien		snmp_error("cannot decode pdu header");
47160484Sobrien		return (SNMP_CODE_FAILED);
47260484Sobrien	}
47360484Sobrien	if (b->asn_len < len) {
47460484Sobrien		snmp_error("outer sequence value too short");
47560484Sobrien		return (SNMP_CODE_FAILED);
47660484Sobrien	}
47760484Sobrien	if (b->asn_len != len) {
47860484Sobrien		snmp_error("ignoring trailing junk in message");
47960484Sobrien		b->asn_len = len;
48060484Sobrien	}
48160484Sobrien
48260484Sobrien	if (asn_get_integer(b, &version) != ASN_ERR_OK) {
48360484Sobrien		snmp_error("cannot decode version");
48460484Sobrien		return (SNMP_CODE_FAILED);
48560484Sobrien	}
48660484Sobrien
48760484Sobrien	if (version == 0)
48860484Sobrien		pdu->version = SNMP_V1;
48960484Sobrien	else if (version == 1)
49060484Sobrien		pdu->version = SNMP_V2c;
49160484Sobrien	else if (version == 3)
49260484Sobrien		pdu->version = SNMP_V3;
49360484Sobrien	else {
49460484Sobrien		pdu->version = SNMP_Verr;
49560484Sobrien		snmp_error("unsupported SNMP version");
49660484Sobrien		return (SNMP_CODE_BADENC);
49760484Sobrien	}
49860484Sobrien
49960484Sobrien	if (pdu->version == SNMP_V3) {
50060484Sobrien		if (asn_get_sequence(b, &len) != ASN_ERR_OK) {
50160484Sobrien			snmp_error("cannot decode pdu global data header");
50260484Sobrien			return (SNMP_CODE_FAILED);
50360484Sobrien		}
50460484Sobrien
50560484Sobrien		if (asn_get_integer(b, &pdu->identifier) != ASN_ERR_OK) {
50660484Sobrien			snmp_error("cannot decode msg indetifier");
50760484Sobrien			return (SNMP_CODE_FAILED);
50860484Sobrien		}
50960484Sobrien
51060484Sobrien		if (asn_get_integer(b, &pdu->engine.max_msg_size)
51160484Sobrien		    != ASN_ERR_OK) {
51260484Sobrien			snmp_error("cannot decode msg size");
51360484Sobrien			return (SNMP_CODE_FAILED);
51460484Sobrien		}
51560484Sobrien
51660484Sobrien		octs_len = 1;
51760484Sobrien		if (asn_get_octetstring(b, (u_char *)&pdu->flags,
51860484Sobrien		    &octs_len) != ASN_ERR_OK) {
51960484Sobrien			snmp_error("cannot decode msg flags");
52060484Sobrien			return (SNMP_CODE_FAILED);
52160484Sobrien		}
52260484Sobrien
52360484Sobrien		if (asn_get_integer(b, &pdu->security_model) != ASN_ERR_OK) {
52460484Sobrien			snmp_error("cannot decode msg size");
52560484Sobrien			return (SNMP_CODE_FAILED);
52660484Sobrien		}
52760484Sobrien
52860484Sobrien		if (pdu->security_model != SNMP_SECMODEL_USM)
52960484Sobrien			return (SNMP_CODE_FAILED);
53060484Sobrien
53160484Sobrien		if (parse_secparams(b, pdu) != ASN_ERR_OK)
53260484Sobrien			return (SNMP_CODE_FAILED);
53360484Sobrien	} else {
53460484Sobrien		octs_len = SNMP_COMMUNITY_MAXLEN;
53560484Sobrien		if (asn_get_octetstring(b, (u_char *)pdu->community,
53660484Sobrien		    &octs_len) != ASN_ERR_OK) {
53760484Sobrien			snmp_error("cannot decode community");
53860484Sobrien			return (SNMP_CODE_FAILED);
53960484Sobrien		}
54060484Sobrien		pdu->community[octs_len] = '\0';
54160484Sobrien	}
54260484Sobrien
54360484Sobrien	return (SNMP_CODE_OK);
54460484Sobrien}
54560484Sobrien
54660484Sobrienenum snmp_code
54760484Sobriensnmp_pdu_decode_scoped(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip)
54860484Sobrien{
54960484Sobrien	u_char type;
55060484Sobrien	asn_len_t len, trailer;
55160484Sobrien	enum asn_err err;
55260484Sobrien
55360484Sobrien	if (pdu->version == SNMP_V3) {
55460484Sobrien		if (asn_get_sequence(b, &len) != ASN_ERR_OK) {
55560484Sobrien			snmp_error("cannot decode scoped pdu header");
55660484Sobrien			return (SNMP_CODE_FAILED);
55760484Sobrien		}
55860484Sobrien
55960484Sobrien		len = SNMP_ENGINE_ID_SIZ;
56060484Sobrien		if (asn_get_octetstring(b, (u_char *)&pdu->context_engine,
56160484Sobrien		    &len) != ASN_ERR_OK) {
56260484Sobrien			snmp_error("cannot decode msg context engine");
56360484Sobrien			return (SNMP_CODE_FAILED);
56460484Sobrien		}
56560484Sobrien		pdu->context_engine_len = len;
56660484Sobrien
56760484Sobrien		len = SNMP_CONTEXT_NAME_SIZ;
56860484Sobrien		if (asn_get_octetstring(b, (u_char *)&pdu->context_name,
56960484Sobrien		    &len) != ASN_ERR_OK) {
57060484Sobrien			snmp_error("cannot decode msg context name");
57160484Sobrien			return (SNMP_CODE_FAILED);
57260484Sobrien		}
57360484Sobrien		pdu->context_name[len] = '\0';
57460484Sobrien	}
57560484Sobrien
57660484Sobrien	if (asn_get_header(b, &type, &len) != ASN_ERR_OK) {
57760484Sobrien		snmp_error("cannot get pdu header");
57860484Sobrien		return (SNMP_CODE_FAILED);
57960484Sobrien	}
58060484Sobrien	if ((type & ~ASN_TYPE_MASK) !=
58160484Sobrien	    (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT)) {
58260484Sobrien		snmp_error("bad pdu header tag");
58360484Sobrien		return (SNMP_CODE_FAILED);
58460484Sobrien	}
58560484Sobrien	pdu->type = type & ASN_TYPE_MASK;
58660484Sobrien
58760484Sobrien	switch (pdu->type) {
58860484Sobrien
58960484Sobrien	  case SNMP_PDU_GET:
59060484Sobrien	  case SNMP_PDU_GETNEXT:
59160484Sobrien	  case SNMP_PDU_RESPONSE:
59260484Sobrien	  case SNMP_PDU_SET:
59360484Sobrien		break;
59460484Sobrien
59560484Sobrien	  case SNMP_PDU_TRAP:
59660484Sobrien		if (pdu->version != SNMP_V1) {
59760484Sobrien			snmp_error("bad pdu type %u", pdu->type);
59860484Sobrien			return (SNMP_CODE_FAILED);
59960484Sobrien		}
60060484Sobrien		break;
60160484Sobrien
60260484Sobrien	  case SNMP_PDU_GETBULK:
60360484Sobrien	  case SNMP_PDU_INFORM:
60460484Sobrien	  case SNMP_PDU_TRAP2:
60560484Sobrien	  case SNMP_PDU_REPORT:
60660484Sobrien		if (pdu->version == SNMP_V1) {
60760484Sobrien			snmp_error("bad pdu type %u", pdu->type);
60860484Sobrien			return (SNMP_CODE_FAILED);
60960484Sobrien		}
61060484Sobrien		break;
61160484Sobrien
61260484Sobrien	  default:
61360484Sobrien		snmp_error("bad pdu type %u", pdu->type);
61460484Sobrien		return (SNMP_CODE_FAILED);
61560484Sobrien	}
61660484Sobrien
61760484Sobrien	trailer = b->asn_len - len;
61860484Sobrien	b->asn_len = len;
61960484Sobrien
62060484Sobrien	err = parse_pdus(b, pdu, ip);
62160484Sobrien	if (ASN_ERR_STOPPED(err))
62260484Sobrien		return (SNMP_CODE_FAILED);
62360484Sobrien
62460484Sobrien	if (b->asn_len != 0)
62560484Sobrien		snmp_error("ignoring trailing junk after pdu");
62660484Sobrien
62760484Sobrien	b->asn_len = trailer;
62860484Sobrien
62960484Sobrien	return (SNMP_CODE_OK);
63060484Sobrien}
63160484Sobrien
63260484Sobrienenum snmp_code
63360484Sobriensnmp_pdu_decode_secmode(struct asn_buf *b, struct snmp_pdu *pdu)
63460484Sobrien{
63560484Sobrien	u_char type;
63660484Sobrien	enum snmp_code code;
63760484Sobrien	uint8_t	digest[SNMP_USM_AUTH_SIZE];
63860484Sobrien
63960484Sobrien	if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH &&
64060484Sobrien	    (pdu->flags & SNMP_MSG_AUTH_FLAG) == 0)
64160484Sobrien		return (SNMP_CODE_BADSECLEVEL);
64260484Sobrien
64360484Sobrien	if ((code = snmp_pdu_calc_digest(pdu, digest)) !=
64460484Sobrien	    SNMP_CODE_OK)
64560484Sobrien		return (SNMP_CODE_FAILED);
64660484Sobrien
64760484Sobrien	if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH &&
64860484Sobrien	    memcmp(digest, pdu->msg_digest, sizeof(pdu->msg_digest)) != 0)
64960484Sobrien		return (SNMP_CODE_BADDIGEST);
65060484Sobrien
65160484Sobrien	if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV && (asn_get_header(b, &type,
65260484Sobrien	    &pdu->scoped_len) != ASN_ERR_OK || type != ASN_TYPE_OCTETSTRING)) {
65360484Sobrien		snmp_error("cannot decode encrypted pdu");
65460484Sobrien		return (SNMP_CODE_FAILED);
65560484Sobrien	}
65660484Sobrien	pdu->scoped_ptr = b->asn_ptr;
65760484Sobrien
65860484Sobrien	if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV &&
65960484Sobrien	    (pdu->flags & SNMP_MSG_PRIV_FLAG) == 0)
66060484Sobrien		return (SNMP_CODE_BADSECLEVEL);
66160484Sobrien
66260484Sobrien	if ((code = snmp_pdu_decrypt(pdu)) != SNMP_CODE_OK)
66360484Sobrien		return (SNMP_CODE_FAILED);
66460484Sobrien
66560484Sobrien	return (code);
66660484Sobrien}
66760484Sobrien
66860484Sobrien/*
66960484Sobrien * Check whether what we have is the complete PDU by snooping at the
67060484Sobrien * enclosing structure header. This returns:
67160484Sobrien *   -1		if there are ASN.1 errors
67260484Sobrien *    0		if we need more data
67360484Sobrien *  > 0		the length of this PDU
67460484Sobrien */
67560484Sobrienint
67660484Sobriensnmp_pdu_snoop(const struct asn_buf *b0)
67760484Sobrien{
67860484Sobrien	u_int length;
67960484Sobrien	asn_len_t len;
68060484Sobrien	struct asn_buf b = *b0;
68160484Sobrien
68260484Sobrien	/* <0x10|0x20> <len> <data...> */
68360484Sobrien
68460484Sobrien	if (b.asn_len == 0)
68560484Sobrien		return (0);
68660484Sobrien	if (b.asn_cptr[0] != (ASN_TYPE_SEQUENCE | ASN_TYPE_CONSTRUCTED)) {
68760484Sobrien		asn_error(&b, "bad sequence type %u", b.asn_cptr[0]);
68860484Sobrien		return (-1);
68960484Sobrien	}
69060484Sobrien	b.asn_len--;
69160484Sobrien	b.asn_cptr++;
69260484Sobrien
69360484Sobrien	if (b.asn_len == 0)
69460484Sobrien		return (0);
69560484Sobrien
69660484Sobrien	if (*b.asn_cptr & 0x80) {
69760484Sobrien		/* long length */
69860484Sobrien		length = *b.asn_cptr++ & 0x7f;
69960484Sobrien		b.asn_len--;
70060484Sobrien		if (length == 0) {
70160484Sobrien			asn_error(&b, "indefinite length not supported");
70260484Sobrien			return (-1);
70360484Sobrien		}
70460484Sobrien		if (length > ASN_MAXLENLEN) {
70560484Sobrien			asn_error(&b, "long length too long (%u)", length);
70660484Sobrien			return (-1);
70760484Sobrien		}
70860484Sobrien		if (length > b.asn_len)
70960484Sobrien			return (0);
71060484Sobrien		len = 0;
71160484Sobrien		while (length--) {
71260484Sobrien			len = (len << 8) | *b.asn_cptr++;
71360484Sobrien			b.asn_len--;
71460484Sobrien		}
71560484Sobrien	} else {
71660484Sobrien		len = *b.asn_cptr++;
71760484Sobrien		b.asn_len--;
71860484Sobrien	}
71960484Sobrien
72060484Sobrien	if (len > b.asn_len)
72160484Sobrien		return (0);
72260484Sobrien
72360484Sobrien	return (len + b.asn_cptr - b0->asn_cptr);
72460484Sobrien}
72560484Sobrien
72660484Sobrien/*
72760484Sobrien * Encode the SNMP PDU without the variable bindings field.
72860484Sobrien * We do this the rather uneffective way by
72960484Sobrien * moving things around and assuming that the length field will never
73060484Sobrien * use more than 2 bytes.
73160484Sobrien * We need a number of pointers to apply the fixes afterwards.
73260484Sobrien */
73360484Sobrienenum snmp_code
73460484Sobriensnmp_pdu_encode_header(struct asn_buf *b, struct snmp_pdu *pdu)
73560484Sobrien{
73660484Sobrien	enum asn_err err;
73760484Sobrien	u_char *v3_hdr_ptr;
73860484Sobrien
73960484Sobrien	if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED),
74060484Sobrien	    &pdu->outer_ptr) != ASN_ERR_OK)
74160484Sobrien		return (SNMP_CODE_FAILED);
74260484Sobrien
74360484Sobrien	if (pdu->version == SNMP_V1)
74460484Sobrien		err = asn_put_integer(b, 0);
74560484Sobrien	else if (pdu->version == SNMP_V2c)
74660484Sobrien		err = asn_put_integer(b, 1);
74760484Sobrien	else if (pdu->version == SNMP_V3)
74860484Sobrien		err = asn_put_integer(b, 3);
74960484Sobrien	else
75060484Sobrien		return (SNMP_CODE_BADVERS);
75160484Sobrien	if (err != ASN_ERR_OK)
75260484Sobrien		return (SNMP_CODE_FAILED);
75360484Sobrien
75460484Sobrien	if (pdu->version == SNMP_V3) {
75560484Sobrien		if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE |
75660484Sobrien		    ASN_TYPE_CONSTRUCTED), &v3_hdr_ptr) != ASN_ERR_OK)
75760484Sobrien			return (SNMP_CODE_FAILED);
75860484Sobrien
75960484Sobrien		if (asn_put_integer(b, pdu->identifier) != ASN_ERR_OK)
76060484Sobrien			return (SNMP_CODE_FAILED);
76160484Sobrien
76260484Sobrien		if (asn_put_integer(b, pdu->engine.max_msg_size) != ASN_ERR_OK)
76360484Sobrien			return (SNMP_CODE_FAILED);
76460484Sobrien
76560484Sobrien		if (pdu->type != SNMP_PDU_RESPONSE &&
76660484Sobrien		    pdu->type != SNMP_PDU_TRAP &&
76760484Sobrien		    pdu->type != SNMP_PDU_TRAP2 &&
76860484Sobrien		    pdu->type != SNMP_PDU_REPORT)
76960484Sobrien			pdu->flags |= SNMP_MSG_REPORT_FLAG;
77060484Sobrien
77160484Sobrien		if (asn_put_octetstring(b, (u_char *)&pdu->flags, 1)
77260484Sobrien		    != ASN_ERR_OK)
77360484Sobrien			return (SNMP_CODE_FAILED);
77460484Sobrien
77560484Sobrien		if (asn_put_integer(b, pdu->security_model) != ASN_ERR_OK)
77660484Sobrien			return (SNMP_CODE_FAILED);
77760484Sobrien
77860484Sobrien		if (asn_commit_header(b, v3_hdr_ptr, NULL) != ASN_ERR_OK)
77960484Sobrien			return (SNMP_CODE_FAILED);
78060484Sobrien
78160484Sobrien		if (pdu->security_model != SNMP_SECMODEL_USM)
78260484Sobrien			return (SNMP_CODE_FAILED);
78360484Sobrien
78460484Sobrien		if (pdu_encode_secparams(b, pdu) != SNMP_CODE_OK)
78560484Sobrien			return (SNMP_CODE_FAILED);
78660484Sobrien
78760484Sobrien		/*  View-based Access Conntrol information */
78860484Sobrien		if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE |
78960484Sobrien		    ASN_TYPE_CONSTRUCTED), &pdu->scoped_ptr) != ASN_ERR_OK)
79060484Sobrien			return (SNMP_CODE_FAILED);
79160484Sobrien
79260484Sobrien		if (asn_put_octetstring(b, (u_char *)pdu->context_engine,
79360484Sobrien		    pdu->context_engine_len) != ASN_ERR_OK)
79460484Sobrien			return (SNMP_CODE_FAILED);
79560484Sobrien
79660484Sobrien		if (asn_put_octetstring(b, (u_char *)pdu->context_name,
79760484Sobrien		    strlen(pdu->context_name)) != ASN_ERR_OK)
79860484Sobrien			return (SNMP_CODE_FAILED);
79960484Sobrien	} else {
80060484Sobrien		if (asn_put_octetstring(b, (u_char *)pdu->community,
80160484Sobrien		    strlen(pdu->community)) != ASN_ERR_OK)
80260484Sobrien			return (SNMP_CODE_FAILED);
80360484Sobrien	}
80460484Sobrien
80560484Sobrien	if (asn_put_temp_header(b, (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT |
80660484Sobrien	    pdu->type), &pdu->pdu_ptr) != ASN_ERR_OK)
80760484Sobrien		return (SNMP_CODE_FAILED);
80860484Sobrien
80960484Sobrien	if (pdu->type == SNMP_PDU_TRAP) {
81060484Sobrien		if (pdu->version != SNMP_V1 ||
81160484Sobrien		    asn_put_objid(b, &pdu->enterprise) != ASN_ERR_OK ||
81260484Sobrien		    asn_put_ipaddress(b, pdu->agent_addr) != ASN_ERR_OK ||
81360484Sobrien		    asn_put_integer(b, pdu->generic_trap) != ASN_ERR_OK ||
81460484Sobrien		    asn_put_integer(b, pdu->specific_trap) != ASN_ERR_OK ||
81560484Sobrien		    asn_put_timeticks(b, pdu->time_stamp) != ASN_ERR_OK)
81660484Sobrien			return (SNMP_CODE_FAILED);
81760484Sobrien	} else {
81860484Sobrien		if (pdu->version == SNMP_V1 && (pdu->type == SNMP_PDU_GETBULK ||
81960484Sobrien		    pdu->type == SNMP_PDU_INFORM ||
82060484Sobrien		    pdu->type == SNMP_PDU_TRAP2 ||
82160484Sobrien		    pdu->type == SNMP_PDU_REPORT))
82260484Sobrien			return (SNMP_CODE_FAILED);
82360484Sobrien
82460484Sobrien		if (asn_put_integer(b, pdu->request_id) != ASN_ERR_OK ||
82560484Sobrien		    asn_put_integer(b, pdu->error_status) != ASN_ERR_OK ||
82660484Sobrien		    asn_put_integer(b, pdu->error_index) != ASN_ERR_OK)
82760484Sobrien			return (SNMP_CODE_FAILED);
82860484Sobrien	}
82960484Sobrien
83060484Sobrien	if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED),
83160484Sobrien	    &pdu->vars_ptr) != ASN_ERR_OK)
83260484Sobrien		return (SNMP_CODE_FAILED);
83360484Sobrien
83460484Sobrien	return (SNMP_CODE_OK);
83560484Sobrien}
83660484Sobrien
83760484Sobrienstatic enum asn_err
83860484Sobriensnmp_pdu_fix_padd(struct asn_buf *b, struct snmp_pdu *pdu)
83960484Sobrien{
84060484Sobrien	asn_len_t padlen;
84160484Sobrien
84260484Sobrien	if (pdu->user.priv_proto == SNMP_PRIV_DES && pdu->scoped_len % 8 != 0) {
84360484Sobrien		padlen = 8 - (pdu->scoped_len % 8);
84460484Sobrien		if (asn_pad(b, padlen) != ASN_ERR_OK)
84560484Sobrien			return (ASN_ERR_FAILED);
84660484Sobrien		pdu->scoped_len += padlen;
84760484Sobrien	}
84860484Sobrien
84960484Sobrien	return (ASN_ERR_OK);
85060484Sobrien}
85160484Sobrien
85260484Sobrienenum snmp_code
85360484Sobriensnmp_fix_encoding(struct asn_buf *b, struct snmp_pdu *pdu)
85460484Sobrien{
85560484Sobrien	size_t moved = 0;
85660484Sobrien	enum snmp_code code;
85760484Sobrien
85860484Sobrien	if (asn_commit_header(b, pdu->vars_ptr, NULL) != ASN_ERR_OK ||
85960484Sobrien	    asn_commit_header(b, pdu->pdu_ptr, NULL) != ASN_ERR_OK)
86060484Sobrien		return (SNMP_CODE_FAILED);
86160484Sobrien
86260484Sobrien	if (pdu->version == SNMP_V3) {
86360484Sobrien		if (asn_commit_header(b, pdu->scoped_ptr, NULL) != ASN_ERR_OK)
86460484Sobrien			return (SNMP_CODE_FAILED);
86560484Sobrien
86660484Sobrien		pdu->scoped_len = b->asn_ptr - pdu->scoped_ptr;
86760484Sobrien		if ((code = snmp_pdu_fix_padd(b, pdu))!= ASN_ERR_OK)
86860484Sobrien			return (SNMP_CODE_FAILED);
86960484Sobrien
87060484Sobrien		if (pdu->security_model != SNMP_SECMODEL_USM)
87160484Sobrien			return (SNMP_CODE_FAILED);
87260484Sobrien
87360484Sobrien		if (snmp_pdu_encrypt(pdu) != SNMP_CODE_OK)
87460484Sobrien			return (SNMP_CODE_FAILED);
87560484Sobrien
87660484Sobrien		if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV &&
87760484Sobrien		    asn_commit_header(b, pdu->encrypted_ptr, NULL) != ASN_ERR_OK)
87860484Sobrien			return (SNMP_CODE_FAILED);
87960484Sobrien	}
88060484Sobrien
88160484Sobrien	if (asn_commit_header(b, pdu->outer_ptr, &moved) != ASN_ERR_OK)
88260484Sobrien		return (SNMP_CODE_FAILED);
88360484Sobrien
88460484Sobrien	pdu->outer_len = b->asn_ptr - pdu->outer_ptr;
88560484Sobrien	pdu->digest_ptr -= moved;
88660484Sobrien
88760484Sobrien	if (pdu->version == SNMP_V3) {
88860484Sobrien		if ((code = snmp_pdu_calc_digest(pdu, pdu->msg_digest)) !=
88960484Sobrien		    SNMP_CODE_OK)
89060484Sobrien			return (SNMP_CODE_FAILED);
89160484Sobrien
89260484Sobrien		if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0)
89360484Sobrien			memcpy(pdu->digest_ptr, pdu->msg_digest,
89460484Sobrien			    sizeof(pdu->msg_digest));
89560484Sobrien	}
89660484Sobrien
89760484Sobrien	return (SNMP_CODE_OK);
89860484Sobrien}
89960484Sobrien
90060484Sobrien/*
90160484Sobrien * Encode a binding. Caller must ensure, that the syntax is ok for that version.
90260484Sobrien * Be sure not to cobber b, when something fails.
90360484Sobrien */
90460484Sobrienenum asn_err
90560484Sobriensnmp_binding_encode(struct asn_buf *b, const struct snmp_value *binding)
90660484Sobrien{
90760484Sobrien	u_char *ptr;
90860484Sobrien	enum asn_err err;
90960484Sobrien	struct asn_buf save = *b;
91060484Sobrien
91160484Sobrien	if ((err = asn_put_temp_header(b, (ASN_TYPE_SEQUENCE |
91260484Sobrien	    ASN_TYPE_CONSTRUCTED), &ptr)) != ASN_ERR_OK) {
91360484Sobrien		*b = save;
91460484Sobrien		return (err);
91560484Sobrien	}
91660484Sobrien
91760484Sobrien	if ((err = asn_put_objid(b, &binding->var)) != ASN_ERR_OK) {
91860484Sobrien		*b = save;
91960484Sobrien		return (err);
92060484Sobrien	}
92160484Sobrien
92260484Sobrien	switch (binding->syntax) {
92360484Sobrien
92460484Sobrien	  case SNMP_SYNTAX_NULL:
92560484Sobrien		err = asn_put_null(b);
92660484Sobrien		break;
92760484Sobrien
92860484Sobrien	  case SNMP_SYNTAX_INTEGER:
92960484Sobrien		err = asn_put_integer(b, binding->v.integer);
93060484Sobrien		break;
93160484Sobrien
93260484Sobrien	  case SNMP_SYNTAX_OCTETSTRING:
93360484Sobrien		err = asn_put_octetstring(b, binding->v.octetstring.octets,
93460484Sobrien		    binding->v.octetstring.len);
93560484Sobrien		break;
93660484Sobrien
93760484Sobrien	  case SNMP_SYNTAX_OID:
93860484Sobrien		err = asn_put_objid(b, &binding->v.oid);
93960484Sobrien		break;
94060484Sobrien
94160484Sobrien	  case SNMP_SYNTAX_IPADDRESS:
94260484Sobrien		err = asn_put_ipaddress(b, binding->v.ipaddress);
94360484Sobrien		break;
94460484Sobrien
94560484Sobrien	  case SNMP_SYNTAX_TIMETICKS:
94660484Sobrien		err = asn_put_uint32(b, ASN_APP_TIMETICKS, binding->v.uint32);
94760484Sobrien		break;
94860484Sobrien
94960484Sobrien	  case SNMP_SYNTAX_COUNTER:
95060484Sobrien		err = asn_put_uint32(b, ASN_APP_COUNTER, binding->v.uint32);
95160484Sobrien		break;
95260484Sobrien
95360484Sobrien	  case SNMP_SYNTAX_GAUGE:
95460484Sobrien		err = asn_put_uint32(b, ASN_APP_GAUGE, binding->v.uint32);
95560484Sobrien		break;
95660484Sobrien
95760484Sobrien	  case SNMP_SYNTAX_COUNTER64:
95860484Sobrien		err = asn_put_counter64(b, binding->v.counter64);
95960484Sobrien		break;
96060484Sobrien
96160484Sobrien	  case SNMP_SYNTAX_NOSUCHOBJECT:
96260484Sobrien		err = asn_put_exception(b, ASN_EXCEPT_NOSUCHOBJECT);
96360484Sobrien		break;
96460484Sobrien
96560484Sobrien	  case SNMP_SYNTAX_NOSUCHINSTANCE:
96660484Sobrien		err = asn_put_exception(b, ASN_EXCEPT_NOSUCHINSTANCE);
96760484Sobrien		break;
96860484Sobrien
96960484Sobrien	  case SNMP_SYNTAX_ENDOFMIBVIEW:
97060484Sobrien		err = asn_put_exception(b, ASN_EXCEPT_ENDOFMIBVIEW);
97160484Sobrien		break;
97260484Sobrien	}
97360484Sobrien
97460484Sobrien	if (err != ASN_ERR_OK) {
97560484Sobrien		*b = save;
97660484Sobrien		return (err);
97760484Sobrien	}
97860484Sobrien
97960484Sobrien	err = asn_commit_header(b, ptr, NULL);
98060484Sobrien	if (err != ASN_ERR_OK) {
98160484Sobrien		*b = save;
98260484Sobrien		return (err);
98360484Sobrien	}
98460484Sobrien
98560484Sobrien	return (ASN_ERR_OK);
98660484Sobrien}
98760484Sobrien
98860484Sobrien/*
98960484Sobrien * Encode an PDU.
99060484Sobrien */
99160484Sobrienenum snmp_code
99260484Sobriensnmp_pdu_encode(struct snmp_pdu *pdu, struct asn_buf *resp_b)
99360484Sobrien{
99460484Sobrien	u_int idx;
99560484Sobrien	enum snmp_code err;
99660484Sobrien
99760484Sobrien	if ((err = snmp_pdu_encode_header(resp_b, pdu)) != SNMP_CODE_OK)
99860484Sobrien		return (err);
99960484Sobrien	for (idx = 0; idx < pdu->nbindings; idx++)
100060484Sobrien		if ((err = snmp_binding_encode(resp_b, &pdu->bindings[idx]))
100160484Sobrien		    != ASN_ERR_OK)
100260484Sobrien			return (SNMP_CODE_FAILED);
100360484Sobrien
100460484Sobrien	return (snmp_fix_encoding(resp_b, pdu));
100560484Sobrien}
100660484Sobrien
100760484Sobrienstatic void
100860484Sobriendump_binding(const struct snmp_value *b)
100960484Sobrien{
101060484Sobrien	u_int i;
101160484Sobrien	char buf[ASN_OIDSTRLEN];
101260484Sobrien
101360484Sobrien	snmp_printf("%s=", asn_oid2str_r(&b->var, buf));
101460484Sobrien	switch (b->syntax) {
101560484Sobrien
101660484Sobrien	  case SNMP_SYNTAX_NULL:
101760484Sobrien		snmp_printf("NULL");
101860484Sobrien		break;
101960484Sobrien
102060484Sobrien	  case SNMP_SYNTAX_INTEGER:
102160484Sobrien		snmp_printf("INTEGER %d", b->v.integer);
102260484Sobrien		break;
102360484Sobrien
102460484Sobrien	  case SNMP_SYNTAX_OCTETSTRING:
102560484Sobrien		snmp_printf("OCTET STRING %lu:", b->v.octetstring.len);
102660484Sobrien		for (i = 0; i < b->v.octetstring.len; i++)
102760484Sobrien			snmp_printf(" %02x", b->v.octetstring.octets[i]);
102860484Sobrien		break;
102960484Sobrien
103060484Sobrien	  case SNMP_SYNTAX_OID:
103160484Sobrien		snmp_printf("OID %s", asn_oid2str_r(&b->v.oid, buf));
103260484Sobrien		break;
103360484Sobrien
103460484Sobrien	  case SNMP_SYNTAX_IPADDRESS:
103560484Sobrien		snmp_printf("IPADDRESS %u.%u.%u.%u", b->v.ipaddress[0],
103660484Sobrien		    b->v.ipaddress[1], b->v.ipaddress[2], b->v.ipaddress[3]);
103760484Sobrien		break;
103860484Sobrien
103960484Sobrien	  case SNMP_SYNTAX_COUNTER:
104060484Sobrien		snmp_printf("COUNTER %u", b->v.uint32);
104160484Sobrien		break;
104260484Sobrien
104360484Sobrien	  case SNMP_SYNTAX_GAUGE:
104460484Sobrien		snmp_printf("GAUGE %u", b->v.uint32);
104560484Sobrien		break;
104660484Sobrien
104760484Sobrien	  case SNMP_SYNTAX_TIMETICKS:
104860484Sobrien		snmp_printf("TIMETICKS %u", b->v.uint32);
104960484Sobrien		break;
105060484Sobrien
105160484Sobrien	  case SNMP_SYNTAX_COUNTER64:
105260484Sobrien		snmp_printf("COUNTER64 %lld", b->v.counter64);
105360484Sobrien		break;
105460484Sobrien
105560484Sobrien	  case SNMP_SYNTAX_NOSUCHOBJECT:
105660484Sobrien		snmp_printf("NoSuchObject");
105760484Sobrien		break;
105860484Sobrien
105960484Sobrien	  case SNMP_SYNTAX_NOSUCHINSTANCE:
106060484Sobrien		snmp_printf("NoSuchInstance");
106160484Sobrien		break;
106260484Sobrien
106360484Sobrien	  case SNMP_SYNTAX_ENDOFMIBVIEW:
106460484Sobrien		snmp_printf("EndOfMibView");
106560484Sobrien		break;
106660484Sobrien
106760484Sobrien	  default:
106860484Sobrien		snmp_printf("UNKNOWN SYNTAX %u", b->syntax);
106960484Sobrien		break;
107060484Sobrien	}
107160484Sobrien}
107260484Sobrien
107360484Sobrienstatic __inline void
107460484Sobriendump_bindings(const struct snmp_pdu *pdu)
107560484Sobrien{
107660484Sobrien	u_int i;
107760484Sobrien
107860484Sobrien	for (i = 0; i < pdu->nbindings; i++) {
107960484Sobrien		snmp_printf(" [%u]: ", i);
108060484Sobrien		dump_binding(&pdu->bindings[i]);
108160484Sobrien		snmp_printf("\n");
108260484Sobrien	}
108360484Sobrien}
108460484Sobrien
108560484Sobrienstatic __inline void
108660484Sobriendump_notrap(const struct snmp_pdu *pdu)
108760484Sobrien{
108860484Sobrien	snmp_printf(" request_id=%d", pdu->request_id);
108960484Sobrien	snmp_printf(" error_status=%d", pdu->error_status);
109060484Sobrien	snmp_printf(" error_index=%d\n", pdu->error_index);
109160484Sobrien	dump_bindings(pdu);
109260484Sobrien}
109360484Sobrien
109460484Sobrienvoid
109560484Sobriensnmp_pdu_dump(const struct snmp_pdu *pdu)
109660484Sobrien{
109760484Sobrien	char buf[ASN_OIDSTRLEN];
109860484Sobrien	const char *vers;
109960484Sobrien	static const char *types[] = {
110060484Sobrien		[SNMP_PDU_GET] =	"GET",
110160484Sobrien		[SNMP_PDU_GETNEXT] =	"GETNEXT",
110260484Sobrien		[SNMP_PDU_RESPONSE] =	"RESPONSE",
110360484Sobrien		[SNMP_PDU_SET] =	"SET",
110460484Sobrien		[SNMP_PDU_TRAP] =	"TRAPv1",
110560484Sobrien		[SNMP_PDU_GETBULK] =	"GETBULK",
110660484Sobrien		[SNMP_PDU_INFORM] =	"INFORM",
110760484Sobrien		[SNMP_PDU_TRAP2] =	"TRAPv2",
110860484Sobrien		[SNMP_PDU_REPORT] =	"REPORT",
110960484Sobrien	};
111060484Sobrien
111160484Sobrien	if (pdu->version == SNMP_V1)
111260484Sobrien		vers = "SNMPv1";
111360484Sobrien	else if (pdu->version == SNMP_V2c)
111460484Sobrien		vers = "SNMPv2c";
111560484Sobrien	else if (pdu->version == SNMP_V3)
111660484Sobrien		vers = "SNMPv3";
111760484Sobrien	else
111860484Sobrien		vers = "v?";
111960484Sobrien
112060484Sobrien	switch (pdu->type) {
112160484Sobrien	  case SNMP_PDU_TRAP:
112260484Sobrien		snmp_printf("%s %s '%s'", types[pdu->type], vers, pdu->community);
112360484Sobrien		snmp_printf(" enterprise=%s", asn_oid2str_r(&pdu->enterprise, buf));
112460484Sobrien		snmp_printf(" agent_addr=%u.%u.%u.%u", pdu->agent_addr[0],
112560484Sobrien		    pdu->agent_addr[1], pdu->agent_addr[2], pdu->agent_addr[3]);
112660484Sobrien		snmp_printf(" generic_trap=%d", pdu->generic_trap);
112760484Sobrien		snmp_printf(" specific_trap=%d", pdu->specific_trap);
112860484Sobrien		snmp_printf(" time-stamp=%u\n", pdu->time_stamp);
112960484Sobrien		dump_bindings(pdu);
113060484Sobrien		break;
113160484Sobrien
113260484Sobrien	  case SNMP_PDU_GET:
113360484Sobrien	  case SNMP_PDU_GETNEXT:
113460484Sobrien	  case SNMP_PDU_RESPONSE:
113560484Sobrien	  case SNMP_PDU_SET:
113660484Sobrien	  case SNMP_PDU_GETBULK:
113760484Sobrien	  case SNMP_PDU_INFORM:
113860484Sobrien	  case SNMP_PDU_TRAP2:
113960484Sobrien	  case SNMP_PDU_REPORT:
114060484Sobrien		snmp_printf("%s %s '%s'", types[pdu->type], vers, pdu->community);
114160484Sobrien		dump_notrap(pdu);
114260484Sobrien		break;
114360484Sobrien
114460484Sobrien	  default:
114560484Sobrien		snmp_printf("bad pdu type %u\n", pdu->type);
114660484Sobrien		break;
114760484Sobrien	}
114860484Sobrien}
114960484Sobrien
115060484Sobrienvoid
115160484Sobriensnmp_value_free(struct snmp_value *value)
115260484Sobrien{
115360484Sobrien	if (value->syntax == SNMP_SYNTAX_OCTETSTRING)
115460484Sobrien		free(value->v.octetstring.octets);
115560484Sobrien	value->syntax = SNMP_SYNTAX_NULL;
115660484Sobrien}
115760484Sobrien
115860484Sobrienint
115960484Sobriensnmp_value_copy(struct snmp_value *to, const struct snmp_value *from)
116060484Sobrien{
116160484Sobrien	to->var = from->var;
116260484Sobrien	to->syntax = from->syntax;
116360484Sobrien
116460484Sobrien	if (from->syntax == SNMP_SYNTAX_OCTETSTRING) {
116560484Sobrien		if ((to->v.octetstring.len = from->v.octetstring.len) == 0)
116660484Sobrien			to->v.octetstring.octets = NULL;
116760484Sobrien		else {
116860484Sobrien			to->v.octetstring.octets = malloc(to->v.octetstring.len);
116960484Sobrien			if (to->v.octetstring.octets == NULL)
117060484Sobrien				return (-1);
117160484Sobrien			(void)memcpy(to->v.octetstring.octets,
117260484Sobrien			    from->v.octetstring.octets, to->v.octetstring.len);
117360484Sobrien		}
117460484Sobrien	} else
117560484Sobrien		to->v = from->v;
117660484Sobrien	return (0);
117760484Sobrien}
117860484Sobrien
117960484Sobrienvoid
118060484Sobriensnmp_pdu_init_secparams(struct snmp_pdu *pdu)
118160484Sobrien{
118260484Sobrien	int32_t rval;
118360484Sobrien
118460484Sobrien	if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH)
118560484Sobrien		pdu->flags |= SNMP_MSG_AUTH_FLAG;
118660484Sobrien
118760484Sobrien	switch (pdu->user.priv_proto) {
118860484Sobrien	case SNMP_PRIV_DES:
118960484Sobrien		memcpy(pdu->msg_salt, &pdu->engine.engine_boots,
119060484Sobrien		    sizeof(pdu->engine.engine_boots));
119160484Sobrien		rval = random();
119260484Sobrien		memcpy(pdu->msg_salt + sizeof(pdu->engine.engine_boots), &rval,
119360484Sobrien		    sizeof(int32_t));
119460484Sobrien		pdu->flags |= SNMP_MSG_PRIV_FLAG;
119560484Sobrien		break;
119660484Sobrien	case SNMP_PRIV_AES:
119760484Sobrien		rval = random();
119860484Sobrien		memcpy(pdu->msg_salt, &rval, sizeof(int32_t));
119960484Sobrien		rval = random();
120060484Sobrien		memcpy(pdu->msg_salt + sizeof(int32_t), &rval, sizeof(int32_t));
120160484Sobrien		pdu->flags |= SNMP_MSG_PRIV_FLAG;
120260484Sobrien		break;
120360484Sobrien	default:
120460484Sobrien		break;
120560484Sobrien	}
120660484Sobrien}
120760484Sobrien
120860484Sobrienvoid
120960484Sobriensnmp_pdu_free(struct snmp_pdu *pdu)
121060484Sobrien{
121160484Sobrien	u_int i;
121260484Sobrien
121360484Sobrien	for (i = 0; i < pdu->nbindings; i++)
121460484Sobrien		snmp_value_free(&pdu->bindings[i]);
121560484Sobrien}
121660484Sobrien
121760484Sobrien/*
121860484Sobrien * Parse an ASCII SNMP value into the binary form
121960484Sobrien */
122060484Sobrienint
122160484Sobriensnmp_value_parse(const char *str, enum snmp_syntax syntax, union snmp_values *v)
122260484Sobrien{
122360484Sobrien	char *end;
122460484Sobrien
122560484Sobrien	switch (syntax) {
122660484Sobrien
122760484Sobrien	  case SNMP_SYNTAX_NULL:
122860484Sobrien	  case SNMP_SYNTAX_NOSUCHOBJECT:
122960484Sobrien	  case SNMP_SYNTAX_NOSUCHINSTANCE:
123060484Sobrien	  case SNMP_SYNTAX_ENDOFMIBVIEW:
123160484Sobrien		if (*str != '\0')
123260484Sobrien			return (-1);
123360484Sobrien		return (0);
123460484Sobrien
123560484Sobrien	  case SNMP_SYNTAX_INTEGER:
123660484Sobrien		v->integer = strtoll(str, &end, 0);
123760484Sobrien		if (*end != '\0')
123860484Sobrien			return (-1);
123960484Sobrien		return (0);
124060484Sobrien
124160484Sobrien	  case SNMP_SYNTAX_OCTETSTRING:
124260484Sobrien	    {
124360484Sobrien		u_long len;	/* actual length of string */
124460484Sobrien		u_long alloc;	/* allocate length of string */
124560484Sobrien		u_char *octs;	/* actual octets */
124660484Sobrien		u_long oct;	/* actual octet */
124760484Sobrien		u_char *nocts;	/* to avoid memory leak */
124860484Sobrien		u_char c;	/* actual character */
124960484Sobrien
125060484Sobrien# define STUFFC(C)							\
125160484Sobrien		if (alloc == len) {					\
125260484Sobrien			alloc += 100;					\
125360484Sobrien			if ((nocts = realloc(octs, alloc)) == NULL) {	\
125460484Sobrien				free(octs);				\
125560484Sobrien				return (-1);				\
125660484Sobrien			}						\
125760484Sobrien			octs = nocts;					\
125860484Sobrien		}							\
125960484Sobrien		octs[len++] = (C);
126060484Sobrien
126160484Sobrien		len = alloc = 0;
126260484Sobrien		octs = NULL;
126360484Sobrien
126460484Sobrien		if (*str == '"') {
126560484Sobrien			str++;
126660484Sobrien			while((c = *str++) != '\0') {
126760484Sobrien				if (c == '"') {
126860484Sobrien					if (*str != '\0') {
126960484Sobrien						free(octs);
127060484Sobrien						return (-1);
127160484Sobrien					}
127260484Sobrien					break;
127360484Sobrien				}
127460484Sobrien				if (c == '\\') {
127560484Sobrien					switch (c = *str++) {
127660484Sobrien
127760484Sobrien					  case '\\':
127860484Sobrien						break;
127960484Sobrien					  case 'a':
128060484Sobrien						c = '\a';
128160484Sobrien						break;
128260484Sobrien					  case 'b':
128360484Sobrien						c = '\b';
128460484Sobrien						break;
128560484Sobrien					  case 'f':
128660484Sobrien						c = '\f';
128760484Sobrien						break;
128860484Sobrien					  case 'n':
128960484Sobrien						c = '\n';
129060484Sobrien						break;
129160484Sobrien					  case 'r':
129260484Sobrien						c = '\r';
129360484Sobrien						break;
129460484Sobrien					  case 't':
129560484Sobrien						c = '\t';
129660484Sobrien						break;
129760484Sobrien					  case 'v':
129860484Sobrien						c = '\v';
129960484Sobrien						break;
130060484Sobrien					  case 'x':
130160484Sobrien						c = 0;
130260484Sobrien						if (!isxdigit(*str))
130360484Sobrien							break;
130460484Sobrien						if (isdigit(*str))
130560484Sobrien							c = *str++ - '0';
130660484Sobrien						else if (isupper(*str))
130760484Sobrien							c = *str++ - 'A' + 10;
130860484Sobrien						else
130960484Sobrien							c = *str++ - 'a' + 10;
131060484Sobrien						if (!isxdigit(*str))
131160484Sobrien							break;
131260484Sobrien						if (isdigit(*str))
131360484Sobrien							c += *str++ - '0';
131460484Sobrien						else if (isupper(*str))
131560484Sobrien							c += *str++ - 'A' + 10;
131660484Sobrien						else
131760484Sobrien							c += *str++ - 'a' + 10;
131860484Sobrien						break;
131960484Sobrien					  case '0': case '1': case '2':
132060484Sobrien					  case '3': case '4': case '5':
132160484Sobrien					  case '6': case '7':
132260484Sobrien						c = *str++ - '0';
132360484Sobrien						if (*str < '0' || *str > '7')
132460484Sobrien							break;
132560484Sobrien						c = *str++ - '0';
132660484Sobrien						if (*str < '0' || *str > '7')
132760484Sobrien							break;
132860484Sobrien						c = *str++ - '0';
132960484Sobrien						break;
133060484Sobrien					  default:
133160484Sobrien						break;
133260484Sobrien					}
133360484Sobrien				}
133460484Sobrien				STUFFC(c);
133560484Sobrien			}
133660484Sobrien		} else {
133760484Sobrien			while (*str != '\0') {
133860484Sobrien				oct = strtoul(str, &end, 16);
133960484Sobrien				str = end;
134060484Sobrien				if (oct > 0xff) {
134160484Sobrien					free(octs);
134260484Sobrien					return (-1);
134360484Sobrien				}
134460484Sobrien				STUFFC(oct);
134560484Sobrien				if (*str == ':')
134660484Sobrien					str++;
134760484Sobrien				else if(*str != '\0') {
134860484Sobrien					free(octs);
134960484Sobrien					return (-1);
135060484Sobrien				}
135160484Sobrien			}
135260484Sobrien		}
135360484Sobrien		v->octetstring.octets = octs;
135460484Sobrien		v->octetstring.len = len;
135560484Sobrien		return (0);
135660484Sobrien# undef STUFFC
135760484Sobrien	    }
135860484Sobrien
135960484Sobrien	  case SNMP_SYNTAX_OID:
136060484Sobrien	    {
136160484Sobrien		u_long subid;
136260484Sobrien
136360484Sobrien		v->oid.len = 0;
136460484Sobrien
136560484Sobrien		for (;;) {
136660484Sobrien			if (v->oid.len == ASN_MAXOIDLEN)
136760484Sobrien				return (-1);
136860484Sobrien			subid = strtoul(str, &end, 10);
136960484Sobrien			str = end;
137060484Sobrien			if (subid > ASN_MAXID)
137160484Sobrien				return (-1);
137260484Sobrien			v->oid.subs[v->oid.len++] = (asn_subid_t)subid;
137360484Sobrien			if (*str == '\0')
137460484Sobrien				break;
137560484Sobrien			if (*str != '.')
137660484Sobrien				return (-1);
137760484Sobrien			str++;
137860484Sobrien		}
137960484Sobrien		return (0);
138060484Sobrien	    }
138160484Sobrien
138260484Sobrien	  case SNMP_SYNTAX_IPADDRESS:
138360484Sobrien	    {
138460484Sobrien		struct hostent *he;
138560484Sobrien		u_long ip[4];
138660484Sobrien		int n;
138760484Sobrien
138860484Sobrien		if (sscanf(str, "%lu.%lu.%lu.%lu%n", &ip[0], &ip[1], &ip[2],
138960484Sobrien		    &ip[3], &n) == 4 && (size_t)n == strlen(str) &&
139060484Sobrien		    ip[0] <= 0xff && ip[1] <= 0xff &&
139160484Sobrien		    ip[2] <= 0xff && ip[3] <= 0xff) {
139260484Sobrien			v->ipaddress[0] = (u_char)ip[0];
139360484Sobrien			v->ipaddress[1] = (u_char)ip[1];
139460484Sobrien			v->ipaddress[2] = (u_char)ip[2];
139560484Sobrien			v->ipaddress[3] = (u_char)ip[3];
139660484Sobrien			return (0);
139760484Sobrien		}
139860484Sobrien
139960484Sobrien		if ((he = gethostbyname(str)) == NULL)
140060484Sobrien			return (-1);
140160484Sobrien		if (he->h_addrtype != AF_INET)
140260484Sobrien			return (-1);
140360484Sobrien
140460484Sobrien		v->ipaddress[0] = he->h_addr[0];
140560484Sobrien		v->ipaddress[1] = he->h_addr[1];
140660484Sobrien		v->ipaddress[2] = he->h_addr[2];
140760484Sobrien		v->ipaddress[3] = he->h_addr[3];
140860484Sobrien		return (0);
140960484Sobrien	    }
141060484Sobrien
141160484Sobrien	  case SNMP_SYNTAX_COUNTER:
141260484Sobrien	  case SNMP_SYNTAX_GAUGE:
141360484Sobrien	  case SNMP_SYNTAX_TIMETICKS:
141460484Sobrien	    {
141560484Sobrien		uint64_t sub;
141660484Sobrien
141760484Sobrien		sub = strtoull(str, &end, 0);
141860484Sobrien		if (*end != '\0' || sub > 0xffffffff)
141960484Sobrien			return (-1);
142060484Sobrien		v->uint32 = (uint32_t)sub;
142160484Sobrien		return (0);
142260484Sobrien	    }
142360484Sobrien
142460484Sobrien	  case SNMP_SYNTAX_COUNTER64:
142560484Sobrien		v->counter64 = strtoull(str, &end, 0);
142660484Sobrien		if (*end != '\0')
142760484Sobrien			return (-1);
142860484Sobrien		return (0);
142960484Sobrien	}
143060484Sobrien	abort();
143160484Sobrien}
143260484Sobrien
143360484Sobrienstatic void
143460484Sobriensnmp_error_func(const char *fmt, ...)
143560484Sobrien{
143660484Sobrien	va_list ap;
143760484Sobrien
143860484Sobrien	va_start(ap, fmt);
143960484Sobrien	fprintf(stderr, "SNMP: ");
144060484Sobrien	vfprintf(stderr, fmt, ap);
144160484Sobrien	fprintf(stderr, "\n");
144260484Sobrien	va_end(ap);
144360484Sobrien}
144460484Sobrien
144560484Sobrienstatic void
144660484Sobriensnmp_printf_func(const char *fmt, ...)
144760484Sobrien{
144860484Sobrien	va_list ap;
144960484Sobrien
145060484Sobrien	va_start(ap, fmt);
145160484Sobrien	vfprintf(stderr, fmt, ap);
145260484Sobrien	va_end(ap);
145360484Sobrien}
145460484Sobrien