Skip to content

Commit 4fb2666

Browse files
authored
Merge pull request #5372 from raulk/dns-resolution-api-endpoint
Run DNS lookup for --api endpoint provided in CLI
2 parents 086ea32 + c89102a commit 4fb2666

File tree

4 files changed

+132
-5
lines changed

4 files changed

+132
-5
lines changed

cmd/ipfs/dnsresolve_test.go

+72
Original file line numberDiff line numberDiff line change
@@ -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

+30-5
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,17 @@ import (
3434
osh "gx/ipfs/QmXuBJ7DR6k3rmUEKtvVMhwjmXDuJgXXPUt4LQXKBMsU93/go-os-helper"
3535
ma "gx/ipfs/QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7/go-multiaddr"
3636
loggables "gx/ipfs/QmZ4zF1mBrt8C2mSCM4ZYE4aAnv78f7GvrzufJC4G5tecK/go-libp2p-loggables"
37+
madns "gx/ipfs/QmfXU2MhWoegxHoeMd3A2ytL2P6CY4FfqGWc23LTNWBwZt/go-multiaddr-dns"
3738
)
3839

3940
// log is the command logger
4041
var log = logging.Logger("cmd/ipfs")
4142

4243
var errRequestCanceled = errors.New("request canceled")
4344

45+
// declared as a var for testing purposes
46+
var dnsResolver = madns.DefaultResolver
47+
4448
const (
4549
EnvEnableProfiling = "IPFS_PROF"
4650
cpuProfile = "ipfs.cpuprof"
@@ -235,7 +239,7 @@ func commandShouldRunOnDaemon(details cmdDetails, req *cmds.Request, cctx *oldcm
235239
// did user specify an api to use for this command?
236240
apiAddrStr, _ := req.Options[corecmds.ApiOption].(string)
237241

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

406-
// getApiClient checks the repo, and the given options, checking for
410+
// getAPIClient checks the repo, and the given options, checking for
407411
// a running API service. if there is one, it returns a client.
408412
// otherwise, it returns errApiNotRunning, or another error.
409-
func getApiClient(repoPath, apiAddrStr string) (http.Client, error) {
413+
func getAPIClient(ctx context.Context, repoPath, apiAddrStr string) (http.Client, error) {
410414
var apiErrorFmt string
411415
switch {
412416
case osh.IsUnix():
@@ -440,14 +444,35 @@ func getApiClient(repoPath, apiAddrStr string) (http.Client, error) {
440444
if len(addr.Protocols()) == 0 {
441445
return nil, fmt.Errorf(apiErrorFmt, repoPath, "multiaddr doesn't provide any protocols")
442446
}
443-
return apiClientForAddr(addr)
447+
return apiClientForAddr(ctx, addr)
444448
}
445449

446-
func apiClientForAddr(addr ma.Multiaddr) (http.Client, error) {
450+
func apiClientForAddr(ctx context.Context, addr ma.Multiaddr) (http.Client, error) {
451+
addr, err := resolveAddr(ctx, addr)
452+
if err != nil {
453+
return nil, err
454+
}
455+
447456
_, host, err := manet.DialArgs(addr)
448457
if err != nil {
449458
return nil, err
450459
}
451460

452461
return http.NewClient(host, http.ClientWithAPIPrefix(corehttp.APIPath)), nil
453462
}
463+
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+
473+
if len(addrs) == 0 {
474+
return nil, errors.New("non-resolvable API endpoint")
475+
}
476+
477+
return addrs[0], nil
478+
}

package.json

+6
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,12 @@
539539
"hash": "QmPyxJ2QS7L5FhGkNYkNcXHGjDhvGHueJ4auqAstFHYxy5",
540540
"name": "go-cidutil",
541541
"version": "0.0.2"
542+
},
543+
{
544+
"author": "lgierth",
545+
"hash": "QmfXU2MhWoegxHoeMd3A2ytL2P6CY4FfqGWc23LTNWBwZt",
546+
"name": "go-multiaddr-dns",
547+
"version": "0.2.4"
542548
}
543549
],
544550
"gxVersion": "0.10.0",
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Copyright (c) 2015 Jeromy Johnson
4+
# MIT Licensed; see the LICENSE file in this repository.
5+
#
6+
7+
test_description="test dns resolution of api endpoint by cli"
8+
9+
. lib/test-lib.sh
10+
11+
test_init_ipfs
12+
13+
test_expect_success "can make http request against dns resolved nc server" '
14+
nc -ld 5005 > nc_out &
15+
NCPID=$!
16+
go-sleep 0.5s && kill "$NCPID" &
17+
ipfs cat /ipfs/Qmabcdef --api /dns4/localhost/tcp/5005 || true
18+
'
19+
20+
test_expect_success "request was received by local nc server" '
21+
grep "POST /api/v0/cat" nc_out
22+
'
23+
24+
test_done

0 commit comments

Comments
 (0)