133 lines
3.9 KiB
Elixir
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
|