-
Notifications
You must be signed in to change notification settings - Fork 0
/
quic_transport_parameters.go
157 lines (139 loc) · 5.89 KB
/
quic_transport_parameters.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
package clienthellod
import (
"bytes" // skipcq: GSC-G505
"errors"
"fmt"
"sort"
"github.com/refraction-networking/clienthellod/internal/utils"
"github.com/refraction-networking/utls/dicttls"
)
const (
QTP_GREASE = 27
UNSET_VLI_BITS = true // if false, unsetVLIBits() will be nop
)
// QUICTransportParameters is a struct to hold the parsed QUIC transport parameters
// as a combination.
type QUICTransportParameters struct {
MaxIdleTimeout utils.Uint8Arr `json:"max_idle_timeout,omitempty"`
MaxUDPPayloadSize utils.Uint8Arr `json:"max_udp_payload_size,omitempty"`
InitialMaxData utils.Uint8Arr `json:"initial_max_data,omitempty"`
InitialMaxStreamDataBidiLocal utils.Uint8Arr `json:"initial_max_stream_data_bidi_local,omitempty"`
InitialMaxStreamDataBidiRemote utils.Uint8Arr `json:"initial_max_stream_data_bidi_remote,omitempty"`
InitialMaxStreamDataUni utils.Uint8Arr `json:"initial_max_stream_data_uni,omitempty"`
InitialMaxStreamsBidi utils.Uint8Arr `json:"initial_max_streams_bidi,omitempty"`
InitialMaxStreamsUni utils.Uint8Arr `json:"initial_max_streams_uni,omitempty"`
AckDelayExponent utils.Uint8Arr `json:"ack_delay_exponent,omitempty"`
MaxAckDelay utils.Uint8Arr `json:"max_ack_delay,omitempty"`
ActiveConnectionIDLimit utils.Uint8Arr `json:"active_connection_id_limit,omitempty"`
QTPIDs []uint64 `json:"tpids,omitempty"` // sorted
HexID string `json:"hex_id,omitempty"`
NumID uint64 `json:"num_id,omitempty"`
parseError error
}
// ParseQUICTransportParameters parses the transport parameters from the extension data of
// TLS Extension "QUIC Transport Parameters" (57)
//
// If any error occurs, the returned struct will have parseError set to the error.
func ParseQUICTransportParameters(extData []byte) *QUICTransportParameters { // skipcq: GO-R1005
qtp := &QUICTransportParameters{
parseError: errors.New("unknown error"),
}
r := bytes.NewReader(extData)
var paramType uint64
var paramValLen uint64
var paramData []byte
var n int
for r.Len() > 0 {
paramType, _, qtp.parseError = ReadNextVLI(r)
if qtp.parseError != nil {
qtp.parseError = fmt.Errorf("failed to read transport parameter type: %w", qtp.parseError)
return qtp
}
paramValLen, _, qtp.parseError = ReadNextVLI(r)
if qtp.parseError != nil {
qtp.parseError = fmt.Errorf("failed to read transport parameter value length: %w", qtp.parseError)
return qtp
}
if IsGREASETransportParameter(paramType) {
qtp.QTPIDs = append(qtp.QTPIDs, QTP_GREASE) // replace with placeholder
} else {
qtp.QTPIDs = append(qtp.QTPIDs, paramType)
}
if paramValLen == 0 {
continue // skip empty transport parameter, no need to try to read
}
paramData = make([]byte, paramValLen)
n, qtp.parseError = r.Read(paramData)
if qtp.parseError != nil {
qtp.parseError = fmt.Errorf("failed to read transport parameter value: %w", qtp.parseError)
return qtp
}
if uint64(n) != paramValLen {
qtp.parseError = errors.New("corrupted transport parameter")
return qtp
}
switch paramType {
case dicttls.QUICTransportParameter_max_idle_timeout:
// qtp.MaxIdleTimeoutLength = uint32(paramValLen)
qtp.MaxIdleTimeout = paramData
unsetVLIBits(qtp.MaxIdleTimeout) // toggle the UNSET_VLI_BITS flag to control behavior
case dicttls.QUICTransportParameter_max_udp_payload_size:
// qtp.MaxUDPPayloadSizeLength = uint32(paramValLen)
qtp.MaxUDPPayloadSize = paramData
unsetVLIBits(qtp.MaxUDPPayloadSize)
case dicttls.QUICTransportParameter_initial_max_data:
// qtp.InitialMaxDataLength = uint32(paramValLen)
qtp.InitialMaxData = paramData
unsetVLIBits(qtp.InitialMaxData)
case dicttls.QUICTransportParameter_initial_max_stream_data_bidi_local:
// qtp.InitialMaxStreamDataBidiLocalLength = uint32(paramValLen)
qtp.InitialMaxStreamDataBidiLocal = paramData
unsetVLIBits(qtp.InitialMaxStreamDataBidiLocal)
case dicttls.QUICTransportParameter_initial_max_stream_data_bidi_remote:
// qtp.InitialMaxStreamDataBidiRemoteLength = uint32(paramValLen)
qtp.InitialMaxStreamDataBidiRemote = paramData
unsetVLIBits(qtp.InitialMaxStreamDataBidiRemote)
case dicttls.QUICTransportParameter_initial_max_stream_data_uni:
// qtp.InitialMaxStreamDataUniLength = uint32(paramValLen)
qtp.InitialMaxStreamDataUni = paramData
unsetVLIBits(qtp.InitialMaxStreamDataUni)
case dicttls.QUICTransportParameter_initial_max_streams_bidi:
// qtp.InitialMaxStreamsBidiLength = uint32(paramValLen)
qtp.InitialMaxStreamsBidi = paramData
unsetVLIBits(qtp.InitialMaxStreamsBidi)
case dicttls.QUICTransportParameter_initial_max_streams_uni:
// qtp.InitialMaxStreamsUniLength = uint32(paramValLen)
qtp.InitialMaxStreamsUni = paramData
unsetVLIBits(qtp.InitialMaxStreamsUni)
case dicttls.QUICTransportParameter_ack_delay_exponent:
// qtp.AckDelayExponentLength = uint32(paramValLen)
qtp.AckDelayExponent = paramData
unsetVLIBits(qtp.AckDelayExponent)
case dicttls.QUICTransportParameter_max_ack_delay:
// qtp.MaxAckDelayLength = uint32(paramValLen)
qtp.MaxAckDelay = paramData
unsetVLIBits(qtp.MaxAckDelay)
case dicttls.QUICTransportParameter_active_connection_id_limit:
// qtp.ActiveConnectionIDLimitLength = uint32(paramValLen)
qtp.ActiveConnectionIDLimit = paramData
unsetVLIBits(qtp.ActiveConnectionIDLimit)
}
// if IsGREASETransportParameter(paramType) {
// qtp.QTPIDs = append(qtp.QTPIDs, QTP_GREASE) // replace with placeholder
// } else {
// qtp.QTPIDs = append(qtp.QTPIDs, paramType)
// }
}
// sort QTPIDs
sort.Slice(qtp.QTPIDs, func(i, j int) bool {
return qtp.QTPIDs[i] < qtp.QTPIDs[j]
})
qtp.parseError = nil
qtp.NumID = qtp.calcNumericID()
qtp.HexID = FingerprintID(qtp.NumID).AsHex()
return qtp
}
// ParseError returns the error that occurred during parsing, if any.
func (qtp *QUICTransportParameters) ParseError() error {
return qtp.parseError
}