Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run DNS lookup for --api endpoint provided in CLI #5372

Merged
merged 5 commits into from
Aug 23, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
}
}
35 changes: 30 additions & 5 deletions cmd/ipfs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,17 @@ import (
osh "gx/ipfs/QmXuBJ7DR6k3rmUEKtvVMhwjmXDuJgXXPUt4LQXKBMsU93/go-os-helper"
ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr"
loggables "gx/ipfs/QmZ4zF1mBrt8C2mSCM4ZYE4aAnv78f7GvrzufJC4G5tecK/go-libp2p-loggables"
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 @@ -235,7 +239,7 @@ func commandShouldRunOnDaemon(details cmdDetails, req *cmds.Request, cctx *oldcm
// did user specify an api to use for this command?
apiAddrStr, _ := req.Options[corecmds.ApiOption].(string)

client, err := getApiClient(cctx.ConfigRoot, apiAddrStr)
client, err := getAPIClient(req.Context, cctx.ConfigRoot, apiAddrStr)
if err == repo.ErrApiNotRunning {
if apiAddrStr != "" && req.Command != daemonCmd {
// if user SPECIFIED an api, and this cmd is not daemon
Expand Down Expand Up @@ -403,10 +407,10 @@ If you're sure go-ipfs isn't running, you can just delete it.
var checkIPFSUnixFmt = "Otherwise check:\n\tps aux | grep ipfs"
var checkIPFSWinFmt = "Otherwise check:\n\ttasklist | findstr ipfs"

// getApiClient checks the repo, and the given options, checking for
// getAPIClient checks the repo, and the given options, checking for
// a running API service. if there is one, it returns a client.
// otherwise, it returns errApiNotRunning, or another error.
func getApiClient(repoPath, apiAddrStr string) (http.Client, error) {
func getAPIClient(ctx context.Context, repoPath, apiAddrStr string) (http.Client, error) {
var apiErrorFmt string
switch {
case osh.IsUnix():
Expand Down Expand Up @@ -440,14 +444,35 @@ func getApiClient(repoPath, apiAddrStr string) (http.Client, error) {
if len(addr.Protocols()) == 0 {
return nil, fmt.Errorf(apiErrorFmt, repoPath, "multiaddr doesn't provide any protocols")
}
return apiClientForAddr(addr)
return apiClientForAddr(ctx, addr)
}

func apiClientForAddr(addr ma.Multiaddr) (http.Client, error) {
func apiClientForAddr(ctx context.Context, addr ma.Multiaddr) (http.Client, error) {
addr, err := resolveAddr(ctx, addr)
if err != nil {
return nil, err
}

_, host, err := manet.DialArgs(addr)
if err != nil {
return nil, err
}

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
}
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,12 @@
"hash": "QmPyxJ2QS7L5FhGkNYkNcXHGjDhvGHueJ4auqAstFHYxy5",
"name": "go-cidutil",
"version": "0.0.2"
},
{
"author": "lgierth",
"hash": "QmfXU2MhWoegxHoeMd3A2ytL2P6CY4FfqGWc23LTNWBwZt",
"name": "go-multiaddr-dns",
"version": "0.2.4"
}
],
"gxVersion": "0.10.0",
Expand Down
24 changes: 24 additions & 0 deletions test/sharness/t0236-cli-api-dns-resolve.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
#
# Copyright (c) 2015 Jeromy Johnson
# MIT Licensed; see the LICENSE file in this repository.
#

test_description="test dns resolution of api endpoint by cli"

. lib/test-lib.sh

test_init_ipfs

test_expect_success "can make http request against dns resolved nc server" '
nc -ld 5005 > nc_out &
NCPID=$!
go-sleep 0.5s && kill "$NCPID" &
ipfs cat /ipfs/Qmabcdef --api /dns4/localhost/tcp/5005 || true
'

test_expect_success "request was received by local nc server" '
grep "POST /api/v0/cat" nc_out
'

test_done