File: [local] / src / usr.sbin / snmpd / mib.y (download)
Revision 1.2, Tue Feb 20 12:41:13 2024 UTC (3 months, 2 weeks ago) by martijn
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, HEAD Changes since 1.1: +4 -4 lines
strptime() only touches the fields specified in the format string,
meaning there could be garbage left in the other fields. Somehow this
only caused issues in mktime() when /etc/localtime is set to GMT.
Initialize tm to 0.
While here fix a type-O in the format string and make the invalid
strlen for LAST-UPDATED message more consistent with the other 2 error
messages.
Found by and OK sthen@
|
/* $OpenBSD: mib.y,v 1.2 2024/02/20 12:41:13 martijn Exp $ */
/*
* Copyright (c) 2023 Martijn van Duren <martijn@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
%{
#include <sys/tree.h>
#include <assert.h>
#include <ber.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <strings.h>
#include <time.h>
#include "log.h"
#include "mib.h"
/* RFC2578 section 3.1 */
#define DESCRIPTOR_MAX 64
/* Values from real life testing, could be adjusted */
#define ITEM_MAX DESCRIPTOR_MAX
#define MODULENAME_MAX 64
#define SYMBOLS_MAX 256
#define IMPORTS_MAX 16
#define TEXT_MAX 16384
#ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif
struct objidcomponent {
enum {
OCT_DESCRIPTOR,
OCT_NUMBER,
OCT_NAMEANDNUMBER
} type;
uint32_t number;
char name[DESCRIPTOR_MAX + 1];
};
struct oid_unresolved {
/* Unusual to have long lists of unresolved components */
struct objidcomponent bo_id[16];
size_t bo_n;
};
struct oid_resolved {
uint32_t *bo_id;
size_t bo_n;
};
enum status {
CURRENT,
DEPRECATED,
OBSOLETE
};
enum access {
NOTACCESSIBLE,
ACCESSIBLEFORNOTIFY,
READONLY,
READWRITE,
READCREATE
};
struct objectidentity {
enum status status;
char *description;
char *reference;
};
struct objecttype {
void *syntax;
char *units;
enum access maxaccess;
enum status status;
char *description;
char *reference;
void *index;
void *defval;
};
struct notificationtype {
void *objects;
enum status status;
char *description;
char *reference;
};
struct textualconvention {
char *displayhint;
enum status status;
char *description;
char *reference;
void *syntax;
};
struct item {
char name[DESCRIPTOR_MAX + 1];
enum item_type {
IT_OID,
IT_MACRO,
IT_MODULE_IDENTITY,
IT_OBJECT_IDENTITY,
IT_APPLICATIONSYNTAX,
IT_OBJECT_TYPE,
IT_NOTIFICATION_TYPE,
IT_TEXTUAL_CONVENTION,
IT_OBJECT_GROUP,
IT_NOTIFICATION_GROUP,
IT_MODULE_COMPLIANCE,
IT_AGENT_CAPABITIES
} type;
int resolved;
struct module *module;
union {
struct oid_unresolved *oid_unresolved;
struct oid_resolved oid;
};
union {
struct objectidentity objectidentity;
struct objecttype objecttype;
struct notificationtype notificationtype;
struct textualconvention textualconvention;
};
/* Global case insensitive */
RB_ENTRY(item) entrygci;
/* Module case insensitive */
RB_ENTRY(item) entryci;
/* Module case sensitive */
RB_ENTRY(item) entrycs;
/* Global oid */
RB_ENTRY(item) entry;
};
struct import_symbol {
char name[DESCRIPTOR_MAX + 1];
struct item *item;
};
struct import {
char name[MODULENAME_MAX + 1];
struct module *module;
struct import_symbol symbols[SYMBOLS_MAX];
size_t nsymbols;
};
static struct module {
char name[MODULENAME_MAX + 1];
int8_t resolved;
time_t lastupdated;
struct import *imports;
RB_HEAD(itemscs, item) itemscs;
RB_HEAD(itemsci, item) itemsci;
RB_ENTRY(module) entryci;
RB_ENTRY(module) entrycs;
} *module;
int yylex(void);
static void yyerror(const char *, ...)
__attribute__((__format__ (printf, 1, 2)));
void mib_defaults(void);
void mib_modulefree(struct module *);
int mib_imports_add(char *, char **);
int mib_oid_append(struct oid_unresolved *,
const struct objidcomponent *);
int mib_macro(const char *);
int mib_oid_concat(struct oid_unresolved *,
const struct oid_unresolved *);
struct item *mib_item(const char *, enum item_type);
int mib_item_oid(struct item *,
const struct oid_unresolved *);
int mib_macro(const char *);
int mib_applicationsyntax(const char *);
struct item *mib_oid(const char *, const struct oid_unresolved *);
int mib_moduleidentity(const char *, time_t, const char *,
const char *, const char *, const struct oid_unresolved *);
int mib_objectidentity(const char *, enum status, const char *,
const char *, const struct oid_unresolved *);
int mib_objecttype(const char *, void *, const char *,
enum access, enum status, const char *, const char *,
void *, void *, const struct oid_unresolved *);
int mib_notificationtype(const char *, void *, enum status,
const char *, const char *, const struct oid_unresolved *);
int mib_textualconvetion(const char *, const char *, enum status,
const char *, const char *, void *);
int mib_objectgroup(const char *, void *, enum status,
const char *, const char *, const struct oid_unresolved *);
int mib_notificationgroup(const char *, void *, enum status,
const char *, const char *, const struct oid_unresolved *);
int mib_modulecompliance(const char *, enum status, const char *,
const char *, void *, const struct oid_unresolved *);
struct item *mib_item_find(struct item *, const char *);
struct item *mib_item_parent(struct ber_oid *);
int mib_resolve_oid(struct oid_resolved *,
struct oid_unresolved *, struct item *);
int mib_resolve_item(struct item *);
int mib_resolve_module(struct module *);
int module_cmp_cs(struct module *, struct module *);
int module_cmp_ci(struct module *, struct module *);
int item_cmp_cs(struct item *, struct item *);
int item_cmp_ci(struct item *, struct item *);
int item_cmp_oid(struct item *, struct item *);
RB_HEAD(modulesci, module) modulesci = RB_INITIALIZER(&modulesci);
RB_HEAD(modulescs, module) modulescs = RB_INITIALIZER(&modulescs);
RB_HEAD(items, item) items = RB_INITIALIZER(&items);
RB_HEAD(itemsgci, item) itemsci = RB_INITIALIZER(&itemsci);
/*
* Use case sensitive matching internally (for resolving IMPORTS) and
* case sensitive matching, followed by case insensitive matching
* for end-user resolving (e.g. mib_string2oid()).
* It shouldn't happen there's case-based overlap in module/item names,
* but allow all to be resolved in case there is.
*/
RB_PROTOTYPE_STATIC(modulesci, module, entryci, module_cmp_ci);
RB_PROTOTYPE_STATIC(modulescs, module, entrycs, module_cmp_cs);
/*
* mib_string2oid() should match case insensitive on:
* <module>::<descriptor>
* <descriptor>
*/
RB_PROTOTYPE_STATIC(itemsgci, item, entrygci, item_cmp_ci);
RB_PROTOTYPE_STATIC(itemsci, item, entryci, item_cmp_ci);
RB_PROTOTYPE_STATIC(itemscs, item, entrycs, item_cmp_cs);
RB_PROTOTYPE_STATIC(items, item, entry, item_cmp_oid);
struct file {
FILE *stream;
const char *name;
size_t lineno;
enum {
FILE_UNDEFINED,
FILE_ASN1,
FILE_SMI2
} state;
} file;
typedef union {
char string[TEXT_MAX];
unsigned long long number;
long long signednumber;
char symbollist[SYMBOLS_MAX][DESCRIPTOR_MAX + 1];
struct objidcomponent objidcomponent;
struct oid_unresolved oid;
time_t time;
enum status status;
enum access access;
} YYSTYPE;
%}
%token ERROR
%token HSTRING BSTRING
/* RFC2578 section 3.7 */
%token ABSENT ACCESS AGENTCAPABILITIES ANY APPLICATION AUGMENTS BEGIN
%token BIT BITS BOOLEAN BY CHOICE COMPONENT COMPONENTS CONTACTINFO
%token CREATIONREQUIRES Counter32 Counter64 DEFAULT DEFINED
%token DEFINITIONS DEFVAL DESCRIPTION DISPLAYHINT END ENUMERATED
%token ENTERPRISE EXPLICIT EXPORTS EXTERNAL FALSE FROM GROUP Gauge32
%token IDENTIFIER IMPLICIT IMPLIED IMPORTS INCLUDES INDEX INTEGER
%token Integer32 IpAddress LASTUPDATED MANDATORYGROUPS MAX MAXACCESS
%token MIN MINACCESS MINUSINFINITY MODULE MODULECOMPLIANCE MODULEIDENTITY
%token NOTIFICATIONGROUP NOTIFICATIONTYPE NOTIFICATIONS ASNNULL
%token OBJECT OBJECTGROUP OBJECTIDENTITY OBJECTTYPE OBJECTS OCTET OF
%token OPTIONAL ORGANIZATION Opaque PLUSINFINITY PRESENT PRIVATE
%token PRODUCTRELEASE REAL REFERENCE REVISION SEQUENCE SET SIZE STATUS
%token STRING SUPPORTS SYNTAX TAGS TEXTUALCONVENTION TRAPTYPE TRUE
%token TimeTicks UNITS UNIVERSAL Unsigned32 VARIABLES VARIATION WITH
%token WRITESYNTAX
/* SMIv2 */
%token SNMPv2SMI SNMPv2CONF SNMPv2TC
/* X.208 */
%token PRODUCTION RANGESEPARATOR
%token <string> typereference identifier TEXT HSTRING BSTRING
%token <number> NUMBER
%token <signednumber> SIGNEDNUMBER
%type <string> moduleidentifier smiv2moduleidentifier
%type <import> symbolsfrom
%type <symbollist> symbollist
%type <string> descriptor symbol
%type <objidcomponent>objidcomponentfirst objidcomponent
%type <oid> objidcomponentlist objectidentifiervalue
%type <string> displaypart referpart unitspart
%type <time> lastupdated
%type <status> status
%type <access> access
%%
grammar : /* empty */
| grammar module
;
module : moduleidentifier DEFINITIONS PRODUCTION BEGIN {
file.state = FILE_ASN1;
module = calloc(1, sizeof(*module));
if (module == NULL) {
yyerror("malloc");
YYERROR;
}
RB_INIT(&module->itemscs);
RB_INIT(&module->itemsci);
module->resolved = 0;
if (strlcpy(module->name, $1,
sizeof(module->name)) >=
sizeof(module->name)) {
yyerror("module name too long");
free(module);
YYERROR;
}
} imports moduleidentity modulebody END {
struct module *mprev;
if ((mprev = RB_INSERT(modulescs, &modulescs,
module)) != NULL) {
if (module->lastupdated >
mprev->lastupdated) {
mib_modulefree(mprev);
RB_INSERT(modulescs, &modulescs,
module);
RB_INSERT(modulesci, &modulesci,
module);
} else
mib_modulefree(module);
} else
RB_INSERT(modulesci, &modulesci, module);
module = NULL;
}
| smiv2moduleidentifier {
log_debug("%s: SMIv2 definitions: skipping",
file.name);
YYACCEPT;
}
;
moduleidentifier : typereference { strlcpy($$, $1, sizeof($$)); }
;
smiv2moduleidentifier : SNMPv2SMI { strlcpy($$, "SNMPv2-SMI", sizeof($$)); }
| SNMPv2CONF { strlcpy($$, "SNMPv2-CONF", sizeof($$)); }
| SNMPv2TC { strlcpy($$, "SNMPv2-TC", sizeof($$)); }
;
modulebody : assignmentlist
;
imports : IMPORTS importlist ';'
;
importlist : importlist symbolsfrom
| /* start */
;
symbolsfrom : symbollist FROM moduleidentifier {
size_t i;
char *symbols[SYMBOLS_MAX];
for (i = 0; $1[i][0] != '\0'; i++)
symbols[i] = $1[i];
symbols[i] = NULL;
if (mib_imports_add($3, symbols) == -1)
YYERROR;
}
| symbollist FROM smiv2moduleidentifier {
size_t i;
char *symbols[SYMBOLS_MAX];
for (i = 0; $1[i][0] != '\0'; i++)
symbols[i] = $1[i];
symbols[i] = NULL;
if (mib_imports_add($3, symbols) == -1)
YYERROR;
file.state = FILE_SMI2;
}
;
symbollist : symbollist ',' symbol {
size_t i;
for (i = 0; $1[i][0] != '\0'; i++)
strlcpy($$[i], $1[i], sizeof($$[i]));
if (i + 1 == nitems($$)) {
yyerror("too many symbols from module");
YYERROR;
}
if (strlcpy($$[i], $3, sizeof($$[i])) >=
sizeof($$[i])) {
yyerror("symbol too long");
YYERROR;
}
$$[i + 1][0] = '\0';
}
| symbol {
if (strlcpy($$[0], $1, sizeof($$[0])) >=
sizeof($$[0])) {
yyerror("symbol too long");
YYERROR;
}
$$[1][0] = '\0';
}
;
symbol : typereference { strlcpy($$, $1, sizeof($$)); }
| descriptor { strlcpy($$, $1, sizeof($$)); }
/* SNMPv2-SMI */
| MODULEIDENTITY {
strlcpy($$, "MODULE-IDENTITY", sizeof($$));
}
| OBJECTIDENTITY {
strlcpy($$, "OBJECT-IDENTITY", sizeof($$));
}
| Integer32 { strlcpy($$, "Integer32", sizeof($$)); }
| IpAddress { strlcpy($$, "IpAddress", sizeof($$)); }
| Counter32 { strlcpy($$, "Counter32", sizeof($$)); }
| Gauge32 { strlcpy($$, "Gauge32", sizeof($$)); }
| Unsigned32 { strlcpy($$, "Unsigned32", sizeof($$)); }
| TimeTicks { strlcpy($$, "TimeTicks", sizeof($$)); }
| Opaque { strlcpy($$, "Opaque", sizeof($$)); }
| Counter64 { strlcpy($$, "Counter64", sizeof($$)); }
| OBJECTTYPE { strlcpy($$, "OBJECT-TYPE", sizeof($$)); }
| NOTIFICATIONTYPE {
strlcpy($$, "NOTIFICATION-TYPE", sizeof($$));
}
/* SNMPv2-TC */
| TEXTUALCONVENTION {
strlcpy($$, "TEXTUAL-CONVENTION", sizeof($$));
}
/* SNMPv2-CONF */
| OBJECTGROUP {
strlcpy($$, "OBJECT-GROUP", sizeof($$));
}
| NOTIFICATIONGROUP {
strlcpy($$, "NOTIFICATION-GROUP", sizeof($$));
}
| MODULECOMPLIANCE {
strlcpy($$, "MODULE-COMPLIANCE", sizeof($$));
}
| AGENTCAPABILITIES {
strlcpy($$, "AGENT-CAPABILITIES", sizeof($$));
}
;
descriptor : identifier {
if (strlen($1) > DESCRIPTOR_MAX) {
yyerror("descriptor too long");
YYERROR;
}
strlcpy($$, $1, sizeof($$));
}
;
moduleidentity : descriptor MODULEIDENTITY lastupdated
ORGANIZATION TEXT CONTACTINFO TEXT DESCRIPTION TEXT
revisionpart PRODUCTION objectidentifiervalue {
if (mib_moduleidentity(
$1, $3, $5, $7, $9, &$12) == -1)
YYERROR;
}
;
lastupdated : LASTUPDATED TEXT {
char timebuf[14] = "";
struct tm tm = {};
size_t len;
if ((len = strlen($2)) == 11)
snprintf(timebuf, sizeof(timebuf),
"19%s", $2);
else if (len == 13)
strlcpy(timebuf, $2, sizeof(timebuf));
else {
yyerror("Invalid LAST-UPDATED: %s", $2);
YYERROR;
}
if (strptime(timebuf, "%Y%m%d%H%MZ", &tm) == NULL) {
yyerror("Invalid LAST-UPDATED: %s", $2);
YYERROR;
}
if (($$ = mktime(&tm)) == -1) {
yyerror("Invalid LAST-UPDATED: %s", $2);
YYERROR;
}
}
;
revisionpart : revisions
| /* empty */
;
revisions : revision
| revisions revision
;
revision : REVISION TEXT DESCRIPTION TEXT
;
assignmentlist : assignment assignmentlist
| /* empty */
;
assignment : descriptor OBJECT IDENTIFIER PRODUCTION
objectidentifiervalue {
if (mib_oid($1, &$5) == NULL)
YYERROR;
}
| descriptor OBJECTIDENTITY STATUS status
DESCRIPTION TEXT referpart PRODUCTION
objectidentifiervalue {
const char *reference;
reference = $7[0] == '\0' ? NULL : $7;
if (mib_objectidentity($1, $4, $6, reference,
&$9) == -1)
YYERROR;
}
| descriptor OBJECTTYPE SYNTAX syntax unitspart
MAXACCESS access STATUS status DESCRIPTION TEXT
referpart indexpart defvalpart PRODUCTION
objectidentifiervalue {
const char *units, *reference;
units = $5[0] == '\0' ? NULL : $5;
reference = $12[0] == '\0' ? NULL : $12;
if (mib_objecttype($1, NULL, units, $7, $9, $11,
reference, NULL, NULL, &$16) == -1)
YYERROR;
}
| descriptor NOTIFICATIONTYPE objectspart STATUS status
DESCRIPTION TEXT referpart PRODUCTION
objectidentifiervalue {
const char *reference;
reference = $8[0] == '\0' ? NULL : $8;
if (mib_notificationtype($1, NULL, $5, $7,
reference, &$10) == -1)
YYERROR;
}
| typereference PRODUCTION SEQUENCE '{' entries '}' {
/* Table entry, ignore for now */
}
| typereference PRODUCTION TEXTUALCONVENTION displaypart
STATUS status DESCRIPTION TEXT referpart SYNTAX syntax {
const char *displayhint, *reference;
displayhint = $4[0] == '\0' ? NULL : $4;
reference = $9[0] == '\0' ? NULL : $9;
if (mib_textualconvetion($1, displayhint, $6,
$8, reference, NULL) == -1)
YYERROR;
}
| descriptor MODULECOMPLIANCE STATUS status
DESCRIPTION TEXT referpart compliancemodulepart
PRODUCTION objectidentifiervalue {
const char *reference;
reference = $7[0] == '\0' ? NULL : $7;
if (mib_modulecompliance($1, $4, $6, reference,
NULL, &$10) == -1)
YYERROR;
}
| descriptor OBJECTGROUP objectspart STATUS status
DESCRIPTION TEXT referpart PRODUCTION
objectidentifiervalue {
const char *reference;
reference = $8[0] == '\0' ? NULL : $8;
if (mib_objectgroup($1, NULL, $5, $7, reference,
&$10) == -1)
YYERROR;
}
| descriptor NOTIFICATIONGROUP notificationspart
STATUS status DESCRIPTION TEXT referpart PRODUCTION
objectidentifiervalue {
const char *reference;
reference = $8[0] == '\0' ? NULL : $8;
if (mib_notificationgroup($1, NULL, $5, $7, reference,
&$10) == -1)
YYERROR;
}
| typereference PRODUCTION syntax
;
notificationspart : NOTIFICATIONS '{' notifications '}'
;
notifications : notification
| notification ',' notifications
;
notification : descriptor
;
objectspart : OBJECTS '{' objects '}'
| /* empty */
;
objects : object
| objects ',' object
;
object : descriptor
;
syntax : type
| SEQUENCE OF typereference
| integersubtype
| octetstringsubtype
| enumeration
| bits
;
unitspart : UNITS TEXT {
strlcpy($$, $2, sizeof($$));
}
| /* empty */ {
$$[0] = '\0';
}
;
referpart : REFERENCE TEXT {
strlcpy($$, $2, sizeof($$));
}
| /* empty */ {
$$[0] = '\0';
}
;
indexpart : INDEX '{' indextypes '}'
| AUGMENTS '{' descriptor '}'
| /* empty */
;
indextypes : indextype
| indextypes ',' indextype
;
indextype : IMPLIED index
| index
;
index : descriptor
;
defvalpart : DEFVAL '{' defvalue '}'
| /* empty */
;
defvalue : descriptor
| NUMBER
| SIGNEDNUMBER
| HSTRING
| TEXT
| bitsvalue
| objectidentifiervalue
;
/* single-value and empty are covered by objectidentifiervalue */
bitsvalue : '{' bitscomponentlist '}'
;
bitscomponentlist : bitscomponentlist ',' bitscomponent
| bitscomponent
| /* empty */
;
bitscomponent : identifier
;
displaypart : DISPLAYHINT TEXT {
strlcpy($$, $2, sizeof($$));
}
| /* empty */ {
$$[0] = '\0';
}
;
entries : entry ',' entries
| entry
;
entry : descriptor syntax
;
compliancemodulepart : compliancemodules
;
compliancemodules : compliancemodule
| compliancemodules compliancemodule
;
compliancemodule : MODULE modulename modulemandatorypart
modulecompliancepart
;
modulename : moduleidentifier
| /* empty */
;
modulemandatorypart : MANDATORYGROUPS '{' modulegroups '}'
| /* empty */
;
modulegroups : modulegroup
| modulegroups ',' modulegroup
;
modulegroup : objectidentifiervalue
| descriptor
;
modulecompliancepart : modulecompliances
| /* empty */
;
modulecompliances : modulecompliance
| modulecompliances modulecompliance
;
modulecompliance : modulecompliancegroup
| moduleobject
;
modulecompliancegroup : GROUP modulegroupobjectname DESCRIPTION TEXT
;
moduleobject : OBJECT moduleobjectname modulesyntaxpart
writesyntaxpart moduleaccesspart DESCRIPTION TEXT
;
moduleobjectname : objectidentifiervalue
| descriptor
;
modulegroupobjectname : objectidentifiervalue
| descriptor
;
modulesyntaxpart : SYNTAX syntax
| /* empty */
;
writesyntaxpart : WRITESYNTAX syntax
| /* empty */
;
moduleaccesspart : MINACCESS access
| /* empty */
;
/*
* An OBJECT IDENTIFIER needs at least 2 components and are needed to
* distinguish from BITS, which can have 0 elements and only differentiate by a
* comma separator.
*/
objectidentifiervalue : '{' objidcomponentfirst objidcomponent
objidcomponentlist '}' {
$$.bo_n = 0;
if ($3.type == OCT_DESCRIPTOR) {
yyerror("only first component of "
"OBJECT IDENTIFIER can be a "
"descriptor");
YYERROR;
}
if (mib_oid_append(&$$, &$2) == -1)
YYERROR;
if (mib_oid_append(&$$, &$3) == -1)
YYERROR;
if (mib_oid_concat(&$$, &$4) == -1)
YYERROR;
}
;
objidcomponentlist : objidcomponent objidcomponentlist {
if ($1.type == OCT_DESCRIPTOR) {
yyerror("only first component of "
"OBJECT IDENTIFIER can be a "
"descriptor");
YYERROR;
}
$$.bo_n = 0;
if (mib_oid_append(&$$, &$1) == -1)
YYERROR;
if (mib_oid_concat(&$$, &$2) == -1)
YYERROR;
}
| /* empty */ {
$$.bo_n = 0;
}
;
objidcomponentfirst : descriptor {
$$.type = OCT_DESCRIPTOR;
strlcpy($$.name, $1, sizeof($$.name));
}
| objidcomponent {
$$.type = $1.type;
$$.number = $1.number;
strlcpy($$.name, $1.name, sizeof($$.name));
}
;
objidcomponent : NUMBER {
$$.type = OCT_NUMBER;
if ($1 > UINT32_MAX) {
yyerror("OBJECT IDENTIFIER number "
"too large");
YYERROR;
}
$$.number = $1;
$$.name[0] = '\0';
}
| descriptor '(' NUMBER ')' {
$$.type = OCT_NAMEANDNUMBER;
if ($3 > UINT32_MAX) {
yyerror("OBJECT IDENTIFIER number "
"too large");
YYERROR;
}
$$.number = $3;
strlcpy($$.name, $1, sizeof($$.name));
}
;
type : typereference
| INTEGER
| OBJECT IDENTIFIER
| OCTET STRING
| Integer32
| IpAddress
| Counter32
| Gauge32
| Unsigned32
| TimeTicks
| Opaque
| Counter64
| BITS
;
integersubtype : INTEGER '(' ranges ')'
| Integer32 '(' ranges ')'
| Unsigned32 '(' ranges ')'
| Gauge32 '(' ranges ')'
| typereference '(' ranges ')'
;
octetstringsubtype : OCTET STRING '(' SIZE '(' ranges ')' ')'
| Opaque '(' SIZE '(' ranges ')' ')'
| typereference '(' SIZE '(' ranges ')' ')'
;
bits : BITS '{' namedbits '}'
;
namedbits : namedbit
| namedbits ',' namedbit
;
namedbit : identifier '(' NUMBER ')'
;
enumeration : INTEGER '{' namednumbers '}'
| typereference '{' namednumbers '}'
;
namednumbers : namednumber
| namednumbers ',' namednumber
;
namednumber : identifier '(' NUMBER ')'
| identifier '(' SIGNEDNUMBER ')'
;
ranges : range '|' ranges
| range
;
range : value
| value RANGESEPARATOR value
;
value : SIGNEDNUMBER
| NUMBER
| HSTRING
| BSTRING
;
access : descriptor {
if (strcmp($1, "not-accessible") == 0)
$$ = NOTACCESSIBLE;
else if (
strcmp($1, "accessible-for-notify") == 0)
$$ = ACCESSIBLEFORNOTIFY;
else if (strcmp($1, "read-only") == 0)
$$ = READONLY;
else if (strcmp($1, "read-write") == 0)
$$ = READWRITE;
else if (strcmp($1, "read-create") == 0)
$$ = READCREATE;
else {
yyerror("invalid access");
YYERROR;
}
}
;
status : descriptor {
if (strcmp($1, "current") == 0)
$$ = CURRENT;
else if (strcmp($1, "deprecated") == 0)
$$ = DEPRECATED;
else if (strcmp($1, "obsolete") == 0)
$$ = OBSOLETE;
else {
yyerror("invalid status");
YYERROR;
}
}
;
%%
void
yyerror(const char *fmt, ...)
{
va_list ap;
char msg[1024] = "";
if (file.state == FILE_UNDEFINED) {
log_debug("%s: not an ASN.1 file: skipping", file.name);
return;
}
if (file.state == FILE_ASN1) {
if (strcmp(fmt, "syntax error") == 0) {
log_debug("%s: not an SMIv2 file: skipping", file.name);
return;
}
}
if (fmt != NULL) {
va_start(ap, fmt);
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
}
log_warnx("%s:%zu: %s", file.name, file.lineno, msg);
}
/*
* X.208
*/
int
yylex(void)
{
const struct {
const char *name;
int token;
} keywords[] = {
/* RFC2578 section 3.7 */
{ "ABSENT", ABSENT },
{ "ACCESS", ACCESS },
{ "AGENT-CAPABILITIES", AGENTCAPABILITIES },
{ "ANY", ANY },
{ "APPLICATION", APPLICATION },
{ "AUGMENTS", AUGMENTS },
{ "BEGIN", BEGIN },
{ "BIT", BIT },
{ "BITS", BITS },
{ "BOOLEAN", BOOLEAN },
{ "BY", BY },
{ "CHOICE", CHOICE },
{ "COMPONENT", COMPONENT },
{ "COMPONENTS", COMPONENTS },
{ "CONTACT-INFO", CONTACTINFO },
{ "CREATION-REQUIRES", CREATIONREQUIRES },
{ "Counter32", Counter32 },
{ "Counter64", Counter64 },
{ "DEFAULT", DEFAULT },
{ "DEFINED", DEFINED },
{ "DEFINITIONS", DEFINITIONS },
{ "DEFVAL", DEFVAL },
{ "DESCRIPTION", DESCRIPTION },
{ "DISPLAY-HINT", DISPLAYHINT },
{ "END", END },
{ "ENUMERATED", ENUMERATED },
{ "ENTERPRISE", ENTERPRISE },
{ "EXPLICIT", EXPLICIT },
{ "EXPORTS", EXPORTS },
{ "EXTERNAL", EXTERNAL },
{ "FALSE", FALSE },
{ "FROM", FROM },
{ "GROUP", GROUP },
{ "Gauge32", Gauge32 },
{ "IDENTIFIER", IDENTIFIER },
{ "IMPLICIT", IMPLICIT },
{ "IMPLIED", IMPLIED },
{ "IMPORTS", IMPORTS },
{ "INCLUDES", INCLUDES },
{ "INDEX", INDEX },
{ "INTEGER", INTEGER },
{ "Integer32", Integer32 },
{ "IpAddress", IpAddress },
{ "LAST-UPDATED", LASTUPDATED },
{ "MANDATORY-GROUPS", MANDATORYGROUPS },
{ "MAX", MAX },
{ "MAX-ACCESS", MAXACCESS },
{ "MIN", MIN },
{ "MIN-ACCESS", MINACCESS },
{ "MINUS-INFINITY", MINUSINFINITY },
{ "MODULE", MODULE },
{ "MODULE-COMPLIANCE", MODULECOMPLIANCE },
{ "MODULE-IDENTITY", MODULEIDENTITY },
{ "NOTIFICATION-GROUP", NOTIFICATIONGROUP },
{ "NOTIFICATION-TYPE", NOTIFICATIONTYPE },
{ "NOTIFICATIONS", NOTIFICATIONS },
{ "NULL", ASNNULL },
{ "OBJECT", OBJECT },
{ "OBJECT-GROUP", OBJECTGROUP },
{ "OBJECT-IDENTITY", OBJECTIDENTITY },
{ "OBJECT-TYPE", OBJECTTYPE },
{ "OBJECTS", OBJECTS },
{ "OCTET", OCTET },
{ "OF", OF },
{ "OPTIONAL", OPTIONAL },
{ "ORGANIZATION", ORGANIZATION },
{ "Opaque", Opaque },
{ "PLUS-INFINITY", PLUSINFINITY },
{ "PRESENT", PRESENT },
{ "PRIVATE", PRIVATE },
{ "PRODUCT-RELEASE", PRODUCTRELEASE },
{ "REAL", REAL },
{ "REFERENCE", REFERENCE },
{ "REVISION", REVISION },
{ "SEQUENCE", SEQUENCE },
{ "SET", SET },
{ "SIZE", SIZE },
{ "STATUS", STATUS },
{ "STRING", STRING },
{ "SUPPORTS", SUPPORTS },
{ "SYNTAX", SYNTAX },
{ "TAGS", TAGS },
{ "TEXTUAL-CONVENTION", TEXTUALCONVENTION },
{ "TRAP-TYPE", TRAPTYPE },
{ "TRUE", TRUE },
{ "TimeTicks", TimeTicks },
{ "UNITS", UNITS },
{ "UNIVERSAL", UNIVERSAL },
{ "Unsigned32", Unsigned32 },
{ "VARIABLES", VARIABLES },
{ "VARIATION", VARIATION },
{ "WITH", WITH },
{ "WRITE-SYNTAX", WRITESYNTAX },
/* ASN.1 */
{ "::=", PRODUCTION },
{ "..", RANGESEPARATOR },
/* SMIv2 */
{ "SNMPv2-SMI", SNMPv2SMI },
{ "SNMPv2-CONF", SNMPv2CONF },
{ "SNMPv2-TC", SNMPv2TC },
{}
};
char buf[TEXT_MAX];
size_t i = 0, j;
int c, comment = 0;
const char *errstr;
char *endptr;
while ((c = fgetc(file.stream)) != EOF) {
if (i == sizeof(buf)) {
yyerror("token too large");
return ERROR;
}
if (i > 0 && buf[0] == '"') {
if (c == '"') {
buf[i] = '\0';
(void)strlcpy(yylval.string, buf + 1,
sizeof(yylval.string));
return TEXT;
}
if (c == '\n')
file.lineno++;
buf[i++] = c;
continue;
}
if (comment) {
if (c == '-') {
if (++comment == 3)
comment = 0;
} else if (c == '\n') {
file.lineno++;
comment = 0;
} else
comment = 1;
continue;
}
if (c == '\n') {
if (i != 0) {
if (buf[i - 1] == '\r') {
i--;
if (i == 0) {
file.lineno++;
continue;
}
}
ungetc(c, file.stream);
goto token;
}
file.lineno++;
continue;
}
if (c == ' ' || c == '\t') {
if (i == 0)
continue;
goto token;
}
if (c == '.' || c == ':') {
if (i > 0 && buf[0] != '.' && buf[0] != ':') {
ungetc(c, file.stream);
goto token;
}
}
if (i > 0 && (buf[0] == '.' || buf[0] == ':')) {
if (c != '.' && c != ':' && c != '=') {
ungetc(c, file.stream);
goto token;
}
}
if (c == ',' || c == ';' || c == '{' || c == '}' ||
c == '(' || c == ')' || c == '[' || c == ']' || c == '|') {
if (i == 0)
return c;
ungetc(c, file.stream);
goto token;
}
buf[i++] = c;
if (i >= 2) {
if (buf[i - 2] == '-' && buf[i - 1] == '-') {
if (i > 2) {
ungetc('-', file.stream);
ungetc('-', file.stream);
i -= 2;
goto token;
}
comment = 1;
i = 0;
continue;
}
}
}
if (ferror(file.stream)) {
yyerror(NULL);
return ERROR;
}
if (i == 0)
return 0;
token:
buf[i] = '\0';
for (i = 0; keywords[i].name != NULL; i++) {
if (strcmp(keywords[i].name, buf) == 0)
return keywords[i].token;
}
if (isupper(buf[0])) {
for (i = 1; buf[i] != '\0'; i++) {
if (!isalnum(buf[i]) && buf[i] != '-')
break;
}
if (buf[i] == '\0' && buf[i - 1] != '-') {
strlcpy(yylval.string, buf, sizeof(yylval.string));
return typereference;
}
}
if (islower(buf[0])) {
for (i = 1; buf[i] != '\0'; i++) {
if (!isalnum(buf[i]) && buf[i] != '-')
break;
}
if (buf[i] == '\0'&& buf[i - 1] != '-') {
strlcpy(yylval.string, buf, sizeof(yylval.string));
return identifier;
}
}
if (buf[0] == '\'') {
for (i = 1; buf[i] != '\0'; i++)
continue;
if (i < 3 || buf[i - 2] != '\'') {
yyerror("incomplete binary or hexadecimal string");
return ERROR;
}
if (tolower(buf[i - 1]) == 'b') {
for (j = 1; j < i - 2; j++) {
if (buf[j] != '0' && buf[j] != '1') {
yyerror("invalid character in bstring");
return ERROR;
}
}
strlcpy(yylval.string, buf + 1, sizeof(yylval.string));
yylval.string[i - 2] = '\0';
return BSTRING;
} else if (tolower(buf[i - 1]) == 'h') {
for (j = 1; j < i - 2; j++) {
if (!isxdigit(buf[j])) {
yyerror("invalid character in hstring");
return ERROR;
}
}
strlcpy(yylval.string, buf + 1, sizeof(yylval.string));
yylval.string[i - 2] = '\0';
return HSTRING;
}
yyerror("no valid binary or hexadecimal string");
return ERROR;
}
for (i = 0; buf[i] != '\0'; i++) {
if (i == 0 && buf[i] == '-')
continue;
if (!isdigit(buf[i]))
break;
}
if ((i == 1 && isdigit(buf[0])) || i > 1) {
yylval.signednumber =
strtonum(buf, LLONG_MIN, LLONG_MAX, &errstr);
if (errstr != NULL) {
if (errno == ERANGE && isdigit(buf[0])) {
errno = 0;
yylval.number = strtoull(buf, &endptr, 10);
if (errno == 0)
return NUMBER;
}
yyerror("invalid number: %s: %s", buf, errstr);
return ERROR;
}
if (buf[0] == '-')
return SIGNEDNUMBER;
yylval.number = yylval.signednumber;
return NUMBER;
}
yyerror("unknown token: %s", buf);
return ERROR;
}
void
mib_clear(void)
{
struct module *m;
struct item *iso;
while ((m = RB_ROOT(&modulesci)) != NULL)
mib_modulefree(m);
/* iso */
iso = RB_ROOT(&items);
assert(strcmp(iso->name, "iso") == 0);
RB_REMOVE(itemsgci, &itemsci, iso);
RB_REMOVE(items, &items, iso);
free(iso->oid.bo_id);
free(iso);
assert(RB_EMPTY(&modulesci));
assert(RB_EMPTY(&modulescs));
assert(RB_EMPTY(&items));
assert(RB_EMPTY(&itemsci));
}
void
mib_modulefree(struct module *m)
{
struct item *item;
if (m == NULL)
return;
if (RB_FIND(modulesci, &modulesci, m) == m)
RB_REMOVE(modulesci, &modulesci, m);
if (RB_FIND(modulescs, &modulescs, m) == m)
RB_REMOVE(modulescs, &modulescs, m);
free(m->imports);
while ((item = RB_ROOT(&m->itemscs)) != NULL) {
RB_REMOVE(itemscs, &m->itemscs, item);
if (RB_FIND(itemsci, &m->itemsci, item) == item)
RB_REMOVE(itemsci, &m->itemsci, item);
if (RB_FIND(items, &items, item) == item)
RB_REMOVE(items, &items, item);
if (RB_FIND(itemsgci, &itemsci, item) == item)
RB_REMOVE(itemsgci, &itemsci, item);
if (!item->resolved)
free(item->oid_unresolved);
else
free(item->oid.bo_id);
free(item);
}
free(m);
}
int
mib_imports_add(char *name, char **symbols)
{
size_t im, ism, isi;
struct import *import;
for (im = 0; module->imports != NULL &&
module->imports[im].name[0] != '\0'; im++) {
if (strcmp(module->imports[im].name, name) == 0)
break;
}
if (module->imports == NULL || module->imports[im].name[0] == '\0') {
if ((import = reallocarray(module->imports, im + 2,
sizeof(*module->imports))) == NULL) {
yyerror("malloc");
return -1;
}
module->imports = import;
strlcpy(module->imports[im].name, name,
sizeof(module->imports[im].name));
module->imports[im].nsymbols = 0;
module->imports[im + 1].name[0] = '\0';
}
import = &module->imports[im];
for (isi = 0; symbols[isi] != NULL; isi++) {
for (ism = 0; ism < import->nsymbols; ism++) {
if (strcmp(symbols[isi],
import->symbols[ism].name) == 0) {
yyerror("symbol %s already imported",
symbols[isi]);
break;
}
}
if (ism != import->nsymbols)
continue;
if (import->nsymbols == nitems(import->symbols)) {
yyerror("Too many symbols imported");
return -1;
}
strlcpy(import->symbols[ism].name, symbols[isi],
sizeof(import->symbols[ism].name));
import->symbols[ism].item = NULL;
import->nsymbols++;
}
return 0;
}
int
mib_oid_append(struct oid_unresolved *oid, const struct objidcomponent *subid)
{
if (oid->bo_n == nitems(oid->bo_id)) {
yyerror("oid too long");
return -1;
}
switch (oid->bo_id[oid->bo_n].type = subid->type) {
case OCT_DESCRIPTOR:
strlcpy(oid->bo_id[oid->bo_n].name, subid->name,
sizeof(oid->bo_id[oid->bo_n].name));
break;
case OCT_NUMBER:
oid->bo_id[oid->bo_n].number = subid->number;
break;
case OCT_NAMEANDNUMBER:
oid->bo_id[oid->bo_n].number = subid->number;
strlcpy(oid->bo_id[oid->bo_n].name, subid->name,
sizeof(oid->bo_id[oid->bo_n].name));
}
oid->bo_n++;
return 0;
}
int
mib_oid_concat(struct oid_unresolved *dst, const struct oid_unresolved *src)
{
size_t i;
for (i = 0; i < src->bo_n; i++)
if (mib_oid_append(dst, &src->bo_id[i]) == -1)
return -1;
return 0;
}
struct item *
mib_item(const char *name, enum item_type type)
{
struct item *item;
if ((item = calloc(1, sizeof(*item))) == NULL) {
log_warn("malloc");
return NULL;
}
item->type = type;
item->resolved = 0;
item->module = module;
(void)strlcpy(item->name, name, sizeof(item->name));
if (RB_INSERT(itemscs, &module->itemscs, item) != NULL) {
yyerror("duplicate item %s", name);
free(item);
return NULL;
}
return item;
}
int
mib_item_oid(struct item *item, const struct oid_unresolved *oid)
{
if ((item->oid_unresolved = calloc(1,
sizeof(*item->oid_unresolved))) == NULL) {
yyerror("malloc");
return -1;
}
*item->oid_unresolved = *oid;
return 0;
}
int
mib_macro(const char *name)
{
return mib_item(name, IT_MACRO) == NULL ? -1 : 0;
}
struct item *
mib_oid(const char *name, const struct oid_unresolved *oid)
{
struct item *item;
if ((item = mib_item(name, IT_OID)) == NULL)
return NULL;
if (mib_item_oid(item, oid) == -1)
return NULL;
return item;
}
int
mib_applicationsyntax(const char *name)
{
return mib_item(name, IT_APPLICATIONSYNTAX) == NULL ? -1 : 0;
}
int
mib_moduleidentity(const char *name, time_t lastupdated,
const char *organization, const char *contactinfo, const char *description,
const struct oid_unresolved *oid)
{
struct item *item;
if ((item = mib_item(name, IT_MODULE_IDENTITY)) == NULL)
return -1;
if (mib_item_oid(item, oid) == -1)
return -1;
module->lastupdated = lastupdated;
return 0;
}
int
mib_objectidentity(const char *name, enum status status,
const char *description, const char *reference,
const struct oid_unresolved *oid)
{
struct item *item;
if ((item = mib_item(name, IT_OBJECT_IDENTITY)) == NULL)
return -1;
item->objectidentity.status = status;
if (mib_item_oid(item, oid) == -1)
return -1;
return 0;
}
int
mib_objecttype(const char *name, void *syntax, const char *units,
enum access maxaccess, enum status status, const char *description,
const char *reference, void *index, void *defval,
const struct oid_unresolved *oid)
{
struct item *item;
if ((item = mib_item(name, IT_OBJECT_TYPE)) == NULL)
return -1;
item->objecttype.maxaccess = maxaccess;
item->objecttype.status = status;
if (mib_item_oid(item, oid) == -1)
return -1;
return 0;
}
int
mib_notificationtype(const char *name, void *objects, enum status status,
const char *description, const char *reference,
const struct oid_unresolved *oid)
{
struct item *item;
if ((item = mib_item(name, IT_NOTIFICATION_TYPE)) == NULL)
return -1;
item->notificationtype.status = status;
if (mib_item_oid(item, oid) == -1)
return -1;
return 0;
}
int
mib_textualconvetion(const char *name, const char *displayhint,
enum status status, const char *description, const char *reference,
void *syntax)
{
struct item *item;
if ((item = mib_item(name, IT_TEXTUAL_CONVENTION)) == NULL)
return -1;
item->textualconvention.status = status;
return 0;
}
int
mib_objectgroup(const char *name, void *objects, enum status status,
const char *description, const char *reference,
const struct oid_unresolved *oid)
{
struct item *item;
if ((item = mib_item(name, IT_OBJECT_GROUP)) == NULL)
return -1;
if (mib_item_oid(item, oid) == -1)
return -1;
return 0;
}
int
mib_notificationgroup(const char *name, void *notifications, enum status status,
const char *description, const char *reference,
const struct oid_unresolved *oid)
{
struct item *item;
if ((item = mib_item(name, IT_NOTIFICATION_GROUP)) == NULL)
return -1;
if (mib_item_oid(item, oid) == -1)
return -1;
return 0;
}
int
mib_modulecompliance(const char *name, enum status status,
const char *description, const char *reference, void *mods,
const struct oid_unresolved *oid)
{
struct item *item;
if ((item = mib_item(name, IT_MODULE_COMPLIANCE)) == NULL)
return -1;
if (mib_item_oid(item, oid) == -1)
return -1;
return 0;
}
void
mib_parsefile(const char *path)
{
mib_defaults();
log_debug("mib parsing %s", path);
if ((file.stream = fopen(path, "r")) == NULL) {
log_warn("fopen");
return;
}
file.lineno = 1;
file.name = path;
file.state = FILE_UNDEFINED;
if (yyparse() != 0) {
mib_modulefree(module);
module = NULL;
}
fclose(file.stream);
}
void
mib_parsedir(const char *path)
{
DIR *dir;
struct dirent *dirent;
char mibfile[PATH_MAX];
if ((dir = opendir(path)) == NULL) {
log_warn("opendir(%s)", path);
return;
}
errno = 0;
while ((dirent = readdir(dir)) != NULL) {
if (dirent->d_name[0] == '.')
continue;
if (snprintf(mibfile, sizeof(mibfile), "%s/%s",
path, dirent->d_name) >= (int)sizeof(mibfile))
continue;
if (dirent->d_type == DT_DIR) {
mib_parsedir(mibfile);
continue;
}
if (dirent->d_type != DT_REG)
continue;
mib_parsefile(mibfile);
}
closedir(dir);
}
struct item *
mib_item_parent(struct ber_oid *oid)
{
struct item *item, search;
search.oid.bo_n = oid->bo_n;
search.oid.bo_id = oid->bo_id;
while (search.oid.bo_n > 0) {
if ((item = RB_FIND(items, &items, &search)) != NULL)
return item;
search.oid.bo_n--;
}
return NULL;
}
const char *
mib_string2oid(const char *str, struct ber_oid *oid)
{
char mname[512], *descriptor, *digits;
struct module *m = NULL, msearch;
struct item *item = NULL, isearch;
struct ber_oid oidbuf;
size_t i;
oid->bo_n = 0;
if (isdigit(str[0])) {
if (ober_string2oid(str, oid) == -1)
return "invalid OID";
return NULL;
}
if (strlcpy(mname, str, sizeof(mname)) >= sizeof(mname))
return "OID name too long";
if ((descriptor = strchr(mname, ':')) != NULL) {
descriptor[0] = '\0';
if (descriptor[1] != ':')
return "module and descriptor must be separated by "
"double colon";
descriptor += 2;
if (strlcpy(msearch.name, mname, sizeof(msearch.name)) >=
sizeof(msearch.name))
return "module not found";
if ((m = RB_FIND(modulescs, &modulescs, &msearch)) == NULL) {
m = RB_FIND(modulesci, &modulesci, &msearch);
if (m == NULL)
return "module not found";
}
} else
descriptor = mname;
if ((digits = strchr(descriptor, '.')) != NULL) {
digits++[0] = '\0';
if (ober_string2oid(digits, &oidbuf) == -1)
return "invalid OID";
} else
oidbuf.bo_n = 0;
if (strlcpy(isearch.name, descriptor, sizeof(isearch.name)) >=
sizeof(isearch.name))
return "descriptor not found";
if (m != NULL) {
item = RB_FIND(itemscs, &m->itemscs, &isearch);
if (item == NULL)
item = RB_FIND(itemsci, &m->itemsci, &isearch);
} else
item = RB_FIND(itemsgci, &itemsci, &isearch);
if (item == NULL)
return "descriptor not found";
if (item->oid.bo_n + oidbuf.bo_n > nitems(oid->bo_id))
return "OID too long";
for (i = 0; i < item->oid.bo_n; i++)
oid->bo_id[oid->bo_n++] = item->oid.bo_id[i];
for (i = 0; i < oidbuf.bo_n; i++)
oid->bo_id[oid->bo_n++] = oidbuf.bo_id[i];
return NULL;
}
char *
mib_oid2string(struct ber_oid *oid, char *buf, size_t buflen,
enum mib_oidfmt fmt)
{
struct item *item;
char digit[11];
size_t i = 0;
buf[0] = '\0';
if (fmt == MIB_OIDSYMBOLIC && (item = mib_item_parent(oid)) != NULL) {
snprintf(buf, buflen, "%s::%s", item->module->name,
item->name);
i = item->oid.bo_n;
}
for (; i < oid->bo_n; i++) {
if (i != 0)
strlcat(buf, ".", buflen);
snprintf(digit, sizeof(digit), "%"PRIu32, oid->bo_id[i]);
strlcat(buf, digit, buflen);
}
return buf;
}
void
mib_defaults(void)
{
struct oid_unresolved oid;
struct item *iso;
if (!RB_EMPTY(&modulesci))
return;
/* ASN.1 constant, not part of a module */
if ((iso = calloc(1, sizeof(*iso))) == NULL)
fatal("malloc");
iso->type = IT_OID;
iso->resolved = 1;
iso->module = NULL;
strlcpy(iso->name, "iso", sizeof(iso->name));
if ((iso->oid.bo_id = calloc(1, sizeof(*iso->oid.bo_id))) == NULL)
fatal("malloc");
iso->oid.bo_id[0] = 1;
iso->oid.bo_n = 1;
RB_INSERT(items, &items, iso);
RB_INSERT(itemsgci, &itemsci, iso);
file.state = FILE_SMI2;
file.lineno = 0;
if ((module = calloc(1, sizeof(*module))) == NULL)
fatal("malloc");
RB_INIT(&module->itemscs);
RB_INIT(&module->itemsci);
module->resolved = 0;
strlcpy(module->name, "SNMPv2-SMI", sizeof(module->name));
file.name = module->name;
oid.bo_id[0].type = oid.bo_id[1].type = OCT_NUMBER;
oid.bo_n = 2;
oid.bo_id[0].number = 1;
oid.bo_id[1].number = 3;
if (mib_oid("org", &oid) == NULL)
exit(1);
oid.bo_id[0].number = oid.bo_id[1].number = 0;
if (mib_oid("zeroDotZero", &oid) == NULL)
exit(1);
oid.bo_id[0].type = OCT_DESCRIPTOR;
strlcpy(oid.bo_id[0].name, "org", sizeof(oid.bo_id[0].name));
oid.bo_id[1].number = 6;
if (mib_oid("dod", &oid) == NULL)
exit(1);
strlcpy(oid.bo_id[0].name, "dod", sizeof(oid.bo_id[0].name));
oid.bo_id[1].number = 1;
if (mib_oid("internet", &oid) == NULL)
exit(1);
strlcpy(oid.bo_id[0].name, "internet", sizeof(oid.bo_id[0].name));
oid.bo_id[1].number = 1;
if (mib_oid("directory", &oid) == NULL)
exit(1);
oid.bo_id[1].number = 2;
if (mib_oid("mgmt", &oid) == NULL)
exit(1);
strlcpy(oid.bo_id[0].name, "mgmt", sizeof(oid.bo_id[0].name));
oid.bo_id[1].number = 1;
if (mib_oid("mib-2", &oid) == NULL)
exit(1);
strlcpy(oid.bo_id[0].name, "mib-2", sizeof(oid.bo_id[0].name));
oid.bo_id[1].number = 10;
if (mib_oid("transmission", &oid) == NULL)
exit(1);
strlcpy(oid.bo_id[0].name, "internet", sizeof(oid.bo_id[0].name));
oid.bo_id[1].number = 3;
if (mib_oid("experimental", &oid) == NULL)
exit(1);
oid.bo_id[1].number = 4;
if (mib_oid("private", &oid) == NULL)
exit(1);
oid.bo_id[1].number = 5;
if (mib_oid("security", &oid) == NULL)
exit(1);
oid.bo_id[1].number = 6;
if (mib_oid("snmpV2", &oid) == NULL)
exit(1);
strlcpy(oid.bo_id[0].name, "private", sizeof(oid.bo_id[0].name));
oid.bo_id[1].number = 1;
if (mib_oid("enterprises", &oid) == NULL)
exit(1);
strlcpy(oid.bo_id[0].name, "snmpV2", sizeof(oid.bo_id[0].name));
oid.bo_id[1].number = 1;
if (mib_oid("snmpDomains", &oid) == NULL)
exit(1);
oid.bo_id[1].number = 2;
if (mib_oid("snmpProxys", &oid) == NULL)
exit(1);
oid.bo_id[1].number = 3;
if (mib_oid("snmpModules", &oid) == NULL)
exit(1);
if (mib_macro("MODULE-IDENTITY") == -1 ||
mib_macro("OBJECT-IDENTITY") == -1 ||
mib_macro("OBJECT-TYPE") == -1 ||
mib_macro("NOTIFICATION-TYPE") == -1)
exit(1);
if (mib_applicationsyntax("Integer32") == -1 ||
mib_applicationsyntax("IpAddress") == -1 ||
mib_applicationsyntax("Counter32") == -1 ||
mib_applicationsyntax("Gauge32") == -1 ||
mib_applicationsyntax("Unsigned32") == -1 ||
mib_applicationsyntax("TimeTicks") == -1 ||
mib_applicationsyntax("Opaque") == -1 ||
mib_applicationsyntax("Counter64") == -1)
exit(1);
RB_INSERT(modulesci, &modulesci, module);
RB_INSERT(modulescs, &modulescs, module);
if ((module = calloc(1, sizeof(*module))) == NULL)
fatal("malloc");
RB_INIT(&module->itemscs);
RB_INIT(&module->itemsci);
module->resolved = 0;
strlcpy(module->name, "SNMPv2-TC", sizeof(module->name));
file.name = module->name;
if (mib_macro("TEXTUAL-CONVENTION") == -1)
exit(1);
if (mib_textualconvetion(
"DisplayString", "255a", CURRENT, "", NULL, NULL) == -1 ||
mib_textualconvetion(
"PhysAddress", "1x:", CURRENT, "", NULL, NULL) == -1 ||
mib_textualconvetion(
"MacAddress", "1x:", CURRENT, "", NULL, NULL) == -1 ||
mib_textualconvetion(
"TruthValue", NULL, CURRENT, "", NULL, NULL) == -1 ||
mib_textualconvetion(
"TestAndIncr", NULL, CURRENT, "", NULL, NULL) == -1 ||
mib_textualconvetion(
"AutonomousType", NULL, CURRENT, "", NULL, NULL) == -1 ||
mib_textualconvetion(
"InstancePointer", NULL, OBSOLETE, "", NULL, NULL) == -1 ||
mib_textualconvetion(
"VariablePointer", NULL, CURRENT, "", NULL, NULL) == -1 ||
mib_textualconvetion(
"RowPointer", NULL, CURRENT, "", NULL, NULL) == -1 ||
mib_textualconvetion(
"RowStatus", NULL, CURRENT, "", NULL, NULL) == -1 ||
mib_textualconvetion(
"TimeStamp", NULL, CURRENT, "", NULL, NULL) == -1 ||
mib_textualconvetion(
"TimeInterval", NULL, CURRENT, "", NULL, NULL) == -1 ||
mib_textualconvetion(
"DateAndTime", NULL, CURRENT, "", NULL, NULL) == -1 ||
mib_textualconvetion(
"StorageType", NULL, CURRENT, "", NULL, NULL) == -1 ||
mib_textualconvetion(
"TDomain", NULL, CURRENT, "", NULL, NULL) == -1 ||
mib_textualconvetion(
"TAddress", NULL, CURRENT, "", NULL, NULL) == -1)
exit(1);
RB_INSERT(modulesci, &modulesci, module);
RB_INSERT(modulescs, &modulescs, module);
if ((module = calloc(1, sizeof(*module))) == NULL)
fatal("malloc");
RB_INIT(&module->itemscs);
RB_INIT(&module->itemsci);
module->resolved = 0;
strlcpy(module->name, "SNMPv2-CONF", sizeof(module->name));
file.name = module->name;
if (mib_macro("OBJECT-GROUP") == -1 ||
mib_macro("NOTIFICATION-GROUP") == -1 ||
mib_macro("MODULE-COMPLIANCE") == -1 ||
mib_macro("AGENT-CAPABILITIES") == -1)
exit(1);
RB_INSERT(modulesci, &modulesci, module);
RB_INSERT(modulescs, &modulescs, module);
module = NULL;
}
/*
* Used only for resolving phase
*/
struct item *
mib_item_find(struct item *orig, const char *name)
{
struct module *m = orig->module;
struct item *item, search;
struct import *import;
size_t i, j;
strlcpy(search.name, name, sizeof(search.name));
if ((item = RB_FIND(itemscs, &m->itemscs, &search)) != NULL) {
if (mib_resolve_item(item) == -1)
return NULL;
return item;
}
for (i = 0; m->imports != NULL && m->imports[i].name[0] != '\0'; i++) {
import = &m->imports[i];
for (j = 0; j < import->nsymbols; j++) {
if (strcmp(name, import->symbols[j].name) == 0)
return import->symbols[j].item;
}
}
log_warnx("%s::%s: item %s not found: disabling",
m->name, orig->name, name);
return NULL;
}
int
mib_resolve_oid(struct oid_resolved *dst, struct oid_unresolved *src,
struct item *item)
{
struct module *m = item->module;
struct item *reference, search;
struct ber_oid oid;
size_t i, j, bo_n;
oid.bo_n = 0;
for (i = 0; i < src->bo_n; i++) {
switch (src->bo_id[i].type) {
case OCT_DESCRIPTOR:
if ((reference = mib_item_find(item,
src->bo_id[i].name)) == NULL)
return -1;
for (j = 0; j < reference->oid.bo_n; j++)
oid.bo_id[oid.bo_n++] =
reference->oid.bo_id[j];
break;
case OCT_NUMBER:
if (oid.bo_n == nitems(oid.bo_id)) {
log_warnx("%s::%s: OID too long: disabling",
m->name, item->name);
return -1;
}
oid.bo_id[oid.bo_n++] = src->bo_id[i].number;
break;
case OCT_NAMEANDNUMBER:
if (oid.bo_n == nitems(oid.bo_id)) {
log_warnx("%s::%s: OID too long: disabling",
m->name, item->name);
return -1;
}
oid.bo_id[oid.bo_n++] = src->bo_id[i].number;
if (i == src->bo_n - 1) {
if (strcmp(src->bo_id[i].name, item->name) != 0) {
log_warnx("%s::%s: last OBJECT "
"IDENTIFIER component name doesn't "
"match item: disabling", m->name,
item->name);
return -1;
}
break;
}
strlcpy(search.name, src->bo_id[i].name,
sizeof(search.name));
reference = RB_FIND(itemscs, &m->itemscs, &search);
if (reference != NULL) {
search.oid.bo_n = oid.bo_n;
search.oid.bo_id = oid.bo_id;
if (item_cmp_oid(reference, &search) != 0) {
log_warnx("%s::%s: two different OIDs "
"for same descriptor: disabling",
m->name, item->name);
return -1;
}
break;
}
bo_n = src->bo_n;
src->bo_n = i + 1;
module = m;
reference = mib_oid(src->bo_id[i].name, src);
module = NULL;
if (reference == NULL)
return -1;
if (mib_resolve_item(reference) == -1)
return -1;
src->bo_n = bo_n;
break;
}
}
dst->bo_n = oid.bo_n;
if ((dst->bo_id = calloc(dst->bo_n, sizeof(*dst->bo_id))) == NULL) {
log_warn("malloc");
return -1;
}
for (i = 0; i < oid.bo_n; i++)
dst->bo_id[i] = oid.bo_id[i];
return 0;
}
/*
* No recursion protection. Assume MIBs do the right thing
*/
int
mib_resolve_item(struct item *item)
{
struct item *prev;
struct oid_resolved oid;
if (item->resolved)
return 0;
item->resolved = 1;
if (item->type == IT_MACRO ||
item->type == IT_APPLICATIONSYNTAX ||
item->type == IT_TEXTUAL_CONVENTION)
return 0;
if (mib_resolve_oid(&oid, item->oid_unresolved, item) == -1)
return -1;
free(item->oid_unresolved);
item->oid = oid;
if ((prev = RB_INSERT(items, &items, item)) != NULL) {
/* Prioritize an OID derived from a MACRO over a plain OID */
if (prev->type == IT_OID && item->type != IT_OID) {
RB_REMOVE(items, &items, prev);
RB_INSERT(items, &items, item);
}
}
RB_INSERT(itemsgci, &itemsci, item);
RB_INSERT(itemsci, &item->module->itemsci, item);
return 0;
}
int
mib_resolve_module(struct module *m)
{
struct module msearch;
struct import *import;
struct import_symbol *symbol;
struct item *item, isearch;
size_t i, j;
if (m->resolved)
return 0;
m->resolved = 1;
for (i = 0; m->imports != NULL && m->imports[i].name[0] != '\0'; i++) {
import = &m->imports[i];
strlcpy(msearch.name, import->name, sizeof(msearch.name));
import->module = RB_FIND(modulescs, &modulescs, &msearch);
if (import->module == NULL ||
mib_resolve_module(import->module) == -1) {
log_warnx("%s: import %s not found: disabling",
m->name, import->name);
goto fail;
}
for (j = 0; j < import->nsymbols; j++) {
symbol = &import->symbols[j];
strlcpy(isearch.name, symbol->name,
sizeof(isearch.name));
symbol->item = RB_FIND(itemscs,
&import->module->itemscs, &isearch);
if (symbol->item == NULL) {
log_warnx("%s: symbol %s not found in %s: "
"disabling", m->name, symbol->name,
import->name);
goto fail;
}
}
}
RB_FOREACH(item, itemscs, &m->itemscs) {
if (mib_resolve_item(item) == -1)
goto fail;
}
free(m->imports);
m->imports = NULL;
return 0;
fail:
mib_modulefree(m);
return -1;
}
void
mib_resolve(void)
{
struct module *m;
mib_defaults();
next:
RB_FOREACH(m, modulescs, &modulescs) {
if (mib_resolve_module(m) == -1) {
/*
* mib_resolve_module can recurse and remove,
* for which RB_FOEACH_SAFE doesn't protect.
*/
goto next;
}
}
}
int
module_cmp_cs(struct module *m1, struct module *m2)
{
return strcmp(m1->name, m2->name);
}
int
module_cmp_ci(struct module *m1, struct module *m2)
{
return strcasecmp(m1->name, m2->name);
}
int
item_cmp_cs(struct item *d1, struct item *d2)
{
return strcmp(d1->name, d2->name);
}
int
item_cmp_ci(struct item *d1, struct item *d2)
{
return strcasecmp(d1->name, d2->name);
}
int
item_cmp_oid(struct item *i1, struct item *i2)
{
size_t i, min;
min = i1->oid.bo_n < i2->oid.bo_n ? i1->oid.bo_n : i2->oid.bo_n;
for (i = 0; i < min; i++) {
if (i1->oid.bo_id[i] < i2->oid.bo_id[i])
return (-1);
if (i1->oid.bo_id[i] > i2->oid.bo_id[i])
return (1);
}
/* i1 is parent of i2 */
if (i1->oid.bo_n < i2->oid.bo_n)
return (-2);
/* i1 is child of i2 */
if (i1->oid.bo_n > i2->oid.bo_n)
return 2;
return (0);
}
RB_GENERATE_STATIC(modulesci, module, entryci, module_cmp_ci);
RB_GENERATE_STATIC(modulescs, module, entrycs, module_cmp_cs);
RB_GENERATE_STATIC(itemsgci, item, entrygci, item_cmp_ci);
RB_GENERATE_STATIC(itemsci, item, entryci, item_cmp_ci);
RB_GENERATE_STATIC(itemscs, item, entrycs, item_cmp_cs);
RB_GENERATE_STATIC(items, item, entry, item_cmp_oid);