Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion transcoders.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func ip4BtS(b []byte) (string, error) {
return net.IP(b).String(), nil
}

var TranscoderPort = NewTranscoderFromFunctions(portStB, portBtS, nil)
var TranscoderPort = NewTranscoderFromFunctions(portStB, portBtS, portValidate)

func portStB(s string) ([]byte, error) {
i, err := strconv.ParseUint(s, 10, 16)
Expand All @@ -151,10 +151,20 @@ func portStB(s string) ([]byte, error) {
}

func portBtS(b []byte) (string, error) {
if len(b) < 2 {
return "", fmt.Errorf("port: byte slice too short: %d bytes, want 2", len(b))
}
i := binary.BigEndian.Uint16(b)
return strconv.FormatUint(uint64(i), 10), nil
}

func portValidate(b []byte) error {
if len(b) != 2 {
return fmt.Errorf("port: invalid length: %d bytes, want 2", len(b))
}
return nil
}

var TranscoderOnion = NewTranscoderFromFunctions(onionStB, onionBtS, onionValidate)

func onionStB(s string) ([]byte, error) {
Expand Down Expand Up @@ -191,6 +201,9 @@ func onionStB(s string) ([]byte, error) {
}

func onionBtS(b []byte) (string, error) {
if len(b) != 12 {
return "", fmt.Errorf("invalid len for onion addr: got %d expected 12", len(b))
}
addr := strings.ToLower(base32.StdEncoding.EncodeToString(b[0:10]))
port := binary.BigEndian.Uint16(b[10:12])
if port == 0 {
Expand Down Expand Up @@ -245,6 +258,9 @@ func onion3StB(s string) ([]byte, error) {
}

func onion3BtS(b []byte) (string, error) {
if len(b) != 37 {
return "", fmt.Errorf("invalid len for onion addr: got %d expected 37", len(b))
}
addr := strings.ToLower(base32.StdEncoding.EncodeToString(b[0:35]))
port := binary.BigEndian.Uint16(b[35:37])
if port < 1 {
Expand Down
55 changes: 55 additions & 0 deletions transcoders_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package multiaddr

import "testing"

// Regression test for https://github.com/multiformats/go-multiaddr/issues/288.
// onionBtS and onion3BtS used to index their input at fixed offsets without
// a length check, panicking with `slice bounds out of range` when called with
// short input (e.g. directly via the exported TranscoderOnion / TranscoderOnion3).
func TestOnionTranscodersRejectShortInput(t *testing.T) {
cases := []struct {
name string
tr Transcoder
b []byte
}{
{"onion-short", TranscoderOnion, []byte{0x00, 0x01}},
{"onion-empty", TranscoderOnion, nil},
{"onion3-short", TranscoderOnion3, []byte{0x00, 0x01}},
{"onion3-empty", TranscoderOnion3, nil},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if _, err := tc.tr.BytesToString(tc.b); err == nil {
t.Fatalf("expected error for short input, got nil")
}
})
}
}

// portBtS used to call binary.BigEndian.Uint16 without checking len(b) >= 2.
// Direct callers of TranscoderPort (outside the codec.go length checks) could
// panic with `index out of range`. The internal guard and the new validator
// must turn both into normal errors.
func TestTranscoderPortRejectsShortInput(t *testing.T) {
t.Run("BytesToString-short", func(t *testing.T) {
if _, err := TranscoderPort.BytesToString(nil); err == nil {
t.Fatalf("expected error on nil input")
}
if _, err := TranscoderPort.BytesToString([]byte{0x00}); err == nil {
t.Fatalf("expected error on 1-byte input")
}
})
t.Run("Validate-rejects-wrong-length", func(t *testing.T) {
if err := TranscoderPort.ValidateBytes(nil); err == nil {
t.Fatalf("expected validate error on nil input")
}
if err := TranscoderPort.ValidateBytes([]byte{0x00, 0x01, 0x02}); err == nil {
t.Fatalf("expected validate error on 3-byte input")
}
})
t.Run("Validate-accepts-2-byte", func(t *testing.T) {
if err := TranscoderPort.ValidateBytes([]byte{0x00, 0x50}); err != nil {
t.Fatalf("expected no error on 2-byte input, got %v", err)
}
})
}