From a448431c5ec5252405284cb344005c5d8f06d7d6 Mon Sep 17 00:00:00 2001 From: Overbool Date: Fri, 5 Oct 2018 13:12:55 +0800 Subject: [PATCH] fix(swarm): add dnsaddr support in swarm connect License: MIT Signed-off-by: Overbool --- core/commands/swarm.go | 112 +++++++++++++++++++++++++++++++++++------ 1 file changed, 98 insertions(+), 14 deletions(-) diff --git a/core/commands/swarm.go b/core/commands/swarm.go index d49157c5aaa4..5c22966a66a3 100644 --- a/core/commands/swarm.go +++ b/core/commands/swarm.go @@ -1,18 +1,21 @@ package commands import ( + "context" "errors" "fmt" "io" "path" "sort" "strings" + "sync" + "time" - "github.com/ipfs/go-ipfs/commands" - "github.com/ipfs/go-ipfs/core/commands/cmdenv" - "github.com/ipfs/go-ipfs/core/commands/e" - "github.com/ipfs/go-ipfs/repo" - "github.com/ipfs/go-ipfs/repo/fsrepo" + commands "github.com/ipfs/go-ipfs/commands" + cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv" + e "github.com/ipfs/go-ipfs/core/commands/e" + repo "github.com/ipfs/go-ipfs/repo" + fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo" inet "gx/ipfs/QmQdLcvoy3JuSqhV6iwQ9T6Cv7hWLAdzob4jUZRPqFL67Z/go-libp2p-net" pstore "gx/ipfs/QmSJ36wcYQyEViJUWUEhJU81tw1KdakTKqLLHbvYbA9zDv/go-libp2p-peerstore" @@ -24,6 +27,11 @@ import ( peer "gx/ipfs/QmbNepETomvmXfz1X5pHNFD2QuPqnqi47dTd94QJWSorQ3/go-libp2p-peer" "gx/ipfs/QmcALnLPFXgLLaZkXzGv99yt8xPfiWqFTumiXpWBJbU9cY/go-libp2p-swarm" iaddr "gx/ipfs/QmesXvbRGyKQn1XbPHx1Mr5E6RTJYR9c8zwFVuGZq9Aa1j/go-ipfs-addr" + madns "gx/ipfs/QmfXU2MhWoegxHoeMd3A2ytL2P6CY4FfqGWc23LTNWBwZt/go-multiaddr-dns" +) + +const ( + dnsResolveTimeout = 10 * time.Second ) type stringList struct { @@ -52,6 +60,13 @@ ipfs peers in the internet. }, } +const ( + swarmVerboseOptionName = "verbose" + swarmStreamsOptionName = "streams" + swarmLatencyOptionName = "latency" + swarmDirectionOptionName = "direction" +) + var swarmPeersCmd = &cmds.Command{ Helptext: cmdkit.HelpText{ Tagline: "List peers with open connections.", @@ -60,10 +75,10 @@ var swarmPeersCmd = &cmds.Command{ `, }, Options: []cmdkit.Option{ - cmdkit.BoolOption("verbose", "v", "display all extra information"), - cmdkit.BoolOption("streams", "Also list information about open streams for each peer"), - cmdkit.BoolOption("latency", "Also list information about latency to each peer"), - cmdkit.BoolOption("direction", "Also list information about the direction of connection"), + cmdkit.BoolOption(swarmVerboseOptionName, "v", "display all extra information"), + cmdkit.BoolOption(swarmStreamsOptionName, "Also list information about open streams for each peer"), + cmdkit.BoolOption(swarmLatencyOptionName, "Also list information about latency to each peer"), + cmdkit.BoolOption(swarmDirectionOptionName, "Also list information about the direction of connection"), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { api, err := cmdenv.GetApi(env) @@ -71,10 +86,10 @@ var swarmPeersCmd = &cmds.Command{ return err } - verbose, _ := req.Options["verbose"].(bool) - latency, _ := req.Options["latency"].(bool) - streams, _ := req.Options["streams"].(bool) - direction, _ := req.Options["direction"].(bool) + verbose, _ := req.Options[swarmVerboseOptionName].(bool) + latency, _ := req.Options[swarmLatencyOptionName].(bool) + streams, _ := req.Options[swarmStreamsOptionName].(bool) + direction, _ := req.Options[swarmDirectionOptionName].(bool) conns, err := api.Swarm().Peers(req.Context) if err != nil { @@ -453,10 +468,29 @@ func parseAddresses(addrs []string) (iaddrs []iaddr.IPFSAddr, err error) { return } +// parseMultiaddrs is a function that takes in a slice of peer multiaddr +// and returns slices of multiaddrs and peerids +func parseMultiaddrs(maddrs []ma.Multiaddr) (iaddrs []iaddr.IPFSAddr, err error) { + iaddrs = make([]iaddr.IPFSAddr, len(maddrs)) + for i, maddr := range maddrs { + iaddrs[i], err = iaddr.ParseMultiaddr(maddr) + if err != nil { + return nil, cmds.ClientError("invalid peer address: " + err.Error()) + } + } + return +} + // peersWithAddresses is a function that takes in a slice of string peer addresses // (multiaddr + peerid) and returns a slice of properly constructed peers func peersWithAddresses(addrs []string) ([]pstore.PeerInfo, error) { - iaddrs, err := parseAddresses(addrs) + // resolve addresses + maddrs, err := resolveAddresses(addrs) + if err != nil { + return nil, err + } + + iaddrs, err := parseMultiaddrs(maddrs) if err != nil { return nil, err } @@ -481,6 +515,56 @@ func peersWithAddresses(addrs []string) ([]pstore.PeerInfo, error) { return pis, nil } +// resolveAddresses resolves addresses parallelly +func resolveAddresses(addrs []string) ([]ma.Multiaddr, error) { + var maddrs []ma.Multiaddr + var wg sync.WaitGroup + resolveErrC := make(chan error, len(addrs)) + + for _, addr := range addrs { + maddr, err := ma.NewMultiaddr(addr) + if err != nil { + return nil, err + } + // check whether address ends in `ipfs/Qm...` + if _, err := maddr.ValueForProtocol(ma.P_IPFS); err != ma.ErrProtocolNotFound { + maddrs = append(maddrs, maddr) + continue + } + wg.Add(1) + go func(maddr ma.Multiaddr) { + defer wg.Done() + ctx, cancel := context.WithTimeout(context.Background(), dnsResolveTimeout) + raddrs, err := madns.Resolve(ctx, maddr) + cancel() + if err != nil { + resolveErrC <- err + return + } + if len(raddrs) == 0 { + resolveErrC <- fmt.Errorf("non-resolvable multiaddr about %v", maddr) + return + } + // filter out addresses that still doesn't end in `ipfs/Qm...` + for _, raddr := range raddrs { + if _, err := raddr.ValueForProtocol(ma.P_IPFS); err != ma.ErrProtocolNotFound { + maddrs = append(maddrs, raddr) + } + } + }(maddr) + } + // wait for address resolving + wg.Wait() + + select { + case err := <-resolveErrC: + return nil, err + default: + } + + return maddrs, nil +} + var swarmFiltersCmd = &cmds.Command{ Helptext: cmdkit.HelpText{ Tagline: "Manipulate address filters.",