From 0ddaf58336af04c9fe822262b2e2e000fecfd4ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rau=CC=81l=20Kripalani?= Date: Sat, 11 Aug 2018 23:26:16 +0100 Subject: [PATCH] main: make --api option resolve hostnames via dns (#5249) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves #5249. Calls multiaddr-dns, and picks the first result. Uses a fixed timeout of 10 seconds. Adds test cases for one, multiple, and no DNS results. License: MIT Signed-off-by: Raúl Kripalani --- cmd/ipfs/dnsresolve_test.go | 72 +++++++++++++++++++++++++++++++++++++ cmd/ipfs/main.go | 46 +++++++++++++----------- 2 files changed, 97 insertions(+), 21 deletions(-) create mode 100644 cmd/ipfs/dnsresolve_test.go diff --git a/cmd/ipfs/dnsresolve_test.go b/cmd/ipfs/dnsresolve_test.go new file mode 100644 index 000000000..9f43be12d --- /dev/null +++ b/cmd/ipfs/dnsresolve_test.go @@ -0,0 +1,72 @@ +package main + +import ( + "context" + "fmt" + "net" + "strings" + "testing" + + ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" + madns "gx/ipfs/QmfXU2MhWoegxHoeMd3A2ytL2P6CY4FfqGWc23LTNWBwZt/go-multiaddr-dns" +) + +var ( + ctx = context.Background() + testAddr, _ = ma.NewMultiaddr("/dns4/example.com/tcp/5001") +) + +func makeResolver(n uint8) *madns.Resolver { + results := make([]net.IPAddr, n) + for i := uint8(0); i < n; i++ { + results[i] = net.IPAddr{IP: net.ParseIP(fmt.Sprintf("192.0.2.%d", i))} + } + + backend := &madns.MockBackend{ + IP: map[string][]net.IPAddr{ + "example.com": results, + }} + + return &madns.Resolver{ + Backend: backend, + } +} + +func TestApiEndpointResolveDNSOneResult(t *testing.T) { + dnsResolver = makeResolver(1) + + addr, err := resolveAddr(ctx, testAddr) + if err != nil { + t.Error(err) + } + + if ref, _ := ma.NewMultiaddr("/ip4/192.0.2.0/tcp/5001"); !addr.Equal(ref) { + t.Errorf("resolved address was different than expected") + } +} + +func TestApiEndpointResolveDNSMultipleResults(t *testing.T) { + dnsResolver = makeResolver(4) + + addr, err := resolveAddr(ctx, testAddr) + if err != nil { + t.Error(err) + } + + if ref, _ := ma.NewMultiaddr("/ip4/192.0.2.0/tcp/5001"); !addr.Equal(ref) { + t.Errorf("resolved address was different than expected") + } +} + +func TestApiEndpointResolveDNSNoResults(t *testing.T) { + dnsResolver = makeResolver(0) + + addr, err := resolveAddr(ctx, testAddr) + if addr != nil || err == nil { + t.Error("expected test address not to resolve, and to throw an error") + } + + if !strings.HasPrefix(err.Error(), "non-resolvable API endpoint") { + t.Errorf("expected error not thrown; actual: %v", err) + } +} diff --git a/cmd/ipfs/main.go b/cmd/ipfs/main.go index 1f8b82637..b02889310 100644 --- a/cmd/ipfs/main.go +++ b/cmd/ipfs/main.go @@ -34,7 +34,7 @@ import ( osh "gx/ipfs/QmXuBJ7DR6k3rmUEKtvVMhwjmXDuJgXXPUt4LQXKBMsU93/go-os-helper" ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr" loggables "gx/ipfs/QmZ4zF1mBrt8C2mSCM4ZYE4aAnv78f7GvrzufJC4G5tecK/go-libp2p-loggables" - mdns "gx/ipfs/QmfXU2MhWoegxHoeMd3A2ytL2P6CY4FfqGWc23LTNWBwZt/go-multiaddr-dns" + madns "gx/ipfs/QmfXU2MhWoegxHoeMd3A2ytL2P6CY4FfqGWc23LTNWBwZt/go-multiaddr-dns" ) // log is the command logger @@ -42,6 +42,9 @@ var log = logging.Logger("cmd/ipfs") var errRequestCanceled = errors.New("request canceled") +// declared as a var for testing purposes +var dnsResolver = madns.DefaultResolver + const ( EnvEnableProfiling = "IPFS_PROF" cpuProfile = "ipfs.cpuprof" @@ -445,30 +448,31 @@ func getApiClient(ctx context.Context, repoPath, apiAddrStr string) (http.Client } func apiClientForAddr(ctx context.Context, addr ma.Multiaddr) (http.Client, error) { - addrs, err := mdns.Resolve(ctx, addr) + addr, err := resolveAddr(ctx, addr) if err != nil { return nil, err } - dialer := &manet.Dialer{} - for _, addr := range addrs { - ctx, cancelFunc := context.WithTimeout(ctx, 5*time.Second) - defer cancelFunc() - - conn, err := dialer.DialContext(ctx, addr) - if err != nil { - log.Errorf("connection to %s failed, error: %s", addr, err) - continue - } - conn.Close() - - _, host, err := manet.DialArgs(addr) - if err != nil { - continue - } - - return http.NewClient(host, http.ClientWithAPIPrefix(corehttp.APIPath)), nil + _, host, err := manet.DialArgs(addr) + if err != nil { + return nil, err } - return nil, errors.New("non-resolvable API endpoint") + return http.NewClient(host, http.ClientWithAPIPrefix(corehttp.APIPath)), nil +} + +func resolveAddr(ctx context.Context, addr ma.Multiaddr) (ma.Multiaddr, error) { + ctx, cancelFunc := context.WithTimeout(ctx, 10*time.Second) + defer cancelFunc() + + addrs, err := dnsResolver.Resolve(ctx, addr) + if err != nil { + return nil, err + } + + if len(addrs) == 0 { + return nil, errors.New("non-resolvable API endpoint") + } + + return addrs[0], nil }