Skip to content

Commit ef6121f

Browse files
authored
Merge commit from fork
uint32
2 parents f0be51f + 241cec9 commit ef6121f

8 files changed

Lines changed: 289 additions & 20 deletions

File tree

connection.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,13 @@ type Connection struct {
224224
// NewConnection creates a new spdy connection from an existing
225225
// network connection.
226226
func NewConnection(conn net.Conn, server bool) (*Connection, error) {
227-
framer, framerErr := spdy.NewFramer(conn, conn)
227+
return NewConnectionWithOptions(conn, server)
228+
}
229+
230+
// NewConnectionWithOptions creates a new spdy connection and applies frame
231+
// parsing limits via options.
232+
func NewConnectionWithOptions(conn net.Conn, server bool, opts ...spdy.FramerOption) (*Connection, error) {
233+
framer, framerErr := spdy.NewFramerWithOptions(conn, conn, opts...)
228234
if framerErr != nil {
229235
return nil, framerErr
230236
}
@@ -350,6 +356,9 @@ Loop:
350356
} else {
351357
debugMessage("(%p) EOF received", s)
352358
}
359+
if spdyErr, ok := err.(*spdy.Error); ok && spdyErr.Err == spdy.InvalidControlFrame {
360+
_ = s.conn.Close()
361+
}
353362
break
354363
}
355364
var priority uint8

spdy/options.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package spdy
2+
3+
// FramerOption allows callers to customize frame parsing limits.
4+
type FramerOption func(*Framer)
5+
6+
// WithMaxControlFramePayloadSize sets the control-frame payload limit.
7+
func WithMaxControlFramePayloadSize(size uint32) FramerOption {
8+
return func(f *Framer) {
9+
f.maxFrameLength = size
10+
}
11+
}
12+
13+
// WithMaxHeaderFieldSize sets the per-header name/value size limit.
14+
func WithMaxHeaderFieldSize(size uint32) FramerOption {
15+
return func(f *Framer) {
16+
f.maxHeaderFieldSize = size
17+
}
18+
}
19+
20+
// WithMaxHeaderCount sets the maximum number of headers in a frame.
21+
func WithMaxHeaderCount(count uint32) FramerOption {
22+
return func(f *Framer) {
23+
f.maxHeaderCount = count
24+
}
25+
}

spdy/options_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package spdy
2+
3+
import (
4+
"net"
5+
"testing"
6+
)
7+
8+
func TestNewFramerWithOptions(t *testing.T) {
9+
serverConn, clientConn := net.Pipe()
10+
defer func() {
11+
_ = clientConn.Close()
12+
_ = serverConn.Close()
13+
}()
14+
15+
framer, err := NewFramerWithOptions(clientConn, clientConn,
16+
WithMaxControlFramePayloadSize(1024),
17+
WithMaxHeaderFieldSize(128),
18+
WithMaxHeaderCount(16),
19+
)
20+
if err != nil {
21+
t.Fatalf("Error creating spdy connection with options: %s", err)
22+
}
23+
24+
if got := framer.maxFrameLength; got != 1024 {
25+
t.Fatalf("Unexpected MaxControlFramePayloadSize: %d", got)
26+
}
27+
if got := framer.maxHeaderFieldSize; got != 128 {
28+
t.Fatalf("Unexpected MaxHeaderFieldSize: %d", got)
29+
}
30+
if got := framer.maxHeaderCount; got != 16 {
31+
t.Fatalf("Unexpected MaxHeaderCount: %d", got)
32+
}
33+
}

