13941Svenki /*
23941Svenki * CDDL HEADER START
33941Svenki *
43941Svenki * The contents of this file are subject to the terms of the
53941Svenki * Common Development and Distribution License (the "License").
63941Svenki * You may not use this file except in compliance with the License.
73941Svenki *
83941Svenki * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93941Svenki * or http://www.opensolaris.org/os/licensing.
103941Svenki * See the License for the specific language governing permissions
113941Svenki * and limitations under the License.
123941Svenki *
133941Svenki * When distributing Covered Code, include this CDDL HEADER in each
143941Svenki * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153941Svenki * If applicable, add the following below this CDDL HEADER, with the
163941Svenki * fields enclosed by brackets "[]" replaced with your own identifying
173941Svenki * information: Portions Copyright [yyyy] [name of copyright owner]
183941Svenki *
193941Svenki * CDDL HEADER END
203941Svenki */
213941Svenki
223941Svenki /*
233941Svenki * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
243941Svenki * Use is subject to license terms.
253941Svenki */
263941Svenki
273941Svenki #pragma ident "%Z%%M% %I% %E% SMI"
283941Svenki
293941Svenki /*
303941Svenki * SNMP PDU and packet transport related routines
313941Svenki */
323941Svenki
333941Svenki #include <stdio.h>
343941Svenki #include <stdlib.h>
353941Svenki #include <string.h>
363941Svenki #include <sys/types.h>
373941Svenki #include "asn1.h"
383941Svenki #include "pdu.h"
393941Svenki #include "debug.h"
403941Svenki
413941Svenki /*
423941Svenki * Static declarations
433941Svenki */
443941Svenki static int snmp_add_null_vars(snmp_pdu_t *, char *, int, int);
453941Svenki static oid *snmp_oidstr_to_oid(int, char *, int, size_t *);
463941Svenki static uchar_t *snmp_build_pdu(snmp_pdu_t *, uchar_t *, size_t *);
473941Svenki static uchar_t *snmp_build_variable(uchar_t *, size_t *, oid *, size_t,
483941Svenki uchar_t, void *, size_t);
493941Svenki static uchar_t *snmp_parse_pdu(int, uchar_t *, size_t *, snmp_pdu_t *);
503941Svenki static uchar_t *snmp_parse_variable(uchar_t *, size_t *, pdu_varlist_t *);
51*4085Svenki static void snmp_free_null_vars(pdu_varlist_t *);
52*4085Svenki
53*4085Svenki static uchar_t *snmp_def_community = (uchar_t *)SNMP_DEF_COMMUNITY;
543941Svenki
553941Svenki /*
563941Svenki * Allocates and creates a PDU for the specified SNMP command. Currently
573941Svenki * only SNMP_MSG_GET, SNMP_MSG_GETNEXT and SNMP_MSG_GETBULK are supported
583941Svenki */
593941Svenki snmp_pdu_t *
snmp_create_pdu(int cmd,int max_reps,char * oidstrs,int n_oids,int row)603941Svenki snmp_create_pdu(int cmd, int max_reps, char *oidstrs, int n_oids, int row)
613941Svenki {
623941Svenki snmp_pdu_t *pdu;
633941Svenki
643941Svenki if ((cmd != SNMP_MSG_GET) && (cmd != SNMP_MSG_GETNEXT) &&
653941Svenki (cmd != SNMP_MSG_GETBULK)) {
663941Svenki return (NULL);
673941Svenki }
683941Svenki
693941Svenki pdu = (snmp_pdu_t *)calloc(1, sizeof (snmp_pdu_t));
703941Svenki if (pdu == NULL)
713941Svenki return (NULL);
723941Svenki
733941Svenki if (cmd == SNMP_MSG_GET || cmd == SNMP_MSG_GETNEXT) {
743941Svenki pdu->version = SNMP_VERSION_1;
753941Svenki pdu->errstat = 0;
763941Svenki pdu->errindex = 0;
773941Svenki } else if (cmd == SNMP_MSG_GETBULK) {
783941Svenki pdu->version = SNMP_VERSION_2c;
793941Svenki pdu->non_repeaters = 0;
803941Svenki pdu->max_repetitions = max_reps ?
813941Svenki max_reps : SNMP_DEF_MAX_REPETITIONS;
823941Svenki }
833941Svenki
843941Svenki pdu->command = cmd;
853941Svenki pdu->reqid = snmp_get_reqid();
86*4085Svenki pdu->community = snmp_def_community;
873941Svenki pdu->community_len = SNMP_DEF_COMMUNITY_LEN;
883941Svenki
893941Svenki if (snmp_add_null_vars(pdu, oidstrs, n_oids, row) < 0) {
903941Svenki free((void *) pdu);
913941Svenki return (NULL);
923941Svenki }
933941Svenki
943941Svenki pdu->req_pkt = NULL;
953941Svenki pdu->req_pktsz = 0;
963941Svenki pdu->reply_pkt = NULL;
973941Svenki pdu->reply_pktsz = 0;
983941Svenki
993941Svenki return (pdu);
1003941Svenki }
1013941Svenki
1023941Svenki /*
1033941Svenki * Builds a complete ASN.1 encoded snmp message packet out of the PDU.
1043941Svenki * Currently the maximum request packet is limited to SNMP_DEF_PKTBUF_SZ.
1053941Svenki * Since we only send SNMP_MSG_GET, SNMP_MSG_GETNEXT and SNMP_MSG_GETBULK,
1063941Svenki * as long as the number of bulk oids are not *too* many, we're safe with
1073941Svenki * this limit (the typical packet size of a bulk request of 10 vars is
1083941Svenki * around 250 bytes).
1093941Svenki */
1103941Svenki int
snmp_make_packet(snmp_pdu_t * pdu)1113941Svenki snmp_make_packet(snmp_pdu_t *pdu)
1123941Svenki {
1133941Svenki uchar_t *buf, *p;
1143941Svenki uchar_t *msg_seq_end;
1153941Svenki uchar_t id;
1163941Svenki size_t bufsz = SNMP_DEF_PKTBUF_SZ;
1173941Svenki size_t seqlen;
1183941Svenki
1193941Svenki if ((buf = (uchar_t *)calloc(1, SNMP_DEF_PKTBUF_SZ)) == NULL)
1203941Svenki return (-1);
1213941Svenki
1223941Svenki /*
1233941Svenki * Let's start with the ASN sequence tag. Set the length
1243941Svenki * to 0 initially and fill it up once the message packetizing
1253941Svenki * is complete.
1263941Svenki */
1273941Svenki id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
1283941Svenki if ((p = asn_build_sequence(buf, &bufsz, id, 0)) == NULL) {
1293941Svenki free((void *) buf);
1303941Svenki return (-1);
1313941Svenki }
1323941Svenki msg_seq_end = p;
1333941Svenki
1343941Svenki /*
1353941Svenki * Store the version
1363941Svenki */
1373941Svenki id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
1383941Svenki if ((p = asn_build_int(p, &bufsz, id, pdu->version)) == NULL) {
1393941Svenki free((void *) buf);
1403941Svenki return (-1);
1413941Svenki }
1423941Svenki
1433941Svenki /*
1443941Svenki * Store the community string
1453941Svenki */
1463941Svenki id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR;
1473941Svenki p = asn_build_string(p, &bufsz, id, pdu->community, pdu->community_len);
1483941Svenki if (p == NULL) {
1493941Svenki free((void *) buf);
1503941Svenki return (-1);
1513941Svenki }
1523941Svenki
1533941Svenki /*
1543941Svenki * Build the PDU
1553941Svenki */
1563941Svenki if ((p = snmp_build_pdu(pdu, p, &bufsz)) == NULL) {
1573941Svenki free((void *) buf);
1583941Svenki return (-1);
1593941Svenki }
1603941Svenki
1613941Svenki /*
1623941Svenki * Complete the message pkt by updating the message sequence length
1633941Svenki */
1643941Svenki seqlen = p - msg_seq_end;
1653941Svenki id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
1663941Svenki (void) asn_build_sequence(buf, NULL, id, seqlen);
1673941Svenki
1683941Svenki /*
1693941Svenki * Calculate packet size and return
1703941Svenki */
1713941Svenki pdu->req_pkt = buf;
1723941Svenki pdu->req_pktsz = p - buf;
1733941Svenki
1743941Svenki return (0);
1753941Svenki }
1763941Svenki
1773941Svenki /*
1783941Svenki * Makes a PDU out of a reply packet. The reply message is parsed
1793941Svenki * and if the reqid of the incoming packet does not match the reqid
1803941Svenki * we're waiting for, an error is returned. The PDU is allocated
1813941Svenki * inside this routine and must be freed by the caller once it is no
1823941Svenki * longer needed.
1833941Svenki */
1843941Svenki snmp_pdu_t *
snmp_parse_reply(int reqid,uchar_t * reply_pkt,size_t reply_pktsz)1853941Svenki snmp_parse_reply(int reqid, uchar_t *reply_pkt, size_t reply_pktsz)
1863941Svenki {
1873941Svenki snmp_pdu_t *reply_pdu;
1883941Svenki uchar_t *p;
1893941Svenki size_t msgsz = reply_pktsz;
1903941Svenki uchar_t exp_id;
1913941Svenki
1923941Svenki reply_pdu = (snmp_pdu_t *)calloc(1, sizeof (snmp_pdu_t));
1933941Svenki if (reply_pdu == NULL)
1943941Svenki return (NULL);
1953941Svenki
1963941Svenki /*
1973941Svenki * Try to parse the ASN sequence out of the beginning of the reply
1983941Svenki * packet. If we don't find a sequence at the beginning, something's
1993941Svenki * wrong.
2003941Svenki */
2013941Svenki exp_id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
2023941Svenki if ((p = asn_parse_sequence(reply_pkt, &msgsz, exp_id)) == NULL) {
2033941Svenki snmp_free_pdu(reply_pdu);
2043941Svenki return (NULL);
2053941Svenki }
2063941Svenki
2073941Svenki /*
2083941Svenki * Now try to parse the version out of the packet
2093941Svenki */
2103941Svenki if ((p = asn_parse_int(p, &msgsz, &reply_pdu->version)) == NULL) {
2113941Svenki snmp_free_pdu(reply_pdu);
2123941Svenki return (NULL);
2133941Svenki }
2143941Svenki if ((reply_pdu->version != SNMP_VERSION_1) &&
2153941Svenki (reply_pdu->version != SNMP_VERSION_2c)) {
2163941Svenki snmp_free_pdu(reply_pdu);
2173941Svenki return (NULL);
2183941Svenki }
2193941Svenki
2203941Svenki /*
2213941Svenki * Parse the community string (space allocated by asn_parse_string)
2223941Svenki */
2233941Svenki p = asn_parse_string(p, &msgsz, &reply_pdu->community,
2243941Svenki &reply_pdu->community_len);
2253941Svenki if (p == NULL) {
2263941Svenki snmp_free_pdu(reply_pdu);
2273941Svenki return (NULL);
2283941Svenki }
2293941Svenki
2303941Svenki /*
2313941Svenki * Parse the PDU part of the message
2323941Svenki */
2333941Svenki if ((p = snmp_parse_pdu(reqid, p, &msgsz, reply_pdu)) == NULL) {
2343941Svenki snmp_free_pdu(reply_pdu);
2353941Svenki return (NULL);
2363941Svenki }
2373941Svenki
2383941Svenki return (reply_pdu);
2393941Svenki }
2403941Svenki
2413941Svenki
2423941Svenki /*
2433941Svenki * Convert the OID strings into the standard PDU oid form (sequence of
2443941Svenki * integer subids) and add them to the PDU's variable list. Note that
2453941Svenki * this is used only for preparing the request messages (GET, GETNEXT
2463941Svenki * and GETBULK), so the values of the variables are always null.
2473941Svenki */
2483941Svenki static int
snmp_add_null_vars(snmp_pdu_t * pdu,char * oidstrs,int n_oids,int row)2493941Svenki snmp_add_null_vars(snmp_pdu_t *pdu, char *oidstrs, int n_oids, int row)
2503941Svenki {
2513941Svenki pdu_varlist_t *vp, *prev;
252*4085Svenki pdu_varlist_t *varblock_p = NULL;
2533941Svenki char *p;
2543941Svenki int i;
2553941Svenki
2563941Svenki prev = NULL;
2573941Svenki p = oidstrs;
2583941Svenki for (i = 0; i < n_oids; i++) {
259*4085Svenki if ((vp = calloc(1, sizeof (pdu_varlist_t))) == NULL) {
260*4085Svenki snmp_free_null_vars(varblock_p);
261*4085Svenki return (-1);
262*4085Svenki } else if (i == 0) {
263*4085Svenki varblock_p = vp;
264*4085Svenki } else {
265*4085Svenki prev->nextvar = vp;
266*4085Svenki }
267*4085Svenki
2683941Svenki vp->name = snmp_oidstr_to_oid(pdu->command,
2693941Svenki p, row, &vp->name_len);
2703941Svenki if (vp->name == NULL) {
271*4085Svenki snmp_free_null_vars(varblock_p);
2723941Svenki return (-1);
2733941Svenki }
2743941Svenki vp->val.str = NULL;
2753941Svenki vp->val_len = 0;
2763941Svenki vp->type = ASN_NULL;
277*4085Svenki vp->nextvar = NULL;
2783941Svenki
2793941Svenki LOGVAR(TAG_NULL_VAR, vp);
2803941Svenki
2813941Svenki prev = vp;
2823941Svenki p += strlen(p) + 1;
2833941Svenki }
2843941Svenki
2853941Svenki /*
2863941Svenki * append the varlist to the PDU
2873941Svenki */
2883941Svenki if (pdu->vars == NULL)
2893941Svenki pdu->vars = varblock_p;
2903941Svenki else {
2913941Svenki for (vp = pdu->vars; vp->nextvar; vp = vp->nextvar)
2923941Svenki ;
2933941Svenki vp->nextvar = varblock_p;
2943941Svenki }
2953941Svenki
2963941Svenki return (0);
2973941Svenki }
298*4085Svenki
2993941Svenki /*
3003941Svenki * Some assumptions are in place here to eliminate unnecessary complexity.
3013941Svenki * All OID strings passed are assumed to be in the numeric string form, have
3023941Svenki * no leading/trailing '.' or spaces. Since PICL plugin is currently the
3033941Svenki * only customer, this is quite reasonable.
3043941Svenki */
3053941Svenki static oid *
snmp_oidstr_to_oid(int cmd,char * oidstr,int row,size_t * n_subids)3063941Svenki snmp_oidstr_to_oid(int cmd, char *oidstr, int row, size_t *n_subids)
3073941Svenki {
3083941Svenki int i, count;
3093941Svenki char *p, *q;
3103941Svenki char *oidstr_dup;
3113941Svenki oid *objid;
3123941Svenki
3133941Svenki if ((oidstr == NULL) || (n_subids == NULL))
3143941Svenki return (NULL);
3153941Svenki
3163941Svenki for (count = 1, p = oidstr; p; count++, p++) {
3173941Svenki if ((p = strchr(p, '.')) == NULL)
3183941Svenki break;
3193941Svenki }
3203941Svenki
3213941Svenki /*
3223941Svenki * Add one more to count for 'row'. Need special processing
3233941Svenki * for SNMP_MSG_GETNEXT and SNMP_MSG_GETBULK requests; see
3243941Svenki * comment below.
3253941Svenki */
3263941Svenki if ((cmd == SNMP_MSG_GET) || (cmd == SNMP_MSG_GETBULK && row > 0) ||
3273941Svenki (cmd == SNMP_MSG_GETNEXT && row >= 0)) {
3283941Svenki count++;
3293941Svenki }
3303941Svenki
3313941Svenki if ((oidstr_dup = strdup(oidstr)) == NULL)
3323941Svenki return (NULL);
3333941Svenki
3343941Svenki objid = (oid *) calloc(count, sizeof (oid));
3353941Svenki if (objid == NULL) {
3363941Svenki free((void *) p);
3373941Svenki return (NULL);
3383941Svenki }
3393941Svenki
3403941Svenki p = oidstr_dup;
3413941Svenki for (i = 0; i < count - 1; i++) {
3423941Svenki if (q = strchr(p, '.'))
3433941Svenki *q = 0;
3443941Svenki objid[i] = (oid) strtoul(p, NULL, 10);
3453941Svenki p = q + 1;
3463941Svenki }
3473941Svenki
3483941Svenki /*
3493941Svenki * For SNMP_MSG_GET, the leaf subid will simply be the row#.
3503941Svenki *
3513941Svenki * For SNMP_MSG_GETBULK, if the row# passed is greater than 0,
3523941Svenki * we pass 'row-1' as the leaf subid, to include the item that
3533941Svenki * is of interest to us. If the row# is less than or equal to 0,
3543941Svenki * we will simply ignore it and pass only the prefix part of the
3553941Svenki * oidstr. For this case, our count would have been 1 less than
3563941Svenki * usual, and we are yet to save the last subid.
3573941Svenki *
3583941Svenki * For SNMP_MSG_GETNEXT, if the row# passed is less than 0,
3593941Svenki * we'll simply ignore it and pass only the prefix part of the
3603941Svenki * oidstr. For this case, our count would have been 1 less than
3613941Svenki * usual, and we are yet to save the last subid. If the row#
3623941Svenki * passed is greater than or equal to 0, we'll simply pass it
3633941Svenki * verbatim, as the leaf subid.
3643941Svenki */
3653941Svenki switch (cmd) {
3663941Svenki case SNMP_MSG_GET:
3673941Svenki objid[i] = (oid) row;
3683941Svenki break;
3693941Svenki
3703941Svenki case SNMP_MSG_GETBULK:
3713941Svenki if (row > 0)
3723941Svenki objid[i] = (oid) (row - 1);
3733941Svenki else
3743941Svenki objid[i] = (oid) strtoul(p, NULL, 10);
3753941Svenki break;
3763941Svenki
3773941Svenki case SNMP_MSG_GETNEXT:
3783941Svenki if (row < 0)
3793941Svenki objid[i] = (oid) strtoul(p, NULL, 10);
3803941Svenki else
3813941Svenki objid[i] = (oid) row;
3823941Svenki break;
3833941Svenki }
3843941Svenki
3853941Svenki *n_subids = count;
3863941Svenki
3873941Svenki free((void *) oidstr_dup);
3883941Svenki
3893941Svenki return (objid);
3903941Svenki }
3913941Svenki
3923941Svenki /*
3933941Svenki * Builds the PDU part of the snmp message packet.
3943941Svenki */
3953941Svenki static uchar_t *
snmp_build_pdu(snmp_pdu_t * pdu,uchar_t * buf,size_t * bufsz_p)3963941Svenki snmp_build_pdu(snmp_pdu_t *pdu, uchar_t *buf, size_t *bufsz_p)
3973941Svenki {
3983941Svenki uchar_t *p;
3993941Svenki uchar_t *pdu_seq_begin, *pdu_seq_end;
4003941Svenki uchar_t *varlist_seq_begin, *varlist_seq_end;
4013941Svenki uchar_t id;
4023941Svenki size_t seqlen;
4033941Svenki pdu_varlist_t *vp;
4043941Svenki
4053941Svenki /*
4063941Svenki * Build ASN sequence for the PDU command (length will be
4073941Svenki * updated later once the entire command is completely formed)
4083941Svenki */
4093941Svenki pdu_seq_begin = buf;
4103941Svenki p = asn_build_sequence(buf, bufsz_p, (uchar_t)pdu->command, 0);
4113941Svenki if (p == NULL)
4123941Svenki return (NULL);
4133941Svenki pdu_seq_end = p;
4143941Svenki
4153941Svenki /*
4163941Svenki * Build the request id
4173941Svenki */
4183941Svenki id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
4193941Svenki if ((p = asn_build_int(p, bufsz_p, id, pdu->reqid)) == NULL)
4203941Svenki return (NULL);
4213941Svenki
4223941Svenki /*
4233941Svenki * Build the non-repeaters and max-repetitions for SNMP_MSG_GETBULK
4243941Svenki * (same as error status and error index for other message types)
4253941Svenki */
4263941Svenki id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
4273941Svenki if ((p = asn_build_int(p, bufsz_p, id, pdu->non_repeaters)) == NULL)
4283941Svenki return (NULL);
4293941Svenki
4303941Svenki id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
4313941Svenki if ((p = asn_build_int(p, bufsz_p, id, pdu->max_repetitions)) == NULL)
4323941Svenki return (NULL);
4333941Svenki
4343941Svenki /*
4353941Svenki * Build ASN sequence for the variables list (update length
4363941Svenki * after building the varlist)
4373941Svenki */
4383941Svenki varlist_seq_begin = p;
4393941Svenki id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
4403941Svenki if ((p = asn_build_sequence(p, bufsz_p, id, 0)) == NULL)
4413941Svenki return (NULL);
4423941Svenki varlist_seq_end = p;
4433941Svenki
4443941Svenki /*
4453941Svenki * Build the variables list
4463941Svenki */
4473941Svenki for (vp = pdu->vars; vp; vp = vp->nextvar) {
4483941Svenki p = snmp_build_variable(p, bufsz_p, vp->name, vp->name_len,
4493941Svenki vp->type, vp->val.str, vp->val_len);
4503941Svenki if (p == NULL)
4513941Svenki return (NULL);
4523941Svenki }
4533941Svenki
4543941Svenki /*
4553941Svenki * Now update the varlist sequence length
4563941Svenki */
4573941Svenki seqlen = p - varlist_seq_end;
4583941Svenki id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
4593941Svenki (void) asn_build_sequence(varlist_seq_begin, NULL, id, seqlen);
4603941Svenki
4613941Svenki /*
4623941Svenki * And finally, update the length for the PDU sequence
4633941Svenki */
4643941Svenki seqlen = p - pdu_seq_end;
4653941Svenki (void) asn_build_sequence(pdu_seq_begin, NULL, (uchar_t)pdu->command,
4663941Svenki seqlen);
4673941Svenki
4683941Svenki return (p);
4693941Svenki }
4703941Svenki
4713941Svenki /*
4723941Svenki * Builds an object variable into the snmp message packet. Although the
4733941Svenki * code is here to build variables of basic types such as integer, object id
4743941Svenki * and strings, the only type of variable we ever send via snmp request
4753941Svenki * messages is the ASN_NULL type.
4763941Svenki */
4773941Svenki static uchar_t *
snmp_build_variable(uchar_t * buf,size_t * bufsz_p,oid * name,size_t name_len,uchar_t val_type,void * val,size_t val_len)4783941Svenki snmp_build_variable(uchar_t *buf, size_t *bufsz_p, oid *name, size_t name_len,
4793941Svenki uchar_t val_type, void *val, size_t val_len)
4803941Svenki {
4813941Svenki uchar_t *p, *varseq_end;
4823941Svenki size_t seqlen;
4833941Svenki uchar_t id;
4843941Svenki
4853941Svenki /*
4863941Svenki * Each variable binding is in turn defined as a 'SEQUENCE of' by
4873941Svenki * the SNMP PDU format, so we'll prepare the sequence and fill up
4883941Svenki * the length later. Sigh!
4893941Svenki */
4903941Svenki id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
4913941Svenki if ((p = asn_build_sequence(buf, bufsz_p, id, 0)) == NULL)
4923941Svenki return (NULL);
4933941Svenki varseq_end = p;
4943941Svenki
4953941Svenki /*
4963941Svenki * Build the object id
4973941Svenki */
4983941Svenki id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID;
4993941Svenki if ((p = asn_build_objid(p, bufsz_p, id, name, name_len)) == NULL)
5003941Svenki return (NULL);
5013941Svenki
5023941Svenki /*
5033941Svenki * Currently we only ever build ASN_NULL vars while sending requests,
5043941Svenki * since we support only SNMP_MSG_GET, SNMP_MSG_GETNEXT and
5053941Svenki * SNMP_MSG_GETBULK.
5063941Svenki */
5073941Svenki id = ASN_UNIVERSAL | ASN_PRIMITIVE | val_type;
5083941Svenki switch (val_type) {
5093941Svenki case ASN_INTEGER:
5103941Svenki p = asn_build_int(p, bufsz_p, id, *((int *)val));
5113941Svenki if (p == NULL)
5123941Svenki return (NULL);
5133941Svenki break;
5143941Svenki
5153941Svenki case ASN_OBJECT_ID:
5163941Svenki p = asn_build_objid(p, bufsz_p, id, val,
5173941Svenki val_len / sizeof (oid));
5183941Svenki if (p == NULL)
5193941Svenki return (NULL);
5203941Svenki break;
5213941Svenki
5223941Svenki case ASN_OCTET_STR:
5233941Svenki p = asn_build_string(p, bufsz_p, id, (uchar_t *)val, val_len);
5243941Svenki if (p == NULL)
5253941Svenki return (NULL);
5263941Svenki break;
5273941Svenki
5283941Svenki case ASN_NULL:
5293941Svenki if ((p = asn_build_null(p, bufsz_p, id)) == NULL)
5303941Svenki return (NULL);
5313941Svenki break;
5323941Svenki
5333941Svenki default:
5343941Svenki return (NULL);
5353941Svenki }
5363941Svenki
5373941Svenki /*
5383941Svenki * Rebuild the variable sequence length
5393941Svenki */
5403941Svenki seqlen = p - varseq_end;
5413941Svenki id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
5423941Svenki (void) asn_build_sequence(buf, NULL, id, seqlen);
5433941Svenki
5443941Svenki return (p);
5453941Svenki }
5463941Svenki
5473941Svenki /*
5483941Svenki * Parse the PDU portion of the incoming snmp message into the reply_pdu.
5493941Svenki * Space for all structure members are allocated as needed and must be freed
5503941Svenki * by the caller when these are no longer needed.
5513941Svenki */
5523941Svenki static uchar_t *
snmp_parse_pdu(int reqid,uchar_t * msg,size_t * msgsz_p,snmp_pdu_t * reply_pdu)5533941Svenki snmp_parse_pdu(int reqid, uchar_t *msg, size_t *msgsz_p, snmp_pdu_t *reply_pdu)
5543941Svenki {
5553941Svenki uchar_t *p;
5563941Svenki uchar_t id, exp_id;
5573941Svenki pdu_varlist_t *newvp, *vp = NULL;
5583941Svenki
5593941Svenki /*
5603941Svenki * Parse the PDU header out of the message
5613941Svenki */
5623941Svenki if ((p = asn_parse_header(msg, msgsz_p, &id)) == NULL)
5633941Svenki return (NULL);
5643941Svenki if (id != SNMP_MSG_RESPONSE && id != SNMP_MSG_REPORT)
5653941Svenki return (NULL);
5663941Svenki reply_pdu->command = (int)id;
5673941Svenki
5683941Svenki /*
5693941Svenki * Parse the request id and verify that this is the response
5703941Svenki * we're expecting.
5713941Svenki */
5723941Svenki if ((p = asn_parse_int(p, msgsz_p, &reply_pdu->reqid)) == NULL)
5733941Svenki return (NULL);
5743941Svenki if (reply_pdu->reqid != reqid)
5753941Svenki return (NULL);
5763941Svenki
5773941Svenki /*
5783941Svenki * Parse the error-status and error-index values
5793941Svenki */
5803941Svenki if ((p = asn_parse_int(p, msgsz_p, &reply_pdu->errstat)) == NULL)
5813941Svenki return (NULL);
5823941Svenki if ((p = asn_parse_int(p, msgsz_p, &reply_pdu->errindex)) == NULL)
5833941Svenki return (NULL);
5843941Svenki
5853941Svenki /*
5863941Svenki * Parse the header for the variables list sequence.
5873941Svenki */
5883941Svenki exp_id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
5893941Svenki if ((p = asn_parse_sequence(p, msgsz_p, exp_id)) == NULL)
5903941Svenki return (NULL);
5913941Svenki
5923941Svenki while (((int)*msgsz_p) > 0) {
5933941Svenki if ((newvp = calloc(1, sizeof (pdu_varlist_t))) == NULL)
5943941Svenki return (NULL);
5953941Svenki
5963941Svenki if (vp == NULL)
5973941Svenki reply_pdu->vars = newvp;
5983941Svenki else
5993941Svenki vp->nextvar = newvp;
6003941Svenki
6013941Svenki vp = newvp;
6023941Svenki if ((p = snmp_parse_variable(p, msgsz_p, vp)) == NULL)
6033941Svenki return (NULL);
6043941Svenki
6053941Svenki LOGVAR(TAG_RESPONSE_VAR, vp);
6063941Svenki }
6073941Svenki
6083941Svenki return (p);
6093941Svenki }
6103941Svenki
6113941Svenki /*
6123941Svenki * Allocate and parse the next variable into the varlist
6133941Svenki */
6143941Svenki static uchar_t *
snmp_parse_variable(uchar_t * msg,size_t * msgsz_p,pdu_varlist_t * vp)6153941Svenki snmp_parse_variable(uchar_t *msg, size_t *msgsz_p, pdu_varlist_t *vp)
6163941Svenki {
6173941Svenki uchar_t *p;
6183941Svenki uchar_t exp_id;
6193941Svenki
6203941Svenki /*
6213941Svenki * Parse this variable's sequence
6223941Svenki */
6233941Svenki exp_id = ASN_UNIVERSAL | ASN_CONSTRUCTOR | ASN_SEQUENCE;
6243941Svenki if ((p = asn_parse_sequence(msg, msgsz_p, exp_id)) == NULL)
6253941Svenki return (NULL);
6263941Svenki
6273941Svenki /*
6283941Svenki * Parse the variable's object identifier
6293941Svenki */
6303941Svenki p = asn_parse_objid(p, msgsz_p, &vp->name, &vp->name_len);
6313941Svenki if (p == NULL)
6323941Svenki return (NULL);
6333941Svenki
6343941Svenki /*
6353941Svenki * Parse the object's value
6363941Svenki */
6373941Svenki if ((p = asn_parse_objval(p, msgsz_p, vp)) == NULL)
6383941Svenki return (NULL);
6393941Svenki
6403941Svenki return (p);
6413941Svenki }
6423941Svenki
6433941Svenki void
snmp_free_pdu(snmp_pdu_t * pdu)6443941Svenki snmp_free_pdu(snmp_pdu_t *pdu)
6453941Svenki {
6463941Svenki pdu_varlist_t *vp, *nxt;
6473941Svenki
6483941Svenki if (pdu) {
649*4085Svenki if ((pdu->community) && (pdu->community != snmp_def_community))
6503941Svenki free((void *) pdu->community);
6513941Svenki
6523941Svenki for (vp = pdu->vars; vp; vp = nxt) {
6533941Svenki nxt = vp->nextvar;
6543941Svenki
6553941Svenki if (vp->name)
6563941Svenki free((void *) vp->name);
6573941Svenki if (vp->val.str)
6583941Svenki free((void *) vp->val.str);
6593941Svenki free((void *) vp);
6603941Svenki }
6613941Svenki
6623941Svenki if (pdu->req_pkt)
6633941Svenki free((void *) pdu->req_pkt);
6643941Svenki
6653941Svenki if (pdu->reply_pkt)
6663941Svenki free((void *) pdu->reply_pkt);
6673941Svenki
6683941Svenki free((void *) pdu);
6693941Svenki }
6703941Svenki }
671*4085Svenki
672*4085Svenki static void
snmp_free_null_vars(pdu_varlist_t * varblock_p)673*4085Svenki snmp_free_null_vars(pdu_varlist_t *varblock_p)
674*4085Svenki {
675*4085Svenki pdu_varlist_t *vp, *nxt;
676*4085Svenki
677*4085Svenki for (vp = varblock_p; vp; vp = nxt) {
678*4085Svenki nxt = vp->nextvar;
679*4085Svenki
680*4085Svenki if (vp->name)
681*4085Svenki free(vp->name);
682*4085Svenki free(vp);
683*4085Svenki }
684*4085Svenki }
685