阅读完本文你可以学到:
(1)SNMP 中字符串型类型(包括基本类型 OCTER STRING 及其引申类型,如 IpAddress 等)的 BER 编码与解码。
一、理论知识
1、Tag
OCTER STRING 对应的 Tag 为 0x04,IpAddress 对应的 Tag 为 0x40。字符串类型的 Tag 占用一个八位组。
2、Length
Length有三种形式:定长短格式、定长长格式和变长格式。(SNMP 的所有内置类型的 Length 段都采用定长格式)
定长短格式:采用定长方式,当长度不大于127个八位组时,Length 只在一个八位组中编码。此时,bit0~bit6 表示实际长度,最高位 bit7 为0(用于区分其他两种格式)。
定长长格式:采用定长方式,当长度大于127个八位组时,Length 在多个八位组中编码,此时第一个八位组低七位(bit0~bit6)表示的是 Length 所占的长度,第一个八位组的最高位 bit7 为1,第一个八位组的低七位不能全为 0(用于区分其他两种格式)。
变长格式:采用变长方式,Length 所在八位组固定编码为 0x80,但在 Value 编码结束后以两个 0x00 结尾。这种方式使得可以在编码没有完全结束的情况下,可以先发送部分消息给对方。
3、Value(字符串类型的值编码)
SNMP 中字符串类型的值编码是直接将计算机存储的二进制形式数据作为编码值,占用 0 或多个八位组。
二、代码实现
/************************************************************************/ /* asn1.h */ /************************************************************************/ #pragma once typedef unsigned char u_char; static u_char* _asn_build_header(u_char *data, size_t *datalength, u_char type, size_t length); static u_char* _asn_build_length(u_char *data, size_t *datalength, size_t length); static const u_char* _asn_parse_header(const u_char *data, u_char *type, size_t *length); static const u_char* _asn_parse_length(const u_char *data, size_t *length); u_char* _asn_build_string(u_char *data, size_t *datalength, u_char type, const u_char *string, size_t stringlength); const u_char* _asn_parse_string(const u_char *data, u_char *type, u_char *string, size_t *stringlength);
/************************************************************************/ /* asn1.cpp */ /************************************************************************/ #include "stdafx.h" #include "asn1.h" /* 参数 stringlength 为 strlen(string) 得到的结果*/ u_char* _asn_build_string(u_char *data, size_t *datalength, u_char type, const u_char *string, size_t stringlength) { if (nullptr == data || nullptr == datalength || nullptr == string) return nullptr; if ((data = _asn_build_header(data, datalength, type, stringlength)) == nullptr) return nullptr; if (*datalength < stringlength) return nullptr; memmove_s(data, *datalength, string, stringlength); *datalength -= stringlength; data += stringlength; return data; } /* 参数 stringlength 【in】string的缓冲区大小 【out】string的有效长度 */ const u_char* _asn_parse_string(const u_char *data, u_char *type, u_char *string, size_t *stringlength) { if (nullptr == data || nullptr == type || nullptr == string || nullptr == stringlength) return nullptr; size_t asn_length = 0; if ((data = _asn_parse_header(data, type, &asn_length)) == nullptr) return nullptr; if (*stringlength < asn_length) return nullptr; /* 字符串 string 缓冲区太小 */ memmove_s(string, *stringlength, data, asn_length); if (*stringlength > asn_length) string[asn_length] = '\0'; *stringlength = asn_length; data += asn_length; return data; } u_char* _asn_build_header(u_char *data, size_t *datalength, u_char type, size_t length) { if (nullptr == data || nullptr == datalength) return nullptr; if (*datalength < 1) return nullptr; *data++ = type; --(*datalength); return _asn_build_length(data, datalength, length); } const u_char* _asn_parse_header(const u_char *data, u_char *type, size_t *length) { if (nullptr == data || nullptr == type || nullptr == length) return nullptr; *type = *data++; return _asn_parse_length(data, length); } /* 支持的最大长度为65535字节 */ u_char* _asn_build_length(u_char *data, size_t *datalength, size_t length) { if (nullptr == data || nullptr == datalength) return nullptr; const u_char *initdata = data; if (length < 0x80) { /* 定长短格式 */ if (*datalength < 1) return nullptr; *data++ = (u_char)length; } else if (length <= 0xFF) { /* 定长长格式,长度占用一个八位组 */ if (*datalength < 2) return nullptr; *data++ = (u_char)0x81; /* Length 段的第一个八位组为 10000001,对应的十六进制即为 0x81 */ *data++ = (u_char)length; /* 将 length 的低 bit0~bit7 赋值给 *data,并使 data 指向 data 的下一个八位组 */ } else if (length <= 0xFFFF) { /* 定长长格式,长度占用两个八位组 */ if (*datalength < 3) return nullptr; *data++ = (u_char)0x82; /* Length 段的第一个八位组为 10000010,对应的十六进制即为 0x82 */ *data++ = (u_char)(length >> 8); /* 将 length 的低 bit8~bit15 赋值给 *data,并使 data 指向 data 的下一个八位组 */ *data++ = (u_char)length; /* 将 length 的低 bit0~bit7 赋值给 *data,并使 data 指向 data 的下一个八位组 */ } else { /* 长度太长,不支持 */ return nullptr; } *datalength -= (data - initdata); return data; } const u_char* _asn_parse_length(const u_char *data, size_t *length) { if (nullptr == data || nullptr == length) return nullptr; u_char lengthbyte = *data++; if (lengthbyte < 0x80) { /* 定长短格式 */ *length = lengthbyte; } else if (lengthbyte == 0x80) { /* 0x80 为变长格式,不支持 */ return nullptr; } else { /* 定长长格式 */ size_t bytes = (size_t)(lengthbyte - 0x80); /* 计算 Length 段占用的八位组个数 */ if (bytes > sizeof(*length)) return nullptr; /* Length 段太长 */ *length = 0; /* 消除 *length 的初值可能对 *length 最终结果带来的影响 */ while (bytes--) { *length <<= 8; *length |= (*data++); } } return data; }
// snmp_get.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <stdio.h> #include <string.h> #include "asn1.h" void print(const u_char *data, size_t datalength) { if (nullptr == data || datalength < 1) return; for (size_t i = 0; i < datalength; ++i) { printf("0x%.2X ", data[i]); } printf("\n\n"); } int _tmain(int argc, _TCHAR* argv[]) { u_char buf[10000]; size_t validlen = sizeof(buf); u_char msg[] = "0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 "; if (nullptr == _asn_build_string(buf, &validlen, 0x04, msg, strlen((char *)msg))) return -1; print(buf, sizeof(buf)-validlen); u_char msg2[600]; size_t msg2_len = sizeof(msg2); u_char type; validlen = sizeof(buf)-validlen; _asn_parse_string(buf, &type, msg2, &msg2_len); print(msg2, msg2_len); return 0; }