/ core / commands / resolve.go
resolve.go
  1  package commands
  2  
  3  import (
  4  	"errors"
  5  	"fmt"
  6  	"io"
  7  	"strings"
  8  	"time"
  9  
 10  	ns "github.com/ipfs/boxo/namesys"
 11  	cidenc "github.com/ipfs/go-cidutil/cidenc"
 12  	cmdenv "github.com/ipfs/kubo/core/commands/cmdenv"
 13  	"github.com/ipfs/kubo/core/commands/cmdutils"
 14  	ncmd "github.com/ipfs/kubo/core/commands/name"
 15  
 16  	"github.com/ipfs/boxo/path"
 17  	cmds "github.com/ipfs/go-ipfs-cmds"
 18  	options "github.com/ipfs/kubo/core/coreiface/options"
 19  )
 20  
 21  const (
 22  	resolveRecursiveOptionName      = "recursive"
 23  	resolveDhtRecordCountOptionName = "dht-record-count"
 24  	resolveDhtTimeoutOptionName     = "dht-timeout"
 25  )
 26  
 27  var ResolveCmd = &cmds.Command{
 28  	Helptext: cmds.HelpText{
 29  		Tagline: "Resolve the value of names to IPFS.",
 30  		ShortDescription: `
 31  There are a number of mutable name protocols that can link among
 32  themselves and into IPNS. This command accepts any of these
 33  identifiers and resolves them to the referenced item.
 34  `,
 35  		LongDescription: `
 36  There are a number of mutable name protocols that can link among
 37  themselves and into IPNS. For example IPNS references can (currently)
 38  point at an IPFS object, and DNS links can point at other DNS links, IPNS
 39  entries, or IPFS objects. This command accepts any of these
 40  identifiers and resolves them to the referenced item.
 41  
 42  EXAMPLES
 43  
 44  Resolve the value of your identity:
 45  
 46    $ ipfs resolve /ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
 47    /ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj
 48  
 49  Resolve the value of another name:
 50  
 51    $ ipfs resolve /ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n
 52    /ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
 53  
 54  Resolve the value of another name recursively:
 55  
 56    $ ipfs resolve -r /ipns/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n
 57    /ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj
 58  
 59  Resolve the value of an IPFS DAG path:
 60  
 61    $ ipfs resolve /ipfs/QmeZy1fGbwgVSrqbfh9fKQrAWgeyRnj7h8fsHS1oy3k99x/beep/boop
 62    /ipfs/QmYRMjyvAiHKN9UTi8Bzt1HUspmSRD8T8DwxfSMzLgBon1
 63  
 64  `,
 65  	},
 66  
 67  	Arguments: []cmds.Argument{
 68  		cmds.StringArg("name", true, false, "The name to resolve.").EnableStdin(),
 69  	},
 70  	Options: []cmds.Option{
 71  		cmds.BoolOption(resolveRecursiveOptionName, "r", "Resolve until the result is an IPFS name.").WithDefault(true),
 72  		cmds.IntOption(resolveDhtRecordCountOptionName, "dhtrc", "Number of records to request for DHT resolution."),
 73  		cmds.StringOption(resolveDhtTimeoutOptionName, "dhtt", "Max time to collect values during DHT resolution e.g. \"30s\". Pass 0 for no timeout."),
 74  	},
 75  	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
 76  		api, err := cmdenv.GetApi(env, req)
 77  		if err != nil {
 78  			return err
 79  		}
 80  
 81  		name := req.Arguments[0]
 82  		recursive, _ := req.Options[resolveRecursiveOptionName].(bool)
 83  
 84  		// the case when ipns is resolved step by step
 85  		if strings.HasPrefix(name, "/ipns/") && !recursive {
 86  			rc, rcok := req.Options[resolveDhtRecordCountOptionName].(uint)
 87  			dhtt, dhttok := req.Options[resolveDhtTimeoutOptionName].(string)
 88  			ropts := []options.NameResolveOption{
 89  				options.Name.ResolveOption(ns.ResolveWithDepth(1)),
 90  			}
 91  
 92  			if rcok {
 93  				ropts = append(ropts, options.Name.ResolveOption(ns.ResolveWithDhtRecordCount(rc)))
 94  			}
 95  			if dhttok {
 96  				d, err := time.ParseDuration(dhtt)
 97  				if err != nil {
 98  					return err
 99  				}
100  				if d < 0 {
101  					return errors.New("DHT timeout value must be >= 0")
102  				}
103  				ropts = append(ropts, options.Name.ResolveOption(ns.ResolveWithDhtTimeout(d)))
104  			}
105  			p, err := api.Name().Resolve(req.Context, name, ropts...)
106  			// ErrResolveRecursion is fine
107  			if err != nil && err != ns.ErrResolveRecursion {
108  				return err
109  			}
110  			return cmds.EmitOnce(res, &ncmd.ResolvedPath{Path: p.String()})
111  		}
112  
113  		var enc cidenc.Encoder
114  		switch {
115  		case !cmdenv.CidBaseDefined(req) && !strings.HasPrefix(name, "/ipns/"):
116  			// Not specified, check the path.
117  			enc, err = cmdenv.CidEncoderFromPath(name)
118  			if err == nil {
119  				break
120  			}
121  			// Nope, fallback on the default.
122  			fallthrough
123  		default:
124  			enc, err = cmdenv.GetCidEncoder(req)
125  			if err != nil {
126  				return err
127  			}
128  		}
129  
130  		p, err := cmdutils.PathOrCidPath(name)
131  		if err != nil {
132  			return err
133  		}
134  
135  		// else, ipfs path or ipns with recursive flag
136  		rp, remainder, err := api.ResolvePath(req.Context, p)
137  		if err != nil {
138  			return err
139  		}
140  
141  		// Trick to encode path with correct encoding.
142  		encodedPath := "/" + rp.Namespace() + "/" + enc.Encode(rp.RootCid())
143  		if len(remainder) != 0 {
144  			encodedPath += path.SegmentsToString(remainder...)
145  		}
146  
147  		// Ensure valid and sanitized.
148  		ep, err := path.NewPath(encodedPath)
149  		if err != nil {
150  			return err
151  		}
152  
153  		return cmds.EmitOnce(res, &ncmd.ResolvedPath{Path: ep.String()})
154  	},
155  	Encoders: cmds.EncoderMap{
156  		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, rp *ncmd.ResolvedPath) error {
157  			fmt.Fprintln(w, rp.Path)
158  			return nil
159  		}),
160  	},
161  	Type: ncmd.ResolvedPath{},
162  }