-
Notifications
You must be signed in to change notification settings - Fork 54
Expand file tree
/
Copy pathdecode.go
More file actions
316 lines (281 loc) · 9.63 KB
/
Copy pathdecode.go
File metadata and controls
316 lines (281 loc) · 9.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Represents bencode data structure using native Go types: booleans, floats,
// strings, slices, and maps.
package bencode
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"reflect"
)
// RawMessage is a raw encoded bencode value. It can be used to delay
// bencode decoding or to access the original bytes of a bencoded value
// (such as the "info" dictionary in a BitTorrent metainfo file).
type RawMessage []byte
// Default limits for decoding bencode data.
const (
// DefaultMaxStringLength is the default maximum string length (64 MiB).
DefaultMaxStringLength int64 = 64 * 1024 * 1024
// DefaultMaxDepth is the default maximum nesting depth for lists and dictionaries.
DefaultMaxDepth int = 100
// DefaultMaxElements is the default maximum number of bencode values parsed.
DefaultMaxElements int64 = 3_000_000
// maxNumLen is the maximum number of bytes allowed when scanning for an integer or string length delimiter.
maxNumLen int = 64
)
// Options specifies configuration and resource limits for decoding bencode data.
type Options struct {
// MaxStringLength limits the maximum length in bytes of any bencode string.
// Default is DefaultMaxStringLength (64 MiB). Set to -1 for unlimited.
MaxStringLength int64
// MaxDepth limits the maximum nesting depth for nested lists and dictionaries.
// Default is DefaultMaxDepth (100). Set to -1 for unlimited.
MaxDepth int
// MaxElements limits the total number of bencode values (integers, strings, lists, dicts) parsed.
// Default is DefaultMaxElements (3,000,000). Set to -1 for unlimited.
MaxElements int64
// Strict enables strict adherence to the BitTorrent BEP 3 bencode specification:
// - Rejects integer representations with leading zeros (e.g. "i03e" is rejected; "i0e" is allowed).
// - Rejects negative zero ("i-0e").
// - Rejects explicit positive sign ("i+5e").
// - Rejects non-integer numbers (such as floats or exponential notation).
// - Enforces lexicographically sorted dictionary keys without duplicates.
// - Rejects string lengths with leading zeros (e.g. "03:abc").
Strict bool
}
// DefaultOptions returns a new Options struct initialized with standard limits.
func DefaultOptions() Options {
return Options{
MaxStringLength: DefaultMaxStringLength,
MaxDepth: DefaultMaxDepth,
MaxElements: DefaultMaxElements,
Strict: false,
}
}
// Decoder reads and decodes bencode values from an input stream.
type Decoder struct {
r *bufio.Reader
opts Options
}
// NewDecoder returns a new Decoder reading from r with DefaultOptions.
func NewDecoder(r io.Reader) *Decoder {
return NewDecoderWithOptions(r, DefaultOptions())
}
// NewDecoderWithOptions returns a new Decoder reading from r with the given Options.
func NewDecoderWithOptions(r io.Reader, opts Options) *Decoder {
br, ok := r.(*bufio.Reader)
if !ok {
br = bufio.NewReader(r)
}
return &Decoder{
r: br,
opts: opts,
}
}
// Buffered returns a reader of the data remaining in the Decoder's buffer.
// The reader is valid until the next call to Decode or Unmarshal.
func (d *Decoder) Buffered() io.Reader {
n := d.r.Buffered()
if n == 0 {
return bytes.NewReader(nil)
}
buf, err := d.r.Peek(n)
if err != nil {
return bytes.NewReader(nil)
}
return bytes.NewReader(buf)
}
// SetMaxStringLength sets the maximum string length in bytes and returns the Decoder.
func (d *Decoder) SetMaxStringLength(limit int64) *Decoder {
d.opts.MaxStringLength = limit
return d
}
// SetMaxDepth sets the maximum nesting depth and returns the Decoder.
func (d *Decoder) SetMaxDepth(depth int) *Decoder {
d.opts.MaxDepth = depth
return d
}
// SetMaxElements sets the maximum total elements limit and returns the Decoder.
func (d *Decoder) SetMaxElements(elements int64) *Decoder {
d.opts.MaxElements = elements
return d
}
// SetStrict sets whether strict BEP 3 validation is enabled and returns the Decoder.
func (d *Decoder) SetStrict(strict bool) *Decoder {
d.opts.Strict = strict
return d
}
// Decode reads the next bencode value from the stream and returns its generic Go representation.
func (d *Decoder) Decode() (data any, err error) {
state := &decodeState{opts: d.opts}
return state.decode(d.r)
}
// Unmarshal reads the next bencode value and parses it into val (which must be a pointer).
func (d *Decoder) Unmarshal(val any) error {
if reflect.TypeOf(val).Kind() != reflect.Ptr {
return errors.New("Attempt to unmarshal into a non-pointer")
}
state := &decodeState{opts: d.opts}
return unmarshalValueWithState(d.r, reflect.Indirect(reflect.ValueOf(val)), state)
}
// Decode parses the stream r and returns the generic bencode object representation.
// The object representation is a tree of Go data types: string, int64, uint64,
// []any, or map[string]any.
func Decode(reader io.Reader) (data any, err error) {
br, ok := reader.(*bufio.Reader)
if !ok {
br = newBufioReader(reader)
defer bufioReaderPool.Put(br)
}
state := &decodeState{opts: DefaultOptions()}
return state.decode(br)
}
// DecodeWithOptions parses the stream r with custom Options and returns the generic bencode representation.
func DecodeWithOptions(reader io.Reader, opts Options) (data any, err error) {
br, ok := reader.(*bufio.Reader)
if !ok {
br = newBufioReader(reader)
defer bufioReaderPool.Put(br)
}
state := &decodeState{opts: opts}
return state.decode(br)
}
// Unmarshal reads and parses the bencode syntax data from r into val using DefaultOptions.
func Unmarshal(r io.Reader, val any) (err error) {
if reflect.TypeOf(val).Kind() != reflect.Ptr {
return errors.New("Attempt to unmarshal into a non-pointer")
}
br, ok := r.(*bufio.Reader)
if !ok {
br = newBufioReader(r)
defer bufioReaderPool.Put(br)
}
state := &decodeState{opts: DefaultOptions()}
return unmarshalValueWithState(br, reflect.Indirect(reflect.ValueOf(val)), state)
}
// UnmarshalWithOptions reads and parses bencode syntax data from r into val with custom Options.
func UnmarshalWithOptions(r io.Reader, val any, opts Options) error {
if reflect.TypeOf(val).Kind() != reflect.Ptr {
return errors.New("Attempt to unmarshal into a non-pointer")
}
br, ok := r.(*bufio.Reader)
if !ok {
br = newBufioReader(r)
defer bufioReaderPool.Put(br)
}
state := &decodeState{opts: opts}
return unmarshalValueWithState(br, reflect.Indirect(reflect.ValueOf(val)), state)
}
type decodeState struct {
opts Options
depth int
elements int64
}
func (s *decodeState) incDepth() error {
s.depth++
if s.opts.MaxDepth >= 0 && s.depth > s.opts.MaxDepth {
return fmt.Errorf("bencode: nesting depth %d exceeds max depth limit of %d", s.depth, s.opts.MaxDepth)
}
return nil
}
func (s *decodeState) decDepth() {
if s.depth > 0 {
s.depth--
}
}
func (s *decodeState) incElement() error {
s.elements++
if s.opts.MaxElements >= 0 && s.elements > s.opts.MaxElements {
return fmt.Errorf("bencode: element count %d exceeds max elements limit of %d", s.elements, s.opts.MaxElements)
}
return nil
}
func (s *decodeState) checkStringLength(length int64) error {
if length < 0 {
return fmt.Errorf("bencode: invalid negative string length: %d", length)
}
if s.opts.MaxStringLength >= 0 && length > s.opts.MaxStringLength {
return fmt.Errorf("bencode: string length %d exceeds limit of %d bytes", length, s.opts.MaxStringLength)
}
return nil
}
func readNumBytes(r *bufio.Reader, delim byte) ([]byte, error) {
peekLen := maxNumLen
buf, err := r.Peek(peekLen)
if err != nil && len(buf) == 0 {
return nil, err
}
if i := bytes.IndexByte(buf, delim); i >= 0 {
res := make([]byte, i)
copy(res, buf[:i])
_, err = r.Discard(i + 1)
if err != nil {
return nil, err
}
return res, nil
}
if len(buf) >= maxNumLen {
return nil, fmt.Errorf("bencode: number representation exceeds max length limit of %d bytes", maxNumLen)
}
if err == io.EOF {
return nil, io.ErrUnexpectedEOF
}
if err != nil {
return nil, err
}
return nil, fmt.Errorf("bencode: number representation exceeds max length limit of %d bytes", maxNumLen)
}
func validateStrictInteger(buf []byte) error {
if len(buf) == 0 {
return errors.New("bencode: invalid integer: empty value in strict mode")
}
s := string(buf)
if buf[0] == '+' {
return fmt.Errorf("bencode: invalid integer %q: positive sign not allowed in strict mode", s)
}
if buf[0] == '-' {
if len(buf) == 1 {
return fmt.Errorf("bencode: invalid integer %q: missing digits after minus sign in strict mode", s)
}
if buf[1] == '0' {
return fmt.Errorf("bencode: invalid integer %q: negative zero not allowed in strict mode", s)
}
for _, b := range buf[1:] {
if b < '0' || b > '9' {
return fmt.Errorf("bencode: invalid integer %q: non-digit character in strict mode", s)
}
}
return nil
}
if buf[0] == '0' && len(buf) > 1 {
return fmt.Errorf("bencode: invalid integer %q: leading zeros not allowed in strict mode", s)
}
for _, b := range buf {
if b < '0' || b > '9' {
return fmt.Errorf("bencode: invalid integer %q: non-digit character in strict mode", s)
}
}
return nil
}
func validateStrictStringLength(buf []byte) error {
if len(buf) == 0 {
return errors.New("bencode: invalid string length: empty value in strict mode")
}
s := string(buf)
if buf[0] == '+' || buf[0] == '-' {
return fmt.Errorf("bencode: invalid string length %q: sign not allowed in strict mode", s)
}
if buf[0] == '0' && len(buf) > 1 {
return fmt.Errorf("bencode: invalid string length %q: leading zeros not allowed in strict mode", s)
}
for _, b := range buf {
if b < '0' || b > '9' {
return fmt.Errorf("bencode: invalid string length %q: non-digit character in strict mode", s)
}
}
return nil
}