PrintMaster DOCS
GitHub ↗
Documentation

Scanner Module Documentation

Location: agent/scanner/

The scanner module is responsible for device detection, SNMP querying, and printer information extraction. It provides a vendor-aware, configurable scanning system.

Architecture Overview

scanner/
├── detector.go          # Device type detection (IsPrinter? confidence scoring)
├── pipeline.go          # Multi-stage scan orchestration (liveness → detection → deep scan)
├── query.go             # SNMP query execution and vendor-specific data collection
├── snmp.go              # Low-level SNMP communication wrapper
├── enumerator.go        # IP range enumeration and subnet handling
└── vendor/              # Vendor-specific OID profiles and parsers
    ├── hp.go
    ├── canon.go
    ├── brother.go
    ├── epson.go
    ├── kyocera.go
    ├── lexmark.go
    ├── ricoh.go
    ├── samsung.go
    ├── xerox.go
    ├── generic.go       # Fallback standard Printer-MIB
    └── registry.go      # Vendor detection and module selection

Core Components

Detector (detector.go)

Purpose: Determine if a device is a printer and calculate confidence score.

Key Functions:

  • IsPrinterDevice(ctx, ip, timeout) (bool, float64, error): Fast printer detection
  • Checks multiple signals:
    • Open printer ports (9100 JetDirect, 631 IPP, 515 LPD)
    • SNMP sysObjectID matches known printer enterprise IDs
    • Presence of Printer-MIB OIDs
    • HP/Canon/Brother-specific OIDs
  • Returns confidence score 0.0-1.0

Example:

isPrinter, confidence, err := IsPrinterDevice(ctx, "192.168.1.100", 5)
if isPrinter && confidence > 0.7 {
    // High confidence this is a printer
}

Query System (query.go)

Purpose: Execute SNMP queries and extract structured printer information.

Key Functions:

  • QueryDevice(ctx, ip, community, timeout) (*PrinterInfo, error): Main query function
  • Vendor auto-detection via sysObjectID
  • Builds OID list from vendor module + generic Printer-MIB
  • Executes SNMP WALK/GET operations
  • Parses results using vendor-specific parsers

Query Flow:

  1. Initial SNMP GET for sysDescr + sysObjectID
  2. Detect vendor from enterprise OID (e.g., HP = 1.3.6.1.4.1.11)
  3. Load vendor module (hp.go, canon.go, etc.)
  4. Build combined OID list (vendor + generic)
  5. Execute SNMP WALK for all OIDs
  6. Parse results with vendor parser
  7. Fallback to generic parser for unknown vendors

Pipeline (pipeline.go)

Purpose: Orchestrate multi-stage scanning with worker pools.

Stages:

  1. Liveness: Fast TCP/ICMP checks (100-200 workers)
  2. Detection: SNMP-based printer identification (20-50 workers)
  3. Deep Scan: Full SNMP walks + metrics (3-10 workers)

Key Functions:

  • ScanRange(ctx, cidr, config) ([]PrinterInfo, error): Scan entire subnet
  • ScanIPs(ctx, ips, config) ([]PrinterInfo, error): Scan specific IPs

Benefits:

  • Reduces wasted work (don’t deep-scan non-printers)
  • Configurable concurrency per stage
  • Bounded resource usage
  • Context-based cancellation

Vendor System (vendor/)

Purpose: Vendor-specific OID profiles and data parsing.

Interface (VendorModule):

type VendorModule interface {
    Name() string
    GetOIDs() []string
    ParseMetrics(pdus []gosnmp.SnmpPDU) map[string]interface{}
}

Vendor Modules:

  • HP (hp.go): Extended metrics (job accounting, fax pages, ADF scans)
  • Canon (canon.go): Canon-specific counters and status
  • Brother (brother.go): Brother drum life, toner levels
  • Epson (epson.go): Epson ink tank levels
  • Kyocera (kyocera.go): Kyocera maintenance counters
  • Lexmark (lexmark.go): Lexmark enterprise OIDs
  • Ricoh (ricoh.go): Ricoh/Savin/Lanier shared OIDs
  • Samsung (samsung.go): Samsung-specific metrics
  • Xerox (xerox.go): Xerox FreeFlow OIDs
  • Generic (generic.go): Standard Printer-MIB fallback

Vendor Detection (registry.go):

func DetectVendor(sysObjectID string) VendorModule

Maps enterprise OIDs to vendors:

  • 1.3.6.1.4.1.11.* → HP
  • 1.3.6.1.4.1.1602.* → Canon
  • 1.3.6.1.4.1.2435.* → Brother
  • etc.

SNMP Wrapper (snmp.go)

Purpose: Low-level SNMP communication abstraction.

Key Functions:

  • SNMPGet(target, community, oids, timeout) ([]gosnmp.SnmpPDU, error)
  • SNMPWalk(target, community, oid, timeout) ([]gosnmp.SnmpPDU, error)
  • SNMPBulkWalk(target, community, oid, maxReps, timeout) ([]gosnmp.SnmpPDU, error)

