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:
- Initial SNMP GET for sysDescr + sysObjectID
- Detect vendor from enterprise OID (e.g., HP = 1.3.6.1.4.1.11)
- Load vendor module (hp.go, canon.go, etc.)
- Build combined OID list (vendor + generic)
- Execute SNMP WALK for all OIDs
- Parse results with vendor parser
- Fallback to generic parser for unknown vendors
Pipeline (pipeline.go)
Purpose: Orchestrate multi-stage scanning with worker pools.
Stages:
- Liveness: Fast TCP/ICMP checks (100-200 workers)
- Detection: SNMP-based printer identification (20-50 workers)
- Deep Scan: Full SNMP walks + metrics (3-10 workers)
Key Functions:
ScanRange(ctx, cidr, config) ([]PrinterInfo, error): Scan entire subnetScanIPs(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.*→ HP1.3.6.1.4.1.1602.*→ Canon1.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:
config.json(static config file)- SQLite database (via Settings UI)
- 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)
| Stage | Duration | Notes |
|---|---|---|
| Liveness (TCP) | 100-500ms | Fast port checks |
| Detection (SNMP) | 500ms-2s | Limited OID query |
| Deep Scan (SNMP) | 2-10s | Full walk with retries |
Concurrency Limits
| Stage | Default Workers | Tuning Notes |
|---|---|---|
| Liveness | 100-200 | I/O bound, can be high |
| Detection | 20-50 | Network + SNMP processing |
| Deep Scan | 3-10 | Expensive, 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 timeoutno such host: Invalid IP or DNS failureconnection refused: SNMP disabled on deviceno 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()indetect.go: Full network scanLiveDiscoveryDetect()inscanner_api.go: Single device enrichment- Live discovery handlers (mDNS, SSDP, WS-Discovery)
With Storage (agent/storage/)
Results are persisted via:
UpsertDevice(): Save/update discovered printerGetDevices(): Retrieve all stored printersDeleteDevice(): 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
- Check network connectivity:
ping <device_ip> - Verify SNMP enabled on printer
- Test SNMP manually:
snmpwalk -v2c -c public <device_ip> .1.3.6 - Check firewall allows UDP 161 outbound
Incomplete Data
- Increase SNMP timeout in settings
- Check vendor module supports printer model
- Review logs for parsing errors
- Try generic fallback (disables vendor detection)
Slow Scans
- Reduce concurrency if network is saturated
- Lower SNMP timeout for faster failures
- Use liveness pre-filtering to skip dead IPs
- Limit scan range to active subnets
Related Documentation
- 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