Add digip pkg

This commit is contained in:
dwrz
2026-10-24 02:43:25 +00:00
parent 61dfbb855d
commit 5533ade854
2 changed files with 178 additions and 0 deletions
+124
View File
@@ -0,0 +1,124 @@
// Package digip runs "dig" to find the public IP address of the local host.
//
// Each function sends a DNS query directly to a third-party name server that
// returns the source IP address of the query. Because the query does not go
// through the local DNS resolver, the answer is the public IP address of the
// host (or its NAT gateway), not the IP address of the local resolver.
//
// The "dig" executable must be in PATH.
package digip
import (
"context"
"fmt"
"net"
"os/exec"
"strings"
)
// Version is a dig flag that selects IPv4 or IPv6.
type Version string
const (
// V4 is the dig flag for IPv4.
V4 = "-4"
// V6 is the dig flag for IPv6.
V6 = "-6"
)
// Record is a DNS record type to ask for.
type Record string
const (
// A is the DNS record type for an IPv4 address.
A = "A"
// AAAA is the DNS record type for an IPv6 address.
AAAA = "AAAA"
)
const (
// googleDomain is a hostname that resolves to the client's IP address.
// A TXT query for this name returns the IP address as a quoted string.
googleDomain = "o-o.myaddr.l.google.com"
// googleServer is the name server that answers queries for
// googleDomain.
// The "@" prefix tells dig to query this server directly.
googleServer = "@ns1.google.com"
// opendnsDomain is a hostname that resolves to the client's IP address.
// An A query returns the IPv4 address, and an AAAA query returns the
// IPv6 address.
opendnsDomain = "myip.opendns.com"
// opendnsServer is the name server that answers queries for
// opendnsDomain.
// The "@" prefix tells dig to query this server directly.
opendnsServer = "@resolver1.opendns.com"
)
// Google queries the Google name server for the host's public IP address.
// Because Google returns the address in a TXT record, v selects IPv4 (V4) or
// IPv6 (V6) transport.
//
// dig TXT +short -4 o-o.myaddr.l.google.com @ns1.google.com
// dig TXT +short -6 o-o.myaddr.l.google.com @ns1.google.com
func Google(ctx context.Context, v Version) (net.IP, error) {
cmd := exec.CommandContext(
ctx,
"dig",
"TXT",
"+short",
string(v),
googleDomain,
googleServer,
)
output, err := cmd.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("exec dig: %v", err)
}
if string(output) == "" {
return nil, fmt.Errorf("exec dig: no output")
}
if len(output) < 2 {
return nil, fmt.Errorf(
"exec dig: unexpected output: %q", string(output),
)
}
// Remove the quotes from the Google TXT answer.
trimmed := strings.TrimSpace(string(output))
ip := net.ParseIP(trimmed[1 : len(trimmed)-1])
return ip, nil
}
// OpenDNS queries the OpenDNS name server for the host's public IP address.
// Use r to request the IPv4 address (A) or the IPv6 address (AAAA).
//
// dig +short A myip.opendns.com @resolver1.opendns.com
// dig +short AAAA myip.opendns.com @resolver1.opendns.com
func OpenDNS(ctx context.Context, r Record) (net.IP, error) {
cmd := exec.CommandContext(
ctx,
"dig",
"+short",
string(r),
opendnsDomain,
opendnsServer,
)
output, err := cmd.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("exec dig: %v", err)
}
if string(output) == "" {
return nil, fmt.Errorf("exec dig: no output")
}
if len(output) < 2 {
return nil, fmt.Errorf(
"exec dig: unexpected output: %q", string(output),
)
}
ip := net.ParseIP(strings.TrimSpace(string(output)))
return ip, nil
}