scanner

package
v0.0.0-...-04f7c6f Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 14, 2026 License: GPL-2.0 Imports: 21 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrNoRawSocket = errors.New("raw socket access denied")

ErrNoRawSocket is returned by Precheck when raw-socket access is missing.

Functions

func Precheck

func Precheck(iface *netiface.Info) error

Precheck verifies that the calling process can open libpcap on the chosen interface. It is a fast smoke test that pcap_open_live succeeds — exactly the same call ARPWorker will make. If it fails, the user has no raw socket privilege (Linux), Npcap is missing or not in WinPcap-API-compatible mode (Windows), or BPF read access is missing (macOS — install ChmodBPF or run with sudo), and the rest of the program will be useless.

Types

type ARPWorker

type ARPWorker struct {
	Iface *netiface.Info
}

ARPWorker passively sniffs ARP packets on the given interface and emits an Update for every packet seen.

func (*ARPWorker) Run

func (w *ARPWorker) Run(ctx context.Context, out chan<- Update) error

type ActiveWorker

type ActiveWorker struct {
	Subnet      *net.IPNet
	HostIPs     []net.IP // pre-enumerated subnet hosts
	Gateway     net.IP   // default-route gateway IP for the gateway-resolver hostname probe
	Interval    time.Duration
	WorkerCount int
	// KnownIPs returns the set of ARP-confirmed IP addresses (string form).
	// When set, the worker bypasses the liveness gate for these IPs and
	// runs the enrichment chain regardless — useful for hosts that
	// stealth-drop ICMP/TCP probes (Windows 11 default firewall) but
	// still respond to UDP-based queries like NBNS. Optional.
	KnownIPs func() map[string]struct{}
}

ActiveWorker periodically probes every host in Subnet plus any IP it has learned about, calling probe.Ping, probe.ScanPorts, and probe.ResolveHostname. One full sweep emits one Update per responding host.

func (*ActiveWorker) Run

func (w *ActiveWorker) Run(ctx context.Context, out chan<- Update) error

func (*ActiveWorker) SweepOnce

func (w *ActiveWorker) SweepOnce(ctx context.Context, out chan<- Update)

SweepOnce probes every host once and returns when the sweep finishes. Used directly by --once mode.

type Config

type Config struct {
	Iface         *netiface.Info
	MergerOptions MergerOptions
}

Config describes the scanner's runtime parameters.

type MDNSWorker

type MDNSWorker struct{}

MDNSWorker browses for mDNS services on the given interface and emits an Update for each discovered instance.

func (*MDNSWorker) Run

func (w *MDNSWorker) Run(ctx context.Context, out chan<- Update) error

type Merger

type Merger struct {
	// contains filtered or unexported fields
}

Merger owns the live device map and emits DeviceEvents.

func NewMerger

func NewMerger(opts MergerOptions) *Merger

NewMerger constructs an idle merger; call Run to start consuming updates.

func (*Merger) KnownIPs

func (m *Merger) KnownIPs() map[string]struct{}

KnownIPs returns the set of IP addresses owned by ARP-confirmed devices (those keyed by MAC). IP-only entries are excluded since they were created by the active worker itself and don't constitute independent confirmation.

Used by the active worker to skip the liveness gate for IPs we already know are real — e.g., Windows hosts that stealth-drop ICMP and TCP probes but were captured by the passive ARP listener.

func (*Merger) Run

func (m *Merger) Run(ctx context.Context, in <-chan Update, out chan<- model.DeviceEvent)

Run consumes updates from in and publishes events to out. Returns when ctx is cancelled.

func (*Merger) Snapshot

func (m *Merger) Snapshot() []*model.Device

Snapshot returns a copy of all devices in arbitrary order.

type MergerOptions

type MergerOptions struct {
	StaleAfter    time.Duration // Online → Stale after this idle period (default 60s)
	LeftAfter     time.Duration // Stale → Offline + emit Left after this idle period (default 5m)
	SweepInterval time.Duration // how often to scan for status transitions (default 30s)
}

MergerOptions tunes the merger's timing behavior.

type Scanner

type Scanner struct {
	// contains filtered or unexported fields
}

Scanner wires the three workers and the merger together.

func New

func New(cfg Config) *Scanner

New builds a fresh Scanner. Call Run to start it.

func (*Scanner) Events

func (s *Scanner) Events() <-chan model.DeviceEvent

Events returns a read-only channel of DeviceEvent.

func (*Scanner) Run

func (s *Scanner) Run(ctx context.Context) error

Run starts the workers and the merger. Blocks until ctx is cancelled.

func (*Scanner) Snapshot

func (s *Scanner) Snapshot() []*model.Device

Snapshot returns a copy of the current device map.

func (*Scanner) TriggerSweep

func (s *Scanner) TriggerSweep(ctx context.Context)

TriggerSweep runs a single out-of-band active sweep using the same worker pool as the periodic scan. Safe to call concurrently with Run.

type Update

type Update struct {
	Source        string              // "arp" | "mdns" | "active"
	Time          time.Time           // when the observation happened
	MAC           string              // lowercase, colon-separated; "" if unknown
	IP            net.IP              // required for nearly every update
	Hostname      string              // mDNS or rDNS
	Vendor        string              // OUI lookup may have already been applied
	OpenPorts     []model.Port        // active prober only; empty replaces existing
	Services      []model.ServiceInst // mDNS only; appended/deduped
	RTT           time.Duration       // active prober only
	Alive         bool                // active prober: is the device responding?
	TTL           int                 // active prober only; raw TTL for OSDetect
	NBNSResponded bool                // active prober only; true when probe.NBNS returned a name
}

Update is what a Worker emits to the merger. Every field except Source is optional; the merger merges non-zero fields into the Device.

func SeedFromKernelARP

func SeedFromKernelARP(ifaceName string, subnet *net.IPNet) []Update

SeedFromKernelARP reads the kernel's ARP cache from /proc/net/arp and returns one Update per neighbor that the kernel has already resolved for ifaceName within subnet. Returns nil on any read error — seeding is best-effort and not worth failing startup over.

The passive ARPWorker only sees ARP packets that traverse the wire during the scan window. When the kernel cache is already populated, ICMP/TCP probes reuse those entries without emitting fresh ARP requests, leaving affected hosts visible to the active prober but MAC-less in the device map. Seeding closes that gap.

type Worker

type Worker interface {
	Run(ctx context.Context, out chan<- Update) error
}

A Worker emits Updates on its output channel until the context is cancelled. Implementations: arpWorker, mdnsWorker, activeWorker.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL