Line data Source code
1 : #include "source/extensions/transport_sockets/http_11_proxy/connect.h"
2 :
3 : #include <sstream>
4 :
5 : #include "envoy/network/transport_socket.h"
6 :
7 : #include "source/common/buffer/buffer_impl.h"
8 : #include "source/common/common/scalar_to_byte_vector.h"
9 : #include "source/common/common/utility.h"
10 : #include "source/common/network/address_impl.h"
11 : #include "source/common/runtime/runtime_features.h"
12 :
13 : namespace Envoy {
14 : namespace Extensions {
15 : namespace TransportSockets {
16 : namespace Http11Connect {
17 :
18 : bool UpstreamHttp11ConnectSocket::isValidConnectResponse(absl::string_view response_payload,
19 : bool& headers_complete,
20 0 : size_t& bytes_processed) {
21 0 : SelfContainedParser parser;
22 :
23 0 : bytes_processed = parser.parser().execute(response_payload.data(), response_payload.length());
24 0 : headers_complete = parser.headersComplete();
25 :
26 0 : return parser.parser().getStatus() != Http::Http1::ParserStatus::Error &&
27 0 : parser.headersComplete() && parser.parser().statusCode() == Http::Code::OK;
28 0 : }
29 :
30 : UpstreamHttp11ConnectSocket::UpstreamHttp11ConnectSocket(
31 : Network::TransportSocketPtr&& transport_socket,
32 : Network::TransportSocketOptionsConstSharedPtr options)
33 0 : : PassthroughSocket(std::move(transport_socket)), options_(options) {
34 0 : if (options_ && options_->http11ProxyInfo() && transport_socket_->ssl()) {
35 0 : header_buffer_.add(
36 0 : absl::StrCat("CONNECT ", options_->http11ProxyInfo()->hostname, ":443 HTTP/1.1\r\n\r\n"));
37 0 : need_to_strip_connect_response_ = true;
38 0 : }
39 0 : }
40 :
41 : void UpstreamHttp11ConnectSocket::setTransportSocketCallbacks(
42 0 : Network::TransportSocketCallbacks& callbacks) {
43 0 : transport_socket_->setTransportSocketCallbacks(callbacks);
44 0 : callbacks_ = &callbacks;
45 0 : }
46 :
47 0 : Network::IoResult UpstreamHttp11ConnectSocket::doWrite(Buffer::Instance& buffer, bool end_stream) {
48 0 : if (header_buffer_.length() > 0) {
49 0 : return writeHeader();
50 0 : }
51 0 : if (!need_to_strip_connect_response_) {
52 : // Don't pass events up until the connect response is read because TLS reads
53 : // kick off writes which don't pass through the transport socket.
54 0 : return transport_socket_->doWrite(buffer, end_stream);
55 0 : }
56 0 : return Network::IoResult{Network::PostIoAction::KeepOpen, 0, false};
57 0 : }
58 :
59 0 : Network::IoResult UpstreamHttp11ConnectSocket::doRead(Buffer::Instance& buffer) {
60 0 : if (need_to_strip_connect_response_) {
61 : // Limit the CONNECT response headers to an arbitrary 2000 bytes.
62 0 : constexpr uint32_t MAX_RESPONSE_HEADER_SIZE = 2000;
63 0 : char peek_buf[MAX_RESPONSE_HEADER_SIZE];
64 0 : Api::IoCallUint64Result result =
65 0 : callbacks_->ioHandle().recv(peek_buf, MAX_RESPONSE_HEADER_SIZE, MSG_PEEK);
66 0 : if (!result.ok() && result.err_->getErrorCode() != Api::IoError::IoErrorCode::Again) {
67 0 : return {Network::PostIoAction::Close, 0, false};
68 0 : }
69 0 : absl::string_view peek_data(peek_buf, result.return_value_);
70 0 : size_t bytes_processed = 0;
71 0 : bool headers_complete = false;
72 0 : bool is_valid_connect_response =
73 0 : isValidConnectResponse(peek_data, headers_complete, bytes_processed);
74 :
75 0 : if (!headers_complete) {
76 0 : if (peek_data.size() == MAX_RESPONSE_HEADER_SIZE) {
77 0 : ENVOY_CONN_LOG(trace, "failed to receive CONNECT headers within {} bytes",
78 0 : callbacks_->connection(), MAX_RESPONSE_HEADER_SIZE);
79 0 : return {Network::PostIoAction::Close, 0, false};
80 0 : }
81 0 : ENVOY_CONN_LOG(trace, "Incomplete CONNECT header: {} bytes received",
82 0 : callbacks_->connection(), peek_data.size());
83 0 : return Network::IoResult{Network::PostIoAction::KeepOpen, 0, false};
84 0 : }
85 0 : if (!is_valid_connect_response) {
86 0 : ENVOY_CONN_LOG(trace, "Response does not appear to be a successful CONNECT upgrade",
87 0 : callbacks_->connection());
88 0 : return {Network::PostIoAction::Close, 0, false};
89 0 : }
90 :
91 0 : result = callbacks_->ioHandle().read(buffer, bytes_processed);
92 0 : if (!result.ok() || result.return_value_ != bytes_processed) {
93 0 : ENVOY_CONN_LOG(trace, "failed to drain CONNECT header", callbacks_->connection());
94 0 : return {Network::PostIoAction::Close, 0, false};
95 0 : }
96 0 : buffer.drain(bytes_processed);
97 :
98 0 : ENVOY_CONN_LOG(trace, "Successfully stripped {} bytes of CONNECT header",
99 0 : callbacks_->connection(), bytes_processed);
100 0 : need_to_strip_connect_response_ = false;
101 0 : }
102 0 : return transport_socket_->doRead(buffer);
103 0 : }
104 :
105 0 : Network::IoResult UpstreamHttp11ConnectSocket::writeHeader() {
106 0 : Network::PostIoAction action = Network::PostIoAction::KeepOpen;
107 0 : uint64_t bytes_written = 0;
108 0 : do {
109 0 : if (header_buffer_.length() == 0) {
110 0 : break;
111 0 : }
112 :
113 0 : Api::IoCallUint64Result result = callbacks_->ioHandle().write(header_buffer_);
114 :
115 0 : if (!result.ok()) {
116 0 : ENVOY_CONN_LOG(trace, "Failed writing CONNECT header. write error: {}",
117 0 : callbacks_->connection(), result.err_->getErrorDetails());
118 0 : if (result.err_->getErrorCode() != Api::IoError::IoErrorCode::Again) {
119 0 : action = Network::PostIoAction::Close;
120 0 : }
121 0 : break;
122 0 : }
123 0 : ENVOY_CONN_LOG(trace, "Writing CONNECT header. write returned: {}", callbacks_->connection(),
124 0 : result.return_value_);
125 0 : bytes_written += result.return_value_;
126 0 : } while (true);
127 :
128 0 : return {action, bytes_written, false};
129 0 : }
130 :
131 : UpstreamHttp11ConnectSocketFactory::UpstreamHttp11ConnectSocketFactory(
132 : Network::UpstreamTransportSocketFactoryPtr transport_socket_factory)
133 0 : : PassthroughFactory(std::move(transport_socket_factory)) {}
134 :
135 : Network::TransportSocketPtr UpstreamHttp11ConnectSocketFactory::createTransportSocket(
136 : Network::TransportSocketOptionsConstSharedPtr options,
137 0 : std::shared_ptr<const Upstream::HostDescription> host) const {
138 0 : auto inner_socket = transport_socket_factory_->createTransportSocket(options, host);
139 0 : if (inner_socket == nullptr) {
140 0 : return nullptr;
141 0 : }
142 0 : return std::make_unique<UpstreamHttp11ConnectSocket>(std::move(inner_socket), options);
143 0 : }
144 :
145 : void UpstreamHttp11ConnectSocketFactory::hashKey(
146 0 : std::vector<uint8_t>& key, Network::TransportSocketOptionsConstSharedPtr options) const {
147 0 : PassthroughFactory::hashKey(key, options);
148 0 : if (options && options->http11ProxyInfo().has_value()) {
149 0 : pushScalarToByteVector(
150 0 : StringUtil::CaseInsensitiveHash()(options->http11ProxyInfo()->proxy_address->asString()),
151 0 : key);
152 0 : pushScalarToByteVector(StringUtil::CaseInsensitiveHash()(options->http11ProxyInfo()->hostname),
153 0 : key);
154 0 : }
155 0 : }
156 :
157 : } // namespace Http11Connect
158 : } // namespace TransportSockets
159 : } // namespace Extensions
160 : } // namespace Envoy
|