diff --git a/core/commands/swarm.go b/core/commands/swarm.go index 430869721e93..bf864743ff38 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" "gx/ipfs/QmNUhkTWN7iynJZTj1RcTsQDSRGGkh87zMo9ELypxhY8Y6/go-ipfs-config" "gx/ipfs/QmRRovo1DE6i5cMjCbf19mQCSuszF6SKwdZNUMS7MtBnH1/go-ipfs-cmds" @@ -24,6 +27,11 @@ import ( inet "gx/ipfs/QmXuRkCR7BNQa9uqfpTiFWsTQLzmTWYg91Ja1w95gnqb6u/go-libp2p-net" iaddr "gx/ipfs/QmZc5PLgxW61uTPG24TroxHDF6xzgbhZZQf5i53ciQC47Y/go-ipfs-addr" "gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit" + madns "gx/ipfs/QmeHJXPqCNzSFbVkYM1uQLuM2L5FyJB9zukQ7EeqRP8ZC9/go-multiaddr-dns" +) + +const ( + dnsResolveTimeout = 10 * time.Second ) type stringList struct { @@ -460,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 } @@ -488,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.",