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