client

package
v0.25.9 Latest Latest
Warning

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

Go to latest
Published: Jun 16, 2026 License: MIT Imports: 28 Imported by: 0

Documentation

Index

Constants

View Source
const MediaBlockHeaderLen = protocol.MediaBlockHeaderLen

MediaBlockHeaderLen re-exports the protocol header length so callers in the web layer don't have to import the protocol package twice.

Variables

View Source
var (
	ErrChatNoServerKey    = errors.New("chat requires a pinned server key (sk)")
	ErrChatDisabled       = errors.New("chat not enabled on this server")
	ErrChatServerDisabled = errors.New("chat is turned off by this server")
	ErrChatUnverified     = errors.New("chat info signature missing or invalid")
	// ErrChatVersion means the server speaks a chat protocol this client can't
	// (e.g. an old server still on the previous wire) — a stable mismatch, so it
	// is backed off long and never retried in a tight loop.
	ErrChatVersion = errors.New("chat protocol version incompatible")
	// ErrChatUnreachable means a transport/handshake failure (e.g. the server
	// is rebooting) — transient, retry — not "no chat here".
	ErrChatUnreachable = errors.New("chat server unreachable")
)

Chat errors. ChatStatusError carries a server-reported status.

View Source
var ErrContentHashMismatch = fmt.Errorf("content hash mismatch")

ErrContentHashMismatch is returned when the fetched messages do not match the expected content hash from metadata. This typically means the server regenerated its blocks between the metadata fetch and the block fetch (block-version race). The caller should re-fetch metadata and retry.

View Source
var ErrExtraBlockInvalid = errors.New("content signature invalid")

ErrExtraBlockInvalid means an ExtraBlock WAS returned but failed verification (bad signature, wrong channel, rolled-back timestamp, or content-digest mismatch) — i.e. an active tamper attempt. Always hard-fail.

View Source
var ErrMediaHashMismatch = fmt.Errorf("media content hash mismatch")

ErrMediaHashMismatch indicates the assembled bytes don't match the expected CRC32. The caller must discard the returned bytes.

View Source
var ErrUnverified = errors.New("content unverified: no signature block")

