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