forked from quic-go/quic-go
/
public_header.go
242 lines (222 loc) · 7.66 KB
/
public_header.go
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
package wire
import (
"bytes"
"errors"
"fmt"
"io"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/qerr"
)
var (
errResetAndVersionFlagSet = errors.New("PublicHeader: Reset Flag and Version Flag should not be set at the same time")
errReceivedOmittedConnectionID = qerr.Error(qerr.InvalidPacketHeader, "receiving packets with omitted ConnectionID is not supported")
errInvalidConnectionID = qerr.Error(qerr.InvalidPacketHeader, "connection ID cannot be 0")
errGetLengthNotForVersionNegotiation = errors.New("PublicHeader: GetLength cannot be called for VersionNegotiation packets")
)
// writePublicHeader writes a Public Header.
func (h *Header) writePublicHeader(b *bytes.Buffer, pers protocol.Perspective, _ protocol.VersionNumber) error {
if h.VersionFlag && h.ResetFlag {
return errResetAndVersionFlagSet
}
publicFlagByte := uint8(0x00)
if h.VersionFlag {
publicFlagByte |= 0x01
}
if h.ResetFlag {
publicFlagByte |= 0x02
}
if !h.OmitConnectionID {
publicFlagByte |= 0x08
}
if len(h.DiversificationNonce) > 0 {
if len(h.DiversificationNonce) != 32 {
return errors.New("invalid diversification nonce length")
}
publicFlagByte |= 0x04
}
// only set PacketNumberLen bits if a packet number will be written
if h.hasPacketNumber(pers) {
switch h.PacketNumberLen {
case protocol.PacketNumberLen1:
publicFlagByte |= 0x00
case protocol.PacketNumberLen2:
publicFlagByte |= 0x10
case protocol.PacketNumberLen4:
publicFlagByte |= 0x20
case protocol.PacketNumberLen6:
publicFlagByte |= 0x30
}
}
b.WriteByte(publicFlagByte)
if !h.OmitConnectionID {
utils.BigEndian.WriteUint64(b, uint64(h.ConnectionID))
}
if h.VersionFlag && pers == protocol.PerspectiveClient {
utils.BigEndian.WriteUint32(b, uint32(h.Version))
}
if len(h.DiversificationNonce) > 0 {
b.Write(h.DiversificationNonce)
}
// if we're a server, and the VersionFlag is set, we must not include anything else in the packet
if !h.hasPacketNumber(pers) {
return nil
}
switch h.PacketNumberLen {
case protocol.PacketNumberLen1:
b.WriteByte(uint8(h.PacketNumber))
case protocol.PacketNumberLen2:
utils.BigEndian.WriteUint16(b, uint16(h.PacketNumber))
case protocol.PacketNumberLen4:
utils.BigEndian.WriteUint32(b, uint32(h.PacketNumber))
case protocol.PacketNumberLen6:
utils.BigEndian.WriteUint48(b, uint64(h.PacketNumber)&(1<<48-1))
default:
return errors.New("PublicHeader: PacketNumberLen not set")
}
return nil
}
// parsePublicHeader parses a QUIC packet's Public Header.
// The packetSentBy is the perspective of the peer that sent this PublicHeader, i.e. if we're the server, packetSentBy should be PerspectiveClient.
func parsePublicHeader(b *bytes.Reader, packetSentBy protocol.Perspective) (*Header, error) {
header := &Header{}
// First byte
publicFlagByte, err := b.ReadByte()
if err != nil {
return nil, err
}
header.ResetFlag = publicFlagByte&0x02 > 0
header.VersionFlag = publicFlagByte&0x01 > 0
// TODO: activate this check once Chrome sends the correct value
// see https://github.com/lucas-clemente/quic-go/issues/232
// if publicFlagByte&0x04 > 0 {
// return nil, errors.New("diversification nonces should only be sent by servers")
// }
header.OmitConnectionID = publicFlagByte&0x08 == 0
if header.OmitConnectionID && packetSentBy == protocol.PerspectiveClient {
return nil, errReceivedOmittedConnectionID
}
if header.hasPacketNumber(packetSentBy) {
switch publicFlagByte & 0x30 {
case 0x30:
header.PacketNumberLen = protocol.PacketNumberLen6
case 0x20:
header.PacketNumberLen = protocol.PacketNumberLen4
case 0x10:
header.PacketNumberLen = protocol.PacketNumberLen2
case 0x00:
header.PacketNumberLen = protocol.PacketNumberLen1
}
}
// Connection ID
if !header.OmitConnectionID {
var connID uint64
connID, err = utils.BigEndian.ReadUint64(b)
if err != nil {
return nil, err
}
header.ConnectionID = protocol.ConnectionID(connID)
if header.ConnectionID == 0 {
return nil, errInvalidConnectionID
}
}
if packetSentBy == protocol.PerspectiveServer && publicFlagByte&0x04 > 0 {
// TODO: remove the if once the Google servers send the correct value
// assume that a packet doesn't contain a diversification nonce if the version flag or the reset flag is set, no matter what the public flag says
// see https://github.com/lucas-clemente/quic-go/issues/232
if !header.VersionFlag && !header.ResetFlag {
header.DiversificationNonce = make([]byte, 32)
if _, err := io.ReadFull(b, header.DiversificationNonce); err != nil {
return nil, err
}
}
}
// Version (optional)
if !header.ResetFlag && header.VersionFlag {
if packetSentBy == protocol.PerspectiveServer { // parse the version negotiaton packet
if b.Len() == 0 {
return nil, qerr.Error(qerr.InvalidVersionNegotiationPacket, "empty version list")
}
if b.Len()%4 != 0 {
return nil, qerr.InvalidVersionNegotiationPacket
}
header.IsVersionNegotiation = true
header.SupportedVersions = make([]protocol.VersionNumber, 0)
for {
var versionTag uint32
versionTag, err = utils.BigEndian.ReadUint32(b)
if err != nil {
break
}
v := protocol.VersionNumber(versionTag)
header.SupportedVersions = append(header.SupportedVersions, v)
}
// a version negotiation packet doesn't have a packet number
return header, nil
}
// packet was sent by the client. Read the version number
var versionTag uint32
versionTag, err = utils.BigEndian.ReadUint32(b)
if err != nil {
return nil, err
}
header.Version = protocol.VersionNumber(versionTag)
}
// Packet number
if header.hasPacketNumber(packetSentBy) {
packetNumber, err := utils.BigEndian.ReadUintN(b, uint8(header.PacketNumberLen))
if err != nil {
return nil, err
}
header.PacketNumber = protocol.PacketNumber(packetNumber)
}
return header, nil
}
// getPublicHeaderLength gets the length of the publicHeader in bytes.
// It can only be called for regular packets.
func (h *Header) getPublicHeaderLength(pers protocol.Perspective) (protocol.ByteCount, error) {
if h.VersionFlag && h.ResetFlag {
return 0, errResetAndVersionFlagSet
}
if h.VersionFlag && pers == protocol.PerspectiveServer {
return 0, errGetLengthNotForVersionNegotiation
}
length := protocol.ByteCount(1) // 1 byte for public flags
if h.hasPacketNumber(pers) {
if h.PacketNumberLen != protocol.PacketNumberLen1 && h.PacketNumberLen != protocol.PacketNumberLen2 && h.PacketNumberLen != protocol.PacketNumberLen4 && h.PacketNumberLen != protocol.PacketNumberLen6 {
return 0, errPacketNumberLenNotSet
}
length += protocol.ByteCount(h.PacketNumberLen)
}
if !h.OmitConnectionID {
length += 8 // 8 bytes for the connection ID
}
// Version Number in packets sent by the client
if h.VersionFlag {
length += 4
}
length += protocol.ByteCount(len(h.DiversificationNonce))
return length, nil
}
// hasPacketNumber determines if this Public Header will contain a packet number
// this depends on the ResetFlag, the VersionFlag and who sent the packet
func (h *Header) hasPacketNumber(packetSentBy protocol.Perspective) bool {
if h.ResetFlag {
return false
}
if h.VersionFlag && packetSentBy == protocol.PerspectiveServer {
return false
}
return true
}
func (h *Header) logPublicHeader() {
connID := "(omitted)"
if !h.OmitConnectionID {
connID = fmt.Sprintf("%#x", h.ConnectionID)
}
ver := "(unset)"
if h.Version != 0 {
ver = fmt.Sprintf("%s", h.Version)
}
utils.Debugf(" Public Header{ConnectionID: %s, PacketNumber: %#x, PacketNumberLen: %d, Version: %s, DiversificationNonce: %#v}", connID, h.PacketNumber, h.PacketNumberLen, ver, h.DiversificationNonce)
}