1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/quic/masque/masque_encapsulated_client_session.h"
6
7 #include <cstdint>
8 #include <string>
9
10 #include "absl/strings/string_view.h"
11 #include "quiche/quic/core/crypto/quic_crypto_client_config.h"
12 #include "quiche/quic/core/frames/quic_connection_close_frame.h"
13 #include "quiche/quic/core/http/quic_spdy_client_session.h"
14 #include "quiche/quic/core/quic_config.h"
15 #include "quiche/quic/core/quic_connection.h"
16 #include "quiche/quic/core/quic_error_codes.h"
17 #include "quiche/quic/core/quic_packets.h"
18 #include "quiche/quic/core/quic_server_id.h"
19 #include "quiche/quic/core/quic_time.h"
20 #include "quiche/quic/core/quic_types.h"
21 #include "quiche/quic/core/quic_versions.h"
22 #include "quiche/quic/masque/masque_client_session.h"
23 #include "quiche/quic/platform/api/quic_logging.h"
24 #include "quiche/quic/platform/api/quic_socket_address.h"
25 #include "quiche/common/capsule.h"
26 #include "quiche/common/platform/api/quiche_logging.h"
27 #include "quiche/common/quiche_data_reader.h"
28 #include "quiche/common/quiche_ip_address.h"
29 #include "quiche/common/quiche_text_utils.h"
30
31 namespace quic {
32
33 using ::quiche::AddressAssignCapsule;
34 using ::quiche::AddressRequestCapsule;
35 using ::quiche::RouteAdvertisementCapsule;
36
MasqueEncapsulatedClientSession(const QuicConfig & config,const ParsedQuicVersionVector & supported_versions,QuicConnection * connection,const QuicServerId & server_id,QuicCryptoClientConfig * crypto_config,MasqueClientSession * masque_client_session)37 MasqueEncapsulatedClientSession::MasqueEncapsulatedClientSession(
38 const QuicConfig& config, const ParsedQuicVersionVector& supported_versions,
39 QuicConnection* connection, const QuicServerId& server_id,
40 QuicCryptoClientConfig* crypto_config,
41 MasqueClientSession* masque_client_session)
42 : QuicSpdyClientSession(config, supported_versions, connection, server_id,
43 crypto_config),
44 masque_client_session_(masque_client_session) {}
45
ProcessPacket(absl::string_view packet,QuicSocketAddress server_address)46 void MasqueEncapsulatedClientSession::ProcessPacket(
47 absl::string_view packet, QuicSocketAddress server_address) {
48 QuicTime now = connection()->clock()->ApproximateNow();
49 QuicReceivedPacket received_packet(packet.data(), packet.length(), now);
50 connection()->ProcessUdpPacket(connection()->self_address(), server_address,
51 received_packet);
52 }
53
CloseConnection(QuicErrorCode error,const std::string & details,ConnectionCloseBehavior connection_close_behavior)54 void MasqueEncapsulatedClientSession::CloseConnection(
55 QuicErrorCode error, const std::string& details,
56 ConnectionCloseBehavior connection_close_behavior) {
57 connection()->CloseConnection(error, details, connection_close_behavior);
58 }
59
OnConnectionClosed(const QuicConnectionCloseFrame & frame,ConnectionCloseSource source)60 void MasqueEncapsulatedClientSession::OnConnectionClosed(
61 const QuicConnectionCloseFrame& frame, ConnectionCloseSource source) {
62 QuicSpdyClientSession::OnConnectionClosed(frame, source);
63 masque_client_session_->CloseConnectUdpStream(this);
64 }
65
ProcessIpPacket(absl::string_view packet)66 void MasqueEncapsulatedClientSession::ProcessIpPacket(
67 absl::string_view packet) {
68 quiche::QuicheDataReader reader(packet);
69 uint8_t first_byte;
70 if (!reader.ReadUInt8(&first_byte)) {
71 QUIC_DLOG(ERROR) << "Dropping empty CONNECT-IP packet";
72 return;
73 }
74 const uint8_t ip_version = first_byte >> 4;
75 quiche::QuicheIpAddress server_ip;
76 if (ip_version == 6) {
77 if (!reader.Seek(5)) {
78 QUICHE_DLOG(ERROR) << "Failed to seek CONNECT-IP IPv6 start"
79 << "\n"
80 << quiche::QuicheTextUtils::HexDump(packet);
81 return;
82 }
83 uint8_t next_header = 0;
84 if (!reader.ReadUInt8(&next_header)) {
85 QUICHE_DLOG(ERROR) << "Failed to read CONNECT-IP next header"
86 << "\n"
87 << quiche::QuicheTextUtils::HexDump(packet);
88 return;
89 }
90 if (next_header != 17) {
91 // Note that this drops packets with IPv6 extension headers, since we
92 // do not expect to see them in practice.
93 QUIC_DLOG(ERROR)
94 << "Dropping CONNECT-IP packet with unexpected next header "
95 << static_cast<int>(next_header) << "\n"
96 << quiche::QuicheTextUtils::HexDump(packet);
97 return;
98 }
99 if (!reader.Seek(1)) {
100 QUICHE_DLOG(ERROR) << "Failed to seek CONNECT-IP hop limit"
101 << "\n"
102 << quiche::QuicheTextUtils::HexDump(packet);
103 return;
104 }
105 absl::string_view source_ip;
106 if (!reader.ReadStringPiece(&source_ip, 16)) {
107 QUICHE_DLOG(ERROR) << "Failed to read CONNECT-IP source IPv6"
108 << "\n"
109 << quiche::QuicheTextUtils::HexDump(packet);
110 return;
111 }
112 server_ip.FromPackedString(source_ip.data(), source_ip.length());
113 if (!reader.Seek(16)) {
114 QUICHE_DLOG(ERROR) << "Failed to seek CONNECT-IP destination IPv6"
115 << "\n"
116 << quiche::QuicheTextUtils::HexDump(packet);
117 return;
118 }
119 } else if (ip_version == 4) {
120 uint8_t ihl = first_byte & 0xF;
121 if (ihl < 5) {
122 QUICHE_DLOG(ERROR) << "Dropping CONNECT-IP packet with invalid IHL "
123 << static_cast<int>(ihl) << "\n"
124 << quiche::QuicheTextUtils::HexDump(packet);
125 return;
126 }
127 if (!reader.Seek(8)) {
128 QUICHE_DLOG(ERROR) << "Failed to seek CONNECT-IP IPv4 start"
129 << "\n"
130 << quiche::QuicheTextUtils::HexDump(packet);
131 return;
132 }
133 uint8_t ip_proto = 0;
134 if (!reader.ReadUInt8(&ip_proto)) {
135 QUICHE_DLOG(ERROR) << "Failed to read CONNECT-IP ip_proto"
136 << "\n"
137 << quiche::QuicheTextUtils::HexDump(packet);
138 return;
139 }
140 if (ip_proto != 17) {
141 QUIC_DLOG(ERROR) << "Dropping CONNECT-IP packet with unexpected IP proto "
142 << static_cast<int>(ip_proto) << "\n"
143 << quiche::QuicheTextUtils::HexDump(packet);
144 return;
145 }
146 if (!reader.Seek(2)) {
147 QUICHE_DLOG(ERROR) << "Failed to seek CONNECT-IP IP checksum"
148 << "\n"
149 << quiche::QuicheTextUtils::HexDump(packet);
150 return;
151 }
152 absl::string_view source_ip;
153 if (!reader.ReadStringPiece(&source_ip, 4)) {
154 QUICHE_DLOG(ERROR) << "Failed to read CONNECT-IP source IPv4"
155 << "\n"
156 << quiche::QuicheTextUtils::HexDump(packet);
157 return;
158 }
159 server_ip.FromPackedString(source_ip.data(), source_ip.length());
160 if (!reader.Seek(4)) {
161 QUICHE_DLOG(ERROR) << "Failed to seek CONNECT-IP destination IPv4"
162 << "\n"
163 << quiche::QuicheTextUtils::HexDump(packet);
164 return;
165 }
166 uint8_t ip_options_length = (ihl - 5) * 4;
167 if (!reader.Seek(ip_options_length)) {
168 QUICHE_DLOG(ERROR) << "Failed to seek CONNECT-IP IP options of length "
169 << static_cast<int>(ip_options_length) << "\n"
170 << quiche::QuicheTextUtils::HexDump(packet);
171 return;
172 }
173 } else {
174 QUIC_DLOG(ERROR) << "Dropping CONNECT-IP packet with unexpected IP version "
175 << static_cast<int>(ip_version) << "\n"
176 << quiche::QuicheTextUtils::HexDump(packet);
177 return;
178 }
179 // Parse UDP header.
180 uint16_t server_port;
181 if (!reader.ReadUInt16(&server_port)) {
182 QUICHE_DLOG(ERROR) << "Failed to read CONNECT-IP source port"
183 << "\n"
184 << quiche::QuicheTextUtils::HexDump(packet);
185 return;
186 }
187 if (!reader.Seek(2)) {
188 QUICHE_DLOG(ERROR) << "Failed to seek CONNECT-IP destination port"
189 << "\n"
190 << quiche::QuicheTextUtils::HexDump(packet);
191 return;
192 }
193 uint16_t udp_length;
194 if (!reader.ReadUInt16(&udp_length)) {
195 QUICHE_DLOG(ERROR) << "Failed to read CONNECT-IP UDP length"
196 << "\n"
197 << quiche::QuicheTextUtils::HexDump(packet);
198 return;
199 }
200 if (udp_length < 8) {
201 QUICHE_DLOG(ERROR) << "Dropping CONNECT-IP packet with invalid UDP length "
202 << udp_length << "\n"
203 << quiche::QuicheTextUtils::HexDump(packet);
204 return;
205 }
206 if (!reader.Seek(2)) {
207 QUICHE_DLOG(ERROR) << "Failed to seek CONNECT-IP UDP checksum"
208 << "\n"
209 << quiche::QuicheTextUtils::HexDump(packet);
210 return;
211 }
212 absl::string_view quic_packet;
213 if (!reader.ReadStringPiece(&quic_packet, udp_length - 8)) {
214 QUICHE_DLOG(ERROR) << "Failed to read CONNECT-IP UDP payload"
215 << "\n"
216 << quiche::QuicheTextUtils::HexDump(packet);
217 return;
218 }
219 if (!reader.IsDoneReading()) {
220 QUICHE_DLOG(INFO) << "Received CONNECT-IP UDP packet with "
221 << reader.BytesRemaining()
222 << " extra bytes after payload\n"
223 << quiche::QuicheTextUtils::HexDump(packet);
224 }
225 QUIC_DLOG(INFO) << "Received CONNECT-IP encapsulated packet of length "
226 << quic_packet.size();
227 QuicTime now = connection()->clock()->ApproximateNow();
228 QuicReceivedPacket received_packet(quic_packet.data(), quic_packet.size(),
229 now);
230 QuicSocketAddress server_address = QuicSocketAddress(server_ip, server_port);
231 connection()->ProcessUdpPacket(connection()->self_address(), server_address,
232 received_packet);
233 }
234
CloseIpSession(const std::string & details)235 void MasqueEncapsulatedClientSession::CloseIpSession(
236 const std::string& details) {
237 connection()->CloseConnection(QUIC_CONNECTION_CANCELLED, details,
238 ConnectionCloseBehavior::SILENT_CLOSE);
239 }
240
OnAddressAssignCapsule(const AddressAssignCapsule & capsule)241 bool MasqueEncapsulatedClientSession::OnAddressAssignCapsule(
242 const AddressAssignCapsule& capsule) {
243 QUIC_DLOG(INFO) << "Received capsule " << capsule.ToString();
244 for (auto assigned_address : capsule.assigned_addresses) {
245 if (assigned_address.ip_prefix.address().IsIPv4() &&
246 !local_v4_address_.IsInitialized()) {
247 QUIC_LOG(INFO)
248 << "MasqueEncapsulatedClientSession saving local IPv4 address "
249 << assigned_address.ip_prefix.address();
250 local_v4_address_ = assigned_address.ip_prefix.address();
251 } else if (assigned_address.ip_prefix.address().IsIPv6() &&
252 !local_v6_address_.IsInitialized()) {
253 QUIC_LOG(INFO)
254 << "MasqueEncapsulatedClientSession saving local IPv6 address "
255 << assigned_address.ip_prefix.address();
256 local_v6_address_ = assigned_address.ip_prefix.address();
257 }
258 }
259 return true;
260 }
261
OnAddressRequestCapsule(const AddressRequestCapsule & capsule)262 bool MasqueEncapsulatedClientSession::OnAddressRequestCapsule(
263 const AddressRequestCapsule& capsule) {
264 QUIC_DLOG(INFO) << "Ignoring received capsule " << capsule.ToString();
265 return true;
266 }
267
OnRouteAdvertisementCapsule(const RouteAdvertisementCapsule & capsule)268 bool MasqueEncapsulatedClientSession::OnRouteAdvertisementCapsule(
269 const RouteAdvertisementCapsule& capsule) {
270 QUIC_DLOG(INFO) << "Ignoring received capsule " << capsule.ToString();
271 return true;
272 }
273
274 } // namespace quic
275