diff --git a/codec/cbor/multicodec.go b/codec/cbor/multicodec.go new file mode 100644 index 00000000..3df045c2 --- /dev/null +++ b/codec/cbor/multicodec.go @@ -0,0 +1,29 @@ +package cbor + +import ( + "io" + + "github.com/polydawn/refmt/cbor" + + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/codec/dagcbor" + "github.com/ipld/go-ipld-prime/multicodec" +) + +var ( + _ ipld.Decoder = Decode + _ ipld.Encoder = Encode +) + +func init() { + multicodec.RegisterEncoder(0x51, Encode) + multicodec.RegisterDecoder(0x51, Decode) +} + +func Decode(na ipld.NodeAssembler, r io.Reader) error { + return dagcbor.Unmarshal(na, cbor.NewDecoder(cbor.DecodeOptions{}, r), false) +} + +func Encode(n ipld.Node, w io.Writer) error { + return dagcbor.Marshal(n, cbor.NewEncoder(w), false) +} diff --git a/codec/dagcbor/marshal.go b/codec/dagcbor/marshal.go index a4db0605..cbdb5cb1 100644 --- a/codec/dagcbor/marshal.go +++ b/codec/dagcbor/marshal.go @@ -13,12 +13,12 @@ import ( // This should be identical to the general feature in the parent package, // except for the `case ipld.Kind_Link` block, // which is dag-cbor's special sauce for schemafree links. -func Marshal(n ipld.Node, sink shared.TokenSink) error { +func Marshal(n ipld.Node, sink shared.TokenSink, allowLinks bool) error { var tk tok.Token - return marshal(n, &tk, sink) + return marshal(n, &tk, sink, allowLinks) } -func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink) error { +func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink, allowLinks bool) error { switch n.Kind() { case ipld.Kind_Invalid: return fmt.Errorf("cannot traverse a node that is absent") @@ -47,7 +47,7 @@ func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink) error { if _, err := sink.Step(tk); err != nil { return err } - if err := marshal(v, tk, sink); err != nil { + if err := marshal(v, tk, sink, allowLinks); err != nil { return err } } @@ -69,7 +69,7 @@ func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink) error { if err != nil { return err } - if err := marshal(v, tk, sink); err != nil { + if err := marshal(v, tk, sink, allowLinks); err != nil { return err } } @@ -123,6 +123,9 @@ func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink) error { _, err = sink.Step(tk) return err case ipld.Kind_Link: + if !allowLinks { + return fmt.Errorf("cannot Marshal ipld links to CBOR") + } v, err := n.AsLink() if err != nil { return err diff --git a/codec/dagcbor/multicodec.go b/codec/dagcbor/multicodec.go index 85871049..e87eef6e 100644 --- a/codec/dagcbor/multicodec.go +++ b/codec/dagcbor/multicodec.go @@ -28,7 +28,7 @@ func Decode(na ipld.NodeAssembler, r io.Reader) error { return na2.DecodeDagCbor(r) } // Okay, generic builder path. - return Unmarshal(na, cbor.NewDecoder(cbor.DecodeOptions{}, r)) + return Unmarshal(na, cbor.NewDecoder(cbor.DecodeOptions{}, r), true) } func Encode(n ipld.Node, w io.Writer) error { @@ -40,5 +40,5 @@ func Encode(n ipld.Node, w io.Writer) error { return n2.EncodeDagCbor(w) } // Okay, generic inspection path. - return Marshal(n, cbor.NewEncoder(w)) + return Marshal(n, cbor.NewEncoder(w), true) } diff --git a/codec/dagcbor/unmarshal.go b/codec/dagcbor/unmarshal.go index ee066318..36010c52 100644 --- a/codec/dagcbor/unmarshal.go +++ b/codec/dagcbor/unmarshal.go @@ -27,16 +27,16 @@ const ( // except for the `case tok.TBytes` block, // which has dag-cbor's special sauce for detecting schemafree links. -func Unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource) error { +func Unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource, allowLinks bool) error { // Have a gas budget, which will be decremented as we allocate memory, and an error returned when execeeded (or about to be exceeded). // This is a DoS defense mechanism. // It's *roughly* in units of bytes (but only very, VERY roughly) -- it also treats words as 1 in many cases. // FUTURE: this ought be configurable somehow. (How, and at what granularity though?) var gas int = 1048576 * 10 - return unmarshal1(na, tokSrc, &gas) + return unmarshal1(na, tokSrc, &gas, allowLinks) } -func unmarshal1(na ipld.NodeAssembler, tokSrc shared.TokenSource, gas *int) error { +func unmarshal1(na ipld.NodeAssembler, tokSrc shared.TokenSource, gas *int, allowLinks bool) error { var tk tok.Token done, err := tokSrc.Step(&tk) if err != nil { @@ -45,12 +45,12 @@ func unmarshal1(na ipld.NodeAssembler, tokSrc shared.TokenSource, gas *int) erro if done && !tk.Type.IsValue() { return fmt.Errorf("unexpected eof") } - return unmarshal2(na, tokSrc, &tk, gas) + return unmarshal2(na, tokSrc, &tk, gas, allowLinks) } // starts with the first token already primed. Necessary to get recursion // to flow right without a peek+unpeek system. -func unmarshal2(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token, gas *int) error { +func unmarshal2(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token, gas *int, allowLinks bool) error { // FUTURE: check for schema.TypedNodeBuilder that's going to parse a Link (they can slurp any token kind they want). switch tk.Type { case tok.TMapOpen: @@ -97,7 +97,7 @@ func unmarshal2(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token, if err != nil { // return in error if the key was rejected return err } - err = unmarshal1(mva, tokSrc, gas) + err = unmarshal1(mva, tokSrc, gas, allowLinks) if err != nil { // return in error if some part of the recursion errored return err } @@ -140,7 +140,7 @@ func unmarshal2(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token, if observedLen > expectLen { return fmt.Errorf("unexpected continuation of array elements beyond declared length") } - err := unmarshal2(la.AssembleValue(), tokSrc, tk, gas) + err := unmarshal2(la.AssembleValue(), tokSrc, tk, gas, allowLinks) if err != nil { // return in error if some part of the recursion errored return err } @@ -166,6 +166,9 @@ func unmarshal2(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token, } switch tk.Tag { case linkTag: + if !allowLinks { + return fmt.Errorf("unhandled cbor tag %d", tk.Tag) + } if len(tk.Bytes) < 1 || tk.Bytes[0] != 0 { return ErrInvalidMultibase } @@ -173,7 +176,7 @@ func unmarshal2(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token, if err != nil { return err } - return na.AssignLink(cidlink.Link{elCid}) + return na.AssignLink(cidlink.Link{Cid: elCid}) default: return fmt.Errorf("unhandled cbor tag %d", tk.Tag) }