722 lines
21 KiB
C
722 lines
21 KiB
C
|
/*
|
||
|
SNMP protocol handler
|
||
|
|
||
|
This module does two primary things:
|
||
|
|
||
|
#1 track sequence number
|
||
|
|
||
|
Like TCP, we can use seqno-cookies to match requrests with
|
||
|
replies. All SNMP packets have a "request-id" field to match
|
||
|
requests with replies, so we can fill this in with our cookies
|
||
|
This requires any SNMP template to reserve 4 bytes for this
|
||
|
field.
|
||
|
|
||
|
#2 parse the response
|
||
|
|
||
|
This code will report any OIDs in the response along with
|
||
|
their values. However, you should probably hard-code a
|
||
|
"MIB" so that we can translate OIDs into shorter names
|
||
|
as well as format their values correctly. You can look
|
||
|
at sysName and sysDescr for an example of how this works.
|
||
|
|
||
|
NOTES for IDS LULZ:
|
||
|
|
||
|
The default template packet is designed to evade IDS that looks
|
||
|
for OIDS, but inserting "nul" bytes into the OID. This is the
|
||
|
difference between "pattern-matching" IDS and "protocol-analysis"
|
||
|
IDS: those using protocol-analysis decodes the entire protocol,
|
||
|
whereas pattern-matchers don't. How protocol-analysis works is
|
||
|
demonstrated in this module, as it has to analyze the SNMP
|
||
|
protocol itself in order to decode responses. It can handle
|
||
|
responses that that have the deliberate "nul" insertion technique,
|
||
|
as shown in the self-test.
|
||
|
|
||
|
One of the useful tricks is to exploit DFA pattern matches. In
|
||
|
this case, the DFA pattern-matcher that ultimately matches on
|
||
|
the OIDs has been tweaked to handle the nuls as part of the
|
||
|
pattern-matching process.
|
||
|
|
||
|
*/
|
||
|
#include "proto-snmp.h"
|
||
|
#include <stdint.h>
|
||
|
#include <stdlib.h>
|
||
|
#include "smack.h"
|
||
|
#include "util-safefunc.h"
|
||
|
#include "output.h"
|
||
|
#include "masscan-app.h"
|
||
|
#include "proto-preprocess.h"
|
||
|
#include "proto-banner1.h"
|
||
|
#include "syn-cookie.h"
|
||
|
#include "massip-port.h"
|
||
|
|
||
|
static struct SMACK *global_mib;
|
||
|
|
||
|
|
||
|
/****************************************************************************
|
||
|
* We parse an SNMP packet into this structure
|
||
|
****************************************************************************/
|
||
|
struct SNMP
|
||
|
{
|
||
|
uint64_t version;
|
||
|
uint64_t pdu_tag;
|
||
|
const unsigned char *community;
|
||
|
uint64_t community_length;
|
||
|
uint64_t request_id;
|
||
|
uint64_t error_index;
|
||
|
uint64_t error_status;
|
||
|
};
|
||
|
|
||
|
/****************************************************************************
|
||
|
* This is the "compiled MIB" essentially. At program startup, we compile
|
||
|
* this into an OID tree. We use this to replace OIDs with names.
|
||
|
****************************************************************************/
|
||
|
static struct SnmpOid {
|
||
|
const char *oid;
|
||
|
const char *name;
|
||
|
} mib[] = {
|
||
|
{"43.1006.51.341332", "selftest"}, /* for regression test */
|
||
|
{"43", "iso.org"},
|
||
|
{"43.6", "dod"},
|
||
|
{"43.6.1", "inet"},
|
||
|
{"43.6.1.2", "mgmt"},
|
||
|
{"43.6.1.2.1", "mib2"},
|
||
|
{"43.6.1.2.1.", "sys"},
|
||
|
{"43.6.1.2.1.1.1", "sysDescr"},
|
||
|
{"43.6.1.2.1.1.2", "sysObjectID"},
|
||
|
{"43.6.1.2.1.1.3", "sysUpTime"},
|
||
|
{"43.6.1.2.1.1.4", "sysContact"},
|
||
|
{"43.6.1.2.1.1.5", "sysName"},
|
||
|
{"43.6.1.2.1.1.6", "sysLocation"},
|
||
|
{"43.6.1.2.1.1.7", "sysServices"},
|
||
|
{"43.6.1.4", "priv"},
|
||
|
{"43.6.1.4.1", "enterprise"},
|
||
|
{"43.6.1.4.1.2001", "okidata"},
|
||
|
{0,0},
|
||
|
};
|
||
|
|
||
|
/****************************************************************************
|
||
|
* An ASN.1 length field has two formats.
|
||
|
* - if the high-order bit of the length byte is clear, then it
|
||
|
* encodes a length between 0 and 127.
|
||
|
* - if the high-order bit is set, then the length byte is a
|
||
|
* length-of-length, where the low order bits dictate the number of
|
||
|
* remaining bytes to be used in the length.
|
||
|
****************************************************************************/
|
||
|
static uint64_t
|
||
|
asn1_length(const unsigned char *px, uint64_t length, uint64_t *r_offset)
|
||
|
{
|
||
|
uint64_t result;
|
||
|
|
||
|
/* check for errors */
|
||
|
if ( (*r_offset >= length)
|
||
|
|| ((px[*r_offset] & 0x80)
|
||
|
&& ((*r_offset) + (px[*r_offset]&0x7F) >= length))) {
|
||
|
*r_offset = length;
|
||
|
return 0xFFFFffff;
|
||
|
}
|
||
|
|
||
|
/* grab the byte's value */
|
||
|
result = px[(*r_offset)++];
|
||
|
|
||
|
|
||
|
if (result & 0x80) {
|
||
|
unsigned length_of_length = result & 0x7F;
|
||
|
if (length_of_length == 0) {
|
||
|
*r_offset = length;
|
||
|
return 0xFFFFffff;
|
||
|
}
|
||
|
result = 0;
|
||
|
while (length_of_length) {
|
||
|
result = result * 256 + px[(*r_offset)++];
|
||
|
if (result > 0x10000) {
|
||
|
*r_offset = length;
|
||
|
return 0xFFFFffff;
|
||
|
}
|
||
|
length_of_length--;
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Extract an integer. Note
|
||
|
****************************************************************************/
|
||
|
static uint64_t
|
||
|
asn1_integer(const unsigned char *px, uint64_t length, uint64_t *r_offset)
|
||
|
{
|
||
|
uint64_t int_length;
|
||
|
uint64_t result;
|
||
|
|
||
|
if (px[(*r_offset)++] != 0x02) {
|
||
|
*r_offset = length;
|
||
|
return 0xFFFFffff;
|
||
|
}
|
||
|
|
||
|
int_length = asn1_length(px, length, r_offset);
|
||
|
if (int_length == 0xFFFFffff) {
|
||
|
*r_offset = length;
|
||
|
return 0xFFFFffff;
|
||
|
}
|
||
|
if (*r_offset + int_length > length) {
|
||
|
*r_offset = length;
|
||
|
return 0xFFFFffff;
|
||
|
}
|
||
|
if (int_length > 20) {
|
||
|
*r_offset = length;
|
||
|
return 0xFFFFffff;
|
||
|
}
|
||
|
|
||
|
result = 0;
|
||
|
while (int_length--)
|
||
|
result = result * 256 + px[(*r_offset)++];
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
****************************************************************************/
|
||
|
static unsigned
|
||
|
asn1_tag(const unsigned char *px, uint64_t length, uint64_t *r_offset)
|
||
|
{
|
||
|
if (*r_offset >= length)
|
||
|
return 0;
|
||
|
return px[(*r_offset)++];
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
****************************************************************************/
|
||
|
static uint64_t
|
||
|
next_id(const unsigned char *oid, unsigned *offset, uint64_t oid_length)
|
||
|
{
|
||
|
uint64_t result = 0;
|
||
|
while (*offset < oid_length && (oid[*offset] & 0x80)) {
|
||
|
result <<= 7;
|
||
|
result |= oid[(*offset)++]&0x7F;
|
||
|
}
|
||
|
if (*offset < oid_length) {
|
||
|
result <<= 7;
|
||
|
result |= oid[(*offset)++]&0x7F;
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
****************************************************************************/
|
||
|
static void
|
||
|
snmp_banner_oid(const unsigned char *oid, size_t oid_length,
|
||
|
struct BannerOutput *banout)
|
||
|
{
|
||
|
unsigned i;
|
||
|
size_t id;
|
||
|
unsigned offset;
|
||
|
unsigned state;
|
||
|
size_t found_id = SMACK_NOT_FOUND;
|
||
|
size_t found_offset = 0;
|
||
|
|
||
|
/*
|
||
|
* Find the var name
|
||
|
*/
|
||
|
state = 0;
|
||
|
for (offset=0; offset<oid_length; ) {
|
||
|
|
||
|
id = smack_search_next( global_mib,
|
||
|
&state,
|
||
|
oid,
|
||
|
&offset,
|
||
|
(unsigned)oid_length);
|
||
|
if (id != SMACK_NOT_FOUND) {
|
||
|
found_id = id;
|
||
|
found_offset = offset;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Do the string */
|
||
|
if (found_id != SMACK_NOT_FOUND) {
|
||
|
const char *str = mib[found_id].name;
|
||
|
banout_append(banout, PROTO_SNMP, str, strlen(str));
|
||
|
}
|
||
|
|
||
|
/* Do remaining OIDs */
|
||
|
for (i=(unsigned)found_offset; i<oid_length; ) {
|
||
|
char foo[32] = {0};
|
||
|
uint64_t x = next_id(oid, &i, oid_length);
|
||
|
|
||
|
if (x == 0 && i >= oid_length)
|
||
|
break;
|
||
|
|
||
|
snprintf(foo, sizeof(foo), ".%" PRIu64 "", x);
|
||
|
banout_append(banout, PROTO_SNMP, foo, strlen(foo));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
****************************************************************************/
|
||
|
static void
|
||
|
snmp_banner(const unsigned char *oid, size_t oid_length,
|
||
|
uint64_t var_tag,
|
||
|
const unsigned char *var, size_t var_length,
|
||
|
struct BannerOutput *banout)
|
||
|
{
|
||
|
size_t i;
|
||
|
|
||
|
banout_newline(banout, PROTO_SNMP);
|
||
|
|
||
|
/* print the OID */
|
||
|
snmp_banner_oid(oid, oid_length,
|
||
|
banout);
|
||
|
|
||
|
banout_append_char(banout, PROTO_SNMP, ':');
|
||
|
|
||
|
switch (var_tag) {
|
||
|
case 2:
|
||
|
{
|
||
|
char foo[32];
|
||
|
uint64_t result = 0;
|
||
|
for (i=0; i<var_length; i++)
|
||
|
result = result<<8 | var[i];
|
||
|
snprintf(foo, sizeof(foo), "%" PRIu64 "", result);
|
||
|
banout_append(banout, PROTO_SNMP, foo, strlen(foo));
|
||
|
}
|
||
|
break;
|
||
|
case 6:
|
||
|
snmp_banner_oid(var, var_length,
|
||
|
banout);
|
||
|
break;
|
||
|
case 4:
|
||
|
default:
|
||
|
/* TODO: this needs to be normalized */
|
||
|
banout_append(banout, PROTO_SNMP, var, var_length);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************************************************************
|
||
|
* This is a parser for SNMP packets.
|
||
|
*
|
||
|
* TODO: only SNMPv0 is supported, the parser will have to be extended for
|
||
|
* newer SNMP.
|
||
|
****************************************************************************/
|
||
|
static void
|
||
|
snmp_parse(const unsigned char *px, uint64_t length,
|
||
|
struct BannerOutput *banout,
|
||
|
unsigned *request_id)
|
||
|
{
|
||
|
uint64_t offset=0;
|
||
|
uint64_t outer_length;
|
||
|
struct SNMP snmp[1];
|
||
|
|
||
|
memset(&snmp, 0, sizeof(*snmp));
|
||
|
|
||
|
/* tag */
|
||
|
if (asn1_tag(px, length, &offset) != 0x30)
|
||
|
return;
|
||
|
|
||
|
/* length */
|
||
|
outer_length = asn1_length(px, length, &offset);
|
||
|
if (length > outer_length + offset)
|
||
|
length = outer_length + offset;
|
||
|
|
||
|
/* Version */
|
||
|
snmp->version = asn1_integer(px, length, &offset);
|
||
|
if (snmp->version != 0)
|
||
|
return;
|
||
|
|
||
|
/* Community */
|
||
|
if (asn1_tag(px, length, &offset) != 0x04)
|
||
|
return;
|
||
|
snmp->community_length = asn1_length(px, length, &offset);
|
||
|
snmp->community = px+offset;
|
||
|
offset += snmp->community_length;
|
||
|
|
||
|
/* PDU */
|
||
|
snmp->pdu_tag = asn1_tag(px, length, &offset);
|
||
|
if (snmp->pdu_tag < 0xA0 || 0xA5 < snmp->pdu_tag)
|
||
|
return;
|
||
|
outer_length = asn1_length(px, length, &offset);
|
||
|
if (length > outer_length + offset)
|
||
|
length = outer_length + offset;
|
||
|
|
||
|
/* Request ID */
|
||
|
snmp->request_id = asn1_integer(px, length, &offset);
|
||
|
*request_id = (unsigned)snmp->request_id;
|
||
|
snmp->error_status = asn1_integer(px, length, &offset);
|
||
|
snmp->error_index = asn1_integer(px, length, &offset);
|
||
|
|
||
|
/* Varbind List */
|
||
|
if (asn1_tag(px, length, &offset) != 0x30)
|
||
|
return;
|
||
|
outer_length = asn1_length(px, length, &offset);
|
||
|
if (length > outer_length + offset)
|
||
|
length = outer_length + offset;
|
||
|
|
||
|
|
||
|
/* Var-bind list */
|
||
|
while (offset < length) {
|
||
|
uint64_t varbind_length;
|
||
|
uint64_t varbind_end;
|
||
|
if (px[offset++] != 0x30) {
|
||
|
break;
|
||
|
}
|
||
|
varbind_length = asn1_length(px, length, &offset);
|
||
|
if (varbind_length == 0xFFFFffff)
|
||
|
break;
|
||
|
varbind_end = offset + varbind_length;
|
||
|
if (varbind_end > length) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* OID */
|
||
|
if (asn1_tag(px,length,&offset) != 6)
|
||
|
return;
|
||
|
else {
|
||
|
uint64_t oid_length = asn1_length(px, length, &offset);
|
||
|
const unsigned char *oid = px+offset;
|
||
|
uint64_t var_tag;
|
||
|
uint64_t var_length;
|
||
|
const unsigned char *var;
|
||
|
|
||
|
offset += oid_length;
|
||
|
if (offset > length)
|
||
|
return;
|
||
|
|
||
|
var_tag = asn1_tag(px,length,&offset);
|
||
|
var_length = asn1_length(px, length, &offset);
|
||
|
var = px+offset;
|
||
|
|
||
|
offset += var_length;
|
||
|
if (offset > length)
|
||
|
return;
|
||
|
|
||
|
if (var_tag == 5)
|
||
|
continue; /* null */
|
||
|
|
||
|
snmp_banner(oid, (size_t)oid_length, var_tag, var, (size_t)var_length, banout);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
****************************************************************************/
|
||
|
unsigned
|
||
|
snmp_set_cookie(unsigned char *px, size_t length, uint64_t seqno)
|
||
|
{
|
||
|
uint64_t offset=0;
|
||
|
uint64_t outer_length;
|
||
|
uint64_t version;
|
||
|
uint64_t tag;
|
||
|
uint64_t len;
|
||
|
|
||
|
|
||
|
/* tag */
|
||
|
if (asn1_tag(px, length, &offset) != 0x30)
|
||
|
return 0;
|
||
|
|
||
|
/* length */
|
||
|
outer_length = asn1_length(px, length, &offset);
|
||
|
if (length > outer_length + offset)
|
||
|
length = (size_t)(outer_length + offset);
|
||
|
|
||
|
/* Version */
|
||
|
version = asn1_integer(px, length, &offset);
|
||
|
if (version != 0)
|
||
|
return 0;
|
||
|
|
||
|
/* Community */
|
||
|
if (asn1_tag(px, length, &offset) != 0x04)
|
||
|
return 0;
|
||
|
offset += asn1_length(px, length, &offset);
|
||
|
|
||
|
/* PDU */
|
||
|
tag = asn1_tag(px, length, &offset);
|
||
|
if (tag < 0xA0 || 0xA5 < tag)
|
||
|
return 0;
|
||
|
outer_length = asn1_length(px, length, &offset);
|
||
|
if (length > outer_length + offset)
|
||
|
length = (size_t)(outer_length + offset);
|
||
|
|
||
|
/* Request ID */
|
||
|
asn1_tag(px, length, &offset);
|
||
|
len = asn1_length(px, length, &offset);
|
||
|
switch (len) {
|
||
|
case 0:
|
||
|
return 0;
|
||
|
case 1:
|
||
|
px[offset+0] = (unsigned char)(seqno>>0)&0x7F;
|
||
|
return seqno & 0x7F;
|
||
|
case 2:
|
||
|
px[offset+0] = (unsigned char)(seqno>>8)&0x7F;
|
||
|
px[offset+1] = (unsigned char)(seqno>>0);
|
||
|
return seqno & 0x7fff;
|
||
|
case 3:
|
||
|
px[offset+0] = (unsigned char)(seqno>>16)&0x7F;
|
||
|
px[offset+1] = (unsigned char)(seqno>>8);
|
||
|
px[offset+2] = (unsigned char)(seqno>>0);
|
||
|
return seqno & 0x7fffFF;
|
||
|
case 4:
|
||
|
px[offset+0] = (unsigned char)(seqno>>24)&0x7F;
|
||
|
px[offset+1] = (unsigned char)(seqno>>16);
|
||
|
px[offset+2] = (unsigned char)(seqno>>8);
|
||
|
px[offset+3] = (unsigned char)(seqno>>0);
|
||
|
return seqno & 0x7fffFFFF;
|
||
|
case 5:
|
||
|
px[offset+0] = 0;
|
||
|
px[offset+1] = (unsigned char)(seqno>>24);
|
||
|
px[offset+2] = (unsigned char)(seqno>>16);
|
||
|
px[offset+3] = (unsigned char)(seqno>>8);
|
||
|
px[offset+4] = (unsigned char)(seqno>>0);
|
||
|
return seqno & 0xffffFFFF;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#define TWO_BYTE ((unsigned long long)(~0)<<7)
|
||
|
#define THREE_BYTE ((unsigned long long)(~0)<<14)
|
||
|
#define FOUR_BYTE ((unsigned long long)(~0)<<21)
|
||
|
#define FIVE_BYTE ((unsigned long long)(~0)<<28)
|
||
|
|
||
|
|
||
|
/****************************************************************************
|
||
|
****************************************************************************/
|
||
|
static unsigned
|
||
|
id_prefix_count(unsigned id)
|
||
|
{
|
||
|
if (id & FIVE_BYTE)
|
||
|
return 4;
|
||
|
if (id & FOUR_BYTE)
|
||
|
return 3;
|
||
|
if (id & THREE_BYTE)
|
||
|
return 2;
|
||
|
if (id & TWO_BYTE)
|
||
|
return 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Convert text OID to binary
|
||
|
****************************************************************************/
|
||
|
static unsigned
|
||
|
convert_oid(unsigned char *dst, size_t sizeof_dst, const char *src)
|
||
|
{
|
||
|
size_t offset = 0;
|
||
|
|
||
|
while (*src) {
|
||
|
const char *next_src;
|
||
|
unsigned id;
|
||
|
unsigned count;
|
||
|
unsigned i;
|
||
|
|
||
|
while (*src == '.')
|
||
|
src++;
|
||
|
|
||
|
id = (unsigned)strtoul(src, (char**)&next_src, 0);
|
||
|
if (src == next_src)
|
||
|
break;
|
||
|
else
|
||
|
src = next_src;
|
||
|
|
||
|
count = id_prefix_count(id);
|
||
|
for (i=count; i>0; i--) {
|
||
|
if (offset < sizeof_dst)
|
||
|
dst[offset++] = ((id>>(7*i)) & 0x7F) | 0x80;
|
||
|
}
|
||
|
if (offset < sizeof_dst)
|
||
|
dst[offset++] = (id & 0x7F);
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
return (unsigned)offset;
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
* Handles an SNMP response.
|
||
|
****************************************************************************/
|
||
|
unsigned
|
||
|
handle_snmp(struct Output *out, time_t timestamp,
|
||
|
const unsigned char *px, unsigned length,
|
||
|
struct PreprocessedInfo *parsed,
|
||
|
uint64_t entropy
|
||
|
)
|
||
|
{
|
||
|
ipaddress ip_them = parsed->src_ip;
|
||
|
ipaddress ip_me = parsed->dst_ip;
|
||
|
unsigned port_them = parsed->port_src;
|
||
|
unsigned port_me = parsed->port_dst;
|
||
|
unsigned seqno;
|
||
|
unsigned request_id = 0;
|
||
|
struct BannerOutput banout[1];
|
||
|
|
||
|
UNUSEDPARM(length);
|
||
|
|
||
|
/* Initialize the "banner output" module that we'll use to print
|
||
|
* pretty text in place of the raw packet */
|
||
|
banout_init(banout);
|
||
|
|
||
|
/* Parse the SNMP packet */
|
||
|
snmp_parse(
|
||
|
px + parsed->app_offset, /* incoming SNMP response */
|
||
|
parsed->app_length, /* length of SNMP response */
|
||
|
banout, /* banner printing */
|
||
|
&request_id); /* syn-cookie info */
|
||
|
|
||
|
/* Validate the "syn-cookie" style information. In the case of SNMP,
|
||
|
* this will be held in the "request-id" field. If the cookie isn't
|
||
|
* a good one, then we'll ignore the response */
|
||
|
seqno = (unsigned)syn_cookie(ip_them, port_them | Templ_UDP, ip_me, port_me, entropy);
|
||
|
if ((seqno&0x7FFFffff) != request_id)
|
||
|
return 1;
|
||
|
|
||
|
/* Print the banner information, or save to a file, depending */
|
||
|
output_report_banner(
|
||
|
out, timestamp,
|
||
|
ip_them, 17, parsed->port_src,
|
||
|
PROTO_SNMP,
|
||
|
parsed->ip_ttl,
|
||
|
banout_string(banout, PROTO_SNMP),
|
||
|
banout_string_length(banout, PROTO_SNMP));
|
||
|
|
||
|
/* Free memory for the banner, if there was any allocated */
|
||
|
banout_release(banout);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************************************************************
|
||
|
* We need to initialize the OID/MIB parser
|
||
|
* This should be called on program startup.
|
||
|
* This is so that we can show short names, like "sysName", rather than
|
||
|
* the entire OID.
|
||
|
****************************************************************************/
|
||
|
void
|
||
|
snmp_init(void)
|
||
|
{
|
||
|
unsigned i;
|
||
|
|
||
|
/* We use an Aho-Corasick pattern matcher for this. Not necessarily
|
||
|
* the most efficient, but also not bad */
|
||
|
global_mib = smack_create("snmp-mib", 0);
|
||
|
|
||
|
/* We just go through the table of OIDs and add them all one by
|
||
|
* one */
|
||
|
for (i=0; mib[i].name; i++) {
|
||
|
unsigned char pattern[256];
|
||
|
unsigned len;
|
||
|
|
||
|
len = convert_oid(pattern, sizeof(pattern), mib[i].oid);
|
||
|
|
||
|
smack_add_pattern( global_mib,
|
||
|
pattern,
|
||
|
len,
|
||
|
i,
|
||
|
SMACK_ANCHOR_BEGIN | SMACK_SNMP_HACK
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/* Now that we've added all the OIDs, we need to compile this into
|
||
|
* an efficient data structure. Later, when we get packets, we'll
|
||
|
* use this for searching */
|
||
|
smack_compile(global_mib);
|
||
|
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
****************************************************************************/
|
||
|
static int
|
||
|
snmp_selftest_banner()
|
||
|
{
|
||
|
static const unsigned char snmp_response[] = {
|
||
|
0x30, 0x39,
|
||
|
0x02, 0x01, 0x00,
|
||
|
0x04, 0x06, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63,
|
||
|
0xA2, 0x2C,
|
||
|
0x02, 0x01, 0x26,
|
||
|
0x02, 0x01, 0x00,
|
||
|
0x02, 0x01, 0x00,
|
||
|
0x30, 0x21,
|
||
|
0x30, 0x1F,
|
||
|
0x06, 0x09,
|
||
|
0x2B, 0x06, 0x01, 0x80, 0x02, 0x01, 0x01, 0x02, 0x00,
|
||
|
0x06, 0x12,
|
||
|
0x2B, 0x06, 0x01, 0x04, 0x01, 0x8F, 0x51, 0x01, 0x01, 0x01, 0x82, 0x29, 0x5D, 0x01, 0x1B, 0x02, 0x02, 0x01,
|
||
|
};
|
||
|
unsigned request_id = 0;
|
||
|
struct BannerOutput banout[1];
|
||
|
banout_init(banout);
|
||
|
|
||
|
/* parse a test packet */
|
||
|
snmp_parse( snmp_response,
|
||
|
sizeof(snmp_response),
|
||
|
banout,
|
||
|
&request_id
|
||
|
);
|
||
|
|
||
|
|
||
|
if (request_id != 0x26)
|
||
|
return 1;
|
||
|
|
||
|
{
|
||
|
const unsigned char *str = banout_string(banout, PROTO_SNMP);
|
||
|
size_t str_length = banout_string_length(banout, PROTO_SNMP);
|
||
|
if (memcmp(str, "sysObjectID:okidata.1.1.1.297.93.1.27.2.2.1", str_length) != 0)
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
banout_release(banout);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/****************************************************************************
|
||
|
****************************************************************************/
|
||
|
int
|
||
|
snmp_selftest(void)
|
||
|
{
|
||
|
static const unsigned char xx[] = {
|
||
|
43, 0x80|7, 110, 51, 0x80|20, 0x80|106, 84,
|
||
|
};
|
||
|
size_t i;
|
||
|
unsigned state;
|
||
|
unsigned offset;
|
||
|
size_t found_id = SMACK_NOT_FOUND;
|
||
|
|
||
|
|
||
|
if (snmp_selftest_banner())
|
||
|
return 1;
|
||
|
|
||
|
/*
|
||
|
* test of searching OIDs
|
||
|
*/
|
||
|
state = 0;
|
||
|
offset = 0;
|
||
|
while (offset < sizeof(xx)) {
|
||
|
i = smack_search_next( global_mib,
|
||
|
&state,
|
||
|
xx,
|
||
|
&offset,
|
||
|
(unsigned)sizeof(xx)
|
||
|
);
|
||
|
if (i != SMACK_NOT_FOUND)
|
||
|
found_id = i;
|
||
|
}
|
||
|
if (found_id == SMACK_NOT_FOUND) {
|
||
|
fprintf(stderr, "snmp: oid parser failed\n");
|
||
|
return 1;
|
||
|
}
|
||
|
if (strcmp(mib[found_id].name, "selftest") != 0) {
|
||
|
fprintf(stderr, "snmp: oid parser failed\n");
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|