Features:

  • Retry logic with exponential backoff
  • Configurable timeouts
  • Error handling and logging
  • Thread-safe connection pooling

Configuration

Scanner behavior is controlled via ScannerConfig struct in main.go:

type scannerConfigStruct struct {
    SNMPTimeoutMs        int  // SNMP timeout in milliseconds (default: 2000)
    SNMPRetries          int  // SNMP retry count (default: 1)
    DiscoverConcurrency  int  // Max concurrent scan workers (default: 50)
    sync.RWMutex              // Thread-safe access
}

Settings are loaded from:

  1. config.json (static config file)
  2. SQLite database (via Settings UI)
  3. Defaults if not specified

Usage Examples

Simple Single Device Query

ctx := context.Background()
pi, err := scanner.QueryDevice(ctx, "192.168.1.100", "public", 5)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Found printer: %s %s (Serial: %s)\n", pi.Vendor, pi.Model, pi.Serial)

Range Scan with Pipeline

config := &scanner.ScanConfig{
    SNMPCommunity: "public",
    Timeout:       5,
    Concurrency:   50,
}

printers, err := scanner.ScanRange(ctx, "192.168.1.0/24", config)
for _, p := range printers {
    fmt.Printf("%s: %s %s\n", p.IP, p.Vendor, p.Model)
}

Vendor-Specific Query

// Auto-detects vendor from sysObjectID
pi, _ := scanner.QueryDevice(ctx, "10.0.0.50", "public", 5)

// Access vendor-specific metrics
if pi.Vendor == "HP" {
    fmt.Printf("Job Accounting Pages: %d\n", pi.ExtendedMetrics["JobAccountingPages"])
    fmt.Printf("Fax Pages: %d\n", pi.ExtendedMetrics["FaxPages"])
}

Testing

Test Files:

  • detector_test.go: Device detection and confidence scoring (8 tests)
  • query_test.go: SNMP query and vendor parsing (21 tests)
  • pipeline_test.go: Multi-stage pipeline orchestration (5 tests)

Run Tests:

cd agent
go test ./scanner/... -v

Test Coverage:

go test ./scanner/... -cover

Performance Characteristics

Timing (per device)

StageDurationNotes
Liveness (TCP)100-500msFast port checks
Detection (SNMP)500ms-2sLimited OID query
Deep Scan (SNMP)2-10sFull walk with retries

Concurrency Limits

StageDefault WorkersTuning Notes
Liveness100-200I/O bound, can be high
Detection20-50Network + SNMP processing
Deep Scan3-10Expensive, limit to prevent overwhelming devices

Memory Usage

  • ~1KB per discovered device (in-memory PrinterInfo)
  • ~5-10MB for SNMP library overhead
  • Scales linearly with concurrent workers

Error Handling

Common Errors:

  • context.DeadlineExceeded: SNMP timeout
  • no such host: Invalid IP or DNS failure
  • connection refused: SNMP disabled on device
  • no response: Firewall blocking UDP 161

Retry Strategy:

  • SNMP queries: Retry once with exponential backoff
  • TCP probes: No retry (fail fast)
  • Pipeline stages: Continue on individual device failures

Integration Points

With Agent Package (agent/agent/)

Scanner is called by:

  • Discover() in detect.go: Full network scan
  • LiveDiscoveryDetect() in scanner_api.go: Single device enrichment
  • Live discovery handlers (mDNS, SSDP, WS-Discovery)

With Storage (agent/storage/)

Results are persisted via:

  • UpsertDevice(): Save/update discovered printer
  • GetDevices(): Retrieve all stored printers
  • DeleteDevice(): Remove printer from database

With Logger (agent/logger/)

Scanner logs to structured logger:

  • Info: Discovery progress, device found
  • Warn: SNMP failures, timeout warnings
  • Error: Critical failures, configuration errors
  • Debug: Detailed SNMP PDU parsing

Future Enhancements

Planned Features

  • SNMPv3 support with authentication/encryption
  • Bulk GET for improved performance
  • Result caching with configurable TTL
  • Dynamic OID discovery via MIB walking
  • Parallel vendor module querying

Performance Improvements

  • Connection pooling for SNMP clients
  • Adaptive timeout based on network latency
  • Progressive OID querying (core → extended)
  • Background re-scan of changed devices

Troubleshooting

No Devices Found

  1. Check network connectivity: ping <device_ip>
  2. Verify SNMP enabled on printer
  3. Test SNMP manually: snmpwalk -v2c -c public <device_ip> .1.3.6
  4. Check firewall allows UDP 161 outbound

Incomplete Data

  1. Increase SNMP timeout in settings
  2. Check vendor module supports printer model
  3. Review logs for parsing errors
  4. Try generic fallback (disables vendor detection)

Slow Scans

  1. Reduce concurrency if network is saturated
  2. Lower SNMP timeout for faster failures
  3. Use liveness pre-filtering to skip dead IPs
  4. Limit scan range to active subnets
  • Agent Module - Discovery protocols and detection logic
  • Logger Module - Logging system
  • Settings TODO (legacy document unavailable) - Unimplemented scanner features
  • API Reference - HTTP endpoints using scanner