Add digip pkg
This commit is contained in:
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package digip
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGoogleV4(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
ip, err := Google(ctx, V4)
|
||||
if err != nil {
|
||||
t.Errorf("google v4 failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("got ip: %s", ip)
|
||||
}
|
||||
|
||||
func TestGoogleV6(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
ip, err := Google(ctx, V6)
|
||||
if err != nil {
|
||||
t.Errorf("google v6 failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("got ip: %s", ip)
|
||||
}
|
||||
|
||||
func TestOpenDNSV4(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
ip, err := OpenDNS(ctx, A)
|
||||
if err != nil {
|
||||
t.Errorf("opendns A failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("got ip: %s", ip)
|
||||
}
|
||||
|
||||
func TestOpenDNSV6(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
ip, err := OpenDNS(ctx, AAAA)
|
||||
if err != nil {
|
||||
t.Errorf("opendns AAAA failed: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Logf("got ip: %s", ip)
|
||||
}
|
||||
Reference in New Issue
Block a user