- Created a new `platform` package to handle OS-specific hosts file paths. - Added implementations for macOS, Linux, and Windows to retrieve the hosts file path. - Introduced tests for the `GetHostsFilePath` function to ensure correct behavior across platforms. feat: Add DNS resolver for IP lookups - Implemented a `resolver` package for performing DNS lookups. - Created a `DNSResolver` struct with a `LookupIP` method to handle A-record lookups and CNAME resolution. - Added comprehensive tests for various DNS scenarios, including CNAME chains and error handling. chore: Refactor project structure and update dependencies - Restructured the project to follow Go's standard package layout. - Updated `go.mod` to Go 1.20 and added `golang.org/x/net` dependency. - Removed old source files and ensured a clean build with all tests passing.
5.3 KiB
dns-helper Development Guidelines
Project Overview
dns-helper — a Go CLI tool that resolves hostnames against a specified DNS server and writes the resulting IP-to-hostname mappings into the local hosts file. It manages a clearly delimited block within the hosts file so it can cleanly add and remove only its own entries. Built as a single static binary with no runtime dependencies.
Go Toolchain — CRITICAL
Always use go1.20 instead of go for ALL Go toolchain operations.
The system default go command may be a newer version that is incompatible with this project.
# CORRECT — use these commands:
go1.20 build .
go1.20 test ./...
go1.20 vet ./...
go1.20 fmt ./...
# WRONG — do NOT use bare 'go':
go build . # May use incompatible Go version
go test ./... # May use incompatible Go version
Go 1.21+ features that MUST NOT be used:
min,max,clearbuiltins- Range-over-int (
for i := range 10) slices,maps,cmppackageslog/slogpackage- Any other Go 1.21+ standard library additions
Building
# Standard debug build for Windows
.\build.ps1
# Optimized release build for all platforms
.\build.ps1 -Release
# Clean build
.\build.ps1 -Clean
# Cross-compile for all platforms
.\build.ps1 -CrossCompile
# Create release packages (requires binaries already built)
.\build.ps1 -Release -Package
Output: build\dns-helper.exe (default), build\dns-helper-<os>-<arch>[.exe] (cross-compile).
Release packages: releases\dns-helper-<version>-<os>-<arch>.zip / .tar.gz
Running dns-helper
dns-helper has two commands: add and delete (with short aliases a and d/del).
# Add one or more hostnames (resolves via the given DNS server, writes to hosts file)
.\build\dns-helper.exe add -host hostname.example.com -server dns.example.com
.\build\dns-helper.exe a -host hostname.example.com -server dns.example.com
# Add multiple hostnames at once (comma-separated)
.\build\dns-helper.exe add -host "host1.example.com,host2.example.com" -server dns.example.com
# Delete a specific hostname from the managed block
.\build\dns-helper.exe delete -host hostname.example.com
.\build\dns-helper.exe del -host hostname.example.com
.\build\dns-helper.exe d -host hostname.example.com
# Delete multiple hostnames (comma-separated)
.\build\dns-helper.exe delete -host "host1.example.com,host2.example.com"
# Delete ALL entries added by dns-helper
.\build\dns-helper.exe delete all
.\build\dns-helper.exe delete a
Flag Reference
| Command | Flag | Required | Description |
|---|---|---|---|
add |
-host |
Yes | Comma-separated list of hostnames to resolve and add |
add |
-server |
Yes | DNS resolver to query (e.g. dns.example.com) |
delete |
-host |
Yes (unless all) |
Comma-separated list of hostnames to remove |
delete all |
— | — | Removes every entry dns-helper has written |
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Error or partial failure (e.g. one hostname failed to resolve) |
Hosts File Management
dns-helper wraps its entries between two marker lines:
# DNSHelper <<-> START CONFIG
192.0.2.1 hostname.example.com
# DNSHelper <->> END CONFIG
Only lines within this managed block are ever modified or removed. The rest of the hosts file is preserved exactly. A backup of the hosts file is written to the executable's directory before every write.
Testing
# Run all tests
go1.20 test ./...
# Run with coverage
go1.20 test -cover ./...
# Run a specific package
go1.20 test ./hostfile/...
go1.20 test ./resolver/...
go1.20 test ./lockfile/...
go1.20 test ./platform/...
# Vet all code
go1.20 vet ./...
Project Structure
main.go CLI entry point (add / delete commands, flag parsing)
hostfile/
hostfile.go Hosts file parsing, DNSEntry type, add/remove helpers
managed.go Managed block read/write logic
filesystem.go FileSystem interface + OSFileSystem implementation
hostfile_test.go
managed_test.go
lockfile/
lockfile.go File-based mutex to prevent concurrent modifications
lockfile_test.go
platform/
platform.go Platform interface — GetHostsFilePath()
platform_windows.go Windows implementation
platform_darwin.go macOS implementation
platform_linux.go Linux implementation
platform_test.go
resolver/
resolver.go DNS resolution via golang.org/x/net/dns/dnsmessage
resolver_test.go
Code Style
- Go 1.20 only — no Go 1.21+ features
- Standard library first — only
golang.org/x/*dependencies allowed; nogithub.com/*third-party packages - Error wrapping:
fmt.Errorf("context: %w", err) - CGO disabled:
CGO_ENABLED=0for static binaries - Table-driven tests preferred
Dependencies
From go.mod:
golang.org/x/net— DNS message parsing (golang.org/x/net/dns/dnsmessage)
After Every Implementation
After completing any code change, validate in this order:
- Run tests for changed packages:
go1.20 test ./hostfile/... ./resolver/... # example - Run the full suite:
go1.20 test ./... - Build the project:
.\build.ps1 - Report test pass/fail counts and build outcome before considering the task complete.