#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "asn1.h"
#include "pdu.h"
uchar_t *
asn_build_sequence(uchar_t *buf, size_t *bufsz_p, uchar_t id, size_t length)
{
if ((bufsz_p) && (*bufsz_p < 4))
return (NULL);
buf[0] = id;
buf[1] = (uchar_t)(ASN_LONG_LEN | 0x02);
buf[2] = (uchar_t)((length >> 8) & 0xff);
buf[3] = (uchar_t)(length & 0xff);
if (bufsz_p)
*bufsz_p -= 4;
return (buf + 4);
}
uchar_t *
asn_build_header(uchar_t *buf, size_t *bufsz_p, uchar_t id, size_t length)
{
if (*bufsz_p < 1)
return (NULL);
buf[0] = id;
(*bufsz_p)--;
return (asn_build_length(buf + 1, bufsz_p, length));
}
uchar_t *
asn_build_length(uchar_t *buf, size_t *bufsz_p, size_t length)
{
if (length < 0x80) {
if (*bufsz_p < 1)
return (NULL);
buf[0] = (uchar_t)length;
(*bufsz_p)--;
return (buf + 1);
} else if (length <= 0xFF) {
if (*bufsz_p < 2)
return (NULL);
buf[0] = (uchar_t)(ASN_LONG_LEN | 0x01);
buf[1] = (uchar_t)length;
*bufsz_p -= 2;
return (buf + 2);
} else {
if (*bufsz_p < 3)
return (NULL);
buf[0] = (uchar_t)(ASN_LONG_LEN | 0x02);
buf[1] = (uchar_t)((length >> 8) & 0xff);
buf[2] = (uchar_t)(length & 0xff);
*bufsz_p -= 3;
return (buf + 3);
}
}
uchar_t *
asn_build_int(uchar_t *buf, size_t *bufsz_p, uchar_t id, int val)
{
uint_t uival;
int ival, i;
short sval;
char cval;
size_t valsz;
uchar_t *p, *valp;
uival = ((uint_t)val >> BUILD_INT_SHIFT) & BUILD_INT_MASK;
ival = val;
sval = (short)val;
cval = (char)val;
if (val == (int)cval)
valsz = 1;
else if (val == (int)sval)
valsz = 2;
else if (uival == BUILD_INT_MASK || uival == 0)
valsz = 3;
else
valsz = 4;
if ((p = asn_build_header(buf, bufsz_p, id, valsz)) == NULL)
return (NULL);
if (*bufsz_p < valsz)
return (NULL);
else {
valp = (uchar_t *)&ival;
for (i = 0; i < valsz; i++)
p[i] = valp[sizeof (int) - valsz + i];
*bufsz_p -= valsz;
return (p + valsz);
}
}
uchar_t *
asn_build_string(uchar_t *buf, size_t *bufsz_p, uchar_t id, uchar_t *str,
size_t slen)
{
uchar_t *p;
if ((p = asn_build_header(buf, bufsz_p, id, slen)) == NULL)
return (NULL);
if (*bufsz_p < slen)
return (NULL);
else {
if (str) {
(void) memcpy(p, str, slen);
} else {
(void) memset(p, 0, slen);
}
*bufsz_p -= slen;
return (p + slen);
}
}
uchar_t *
asn_build_objid(uchar_t *buf, size_t *bufsz_p, uchar_t id, void *oidp,
size_t n_subids)
{
oid *objid = oidp;
size_t oid_asnlen;
oid subid, first_subid;
uchar_t subid_len[MAX_SUBIDS_IN_OID];
uchar_t *p;
int i, ndx;
if (n_subids < MIN_SUBIDS_IN_OID || n_subids > MAX_SUBIDS_IN_OID)
return (NULL);
if ((objid[0] > 2) || (objid[0] < 2 && objid[1] >= 40))
return (NULL);
oid_asnlen = 0;
for (i = 0, ndx = 0; i < n_subids; i++, ndx++) {
if (i == 0) {
subid = objid[0] * 40 + objid[1];
first_subid = subid;
i++;
} else {
subid = objid[i];
}
if (subid < (oid) 0x80)
subid_len[ndx] = 1;
else if (subid < (oid) 0x4000)
subid_len[ndx] = 2;
else if (subid < (oid) 0x200000)
subid_len[ndx] = 3;
else if (subid < (oid) 0x10000000)
subid_len[ndx] = 4;
else {
subid_len[ndx] = 5;
}
oid_asnlen += subid_len[ndx];
}
if ((p = asn_build_header(buf, bufsz_p, id, oid_asnlen)) == NULL)
return (NULL);
if (*bufsz_p < oid_asnlen)
return (NULL);
for (i = 0, ndx = 0; i < n_subids; i++, ndx++) {
if (i == 0) {
subid = first_subid;
i++;
} else {
subid = objid[i];
}
switch (subid_len[ndx]) {
case 1:
*p++ = (uchar_t)subid;
break;
case 2:
*p++ = (uchar_t)((subid >> 7) | 0x80);
*p++ = (uchar_t)(subid & 0x7f);
break;
case 3:
*p++ = (uchar_t)((subid >> 14) | 0x80);
*p++ = (uchar_t)(((subid >> 7) & 0x7f) | 0x80);
*p++ = (uchar_t)(subid & 0x7f);
break;
case 4:
*p++ = (uchar_t)((subid >> 21) | 0x80);
*p++ = (uchar_t)(((subid >> 14) & 0x7f) | 0x80);
*p++ = (uchar_t)(((subid >> 7) & 0x7f) | 0x80);
*p++ = (uchar_t)(subid & 0x7f);
break;
case 5:
*p++ = (uchar_t)((subid >> 28) | 0x80);
*p++ = (uchar_t)(((subid >> 21) & 0x7f) | 0x80);
*p++ = (uchar_t)(((subid >> 14) & 0x7f) | 0x80);
*p++ = (uchar_t)(((subid >> 7) & 0x7f) | 0x80);
*p++ = (uchar_t)(subid & 0x7f);
break;
}
}
*bufsz_p -= oid_asnlen;
return (p);
}
uchar_t *
asn_build_null(uchar_t *buf, size_t *bufsz_p, uchar_t id)
{
uchar_t *p;
p = asn_build_header(buf, bufsz_p, id, 0);
return (p);
}
uchar_t *
asn_parse_sequence(uchar_t *buf, size_t *bufsz_p, uchar_t exp_id)
{
uchar_t *p;
uchar_t id;
if ((p = asn_parse_header(buf, bufsz_p, &id)) == NULL)
return (NULL);
if (id != exp_id)
return (NULL);
return (p);
}
uchar_t *
asn_parse_header(uchar_t *buf, size_t *bufsz_p, uchar_t *id)
{
uchar_t *p;
size_t asnobj_len, hdrlen;
if ((buf[0] & ASN_EXT_TAG) == ASN_EXT_TAG)
return (NULL);
if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
return (NULL);
hdrlen = p - buf;
if (*bufsz_p < (asnobj_len + hdrlen))
return (NULL);
*id = buf[0];
*bufsz_p -= hdrlen;
return (p);
}
uchar_t *
asn_parse_length(uchar_t *buf, size_t *asnobj_len_p)
{
uchar_t *p;
int n_length_octets;
if ((buf[0] & ASN_LONG_LEN) == 0) {
*asnobj_len_p = (size_t)buf[0];
return (buf + 1);
}
if (buf[0] == (uchar_t)ASN_LONG_LEN)
return (NULL);
n_length_octets = buf[0] & ~ASN_LONG_LEN;
if (n_length_octets > sizeof (*asnobj_len_p))
return (NULL);
p = buf + 1;
*asnobj_len_p = 0;
while (n_length_octets--) {
*asnobj_len_p <<= 8;
*asnobj_len_p |= *p++;
}
return (p);
}
uchar_t *
asn_parse_int(uchar_t *buf, size_t *bufsz_p, int *ival)
{
size_t asnobj_len, hdrlen;
uchar_t int_id;
uchar_t *p;
int_id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER;
if (buf[0] != int_id)
return (NULL);
if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
return (NULL);
hdrlen = p - buf;
if (*bufsz_p < (hdrlen + asnobj_len))
return (NULL);
*bufsz_p -= (hdrlen + asnobj_len);
*ival = (*p & ASN_BIT8) ? -1 : 0;
while (asnobj_len--) {
*ival <<= 8;
*ival |= *p++;
}
return (p);
}
uchar_t *
asn_parse_uint(uchar_t *buf, size_t *bufsz_p, uint_t *uival)
{
size_t asnobj_len, hdrlen;
uchar_t *p;
if ((buf[0] != ASN_COUNTER) && (buf[0] != ASN_TIMETICKS))
return (NULL);
if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
return (NULL);
hdrlen = p - buf;
if (*bufsz_p < (hdrlen + asnobj_len))
return (NULL);
*bufsz_p -= (hdrlen + asnobj_len);
*uival = (*p & ASN_BIT8) ? ~0 : 0;
while (asnobj_len--) {
*uival <<= 8;
*uival |= *p++;
}
return (p);
}
uchar_t *
asn_parse_string(uchar_t *buf, size_t *bufsz_p, uchar_t **str_p, size_t *slen)
{
uchar_t *p;
uchar_t id1, id2;
size_t asnobj_len, hdrlen;
id1 = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR;
id2 = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_BIT_STR;
if ((buf[0] != id1) && (buf[0] != id2))
return (NULL);
if ((p = asn_parse_length(buf + 1, &asnobj_len)) == NULL)
return (NULL);
hdrlen = p - buf;
if (*bufsz_p < (hdrlen + asnobj_len))
return (NULL);
if ((*str_p = (uchar_t *)calloc(1, asnobj_len + 1)) == NULL)
return (NULL);
(void) memcpy(*str_p, p, asnobj_len);
if (buf[0] == id1) {
(*str_p)[asnobj_len] = 0;
}
*slen = asnobj_len;
*bufsz_p -= (hdrlen + asnobj_len);
return (p + asnobj_len);
}
uchar_t *
asn_parse_objid(uchar_t *msg, size_t *varsz_p, void *oidp, size_t *n_subids)
{
oid **objid_p = oidp;
oid *objid;
uchar_t *p;
size_t hdrlen, asnobj_len;
oid subid;
int i, ndx;
uchar_t exp_id;
exp_id = ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID;
if (msg[0] != exp_id)
return (NULL);
if ((p = asn_parse_length(msg + 1, &asnobj_len)) == NULL)
return (NULL);
hdrlen = p - msg;
if (*varsz_p < (hdrlen + asnobj_len))
return (NULL);
*n_subids = 1;
for (i = 0; i < asnobj_len; i++) {
if ((p[i] & ASN_BIT8) == 0)
(*n_subids)++;
}
if ((objid = (oid *) calloc(1, (*n_subids) * sizeof (oid))) == NULL)
return (NULL);
ndx = 1;
subid = 0;
for (i = 0; i < asnobj_len; i++) {
subid = subid << 7;
subid |= (p[i] & ~ASN_BIT8);
if ((p[i] & ASN_BIT8) == 0) {
objid[ndx] = subid;
ndx++;
subid = 0;
}
}
if (objid[1] < 40) {
objid[0] = 0;
} else if (objid[1] < 80) {
objid[0] = 1;
objid[1] -= 40;
} else {
objid[0] = 2;
objid[1] -= 80;
}
*objid_p = objid;
*varsz_p -= (hdrlen + asnobj_len);
return (msg + hdrlen + asnobj_len);
}
uchar_t *
asn_parse_objval(uchar_t *msg, size_t *varsz_p, void *varlistp)
{
pdu_varlist_t *vp = varlistp;
uchar_t *p;
size_t n_subids;
size_t hdrlen, asnobj_len;
vp->type = msg[0] & ASN_EXT_TAG;
if (vp->type == ASN_EXT_TAG)
return (NULL);
switch (msg[0]) {
case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER:
vp->val.iptr = (int *)calloc(1, sizeof (int));
if (vp->val.iptr == NULL)
return (NULL);
if ((p = asn_parse_int(msg, varsz_p, vp->val.iptr)) == NULL) {
free(vp->val.iptr);
return (NULL);
}
vp->val_len = sizeof (int);
break;
case ASN_COUNTER:
case ASN_TIMETICKS:
vp->val.uiptr = (uint_t *)calloc(1, sizeof (uint_t));
if (vp->val.uiptr == NULL)
return (NULL);
if ((p = asn_parse_uint(msg, varsz_p, vp->val.uiptr)) == NULL) {
free(vp->val.uiptr);
return (NULL);
}
vp->val_len = sizeof (uint_t);
vp->type = msg[0];
break;
case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR:
case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_BIT_STR:
p = asn_parse_string(msg, varsz_p, &vp->val.str, &vp->val_len);
if (p == NULL)
return (NULL);
break;
case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID:
p = asn_parse_objid(msg, varsz_p, &vp->val.objid, &n_subids);
if (p == NULL)
return (NULL);
vp->val_len = n_subids * sizeof (oid);
break;
case ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_NULL:
case SNMP_NOSUCHOBJECT:
case SNMP_NOSUCHINSTANCE:
case SNMP_ENDOFMIBVIEW:
default:
p = asn_parse_length(msg + 1, &asnobj_len);
if (p == NULL)
return (NULL);
hdrlen = p - msg;
if (*varsz_p < (hdrlen + asnobj_len))
return (NULL);
vp->type = msg[0];
vp->val_len = asnobj_len;
*varsz_p -= (hdrlen + asnobj_len);
p += asnobj_len;
break;
}
return (p);
}