ErrUnverified means no valid ExtraBlock could be fetched (e.g. an old server that doesn't emit one). Content integrity could not be checked; callers decide policy (feed: accept with a warning; messenger: reject).

Functions

func ChatAddressString added in v0.25.9

func ChatAddressString(addr [protocol.AddressSize]byte) string

ChatAddressString renders an address for sharing (lowercase base32, 20 chars).

func DecompressMediaReader added in v0.13.0

func DecompressMediaReader(r io.Reader, compression protocol.MediaCompression) (io.ReadCloser, error)

DecompressMediaReader wraps r per the given compression.

func ParseChatAddress added in v0.25.9

func ParseChatAddress(s string) ([protocol.AddressSize]byte, error)

ParseChatAddress parses a shared address string.

Types

type Cache

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

Cache stores channel and metadata snapshots on disk, keyed by channel name. Channel files not updated for 7 days are automatically removed by Cleanup.

func NewCache

func NewCache(dir string) (*Cache, error)

NewCache creates a file cache in the given directory.

func (*Cache) Cleanup added in v0.5.1

func (c *Cache) Cleanup() error

Cleanup removes channel cache files (ch_*.json) not modified in 7 days.

func (*Cache) GetAllTitles added in v0.11.0

func (c *Cache) GetAllTitles() map[string]string

GetAllTitles reads the display name from every ch_*.json cache file and returns a map of original channel name → display name. Files without a stored name or display name are skipped silently.

func (*Cache) GetMessages

func (c *Cache) GetMessages(channelName string) *MessagesResult

GetMessages reads the cached message history for a channel by name. Returns nil if the file is missing or has not been updated within 7 days.

func (*Cache) MergeAndPut added in v0.5.1

func (c *Cache) MergeAndPut(channelName string, fresh []protocol.Message) (*MessagesResult, error)

MergeAndPut merges fresh messages with the on-disk history, enforces the 200-message cap, detects gaps, persists the result, and returns it. Existing history is always included regardless of age to preserve context.

func (*Cache) PutMetadata

func (c *Cache) PutMetadata(meta *protocol.Metadata) error

PutMetadata stores metadata.

func (*Cache) PutTitle added in v0.11.0

func (c *Cache) PutTitle(channelName, title string) error

PutTitle persists a display name for a channel into its cache file. If the file already exists it is updated in-place so that stored messages are preserved.

type ChatClient added in v0.25.9

type ChatClient struct {

	// OnHandshake, if set, reports handshake cell progress (done, total).
	OnHandshake ChatProgress
	// contains filtered or unexported fields
}

ChatClient drives the chat protocol for one identity on one server config.

func NewChatClient added in v0.25.9

func NewChatClient(f *Fetcher, id *ChatIdentity) *ChatClient

NewChatClient creates a chat driver bound to a fetcher and identity.

func (*ChatClient) Ack added in v0.25.9

func (c *ChatClient) Ack(ctx context.Context, peer [protocol.AddressSize]byte, upToSeq uint32) error

Ack confirms delivery of peer's messages up to upToSeq, freeing inbox quota and driving the sender's ✓✓.

func (*ChatClient) EnsureInfo added in v0.25.9

func (c *ChatClient) EnsureInfo(ctx context.Context) (*protocol.ChatInfo, error)

EnsureInfo fetches and verifies the ChatInfo capability payload once (and re-fetches after expiry or when marked stale). Fail-closed: chat requires a pinned sk and a VERIFIED signature.

func (*ChatClient) FetchInbox added in v0.25.9

func (c *ChatClient) FetchInbox(ctx context.Context, onQuery ChatProgress) ([]ChatIncoming, error)

FetchInbox polls the inbox and decrypts waiting messages. A message that fails transiently (network, sender key not yet propagated) is withheld along with any later message from the same sender, so the caller never acks past a seq that's still on the server.

func (*ChatClient) FetchPeerKey added in v0.25.9

FetchPeerKey returns the verified registration record for addr (cached). The hash(identity)==address check defeats key substitution by the server.

func (*ChatClient) Identity added in v0.25.9

func (c *ChatClient) Identity() *ChatIdentity

Identity returns the client identity.

func (*ChatClient) IsRegistered added in v0.25.9

func (c *ChatClient) IsRegistered(ctx context.Context, peer [protocol.AddressSize]byte) (bool, error)

IsRegistered reports whether peer can be messaged here. (false, nil) = no record; a non-nil error is transient.

func (*ChatClient) NextSeq added in v0.25.9

func (c *ChatClient) NextSeq(ctx context.Context, peer [protocol.AddressSize]byte) (uint32, error)

NextSeq returns the next usable message sequence for peer, recovered from the server (client amnesia safe).

func (*ChatClient) PeerStatus added in v0.25.9

func (c *ChatClient) PeerStatus(ctx context.Context, peer [protocol.AddressSize]byte) (accepted, delivered uint32, err error)

PeerStatus returns (last_accepted ✓, last_delivered ✓✓) for own messages sent to peer. PeerStatus returns ✓/✓✓ counters, serialized with opSeq so it cannot swap the session out from under an in-progress upload.

func (*ChatClient) Quota added in v0.25.9

func (c *ChatClient) Quota() (remaining uint16, resetUnix uint32, known bool)

Quota returns the latest server-reported send quota.

func (*ChatClient) Register added in v0.25.9

func (c *ChatClient) Register(ctx context.Context, _ ChatProgress) error

Register establishes a session (registering the identity on first contact).

func (*ChatClient) Registered added in v0.25.9

func (c *ChatClient) Registered() bool

Registered reports whether this identity established a session (and thus registered) on the server during this process lifetime.

func (*ChatClient) SendMessage added in v0.25.9

func (c *ChatClient) SendMessage(ctx context.Context, peer [protocol.AddressSize]byte, seq uint32, text string, progress ChatProgress) (*ChatSendResult, error)

SendMessage encrypts and uploads one message. seq must be greater than the pair's last accepted seq (use NextSeq to recover it).

func (*ChatClient) ServerAdvertisesChat added in v0.25.9

func (c *ChatClient) ServerAdvertisesChat(ctx context.Context) (advertises, known bool)

ServerAdvertisesChat is the cheap availability pre-check: it reads the signed feed's ChatAvailable bit from metadata block 0 — one query, no ChatInfo retries. A chatless server has no ChatInfo channel, so the full probe only fails after several retried DNS fetches; this short-circuits that. known=false means block 0 didn't carry the flag or the fetch failed, so the caller should fall back to the authoritative EnsureInfo probe rather than assume anything.

func (*ChatClient) SetBudget added in v0.25.9

func (c *ChatClient) SetBudget(b int)

SetBudget sets the per-cell op-plaintext budget B (clamped to the valid range). Smaller B = shorter query names but more cells per message.

type ChatIdentity added in v0.25.9

type ChatIdentity struct {
	Seed     []byte
	Identity ed25519.PrivateKey
	Enc      *ecdh.PrivateKey
	Addr     [protocol.AddressSize]byte
}

ChatIdentity holds the seed-derived chat keys.

func NewChatIdentity added in v0.25.9

func NewChatIdentity(seed []byte) (*ChatIdentity, error)

NewChatIdentity derives a chat identity from a seed.

type ChatIncoming added in v0.25.9

type ChatIncoming struct {
	From [protocol.AddressSize]byte
	Seq  uint32
	Text string
}

ChatIncoming is one decrypted inbox message.

type ChatProgress added in v0.25.9

type ChatProgress func(done, total int)

ChatProgress reports upload/download progress: done out of total units.

type ChatSendResult added in v0.25.9

type ChatSendResult struct {
	Seq       uint32
	Remaining uint16
	ResetUnix uint32
}

ChatSendResult reports a committed send.

type ChatStatusError added in v0.25.9

type ChatStatusError struct {
	Op           byte
	Status       byte
	Remaining    uint16
	ResetUnix    uint32
	LastAccepted uint32
}

ChatStatusError is a non-OK chat response status.

func (*ChatStatusError) Error added in v0.25.9

func (e *ChatStatusError) Error() string

type Fetcher

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

Fetcher fetches feed blocks over DNS.

func NewFetcher

func NewFetcher(domain, passphrase string, resolvers []string) (*Fetcher, error)

NewFetcher creates a new DNS block fetcher.

func (*Fetcher) AllResolvers

func (f *Fetcher) AllResolvers() []string

AllResolvers returns all user-configured resolvers.

func (*Fetcher) ExportStats added in v0.9.0

func (f *Fetcher) ExportStats() map[string][3]int64

ExportStats returns a snapshot of all resolver stats.

func (*Fetcher) FetchBlock

func (f *Fetcher) FetchBlock(ctx context.Context, channel, block uint16) ([]byte, error)

FetchBlock fetches a single encrypted block from the given channel. It enqueues through the rate limiter and respects ctx cancellation. On transient failure it retries up to 20 times with a short back-off.

func (*Fetcher) FetchChannel

func (f *Fetcher) FetchChannel(ctx context.Context, channelNum int, blockCount int) ([]protocol.Message, error)

FetchChannel fetches all blocks for a channel and returns the parsed messages. Cancelling ctx immediately aborts any queued or in-flight block fetches. Each block is retried individually via FetchBlock before the channel fetch fails.

func (*Fetcher) FetchChannelVerified added in v0.10.0

func (f *Fetcher) FetchChannelVerified(ctx context.Context, channelNum int, blockCount int, expectedHash uint32) ([]protocol.Message, error)

FetchChannelVerified works like FetchChannel but additionally verifies that the parsed messages match the expected content hash from metadata. Returns ErrContentHashMismatch when the hash does not match (block-version race).

func (*Fetcher) FetchLatestVersion added in v0.7.0

func (f *Fetcher) FetchLatestVersion(ctx context.Context) (string, error)

FetchLatestVersion fetches the latest release version from the dedicated version channel. The block is padded to a random size matching regular content blocks (DPI-resistant). Empty string means unknown/unavailable.

func (*Fetcher) FetchMedia added in v0.13.0

func (f *Fetcher) FetchMedia(ctx context.Context, channel uint16, blockCount uint16, expectedCRC32 uint32, progress MediaProgress) ([]byte, error)

FetchMedia returns the assembled bytes of a media blob served on a media channel, optionally verifying expectedCRC32.

func (*Fetcher) FetchMediaBlocksStream added in v0.13.0

func (f *Fetcher) FetchMediaBlocksStream(ctx context.Context, channel, startBlock, count uint16, w io.Writer, progress MediaProgress) error

FetchMediaBlocksStream fetches blocks [startBlock, startBlock+count) and writes each block's raw bytes to w in order as soon as they become contiguous. No header parsing; callers slice off the protocol header themselves and decompress as appropriate. Cancelling ctx aborts both in-flight DNS queries and pending writes.

func (*Fetcher) FetchMetadata

func (f *Fetcher) FetchMetadata(ctx context.Context) (*protocol.Metadata, error)

FetchMetadata returns the current metadata. Block 0 of channel 0 may carry an extended header (magic + block_count + hash) packed into the otherwise-unused Marker + Timestamp fields. New servers emit that; old servers don't. We always fetch block 0 first; if it has the magic we use the fast parallel path, otherwise we fall back to the legacy "fetch-until-parse-succeeds" loop. The cooldown only kicks in when the fast path is repeatedly broken (e.g., snapshot churn that hash-verify can't ride out), in which case we go straight to legacy for a while.