spdy/read.go

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ func (frame *SettingsFrame) read(h ControlFrameHeader, f *Framer) error {
4343
if err := binary.Read(f.r, binary.BigEndian, &numSettings); err != nil {
4444
return err
4545
}
46+
// Each setting is 8 bytes (4-byte id + 4-byte value).
47+
// Payload is 4 bytes for numSettings + numSettings*8.
48+
if h.length < 4 || numSettings > (h.length-4)/8 {
49+
return &Error{InvalidControlFrame, 0}
50+
}
4651
frame.FlagIdValues = make([]SettingsFlagIdValue, numSettings)
4752
for i := uint32(0); i < numSettings; i++ {
4853
if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Id); err != nil {
@@ -161,8 +166,19 @@ func (f *Framer) parseControlFrame(version uint16, frameType ControlFrameType) (
161166
if err := binary.Read(f.r, binary.BigEndian, &length); err != nil {
162167
return nil, err
163168
}
169+
maxControlFramePayload := uint32(MaxDataLength)
170+
if f.maxFrameLength > 0 {
171+
maxControlFramePayload = f.maxFrameLength
172+
}
173+
164174
flags := ControlFlags((length & 0xff000000) >> 24)
165175
length &= 0xffffff
176+
if length > maxControlFramePayload {
177+
if _, err := io.CopyN(io.Discard, f.r, int64(length)); err != nil {
178+
return nil, err
179+
}
180+
return nil, &Error{InvalidControlFrame, 0}
181+
}
166182
header := ControlFrameHeader{version, frameType, flags, length}
167183
cframe, err := newControlFrame(frameType)
168184
if err != nil {
@@ -174,18 +190,32 @@ func (f *Framer) parseControlFrame(version uint16, frameType ControlFrameType) (
174190
return cframe, nil
175191
}
176192

177-
func parseHeaderValueBlock(r io.Reader, streamId StreamId) (http.Header, error) {
193+
func (f *Framer) parseHeaderValueBlock(r io.Reader, streamId StreamId) (http.Header, error) {
178194
var numHeaders uint32
179195
if err := binary.Read(r, binary.BigEndian, &numHeaders); err != nil {
180196
return nil, err
181197
}
198+
maxHeaders := defaultMaxHeaderCount
199+
if f.maxHeaderCount > 0 {
200+
maxHeaders = f.maxHeaderCount
201+
}
202+
if numHeaders > maxHeaders {
203+
return nil, &Error{InvalidControlFrame, streamId}
204+
}
205+
maxFieldSize := defaultMaxHeaderFieldSize
206+
if f.maxHeaderFieldSize > 0 {
207+
maxFieldSize = f.maxHeaderFieldSize
208+
}
182209
var e error
183210
h := make(http.Header, int(numHeaders))
184211
for i := 0; i < int(numHeaders); i++ {
185212
var length uint32
186213
if err := binary.Read(r, binary.BigEndian, &length); err != nil {
187214
return nil, err
188215
}
216+
if length > maxFieldSize {
217+
return nil, &Error{InvalidControlFrame, streamId}
218+
}
189219
nameBytes := make([]byte, length)
190220
if _, err := io.ReadFull(r, nameBytes); err != nil {
191221
return nil, err
@@ -201,6 +231,9 @@ func parseHeaderValueBlock(r io.Reader, streamId StreamId) (http.Header, error)
201231
if err := binary.Read(r, binary.BigEndian, &length); err != nil {
202232
return nil, err
203233
}
234+
if length > maxFieldSize {
235+
return nil, &Error{InvalidControlFrame, streamId}
236+
}
204237
value := make([]byte, length)
205238
if _, err := io.ReadFull(r, value); err != nil {
206239
return nil, err
@@ -240,7 +273,7 @@ func (f *Framer) readSynStreamFrame(h ControlFrameHeader, frame *SynStreamFrame)
240273
}
241274
reader = f.headerDecompressor
242275
}
243-
frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId)
276+
frame.Headers, err = f.parseHeaderValueBlock(reader, frame.StreamId)
244277
if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) {
245278
err = &Error{WrongCompressedPayloadSize, 0}
246279
}
@@ -272,7 +305,7 @@ func (f *Framer) readSynReplyFrame(h ControlFrameHeader, frame *SynReplyFrame) e
272305
}
273306
reader = f.headerDecompressor
274307
}
275-
frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId)
308+
frame.Headers, err = f.parseHeaderValueBlock(reader, frame.StreamId)
276309
if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) {
277310
err = &Error{WrongCompressedPayloadSize, 0}
278311
}
@@ -304,7 +337,7 @@ func (f *Framer) readHeadersFrame(h ControlFrameHeader, frame *HeadersFrame) err
304337
}
305338
reader = f.headerDecompressor
306339
}
307-
frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId)
340+
frame.Headers, err = f.parseHeaderValueBlock(reader, frame.StreamId)
308341
if !f.headerCompressionDisabled && (err == io.EOF && f.headerReader.N == 0 || f.headerReader.N != 0) {
309342
err = &Error{WrongCompressedPayloadSize, 0}
310343
}

