@cryptotaxi247 / kubo / commits / 0ddaf5833

main: make --api option resolve hostnames via dns (#5249)

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 <raul.kripalani@consensys.net>

Raúl Kripalani committed Aug 11, 2018 at 23:26 UTC 0ddaf58336af04c9fe822262b2e2e000fecfd4ab
2 files changed +94 -18
cmd/ipfs/dnsresolve_test.go new
+72
@@ -0,0 +1,72 @@
1 +package main
2 +
3 +import (
4 + "context"
5 + "fmt"
6 + "net"
7 + "strings"
8 + "testing"
9 +
10 + ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr"
11 + madns "gx/ipfs/QmfXU2MhWoegxHoeMd3A2ytL2P6CY4FfqGWc23LTNWBwZt/go-multiaddr-dns"
12 +)
13 +
14 +var (
15 + ctx = context.Background()
16 + testAddr, _ = ma.NewMultiaddr("/dns4/example.com/tcp/5001")
17 +)
18 +
19 +func makeResolver(n uint8) *madns.Resolver {
20 + results := make([]net.IPAddr, n)
21 + for i := uint8(0); i < n; i++ {
22 + results[i] = net.IPAddr{IP: net.ParseIP(fmt.Sprintf("192.0.2.%d", i))}
23 + }
24 +
25 + backend := &madns.MockBackend{
26 + IP: map[string][]net.IPAddr{
27 + "example.com": results,
28 + }}
29 +
30 + return &madns.Resolver{
31 + Backend: backend,
32 + }
33 +}
34 +
35 +func TestApiEndpointResolveDNSOneResult(t *testing.T) {
36 + dnsResolver = makeResolver(1)
37 +
38 + addr, err := resolveAddr(ctx, testAddr)
39 + if err != nil {
40 + t.Error(err)
41 + }
42 +
43 + if ref, _ := ma.NewMultiaddr("/ip4/192.0.2.0/tcp/5001"); !addr.Equal(ref) {
44 + t.Errorf("resolved address was different than expected")
45 + }
46 +}
47 +
48 +func TestApiEndpointResolveDNSMultipleResults(t *testing.T) {
49 + dnsResolver = makeResolver(4)
50 +
51 + addr, err := resolveAddr(ctx, testAddr)
52 + if err != nil {
53 + t.Error(err)
54 + }
55 +
56 + if ref, _ := ma.NewMultiaddr("/ip4/192.0.2.0/tcp/5001"); !addr.Equal(ref) {
57 + t.Errorf("resolved address was different than expected")
58 + }
59 +}
60 +
61 +func TestApiEndpointResolveDNSNoResults(t *testing.T) {
62 + dnsResolver = makeResolver(0)
63 +
64 + addr, err := resolveAddr(ctx, testAddr)
65 + if addr != nil || err == nil {
66 + t.Error("expected test address not to resolve, and to throw an error")
67 + }
68 +
69 + if !strings.HasPrefix(err.Error(), "non-resolvable API endpoint") {
70 + t.Errorf("expected error not thrown; actual: %v", err)
71 + }
72 +}
cmd/ipfs/main.go
+22 -18
@@ -34,7 +34,7 @@ import (
34 osh "gx/ipfs/QmXuBJ7DR6k3rmUEKtvVMhwjmXDuJgXXPUt4LQXKBMsU93/go-os-helper"
35 ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr"
36 loggables "gx/ipfs/QmZ4zF1mBrt8C2mSCM4ZYE4aAnv78f7GvrzufJC4G5tecK/go-libp2p-loggables"
37 - mdns "gx/ipfs/QmfXU2MhWoegxHoeMd3A2ytL2P6CY4FfqGWc23LTNWBwZt/go-multiaddr-dns"
37 + madns "gx/ipfs/QmfXU2MhWoegxHoeMd3A2ytL2P6CY4FfqGWc23LTNWBwZt/go-multiaddr-dns"
38 )
39
40 // log is the command logger
@@ -42,6 +42,9 @@ var log = logging.Logger("cmd/ipfs")
42
43 var errRequestCanceled = errors.New("request canceled")
44
45 +// declared as a var for testing purposes
46 +var dnsResolver = madns.DefaultResolver
47 +
48 const (
49 EnvEnableProfiling = "IPFS_PROF"
50 cpuProfile = "ipfs.cpuprof"
@@ -445,30 +448,31 @@ func getApiClient(ctx context.Context, repoPath, apiAddrStr string) (http.Client
448 }
449
450 func apiClientForAddr(ctx context.Context, addr ma.Multiaddr) (http.Client, error) {
448 - addrs, err := mdns.Resolve(ctx, addr)
451 + addr, err := resolveAddr(ctx, addr)
452 if err != nil {
453 return nil, err
454 }
455
453 - dialer := &manet.Dialer{}
454 - for _, addr := range addrs {
455 - ctx, cancelFunc := context.WithTimeout(ctx, 5*time.Second)
456 - defer cancelFunc()
456 + _, host, err := manet.DialArgs(addr)
457 + if err != nil {
458 + return nil, err
459 + }
460
458 - conn, err := dialer.DialContext(ctx, addr)
459 - if err != nil {
460 - log.Errorf("connection to %s failed, error: %s", addr, err)
461 - continue
462 - }
463 - conn.Close()
461 + return http.NewClient(host, http.ClientWithAPIPrefix(corehttp.APIPath)), nil
462 +}
463
465 - _, host, err := manet.DialArgs(addr)
466 - if err != nil {
467 - continue
468 - }
464 +func resolveAddr(ctx context.Context, addr ma.Multiaddr) (ma.Multiaddr, error) {
465 + ctx, cancelFunc := context.WithTimeout(ctx, 10*time.Second)
466 + defer cancelFunc()
467 +
468 + addrs, err := dnsResolver.Resolve(ctx, addr)
469 + if err != nil {
470 + return nil, err
471 + }
472
470 - return http.NewClient(host, http.ClientWithAPIPrefix(corehttp.APIPath)), nil
473 + if len(addrs) == 0 {
474 + return nil, errors.New("non-resolvable API endpoint")
475 }
476
473 - return nil, errors.New("non-resolvable API endpoint")
477 + return addrs[0], nil
478 }