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 }