A high-performance network utility toolkit written in Go for network diagnostics, packet analysis, and protocol handling.
This library is designed for maximum efficiency and performance. Every function is optimized to be as fast as possible with minimal allocations. We maintain extensive benchmarks to ensure performance remains optimal and to catch any regressions.
Key performance principles:
- Zero-copy where possible - Avoid unnecessary memory allocations
- No reflection - Direct type handling for speed
- Preallocated buffers - Reuse memory to reduce GC pressure
- Benchmark-driven development - All critical paths are benchmarked
- High performance - Optimized for speed with extensive benchmarks
- Cross-platform - Supports Windows, macOS, and Linux
- IPv4/IPv6 - Full dual-stack support throughout
- Zero dependencies for core functionality (optional DNS library for advanced features)
- Type-safe - Leverages Go generics where appropriate
go get github.com/ruilisi/netutilsRequires Go 1.23+
| Package | Description |
|---|---|
device |
Device identification |
dns |
DNS resolution and packet analysis |
ds |
Data structures (generic Set) |
http |
HTTP utilities and speed testing |
ip |
IP address handling, packet parsing, and manipulation |
ping |
ICMP ping and reachability checks |
tcp |
TCP connection utilities |
tun |
TUN device support |
util |
Hex dump and conversion utilities |
Device identification utilities.
Returns a stable unique device identifier based on hardware (MAC address hash on macOS/Linux, hardware ID on Windows), with fallback to a persistent random ID.
import "github.com/ruilisi/netutils/device"
id := device.GetUniqID()
fmt.Println(id) // e.g., "a1b2c3d4e5f6"DNS resolution and packet analysis utilities.
Validates if a raw UDP payload appears to be a DNS packet.
import "github.com/ruilisi/netutils/dns"
if dns.IsLikelyDNSPacket(udpPayload) {
// Process DNS packet
}Robust DNS resolution with multiple servers, racing, and retry logic.
import "github.com/ruilisi/netutils/dns/robust"
// Resolve using multiple DNS servers
ip, err := robust.ResolveDomain("example.com", []string{"8.8.8.8:53", "1.1.1.1:53"})Pre-configured DNS server lists.
import "github.com/ruilisi/netutils/dns/servers"
// Available server lists:
servers.CNDNSServers // Chinese DNS servers
servers.CNDNSServersSmall // Small list of Chinese DNS servers
servers.SecurityDNSServers // Security-focused DNS servers
servers.InternationalDNSServers // International public DNS serversGeneric data structures.
A generic Set implementation using Go generics. Not concurrency-safe.
import "github.com/ruilisi/netutils/ds"
s := ds.NewSet[string]()
s.Add("apple")
s.Add("banana")
s.Has("apple") // true
s.Remove("apple")
s.Values() // []string{"banana"}HTTP utilities for raw requests and speed testing.
Builds a raw HTTP GET request as bytes.
import nethttp "github.com/ruilisi/netutils/http"
reqBytes, err := nethttp.BuildRawRequest("http://example.com/file", map[string]string{
"User-Agent": "MyApp/1.0",
})Measures download speed over an established TCP connection.
import nethttp "github.com/ruilisi/netutils/http"
conn, _ := net.Dial("tcp", "example.com:80")
speed, err := nethttp.DownloadSpeedTCP(conn, reqBytes, 10*time.Second)
fmt.Printf("Speed: %.2f bytes/sec\n", speed)Extracts host:port from a URL with default port handling.
import nethttp "github.com/ruilisi/netutils/http"
hostPort, parsedURL, err := nethttp.HostPortFromURL("https://example.com:8080/path")
// hostPort = "example.com:8080"Wraps a net.Conn to track bytes downloaded.
import nethttp "github.com/ruilisi/netutils/http"
counter := &nethttp.ReadCounterConn{Conn: conn}
io.Copy(io.Discard, counter)
fmt.Printf("Downloaded: %d bytes\n", counter.Downloaded)Comprehensive IP address handling, packet parsing, and manipulation.
import "github.com/ruilisi/netutils/ip"
ip.IsIPV6(&addr) // Check net.IP
ip.IsIPNetV6(&cidr) // Check net.IPNet
ip.IsIPNetStrV6("2001::/64") // Check CIDR string (returns bool, error)
ip.IsIPNetStrV6B("2001::/64") // Check CIDR string (returns bool only)import "github.com/ruilisi/netutils/ip"
// Convert IP to CIDR with full mask (/32 or /128)
ipNet := ip.IP2IPNetFullMask(netIP)
cidr, _ := ip.IPStrFullMask("192.168.1.1") // "192.168.1.1/32"
// Simple IPv4 to CIDR
ip.IP2CIDR("192.168.1.1") // "192.168.1.1/32"
// Parse CIDR strings
nets := ip.StrToIPNets("10.0.0.0/8,172.16.0.0/12", ",")import "github.com/ruilisi/netutils/ip"
ip.IsDefaultIP(addr) // Check if 0.0.0.0 or ::
ip.IsDefaultIPNet(ipNet) // Check if 0.0.0.0/0 or ::/0
ip.EqualIPNet(a, b) // Compare two IPNet objects
ip.IsIPStrInNet("10.0.0.5", ipNet) // Check if IP is in network
ip.GetBroadcastIPV4OfIPNet(ipNet) // Get broadcast addressimport "github.com/ruilisi/netutils/ip"
ip.IsPrivateIP(addr) // RFC1918 (IPv4) / RFC4193 ULA (IPv6)
ip.IsPrivateNetwork(ipNet) // Check entire networkimport "github.com/ruilisi/netutils/ip"
iface, _ := ip.GetOutboundInterface() // Detect default route interface
ipStr, _ := ip.GetOutboundIP(iface) // Get interface's IP
ipNet, _ := ip.GetOutboundIPNet(iface) // Get interface's IPNetimport "github.com/ruilisi/netutils/ip"
// Get IP version and protocol
ver := ip.GetIPVer(packet) // 4, 6, or 0
ver, proto := ip.GetVerProto(packet) // Version and protocol number
// Extract addresses and ports
srcIP, dstIP := ip.GetIPs(packet)
srcPort, dstPort := ip.GetPorts(packet)
// Check protocol
if ip.IsUDP(packet) {
payload, srcIP, srcPort, dstIP, dstPort, err := ip.ExtractUDPPayload(packet)
}
// Human-readable packet summary
summary := ip.SummarizePacket(packet)
// e.g., "IPv4 TCP 192.168.1.1:443 → 10.0.0.1:52341 [SYN] seq=123"import "github.com/ruilisi/netutils/ip"
// Build IPv4 UDP packet
pkt := ip.BuildIPv4UDPPacket(localAddr, remoteAddr, payload)
// Build IPv6 UDP packet
pkt := ip.BuildIPv6UDPPacket(localAddr, remoteAddr, payload)import "github.com/ruilisi/netutils/ip"
qnames, ips, isQuery, dnsServer, ok := ip.ExtractDNSFromPacket(rawPacket)
// qnames: domain names from question section
// ips: resolved IPs from answer section (A/AAAA records)
// isQuery: true for queries, false for responses
// dnsServer: DNS server IP addressimport "github.com/ruilisi/netutils/ip"
// Rewrite DNS packet destination to new server
ip.RewriteIPV4Dest(packet, "8.8.8.8") // Updates dest IP to 8.8.8.8:53
ip.RewriteIPV6Dest(packet, "2001:4860:4860::8888")157 IANA IP protocol numbers are available as constants:
import "github.com/ruilisi/netutils/ip"
ip.ProtoICMP // 1
ip.ProtoTCP // 6
ip.ProtoUDP // 17
ip.ProtoICMPv6 // 58
// ... and 153 moreICMP ping and network reachability utilities.
Simple ICMP ping with timeout.
import "github.com/ruilisi/netutils/ping"
err := ping.FastPing("8.8.8.8", 3*time.Second)
if err == nil {
fmt.Println("Host is reachable")
}ICMP ping that returns round-trip time. Requires elevated privileges on most systems.
import "github.com/ruilisi/netutils/ping"
rtt, err := ping.Ping(net.ParseIP("8.8.8.8"), 3*time.Second)
fmt.Printf("RTT: %v\n", rtt)Uses the system's ping command (no elevated privileges required).
import "github.com/ruilisi/netutils/ping"
rtt, err := ping.PingCmd(net.ParseIP("8.8.8.8"), 3*time.Second)Checks internet connectivity using DNS lookups and pings.
import "github.com/ruilisi/netutils/ping"
if ping.CheckReachability() {
fmt.Println("Internet is reachable")
}TCP connection utilities.
Sets TCP send and receive buffer sizes.
import "github.com/ruilisi/netutils/tcp"
conn, _ := net.Dial("tcp", "example.com:80")
tcp.SetWindow(conn, 65536, 65536) // 64KB buffersTUN device support for packet tunneling. Platform-specific implementations for Windows, Linux, and macOS.
General utilities.
Prints a hex dump of bytes (similar to hexdump -C).
import "github.com/ruilisi/netutils/util"
util.DumpHex(data)
// Output:
// 00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 21 |Hello World!|Converts a space-separated hex string to bytes.
import "github.com/ruilisi/netutils/util"
data, err := util.HexToBytes("48 65 6c 6c 6f")
// data = []byte("Hello")Performance is a first-class concern. All critical code paths include benchmarks to ensure optimal performance and catch regressions.
# Run all benchmarks
make bench
# Run with memory allocation stats
make bench-mem
# Run multiple times for stable results
make bench-count
# Save results for comparison (useful before/after changes)
make bench-save
# Generate CPU/memory profiles for optimization
make bench-cpu
make bench-mem-profileBenchmarkExtractDNSFromPacket_IPv4Query-8 16M 72 ns/op 48 B/op 4 allocs/op
BenchmarkExtractDNSFromPacket_IPv4Response-8 8M 159 ns/op 112 B/op 8 allocs/op
BenchmarkReadDNSName_Simple-8 27M 45 ns/op 24 B/op 2 allocs/op
BenchmarkReadDNSName_Compressed-8 25M 47 ns/op 24 B/op 2 allocs/op
BenchmarkParseDNSMessage_Query-8 19M 63 ns/op 40 B/op 3 allocs/op
Use benchstat to compare benchmark results before and after changes:
# Before changes
make bench-save
mv bench.txt bench-old.txt
# After changes
make bench-save
# Compare
benchstat bench-old.txt bench.txt- Go 1.23+
- golangci-lint (for linting)
- benchstat (optional, for benchmark comparison:
go install golang.org/x/perf/cmd/benchstat@latest)
# Build
make build # Build the project
make install # Install binary to $GOBIN
make run # Run the app
make clean # Remove build files and generated files
# Test
make test # Run tests
make test-v # Run tests (verbose)
make test-race # Run tests with race detector
make test-short # Run short tests only
make test-cover # Run tests with coverage
make test-cover-html # Generate HTML coverage report
# Benchmark
make bench # Run all benchmarks
make bench-mem # Run benchmarks with memory stats
make bench-count # Run benchmarks 5x for stable results
make bench-save # Save benchmark results to bench.txt
make bench-cpu # Run benchmarks with CPU profile
make bench-mem-profile # Run benchmarks with memory profile
# Code Quality
make fmt # Format code
make vet # Run go vet
make lint # Run linter (needs golangci-lint)
make check # Run fmt, vet, lint, and test
# Dependencies
make tidy # Clean up go.mod/go.sum
make deps # Download dependencies# Run all tests
go test ./...
# Run tests for a specific package
go test ./ip/...
# Run with verbose output
go test -v ./...
# Run benchmarks
go test -bench=. -benchmem ./...Contributions are welcome! Please ensure your changes:
- Include benchmarks - All new functions in critical paths must have benchmarks
- Don't regress performance - Run
make bench-savebefore and after, compare withbenchstat - Minimize allocations - Prefer stack allocation, reuse buffers where possible
- Pass all checks - Run
make checkbefore submitting
- Avoid
reflectpackage in hot paths - Prefer
strings.Builderover string concatenation - Use
make([]T, 0, capacity)when slice size is known - Avoid creating closures in loops
- Use
sync.Poolfor frequently allocated objects - Copy small structs instead of using pointers (better cache locality)
MIT License - see LICENSE for details.