func (*Fetcher) FetchProfilePicDirectory added in v0.16.0

func (f *Fetcher) FetchProfilePicDirectory(ctx context.Context) (ProfilePicsBundle, error)

FetchProfilePicDirectory pulls the bundle directory from ProfilePicsChannel — header (bundle metadata + relay availability) and per-username entries. The bundle bytes themselves are NOT fetched here; callers do that with FetchMedia(BundleChannel, BundleBlocks, BundleCRC) once and then slice locally.

Returns (zero-value bundle, nil) when the server has no profile pics configured (or is older and doesn't know the channel).

func (*Fetcher) FetchRelayInfo added in v0.13.0

func (f *Fetcher) FetchRelayInfo(ctx context.Context) (RelayInfo, error)

FetchRelayInfo pulls the relay-info payload from RelayInfoChannel. Block 0 carries a uint16 total-block count prefix; if more than one block is needed the rest are fetched in parallel and concatenated. An empty payload yields a zero-value RelayInfo.

func (*Fetcher) FetchTitles added in v0.11.0

func (f *Fetcher) FetchTitles(ctx context.Context) (map[string]string, error)

FetchTitles fetches and decodes the channel display name map from TitlesChannel. Returns an empty map (not an error) when the server does not support TitlesChannel. Block 0 carries a uint16 total-block count prefix; remaining blocks are fetched in parallel so the overall fetch is bounded by the slowest single block, not the sum.

func (*Fetcher) HasServerKey added in v0.25.9

func (f *Fetcher) HasServerKey() bool

HasServerKey reports whether a server signing key is pinned.

func (*Fetcher) ImportStats added in v0.9.0

func (f *Fetcher) ImportStats(m map[string][3]int64)

ImportStats loads previously exported stats into this fetcher.

func (*Fetcher) QueryMode added in v0.25.9

func (f *Fetcher) QueryMode() protocol.QueryEncoding

QueryMode returns the configured query encoding (single base32 label or the feed's multi-label hex). Chat cells honor it so they blend with feed traffic.

func (*Fetcher) QueryTotals added in v0.25.9

func (f *Fetcher) QueryTotals() (responses, errs int64)

QueryTotals returns cumulative (responses, errors) summed across all resolvers. Snapshotting the delta around an operation tells the caller how many queries it issued (responses+errors) and how many were lost (errors) — the same success/failure signal the resolver scoreboard is built on.

func (*Fetcher) RecordFailure added in v0.3.0

func (f *Fetcher) RecordFailure(resolver string)

RecordFailure records a failed DNS query for the given resolver.

func (*Fetcher) RecordSuccess added in v0.3.0

func (f *Fetcher) RecordSuccess(resolver string, latency time.Duration)

RecordSuccess records a successful DNS query for the given resolver.

func (*Fetcher) RemoveActiveResolver added in v0.9.0

func (f *Fetcher) RemoveActiveResolver(addr string)

RemoveActiveResolver removes a resolver from the active pool.

func (*Fetcher) ResetStats added in v0.9.0

func (f *Fetcher) ResetStats()

ResetStats clears all resolver scoring data.

func (*Fetcher) ResolverScoreboard added in v0.9.0

func (f *Fetcher) ResolverScoreboard() []ResolverInfo

ResolverScoreboard returns stats for all active resolvers sorted by score descending.

func (*Fetcher) Resolvers

func (f *Fetcher) Resolvers() []string

Resolvers returns the currently active (healthy) resolver list.

func (*Fetcher) ScanConcurrency added in v0.5.0

func (f *Fetcher) ScanConcurrency() int

ScanConcurrency returns how many resolvers the scanner should probe in parallel, derived from the configured rate limit. Rule: concurrency = max(1, floor(rateQPS)). If rateQPS is 0 (unlimited), falls back to the default of 10.

func (*Fetcher) SendAdminCommand

func (f *Fetcher) SendAdminCommand(ctx context.Context, cmd protocol.AdminCmd, arg string) (string, error)

SendAdminCommand sends an admin command to the server via chunked upstream DNS queries. The payload is a single AdminCmd byte followed by the argument string.

func (*Fetcher) SendMessage

func (f *Fetcher) SendMessage(ctx context.Context, channelNum int, text string) error

SendMessage sends a text message to the given channel via chunked upstream DNS queries. Returns an error if the message is too long or sending fails.

func (*Fetcher) SetActiveResolvers

func (f *Fetcher) SetActiveResolvers(resolvers []string)

SetActiveResolvers updates the healthy resolver pool. Called by ResolverChecker.

func (*Fetcher) SetCacheEpoch added in v0.19.0

func (f *Fetcher) SetCacheEpoch(epoch uint32)

SetCacheEpoch updates the deterministic seed. Pass the latest Metadata NextFetch (or any monotonically-advancing per-server value) after every successful metadata refresh; advancing it invalidates cached responses.

func (*Fetcher) SetCacheShare added in v0.19.0

func (f *Fetcher) SetCacheShare(on bool)

SetCacheShare toggles the shared-resolver-cache feature. When on, queries to eligible channels use a deterministic suffix derived from the cache epoch so public resolvers can serve identical responses across users.

func (*Fetcher) SetDebug

func (f *Fetcher) SetDebug(debug bool)

SetDebug enables or disables debug logging of generated query names.

func (*Fetcher) SetDomains added in v0.25.9

func (f *Fetcher) SetDomains(extra []string)

SetDomains sets the extra sub-domains feed queries are spread across, in addition to the main domain from NewFetcher (which stays first/canonical). Blanks, the main domain, and duplicates are ignored. Call at init.

func (*Fetcher) SetLogFunc

func (f *Fetcher) SetLogFunc(fn LogFunc)

SetLogFunc sets the debug log callback.

func (*Fetcher) SetNoiseDisabled added in v0.25.9

func (f *Fetcher) SetNoiseDisabled(v bool)

SetNoiseDisabled suppresses the decoy-DNS noise generator for this fetcher. Used by the per-profile chat fetchers, which piggyback on the main feed fetcher's cover traffic — running one noise loop per profile would multiply background DNS for no benefit. Call before Start.

func (*Fetcher) SetQueryMode

func (f *Fetcher) SetQueryMode(mode protocol.QueryEncoding)

SetQueryMode sets the DNS query encoding mode.

func (*Fetcher) SetRateLimit

func (f *Fetcher) SetRateLimit(qps float64)

SetRateLimit sets the maximum queries per second (0 = unlimited). Must be called before Start.

func (*Fetcher) SetResolvers

func (f *Fetcher) SetResolvers(resolvers []string)

SetResolvers replaces the full resolver list and resets the active pool.

func (*Fetcher) SetScatter added in v0.3.0

func (f *Fetcher) SetScatter(n int)

SetScatter sets the number of resolvers queried simultaneously per DNS block request. 1 = sequential (no scatter). Values > 1 fan out to N resolvers and use the fastest response. Must be called before Start().

func (*Fetcher) SetServerPublicKey added in v0.25.9

func (f *Fetcher) SetServerPublicKey(b64 string) error

SetServerPublicKey pins the server signing key (base64url, no padding, as printed by genserverkey / the sk= URI field). Empty disables verification.

func (*Fetcher) SetStatsForward added in v0.25.9

func (f *Fetcher) SetStatsForward(fn func(resolver string, ok bool, latency time.Duration))

SetStatsForward registers a callback that receives a copy of every success/failure so a child fetcher can feed results back to the parent.

func (*Fetcher) SetTimeout

func (f *Fetcher) SetTimeout(d time.Duration)

SetTimeout sets the per-query DNS timeout.

func (*Fetcher) Start

func (f *Fetcher) Start(ctx context.Context)

Start launches background goroutines (rate limiter and, unless disabled, the noise generator). ctx controls their lifetime — cancel it to cleanly stop them. Call once per fetcher configuration; creating a new fetcher replaces the old one.

func (*Fetcher) UpdateResolverPool added in v0.10.0

func (f *Fetcher) UpdateResolverPool(resolvers []string)

UpdateResolverPool replaces the full resolver list and removes any active resolvers that are no longer in the bank.

type Gap added in v0.5.1

type Gap struct {
	AfterID  uint32 `json:"after_id"`
	BeforeID uint32 `json:"before_id"`
	Count    int    `json:"count"`
}

Gap represents a range of missing messages detected between two consecutive cached messages (IDs gap > 1, capped at 500 to exclude natural Telegram gaps).

type LogFunc

type LogFunc func(msg string)

LogFunc is a callback for log messages.

type MediaProgress added in v0.13.0

type MediaProgress func(completed, total int)

MediaProgress reports per-block progress (completed of total). May be invoked from a background goroutine.

type MessagesResult added in v0.5.1

type MessagesResult struct {
	Messages []protocol.Message `json:"messages"`
	Gaps     []Gap              `json:"gaps"`
}

MessagesResult is the wire type for /api/messages/<n>. It carries the full merged history plus any detected gaps.

func NewMessagesResult added in v0.5.1

func NewMessagesResult(msgs []protocol.Message) *MessagesResult

NewMessagesResult wraps a raw message slice with gap detection. Used as a fallback when the on-disk cache is unavailable.

type ProfilePicEntry added in v0.16.0

type ProfilePicEntry struct {
	Username   string
	Offset     uint32
	Size       uint32
	CRC        uint32
	MIME       uint8
	DNSChannel uint16
	DNSBlocks  uint16
}

ProfilePicEntry points at one avatar in two ways:

GitHub bundle path: bytes are bundle[Offset:Offset+Size]; CRC must
  equal CRC32-IEEE of that slice (use protocol.VerifyEntry).
Per-entry DNS path: bytes live on DNS channel DNSChannel with
  DNSBlocks blocks. CRC and Size are checked the same way.

The client picks whichever path is reachable. With the bundle path one HTTPS request fetches every avatar; with the DNS path each avatar is fetched independently so partial sets still show up.

func (ProfilePicEntry) Extension added in v0.16.0

func (p ProfilePicEntry) Extension() string

Extension returns ".jpg" / ".png" / ".webp" for caching on disk.

func (ProfilePicEntry) MimeString added in v0.16.0

func (p ProfilePicEntry) MimeString() string

MimeString returns "image/jpeg" / "image/png" / "image/webp" for the MIME tag, suitable for use as an HTTP Content-Type.

type ProfilePicsBundle added in v0.16.0

type ProfilePicsBundle struct {
	BundleSize uint32
	BundleCRC  uint32
	// Relays describes where the bundle is reachable, indexed by
	// RelayDNS / RelayGitHub. RelayGitHub means the bundle is on
	// GitHub. RelayDNS for the bundle is rarely true — the standard
	// DNS path uses per-entry channels (see ProfilePicEntry).
	Relays []bool

	Entries []ProfilePicEntry
}

ProfilePicsBundle is the client-side view of the profile-pic directory. The bundle (Size/CRC/Relays) describes the GitHub-served concatenated blob; per-entry DNSChannel/DNSBlocks describe an independent DNS fallback for that single avatar.

func (ProfilePicsBundle) HasRelay added in v0.16.0

func (b ProfilePicsBundle) HasRelay(idx int) bool

HasRelay forwards to the relay availability bit at idx.

type RelayInfo added in v0.13.0

type RelayInfo struct {
	GitHubRepo string // "owner/repo"
}

RelayInfo carries the relay-discovery data the server publishes on RelayInfoChannel. Empty fields mean "not configured".

func ParseRelayInfo added in v0.13.0

func ParseRelayInfo(data []byte) RelayInfo

ParseRelayInfo decodes the relay-info payload (one "key=value" pair per line). Unknown keys are ignored so future relays can be added without breaking older clients.

type ResolverChecker

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

ResolverChecker periodically probes the fetcher's configured resolvers and updates the active (healthy) resolver pool. It replaces the old file/CIDR scanner — no file I/O; just a plain DNS probe on channel 0.

func NewResolverChecker

func NewResolverChecker(fetcher *Fetcher, timeout time.Duration) *ResolverChecker

NewResolverChecker creates a health checker for the resolvers in fetcher. timeout is the per-probe deadline; 0 uses a 15-second default.

func (*ResolverChecker) CancelCurrentScan added in v0.4.0

func (rc *ResolverChecker) CancelCurrentScan()

CancelCurrentScan cancels any in-progress CheckNow call, causing it to return early without updating the resolver list.

func (*ResolverChecker) CheckNow

func (rc *ResolverChecker) CheckNow(ctx context.Context) bool

CheckNow runs a single resolver health-check pass immediately. If a scan is already in progress the call is a no-op (returns false). Returns true if the scan ran to completion. Use CancelCurrentScan to abort a running scan from outside.

func (*ResolverChecker) SetAutoScan added in v0.10.0

func (rc *ResolverChecker) SetAutoScan(enabled bool)

SetAutoScan enables or disables the hourly periodic health-check loop.

func (*ResolverChecker) SetLogFunc

func (rc *ResolverChecker) SetLogFunc(fn LogFunc)

SetLogFunc sets the callback used to emit health-check results to the log panel.

func (*ResolverChecker) SetOnScanDone added in v0.5.0

func (rc *ResolverChecker) SetOnScanDone(fn func([]string))

SetOnScanDone registers a callback invoked after each completed CheckNow pass with the list of healthy resolver addresses. Not called when the scan is cancelled.

func (*ResolverChecker) Start

func (rc *ResolverChecker) Start(ctx context.Context)

Start begins the periodic health-check loop in the background. An initial check runs immediately; subsequent checks happen every 10 minutes. ctx controls the lifetime — cancel it to stop the checker.

func (*ResolverChecker) StartAndNotify added in v0.2.0

func (rc *ResolverChecker) StartAndNotify(ctx context.Context, onFirstDone func())

StartAndNotify is like Start but calls onFirstDone (if non-nil) after the first successful health-check pass (i.e. at least one resolver is healthy), before the periodic ticker begins. If the initial scan finds zero healthy resolvers it retries every minute until at least one resolver becomes reachable (or ctx is cancelled). Safe to call only once per checker instance; subsequent calls are no-ops.

func (*ResolverChecker) StartPeriodic added in v0.5.0

func (rc *ResolverChecker) StartPeriodic(ctx context.Context)

StartPeriodic starts only the periodic Hour health-check loop without running an initial scan. Use when resolvers are already available (e.g. loaded from a saved last-scan file on startup). Safe to call only once per checker instance; subsequent calls are no-ops.

type ResolverInfo added in v0.9.0

type ResolverInfo struct {
	Addr    string  `json:"addr"`
	Score   float64 `json:"score"`
	Success int64   `json:"success"`
	Failure int64   `json:"failure"`
	AvgMs   float64 `json:"avgMs"`
}

ResolverInfo holds public stats for a single resolver.

type ResolverScanner added in v0.7.0

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

ResolverScanner scans IP ranges to find working DNS resolvers for thefeed.

func NewResolverScanner added in v0.7.0

func NewResolverScanner() *ResolverScanner

NewResolverScanner creates a new scanner instance.

func (*ResolverScanner) Pause added in v0.7.0

func (rs *ResolverScanner) Pause()

Pause pauses the scanner.

func (*ResolverScanner) Progress added in v0.7.0

func (rs *ResolverScanner) Progress() ScannerProgress

Progress returns the current scan progress.

func (*ResolverScanner) Resume added in v0.7.0

func (rs *ResolverScanner) Resume()

Resume resumes the scanner from pause.

func (*ResolverScanner) SetDebug added in v0.7.0

func (rs *ResolverScanner) SetDebug(v bool)

SetDebug enables or disables verbose scanner logging.

func (*ResolverScanner) SetLogFunc added in v0.7.0

func (rs *ResolverScanner) SetLogFunc(fn LogFunc)

SetLogFunc sets the log callback.

func (*ResolverScanner) Start added in v0.7.0

func (rs *ResolverScanner) Start(cfg ScannerConfig) error

Start begins scanning with the given config. Returns error if already running.

func (*ResolverScanner) State added in v0.7.0

func (rs *ResolverScanner) State() ScannerState

State returns the current scanner state.

func (*ResolverScanner) Stop added in v0.7.0

func (rs *ResolverScanner) Stop()

Stop stops the scanner.

type ScannerConfig added in v0.7.0

type ScannerConfig struct {
	// Targets is a list of IPs, domains, or CIDRs to scan.
	Targets []string `json:"targets"`
	// MaxIPs limits how many IPs to scan from the expanded list (0 = all).
	MaxIPs int `json:"maxIPs"`
	// RateLimit is the concurrent probe limit (default 50).
	RateLimit int `json:"rateLimit"`
	// Timeout is the per-probe timeout in seconds (default 10).
	Timeout float64 `json:"timeout"`
	// ExpandSubnet: if true, when a working resolver is found, also scan its /24.
	ExpandSubnet bool `json:"expandSubnet"`
	// QueryMode is "single" or "double".
	QueryMode string `json:"queryMode"`
	// Domain is the thefeed server domain.
	Domain string `json:"domain"`
	// Passphrase is the encryption key.
	Passphrase string `json:"passphrase"`
}

ScannerConfig holds the configuration for a resolver scan.

type ScannerProgress added in v0.7.0

type ScannerProgress struct {
	State     ScannerState    `json:"state"`
	Total     int             `json:"total"`
	Scanned   int             `json:"scanned"`
	Found     int             `json:"found"`
	Results   []ScannerResult `json:"results"`
	Error     string          `json:"error,omitempty"`
	StartedAt int64           `json:"startedAt,omitempty"`
}

ScannerProgress holds the current progress of the scanner.

type ScannerResult added in v0.7.0

type ScannerResult struct {
	IP        string  `json:"ip"`
	LatencyMs float64 `json:"latencyMs"` // milliseconds
	FoundAt   int64   `json:"foundAt"`   // unix timestamp
}

ScannerResult represents a single working resolver found by the scanner.

type ScannerState added in v0.7.0

type ScannerState string

ScannerState represents the current state of the scanner.

const (
	ScannerIdle    ScannerState = "idle"
	ScannerRunning ScannerState = "running"
	ScannerPaused  ScannerState = "paused"
	ScannerDone    ScannerState = "done"
)

Jump to

Keyboard shortcuts

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