towerops/lib/snmpkit/snmp_lib/mib/preprocessor.ex
2026-01-23 12:52:17 -06:00

133 lines
3.9 KiB
Elixir

defmodule SnmpKit.SnmpLib.MIB.Preprocessor do
@moduledoc """
Preprocessor for MIB files to handle problematic constructs before parsing.
This module addresses issues with very large TEXTUAL-CONVENTION enumerations
that cause parser state corruption in certain edge cases.
"""
require Logger
@doc """
Preprocess MIB content to handle problematic constructs.
"""
def preprocess(content) when is_binary(content) do
content
|> simplify_large_enumerations()
|> normalize_whitespace()
end
@doc """
Simplify very large TEXTUAL-CONVENTION enumerations that cause parser issues.
This replaces complex enumerations with simplified versions that preserve
the essential structure while avoiding parser state corruption.
"""
def simplify_large_enumerations(content) do
# Pattern to match TEXTUAL-CONVENTION with INTEGER syntax
pattern = ~r/
(?P<prefix>
\w+\s*::=\s*TEXTUAL-CONVENTION\s+
(?:.*?\n)*? # Non-greedy match for TC content
SYNTAX\s+INTEGER\s*\{
)
(?P<enumeration>
(?:.*?\n)*? # Enumeration content
)
(?P<suffix>
\}\s* # Closing brace
)
/xms
# Find all matches and process large enumerations
Regex.replace(pattern, content, fn full_match, prefix, enumeration, suffix ->
enum_lines = String.split(enumeration, "\n")
if length(enum_lines) > 50 do
Logger.debug("Simplifying large enumeration with #{length(enum_lines)} lines")
simplified_enum = simplify_enumeration_content(enumeration)
prefix <> simplified_enum <> suffix
else
full_match
end
end)
end
defp simplify_enumeration_content(enumeration) do
lines = String.split(enumeration, "\n")
# Extract enumeration items while preserving structure
simplified_items =
lines
|> Enum.map(&extract_enum_item/1)
# Remove nils
|> Enum.filter(& &1)
# Take only first 10 items to avoid parser issues
|> Enum.take(10)
|> Enum.with_index()
|> Enum.map(fn {{name, _original_value}, index} ->
" #{name}(#{index + 1})"
end)
# Add a final catch-all item
simplified_items = simplified_items ++ [" other(999)"]
Enum.join(simplified_items, ",\n") <> "\n"
end
defp extract_enum_item(line) do
# Pattern to match enumeration items like "itemName(123),"
case Regex.run(~r/^\s*(\w+)\s*\(\s*(\d+)\s*\)\s*,?\s*(?:--.*)?$/, line) do
[_full, name, value] -> {name, String.to_integer(value)}
_ -> nil
end
end
@doc """
Normalize excessive whitespace that might cause tokenizer issues.
"""
def normalize_whitespace(content) do
content
# Replace multiple spaces/tabs with single space
|> String.replace(~r/[ \t]+/, " ")
# Remove whitespace-only lines
|> String.replace(~r/\n[ \t]+\n/, "\n\n")
# Replace multiple newlines with double newline
|> String.replace(~r/\n{3,}/, "\n\n")
end
@doc """
Check if a MIB file contains problematic constructs.
"""
def has_problematic_constructs?(content) do
# Check for very large TEXTUAL-CONVENTION enumerations
case Regex.run(~r/TEXTUAL-CONVENTION.*?SYNTAX\s+INTEGER\s*\{(.*?)\}/ms, content) do
[_full, enumeration] ->
enum_lines = String.split(enumeration, "\n")
length(enum_lines) > 50
_ ->
false
end
end
@doc """
Get statistics about enumeration complexity in a MIB file.
"""
def analyze_enumerations(content) do
pattern = ~r/SYNTAX\s+INTEGER\s*\{(.*?)\}/ms
pattern
|> Regex.scan(content)
|> Enum.map(fn [_full, enumeration] ->
lines = String.split(enumeration, "\n")
items =
Enum.count(lines, fn line ->
Regex.match?(~r/^\s*\w+\s*\(\s*\d+\s*\)/, line)
end)
%{lines: length(lines), items: items}
end)
end
end