Skip to content

Commit

Permalink
Add {Unm,M}arshalOptions for explicit mode switching for cbor vs dagcbor
Browse files Browse the repository at this point in the history
  • Loading branch information
rvagg committed Jul 21, 2021
1 parent fe89893 commit 9bf7d87
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 20 deletions.
6 changes: 4 additions & 2 deletions codec/cbor/multicodec.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ func init() {
}

func Decode(na ipld.NodeAssembler, r io.Reader) error {
return dagcbor.Unmarshal(na, cbor.NewDecoder(cbor.DecodeOptions{}, r), false)
return dagcbor.Unmarshal(na, cbor.NewDecoder(cbor.DecodeOptions{}, r),
dagcbor.UnmarshalOptions{AllowLinks: false})
}

func Encode(n ipld.Node, w io.Writer) error {
return dagcbor.Marshal(n, cbor.NewEncoder(w), false)
return dagcbor.Marshal(n, cbor.NewEncoder(w),
dagcbor.MarshalOptions{AllowLinks: false})
}
18 changes: 12 additions & 6 deletions codec/dagcbor/marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,18 @@ import (
// 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, allowLinks bool) error {
type MarshalOptions struct {
// If true, allow encoding of Link nodes as CBOR tag(42), otherwise reject
// them as unencodable
AllowLinks bool
}

func Marshal(n ipld.Node, sink shared.TokenSink, options MarshalOptions) error {
var tk tok.Token
return marshal(n, &tk, sink, allowLinks)
return marshal(n, &tk, sink, options)
}

func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink, allowLinks bool) error {
func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink, options MarshalOptions) error {
switch n.Kind() {
case ipld.Kind_Invalid:
return fmt.Errorf("cannot traverse a node that is absent")
Expand Down Expand Up @@ -67,7 +73,7 @@ func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink, allowLinks bool)
if _, err := sink.Step(tk); err != nil {
return err
}
if err := marshal(e.value, tk, sink, allowLinks); err != nil {
if err := marshal(e.value, tk, sink, options); err != nil {
return err
}
}
Expand All @@ -89,7 +95,7 @@ func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink, allowLinks bool)
if err != nil {
return err
}
if err := marshal(v, tk, sink, allowLinks); err != nil {
if err := marshal(v, tk, sink, options); err != nil {
return err
}
}
Expand Down Expand Up @@ -143,7 +149,7 @@ func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink, allowLinks bool)
_, err = sink.Step(tk)
return err
case ipld.Kind_Link:
if !allowLinks {
if !options.AllowLinks {
return fmt.Errorf("cannot Marshal ipld links to CBOR")
}
v, err := n.AsLink()
Expand Down
4 changes: 2 additions & 2 deletions codec/dagcbor/multicodec.go
Original file line number Diff line number Diff line change
Expand Up @@ -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), true)
return Unmarshal(na, cbor.NewDecoder(cbor.DecodeOptions{}, r), UnmarshalOptions{AllowLinks: true})
}

func Encode(n ipld.Node, w io.Writer) error {
Expand All @@ -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), true)
return Marshal(n, cbor.NewEncoder(w), MarshalOptions{AllowLinks: true})
}
21 changes: 13 additions & 8 deletions codec/dagcbor/unmarshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,21 @@ 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, allowLinks bool) error {
type UnmarshalOptions struct {
// If true, parse DAG-CBOR tag(42) as Link nodes, otherwise reject them
AllowLinks bool
}

func Unmarshal(na ipld.NodeAssembler, tokSrc shared.TokenSource, options UnmarshalOptions) 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, allowLinks)
return unmarshal1(na, tokSrc, &gas, options)
}

func unmarshal1(na ipld.NodeAssembler, tokSrc shared.TokenSource, gas *int, allowLinks bool) error {
func unmarshal1(na ipld.NodeAssembler, tokSrc shared.TokenSource, gas *int, options UnmarshalOptions) error {
var tk tok.Token
done, err := tokSrc.Step(&tk)
if err != nil {
Expand All @@ -45,12 +50,12 @@ func unmarshal1(na ipld.NodeAssembler, tokSrc shared.TokenSource, gas *int, allo
if done && !tk.Type.IsValue() {
return fmt.Errorf("unexpected eof")
}
return unmarshal2(na, tokSrc, &tk, gas, allowLinks)
return unmarshal2(na, tokSrc, &tk, gas, options)
}

// 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, allowLinks bool) error {
func unmarshal2(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token, gas *int, options UnmarshalOptions) 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:
Expand Down Expand Up @@ -97,7 +102,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, allowLinks)
err = unmarshal1(mva, tokSrc, gas, options)
if err != nil { // return in error if some part of the recursion errored
return err
}
Expand Down Expand Up @@ -140,7 +145,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, allowLinks)
err := unmarshal2(la.AssembleValue(), tokSrc, tk, gas, options)
if err != nil { // return in error if some part of the recursion errored
return err
}
Expand All @@ -166,7 +171,7 @@ func unmarshal2(na ipld.NodeAssembler, tokSrc shared.TokenSource, tk *tok.Token,
}
switch tk.Tag {
case linkTag:
if !allowLinks {
if !options.AllowLinks {
return fmt.Errorf("unhandled cbor tag %d", tk.Tag)
}
if len(tk.Bytes) < 1 || tk.Bytes[0] != 0 {
Expand Down
4 changes: 2 additions & 2 deletions codec/dagjson/unmarshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ import (
// tokens before deciding what kind of value to create).

type UnmarshalOptions struct {
// If true, parse DAG-CBOR `{"/":"cid string"}` as a Link kind node rather
// If true, parse DAG-JSON `{"/":"cid string"}` as a Link kind node rather
// than a plain map
ParseLinks bool

// If true, parse DAG-CBOR `{"/":{"bytes":"base64 bytes..."}}` as a Bytes kind
// If true, parse DAG-JSON `{"/":{"bytes":"base64 bytes..."}}` as a Bytes kind
// node rather than nested plain maps
ParseBytes bool
}
Expand Down

0 comments on commit 9bf7d87

Please sign in to comment.