1 /*!
2  * @file
3  * @brief The implementation of target-connector.
4  */
5 
6 #include <arataga/acl_handler/handlers/http/basics.hpp>
7 #include <arataga/acl_handler/handlers/http/factories.hpp>
8 #include <arataga/acl_handler/handlers/http/helpers.hpp>
9 #include <arataga/acl_handler/handlers/http/responses.hpp>
10 
11 namespace arataga::acl_handler
12 {
13 
14 namespace handlers::http
15 {
16 
17 //
18 // target_connector_handler_t
19 //
20 /*!
21  * @brief Connection-handler that makes a connection to the target host.
22  */
23 class target_connector_handler_t final : public handler_with_out_connection_t
24 {
25 	//! HTTP-request parsing status.
26 	http_handling_state_unique_ptr_t m_request_state;
27 
28 	//! Additional info for the request.
29 	request_info_t m_request_info;
30 
31 	//! Address of the target host.
32 	asio::ip::tcp::endpoint m_target_endpoint;
33 
34 	//! Traffic-limiter for the user.
35 	traffic_limiter_unique_ptr_t m_traffic_limiter;
36 
37 	//! Timepoint at that connection attempt was started.
38 	std::chrono::steady_clock::time_point m_created_at;
39 
40 public:
target_connector_handler_t(handler_context_holder_t ctx,handler_context_t::connection_id_t id,asio::ip::tcp::socket connection,http_handling_state_unique_ptr_t request_state,request_info_t request_info,asio::ip::tcp::endpoint target_endpoint,traffic_limiter_unique_ptr_t traffic_limiter)41 	target_connector_handler_t(
42 		handler_context_holder_t ctx,
43 		handler_context_t::connection_id_t id,
44 		asio::ip::tcp::socket connection,
45 		http_handling_state_unique_ptr_t request_state,
46 		request_info_t request_info,
47 		asio::ip::tcp::endpoint target_endpoint,
48 		traffic_limiter_unique_ptr_t traffic_limiter )
49 		:	handler_with_out_connection_t{
50 				std::move(ctx), id, std::move(connection)
51 			}
52 		,	m_request_state{ std::move(request_state) }
53 		,	m_request_info{ std::move(request_info) }
54 		,	m_target_endpoint{ target_endpoint }
55 		,	m_traffic_limiter{ std::move(traffic_limiter) }
56 		,	m_created_at{ std::chrono::steady_clock::now() }
57 	{}
58 
59 protected:
60 	void
on_start_impl(delete_protector_t delete_protector)61 	on_start_impl( delete_protector_t delete_protector ) override
62 	{
63 		wrap_action_and_handle_exceptions(
64 			delete_protector,
65 			[this]( delete_protector_t delete_protector, can_throw_t can_throw )
66 			{
67 				initiate_connect( delete_protector, can_throw );
68 			} );
69 	}
70 
71 	void
on_timer_impl(delete_protector_t delete_protector)72 	on_timer_impl( delete_protector_t delete_protector ) override
73 	{
74 		if( std::chrono::steady_clock::now() >= m_created_at +
75 				context().config().connect_target_timeout() )
76 		{
77 			wrap_action_and_handle_exceptions(
78 				delete_protector,
79 				[this]( delete_protector_t delete_protector, can_throw_t can_throw )
80 				{
81 					send_negative_response_then_close_connection(
82 							delete_protector,
83 							can_throw,
84 							remove_reason_t::current_operation_timed_out,
85 							response_bad_gateway_connect_timeout );
86 				} );
87 		}
88 	}
89 
90 public:
91 	arataga::utils::string_literal_t
name() const92 	name() const noexcept override
93 	{
94 		using namespace arataga::utils::string_literals;
95 		return "http-target-connect-handler"_static_str;
96 	}
97 
98 private:
99 	void
initiate_connect(delete_protector_t delete_protector,can_throw_t can_throw)100 	initiate_connect(
101 		delete_protector_t delete_protector,
102 		can_throw_t can_throw )
103 	{
104 		try
105 		{
106 			asio::error_code ec;
107 
108 			// Helper local function to avoid data duplication.
109 			const auto finish_on_failure =
110 				[this, &delete_protector, &can_throw](
111 					std::string_view message ) -> void
112 				{
113 					log_problem_then_send_negative_response(
114 							delete_protector,
115 							can_throw,
116 							remove_reason_t::io_error,
117 							spdlog::level::err,
118 							message,
119 							response_internal_server_error );
120 				};
121 
122 			m_out_connection.open( m_target_endpoint.protocol(), ec );
123 			if( ec )
124 			{
125 				return finish_on_failure( fmt::format(
126 						"unable open outgoing socket: {}",
127 						ec.message() ) );
128 			}
129 
130 			// New socket should work in non-blocking mode.
131 			m_out_connection.non_blocking( true, ec );
132 			if( ec )
133 			{
134 				return finish_on_failure( fmt::format(
135 						"unable switch outgoing socket to non-blocking mode: {}",
136 						ec.message() ) );
137 			}
138 
139 			// We have to bind new socket to ACL's external address.
140 			m_out_connection.bind(
141 					// Use 0 as port number, the OS will assign actual number.
142 					asio::ip::tcp::endpoint{ context().config().out_addr(), 0u },
143 					ec );
144 			if( ec )
145 			{
146 				return finish_on_failure( fmt::format(
147 						"unable to bind outgoing socket to address {}: {}",
148 						context().config().out_addr(),
149 						ec.message() ) );
150 			}
151 
152 			::arataga::logging::wrap_logging(
153 					proxy_logging_mode,
154 					spdlog::level::trace,
155 					[this, can_throw]( auto level )
156 					{
157 						log_message_for_connection(
158 								can_throw,
159 								level,
160 								fmt::format( "trying to connect {} from {}",
161 										m_target_endpoint,
162 										m_out_connection.local_endpoint() ) );
163 					} );
164 
165 			// Now we can initiate the connection.
166 			m_out_connection.async_connect(
167 					m_target_endpoint,
168 					with<const asio::error_code &>().make_handler(
169 						[this](
170 							delete_protector_t delete_protector,
171 							can_throw_t can_throw,
172 							const asio::error_code & ec )
173 						{
174 							on_async_connect_result(
175 									delete_protector, can_throw, ec );
176 						} )
177 				);
178 		}
179 		catch( const std::exception & x )
180 		{
181 			//FIXME: what is fmt::format throws?
182 			log_problem_then_send_negative_response(
183 					delete_protector,
184 					can_throw,
185 					remove_reason_t::unhandled_exception,
186 					spdlog::level::err,
187 					fmt::format( "an exception during the creation of "
188 							"outgoing connection from {} to {}: {}",
189 							context().config().out_addr(),
190 							m_target_endpoint,
191 							x.what() ),
192 					response_internal_server_error );
193 		}
194 	}
195 
196 	void
log_problem_then_send_negative_response(delete_protector_t delete_protector,can_throw_t can_throw,remove_reason_t remove_reason,spdlog::level::level_enum log_level,std::string_view log_message,arataga::utils::string_literal_t negative_response)197 	log_problem_then_send_negative_response(
198 		delete_protector_t delete_protector,
199 		can_throw_t can_throw,
200 		remove_reason_t remove_reason,
201 		spdlog::level::level_enum log_level,
202 		std::string_view log_message,
203 		arataga::utils::string_literal_t negative_response )
204 	{
205 		::arataga::logging::wrap_logging(
206 				proxy_logging_mode,
207 				log_level,
208 				[this, can_throw, log_message]( auto level )
209 				{
210 					log_message_for_connection(
211 							can_throw,
212 							level,
213 							log_message );
214 				} );
215 
216 		send_negative_response_then_close_connection(
217 				delete_protector,
218 				can_throw,
219 				remove_reason,
220 				negative_response );
221 	}
222 
223 	void
on_async_connect_result(delete_protector_t delete_protector,can_throw_t can_throw,const asio::error_code & ec)224 	on_async_connect_result(
225 		delete_protector_t delete_protector,
226 		can_throw_t can_throw,
227 		const asio::error_code & ec )
228 	{
229 		if( ec )
230 		{
231 			if( asio::error::operation_aborted != ec )
232 			{
233 				log_problem_then_send_negative_response(
234 						delete_protector,
235 						can_throw,
236 						remove_reason_t::io_error,
237 						spdlog::level::warn,
238 						fmt::format( "can't connect to target host {}: {}",
239 								m_target_endpoint,
240 								ec.message() ),
241 						response_bad_gateway_connect_failure );
242 			}
243 		}
244 		else
245 		{
246 			::arataga::logging::wrap_logging(
247 					proxy_logging_mode,
248 					spdlog::level::debug,
249 					[this, can_throw]( auto level )
250 					{
251 						log_message_for_connection(
252 								can_throw,
253 								level,
254 								fmt::format(
255 										"outgoing connection to {} from {} established",
256 										m_target_endpoint,
257 										m_out_connection.local_endpoint() ) );
258 					} );
259 
260 			// New connection-handler depends on HTTP-method from the request.
261 			// At the moment only CONNECT method requires a special handler.
262 			const auto factory = (HTTP_CONNECT == m_request_info.m_method ?
263 					&make_connect_method_handler :
264 					&make_ordinary_method_handler);
265 
266 			replace_handler(
267 					delete_protector,
268 					can_throw,
269 					[this, &factory]( can_throw_t )
270 					{
271 						return (*factory)(
272 								std::move(m_ctx),
273 								m_id,
274 								std::move(m_connection),
275 								std::move(m_request_state),
276 								std::move(m_request_info),
277 								std::move(m_traffic_limiter),
278 								std::move(m_out_connection) );
279 					} );
280 		}
281 	}
282 };
283 
284 //
285 // make_target_connector_handler
286 //
287 [[nodiscard]]
288 connection_handler_shptr_t
make_target_connector_handler(handler_context_holder_t ctx,handler_context_t::connection_id_t id,asio::ip::tcp::socket connection,http_handling_state_unique_ptr_t request_state,request_info_t request_info,asio::ip::tcp::endpoint target_endpoint,traffic_limiter_unique_ptr_t traffic_limiter)289 make_target_connector_handler(
290 	handler_context_holder_t ctx,
291 	handler_context_t::connection_id_t id,
292 	asio::ip::tcp::socket connection,
293 	http_handling_state_unique_ptr_t request_state,
294 	request_info_t request_info,
295 	asio::ip::tcp::endpoint target_endpoint,
296 	traffic_limiter_unique_ptr_t traffic_limiter )
297 {
298 	return std::make_shared< target_connector_handler_t >(
299 			std::move(ctx),
300 			id,
301 			std::move(connection),
302 			std::move(request_state),
303 			std::move(request_info),
304 			target_endpoint,
305 			std::move(traffic_limiter) );
306 }
307 
308 } /* namespace arataga::acl_handler */
309 
310 } /* namespace handlers::http */
311 
312