Documentation
¶
Index ¶
- func ARPCache(ctx context.Context, runner Runner) map[string]string
- func DiscoverMACs(ctx context.Context, targets []string, runner Runner) (map[string]string, error)
- func HostDiscovery(ctx context.Context, targets []string, runner Runner) ([]string, error)
- func HostDiscoveryWithMACs(ctx context.Context, targets []string, runner Runner) ([]string, map[string]string, error)
- func IsRoot() bool
- func MergeARPHosts(live []string, targets []string, arp map[string]string) []string
- func NativeDiscovery(ctx context.Context, targets []string, runner Runner, ...) ([]string, map[string]string, error)
- func NativeDiscoverySupported(runner Runner) bool
- func NativeDiscoveryTimed(ctx context.Context, targets []string, runner Runner, ...) ([]string, map[string]string, map[string]time.Duration, error)
- func NativePortScanViable(cfg Config, runner Runner) bool
- func PrimeSudo() error
- func SudoAvailable() bool
- func SudoPrimed() bool
- func ValidatePorts(spec string) error
- type Address
- type Config
- type Host
- type HostResult
- func HostsUpResults(ips []string) []HostResult
- func NativePortScan(ctx context.Context, hosts []string, cfg Config, ...) []HostResult
- func NativePortScanWithMetadata(ctx context.Context, hosts []string, metadata map[string]NativeMetadata, ...) []HostResult
- func ScanHosts(ctx context.Context, live []string, cfg Config, runner Runner) ([]HostResult, error)
- func SynthesizeNativeResult(r sweep.PortResult, metadata NativeMetadata) HostResult
- type Hostname
- type Hostnames
- type LocalRunner
- type NativeMetadata
- type NmapRun
- type OS
- type OSClass
- type OSDetail
- type OSMatch
- type Port
- type PortRisk
- type PortState
- type Ports
- type Runner
- type SSHRunner
- type Script
- type ScriptElem
- type ScriptTable
- type Service
- type Severity
- type Status
- type TLSCert
- type Times
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ARPCache ¶
ARPCache reads the OS ARP table (via `arp -a -n`) and returns an IP->MAC map for all resolved L2 neighbors. This requires no privileges and complements nmap, which only reports MACs when run as root. Incomplete entries and the broadcast/multicast addresses are skipped. Runs through the supplied runner so it works locally or over SSH.
func DiscoverMACs ¶
DiscoverMACs runs a lightweight discovery in XML and extracts IP->MAC pairs (same L2 only).
func HostDiscovery ¶
HostDiscovery uses 'nmap -sn -oG -' to find live hosts quickly, via the provided runner (local or ssh).
func HostDiscoveryWithMACs ¶
func HostDiscoveryWithMACs(ctx context.Context, targets []string, runner Runner) ([]string, map[string]string, error)
HostDiscoveryWithMACs performs one XML discovery sweep and returns both live hosts and any L2 MAC addresses nmap observed. It avoids a duplicate sweep when privileged or remote scans request MAC data.
func IsRoot ¶
func IsRoot() bool
IsRoot reports whether the process is already running as root, in which case nmap can do ARP discovery, SYN scans, and MAC reporting without sudo.
func MergeARPHosts ¶
MergeARPHosts adds IPs from the ARP cache that fall inside the scanned targets but were missed by unprivileged host discovery. Returns the combined, de-duplicated host list. This recovers most of what `sudo nmap` would find on a local subnet without needing root.
func NativeDiscovery ¶
func NativeDiscovery(ctx context.Context, targets []string, runner Runner, progress func(done, total int)) ([]string, map[string]string, error)
NativeDiscovery finds live hosts without invoking nmap, using the ARP cache plus a bounded TCP connect sweep (see package sweep). It mirrors HostDiscoveryWithMACs so callers can swap between the two.
On a LAN this is typically an order of magnitude faster than `nmap -sn`, because the timeout policy is ours rather than nmap's WAN-tuned defaults, and because the ARP cache answers for free. It needs no root.
The runner is used only for the ARP read, so this still works over SSH. Note the TCP probes originate from *this* machine even when a runner points elsewhere, so native discovery is meant for local scans; callers scanning via SSH should stay on the nmap path.
func NativeDiscoverySupported ¶
NativeDiscoverySupported reports whether native discovery makes sense for this runner. It requires probing from the local machine, so an SSH runner (which would probe the wrong network) is excluded.
func NativeDiscoveryTimed ¶ added in v0.2.8
func NativeDiscoveryTimed(ctx context.Context, targets []string, runner Runner, progress func(done, total int)) ([]string, map[string]string, map[string]time.Duration, error)
NativeDiscoveryTimed is NativeDiscovery plus the round-trip time each host took to answer.
The sweep already measures it — every discovery is a timed TCP connect — and discarding it meant the RTT column stayed empty on exactly the fast path most scans use. Reporting it costs no extra packets.
func NativePortScanViable ¶
NativePortScanViable reports whether a native port scan can serve this config. It cannot when the caller needs what only nmap provides: service versions, OS detection, or NSE output (the -A presets), or UDP.
func PrimeSudo ¶
func PrimeSudo() error
PrimeSudo runs an interactive `sudo -v` to cache credentials. It connects the real terminal so the password prompt is visible — callers MUST invoke this BEFORE a Bubble Tea alt-screen takes over, otherwise the prompt is hidden. Returns nil if already root or on successful authentication.
func SudoAvailable ¶
func SudoAvailable() bool
SudoAvailable reports whether the sudo binary exists on PATH.
func SudoPrimed ¶
func SudoPrimed() bool
SudoPrimed reports whether sudo can run non-interactively right now (credentials cached from a recent authentication).
func ValidatePorts ¶ added in v0.2.4
ValidatePorts reports whether a port specification is usable, without running anything. Front ends call it before a scan starts so a typo is a message at the prompt rather than an empty result table after the work is done — the native and nmap paths disagree about what malformed syntax means, and neither disagreement should be visible to a user who simply mistyped.
An empty spec is valid: it means "use the preset's default ports".
Types ¶
type Config ¶
type Config struct {
Preset string
Ports string
UseSYN bool
Concurrency int
HostTimeout time.Duration
DisableVendors bool
NeedMAC bool
// BatchSize groups targets into one nmap process. Values <=1 preserve the
// original one-process-per-host behavior.
BatchSize int
// DiscardResults avoids retaining raw XML after OnResult has consumed it.
// Streaming TUI scans use this to bound memory on large/deep scans.
DiscardResults bool
// Progress, if set, is called after each host finishes scanning.
// It may be invoked from multiple goroutines.
Progress func(done, total int)
// OnResult, if set, is called with each host's result as soon as it
// completes, enabling streaming consumers. May be invoked from
// multiple goroutines.
OnResult func(HostResult)
}
type Host ¶
type Host struct {
Status Status `xml:"status"`
Addresses []Address `xml:"address"`
Hostnames Hostnames `xml:"hostnames"`
Ports Ports `xml:"ports"`
OS OS `xml:"os"`
Times Times `xml:"times"`
}
func (Host) BestOSDetail ¶
BestOSDetail returns the structured best OS guess, or nil if none.
func (Host) BestOSGuess ¶
BestOSGuess returns the highest-accuracy OS match name, or "".
type HostResult ¶
type HostResult struct {
IP string
Targets []string // all targets represented when this is a batched result
XMLBytes []byte // raw nmap xml for this host
Err error
Fallback bool // requested SYN scan retried as TCP connect
}
func HostsUpResults ¶
func HostsUpResults(ips []string) []HostResult
HostsUpResults synthesizes minimal "host up, no ports" results for a set of live IPs, for the discover-only fast path. The XML mirrors what nmap emits for an up host with no open ports, so the existing parsers/printers consume it unchanged.
func NativePortScan ¶
func NativePortScan(ctx context.Context, hosts []string, cfg Config, progress func(done, total int)) []HostResult
NativePortScan probes open ports on the given hosts without invoking nmap, returning results in the same shape the nmap path produces so every existing parser, printer, and report renderer consumes them unchanged.
It is dramatically faster than nmap for the common "what's open here" case, but it reports port numbers and well-known service names only — no version detection, OS fingerprinting, or NSE output. Callers needing those must use the nmap path (the deep/default presets).
progress may be nil. When set it is called from multiple goroutines as hosts complete, so it must be safe for concurrent use.
cfg.OnResult, when set, receives each host the instant it finishes probing rather than after every host has. Callers should prefer it over the returned slice: the slice can only be complete once the slowest unreachable address has burned its full timeout, whereas a responsive LAN host streams in milliseconds. The returned slice is still complete, for callers that only want the final answer.
func NativePortScanWithMetadata ¶ added in v0.2.8
func NativePortScanWithMetadata(ctx context.Context, hosts []string, metadata map[string]NativeMetadata, cfg Config, progress func(done, total int)) []HostResult
NativePortScanWithMetadata preserves discovery timing and TLS identification through the native scan's existing XML/parser/rendering path.
The engine supplies the metadata: the discovery sweep already timed each host's answer, and passing that through here is what puts a value in the RTT column on the fast path. Setting IdentifyTLS on one metadata entry opts the run into bounded certificate identification after every open port is known.
func SynthesizeNativeResult ¶ added in v0.2.8
func SynthesizeNativeResult(r sweep.PortResult, metadata NativeMetadata) HostResult
SynthesizeNativeResult exposes the no-I/O XML conversion for pipeline code that enriches a completed open-port result before handing it to renderers.
type LocalRunner ¶
type LocalRunner struct{}
type NativeMetadata ¶ added in v0.2.8
type NativeMetadata struct {
RTT time.Duration
TLS map[int]enrich.TLSInfo
// IdentifyTLS opts this scan into active TLS handshakes. It is deliberately
// separate from TLS: callers may supply already-known certificate data
// without authorizing network activity.
IdentifyTLS bool
// TLSConfig bounds the active identification work. Zero values use the
// conservative defaults in enrich.LookupTLS.
TLSConfig enrich.TLSConfig
// TLSMaxEndpoints caps handshakes across the entire scan. Values <= 0 use
// the package default. The first metadata entry with IdentifyTLS set owns
// these scan-wide settings.
TLSMaxEndpoints int
}
NativeMetadata is enrichment to embed in one host's synthetic nmap XML.
type OS ¶
type OS struct {
Matches []OSMatch `xml:"osmatch"`
}
OS holds nmap's OS-detection guesses (populated by -A / -O presets).
type OSClass ¶
type OSClass struct {
Type string `xml:"type,attr"`
Vendor string `xml:"vendor,attr"`
OSFamily string `xml:"osfamily,attr"`
OSGen string `xml:"osgen,attr"`
Accuracy int `xml:"accuracy,attr"`
CPEs []string `xml:"cpe"`
}
OSClass is nmap's structured OS classification (vendor/family/generation) with optional CPE identifiers.
type OSDetail ¶
type OSDetail struct {
Name string // full match name, e.g. "Linux 5.4 - 5.10"
Accuracy int // 0-100 confidence
Vendor string // e.g. "Linux", "HP", "Apple"
Family string // e.g. "Linux", "embedded", "macOS"
Gen string // e.g. "5.X"
CPE string // first OS CPE, e.g. "cpe:/o:linux:linux_kernel:5"
}
OSDetail describes the best OS guess in structured form for the detail pane.
type Port ¶
type Port struct {
Protocol string `xml:"protocol,attr"`
PortID int `xml:"portid,attr"`
State PortState `xml:"state"`
Service Service `xml:"service"`
Scripts []Script `xml:"script"`
}
type PortRisk ¶
PortRisk describes why an open port is interesting.
func ClassifyPort ¶
ClassifyPort assesses an open port. It prefers a port-number match, then falls back to the service name, returning {SevNone} when nothing stands out.
type Runner ¶
Runner abstracts how we execute commands (locally or over SSH).
func NewLocalRunner ¶
func NewLocalRunner() Runner
type SSHRunner ¶
type SSHRunner struct {
Target string // user@host (or host if agent configured)
}
----- SSH runner ----- Executes: ssh [options] -- <target> <quoted remote command>. The local exec uses an argv and "--" protects the destination from local ssh option parsing. SSH still hands the remote command to the remote login shell, so every remote argument is single-quoted before the command is passed to ssh. The input source is whoever supplies ndscan's target string; this quoting is defense-in-depth for wrapper scripts and CI, not a remote-attacker boundary.
type Script ¶
type Script struct {
ID string `xml:"id,attr"`
Output string `xml:"output,attr"`
Tables []ScriptTable `xml:"table"`
Elems []ScriptElem `xml:"elem"`
}
Script is one NSE script result attached to a port (from --script / -A).
type ScriptElem ¶
ScriptElem is a single key/value leaf in a script result.
type ScriptTable ¶
type ScriptTable struct {
Key string `xml:"key,attr"`
Tables []ScriptTable `xml:"table"`
Elems []ScriptElem `xml:"elem"`
}
ScriptTable is a (possibly nested) keyed table within a script result.
type Service ¶
type Severity ¶
type Severity int
Severity ranks how noteworthy an open port is on a local network.
const ( // SevNone means the port is unremarkable / expected. SevNone Severity = iota // SevInfo flags a service worth noticing (admin panels, databases). SevInfo // SevWarn flags a service that is commonly a security concern when // exposed (remote access, file sharing, legacy protocols). SevWarn // SevHigh flags a service that is almost always a problem to find open // (cleartext credentials, unauthenticated remote control). SevHigh )