Skip to content

Commit

Permalink
main: make --api option resolve hostnames via dns (#5249)
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
Raúl Kripalani committed Aug 11, 2018
1 parent a1375fc commit 9a1d041
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 18 deletions.
72 changes: 72 additions & 0 deletions cmd/ipfs/dnsresolve_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
40 changes: 22 additions & 18 deletions cmd/ipfs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,17 @@ import (
osh "gx/ipfs/QmXuBJ7DR6k3rmUEKtvVMhwjmXDuJgXXPUt4LQXKBMsU93/go-os-helper"
ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr"
loggables "gx/ipfs/QmcEC2rbyMxUMgpLwt16wquaZdG1aPXcpbKYf4Fedt7hkD/go-libp2p-loggables"
mdns "gx/ipfs/QmfXU2MhWoegxHoeMd3A2ytL2P6CY4FfqGWc23LTNWBwZt/go-multiaddr-dns"
madns "gx/ipfs/QmfXU2MhWoegxHoeMd3A2ytL2P6CY4FfqGWc23LTNWBwZt/go-multiaddr-dns"
)

// log is the command logger
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"
Expand Down Expand Up @@ -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()
_, host, err := manet.DialArgs(addr)
if err != nil {
return nil, err
}

conn, err := dialer.DialContext(ctx, addr)
if err != nil {
log.Errorf("connection to %s failed, error: %s", addr, err)
continue
}
conn.Close()
return http.NewClient(host, http.ClientWithAPIPrefix(corehttp.APIPath)), nil
}

_, host, err := manet.DialArgs(addr)
if err != nil {
continue
}
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
}

return http.NewClient(host, http.ClientWithAPIPrefix(corehttp.APIPath)), nil
if len(addrs) == 0 {
return nil, errors.New("non-resolvable API endpoint")
}

return nil, errors.New("non-resolvable API endpoint")
return addrs[0], nil
}

0 comments on commit 9a1d041

Please sign in to comment.