spdy/spdy_test.go

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ func TestHeaderParsing(t *testing.T) {
2727
var headerValueBlockBuf bytes.Buffer
2828
writeHeaderValueBlock(&headerValueBlockBuf, HeadersFixture)
2929
const bogusStreamId = 1
30-
newHeaders, err := parseHeaderValueBlock(&headerValueBlockBuf, bogusStreamId)
30+
f := &Framer{}
31+
newHeaders, err := f.parseHeaderValueBlock(&headerValueBlockBuf, bogusStreamId)
3132
if err != nil {
3233
t.Fatal("parseHeaderValueBlock:", err)
3334
}
@@ -223,6 +224,87 @@ func TestCreateParseSettings(t *testing.T) {
223224
}
224225
}
225226

227+
func TestReadFrameRejectsOversizedControlFrame(t *testing.T) {
228+
buffer := new(bytes.Buffer)
229+
framer, err := NewFramer(buffer, buffer)
230+
if err != nil {
231+
t.Fatal("Failed to create new framer:", err)
232+
}
233+
framer.maxFrameLength = 1
234+
235+
// Control frame header (version 3, SETTINGS, length = 2)
236+
_, _ = buffer.Write([]byte{
237+
0x80, 0x03, // control frame + version
238+
0x00, 0x04, // SETTINGS
239+
0x00, // flags
240+
0x00, 0x00, 0x02, // length = 2
241+
0x00, 0x01, // payload (2 bytes)
242+
})
243+
244+
_, err = framer.ReadFrame()
245+
if err == nil {
246+
t.Fatal("expected error for oversized control frame")
247+
}
248+
}
249+
250+
func TestReadFrameRejectsSettingsLengthMismatch(t *testing.T) {
251+
buffer := new(bytes.Buffer)
252+
framer, err := NewFramer(buffer, buffer)
253+
if err != nil {
254+
t.Fatal("Failed to create new framer:", err)
255+
}
256+
257+
// SETTINGS frame with declared payload length 0, but includes numSettings.
258+
_, _ = buffer.Write([]byte{
259+
0x80, 0x03, // control frame + version
260+
0x00, 0x04, // SETTINGS
261+
0x00, // flags
262+
0x00, 0x00, 0x00, // length = 0
263+
0x00, 0x00, 0x00, 0x01, // numSettings = 1
264+
})
265+
266+
_, err = framer.ReadFrame()
267+
if err == nil {
268+
t.Fatal("expected error for SETTINGS length mismatch")
269+
}
270+
}
271+
272+
func TestParseHeaderValueBlockRejectsHeaderCountOverLimit(t *testing.T) {
273+
buffer := bytes.NewBuffer([]byte{
274+
0x00, 0x00, 0x00, 0x02, // numHeaders = 2
275+
})
276+
277+
f := &Framer{maxHeaderCount: 1}
278+
_, err := f.parseHeaderValueBlock(buffer, 1)
279+
if err == nil {
280+
t.Fatal("expected error for excessive header count")
281+
}
282+
spdyErr, ok := err.(*Error)
283+
if !ok || spdyErr.Err != InvalidControlFrame {
284+
t.Fatalf("unexpected error: %v", err)
285+
}
286+
}
287+
288+
func TestParseHeaderValueBlockRejectsHeaderFieldSizeOverLimit(t *testing.T) {
289+
buffer := bytes.NewBuffer([]byte{
290+
0x00, 0x00, 0x00, 0x01, // numHeaders = 1
291+
0x00, 0x00, 0x00, 0x02, // name length = 2
292+
0x61, 0x62, // name = "ab"
293+
0x00, 0x00, 0x00, 0x01, // value length = 1
294+
0x63, // value = "c"
295+
})
296+
297+
f := &Framer{maxHeaderFieldSize: 1}
298+
_, err := f.parseHeaderValueBlock(buffer, 1)
299+
if err == nil {
300+
t.Fatal("expected error for excessive header field size")
301+
}
302+
spdyErr, ok := err.(*Error)
303+
if !ok || spdyErr.Err != InvalidControlFrame {
304+
t.Fatalf("unexpected error: %v", err)
305+
}
306+
}
307+
226308
func TestCreateParsePing(t *testing.T) {
227309
buffer := new(bytes.Buffer)
228310
framer, err := NewFramer(buffer, buffer)

spdy/types.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,20 @@ const (
4949
)
5050

5151
// MaxDataLength is the maximum number of bytes that can be stored in one frame.
52+
//
53+
// SPDY frame headers encode the payload length using a 24-bit field,
54+
// so the maximum representable size for both data and control frames
55+
// is 2^24-1 bytes.
56+
//
57+
// See the SPDY/3 specification, "Frame Format":
58+
// https://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3-1/
5259
const MaxDataLength = 1<<24 - 1
5360

61+
const (
62+
defaultMaxHeaderFieldSize uint32 = 1 << 20
63+
defaultMaxHeaderCount uint32 = 1000
64+
)
65+
5466
// headerValueSepator separates multiple header values.
5567
const headerValueSeparator = "\x00"
5668

@@ -255,13 +267,27 @@ type Framer struct {
255267
r io.Reader
256268
headerReader io.LimitedReader
257269
headerDecompressor io.ReadCloser
270+
271+
maxFrameLength uint32 // overrides the default frame payload length limit.
272+
maxHeaderFieldSize uint32 // overrides the default per-header name/value length limit.
273+
maxHeaderCount uint32 // overrides the default header count limit.
258274
}
259275

260276
// NewFramer allocates a new Framer for a given SPDY connection, represented by
261277
// a io.Writer and io.Reader. Note that Framer will read and write individual fields
262278
// from/to the Reader and Writer, so the caller should pass in an appropriately
263279
// buffered implementation to optimize performance.
264280
func NewFramer(w io.Writer, r io.Reader) (*Framer, error) {
281+
return newFramer(w, r)
282+
}
283+
284+
// NewFramerWithOptions allocates a new Framer for a given SPDY connection and
285+
// applies frame parsing limits via options.
286+
func NewFramerWithOptions(w io.Writer, r io.Reader, opts ...FramerOption) (*Framer, error) {
287+
return newFramer(w, r, opts...)
288+
}
289+
290+
func newFramer(w io.Writer, r io.Reader, opts ...FramerOption) (*Framer, error) {
265291
compressBuf := new(bytes.Buffer)
266292
compressor, err := zlib.NewWriterLevelDict(compressBuf, zlib.BestCompression, []byte(headerDictionary))
267293
if err != nil {
@@ -273,5 +299,10 @@ func NewFramer(w io.Writer, r io.Reader) (*Framer, error) {
273299
headerCompressor: compressor,
274300
r: r,
275301
}
302+
for _, opt := range opts {
303+
if opt != nil {
304+
opt(framer)
305+
}
306+
}
276307
return framer, nil
277308
}

0 commit comments

Comments
 (0)