Branch data Line data Source code
1 : : /* GIO - GLib Input, Output and Streaming Library
2 : : *
3 : : * Copyright (C) 2010 Collabora, Ltd.
4 : : * Copyright (C) 2014 Red Hat, Inc.
5 : : *
6 : : * SPDX-License-Identifier: LGPL-2.1-or-later
7 : : *
8 : : * This library is free software; you can redistribute it and/or
9 : : * modify it under the terms of the GNU Lesser General Public
10 : : * License as published by the Free Software Foundation; either
11 : : * version 2.1 of the License, or (at your option) any later version.
12 : : *
13 : : * This library is distributed in the hope that it will be useful,
14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : : * Lesser General Public License for more details.
17 : : *
18 : : * You should have received a copy of the GNU Lesser General
19 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 : : *
21 : : * Author: Nicolas Dufresne <nicolas.dufresne@collabora.co.uk>
22 : : * Marc-André Lureau <marcandre.lureau@redhat.com>
23 : : */
24 : :
25 : : #include "config.h"
26 : :
27 : : #include "ghttpproxy.h"
28 : :
29 : : #include <string.h>
30 : : #include <stdlib.h>
31 : :
32 : : #include "giomodule.h"
33 : : #include "giomodule-priv.h"
34 : : #include "giostream.h"
35 : : #include "ginputstream.h"
36 : : #include "glibintl.h"
37 : : #include "goutputstream.h"
38 : : #include "gproxy.h"
39 : : #include "gproxyaddress.h"
40 : : #include "gsocketconnectable.h"
41 : : #include "gtask.h"
42 : : #include "gtlsclientconnection.h"
43 : : #include "gtlsconnection.h"
44 : :
45 : :
46 : : struct _GHttpProxy
47 : : {
48 : : GObject parent;
49 : : };
50 : :
51 : : struct _GHttpProxyClass
52 : : {
53 : : GObjectClass parent_class;
54 : : };
55 : :
56 : : static void g_http_proxy_iface_init (GProxyInterface *proxy_iface);
57 : :
58 : : #define g_http_proxy_get_type _g_http_proxy_get_type
59 : 357 : G_DEFINE_TYPE_WITH_CODE (GHttpProxy, g_http_proxy, G_TYPE_OBJECT,
60 : : G_IMPLEMENT_INTERFACE (G_TYPE_PROXY,
61 : : g_http_proxy_iface_init)
62 : : _g_io_modules_ensure_extension_points_registered ();
63 : : g_io_extension_point_implement (G_PROXY_EXTENSION_POINT_NAME,
64 : : g_define_type_id,
65 : : "http",
66 : : 0))
67 : :
68 : : static void
69 : 0 : g_http_proxy_init (GHttpProxy *proxy)
70 : : {
71 : 0 : }
72 : :
73 : : static gchar *
74 : 0 : create_request (GProxyAddress *proxy_address,
75 : : gboolean *has_cred,
76 : : GError **error)
77 : : {
78 : : const gchar *hostname;
79 : : gint port;
80 : : const gchar *username;
81 : : const gchar *password;
82 : : GString *request;
83 : : gchar *ascii_hostname;
84 : :
85 : 0 : if (has_cred)
86 : 0 : *has_cred = FALSE;
87 : :
88 : 0 : hostname = g_proxy_address_get_destination_hostname (proxy_address);
89 : 0 : ascii_hostname = g_hostname_to_ascii (hostname);
90 : 0 : if (!ascii_hostname)
91 : : {
92 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
93 : : _("Invalid hostname"));
94 : 0 : return NULL;
95 : : }
96 : 0 : port = g_proxy_address_get_destination_port (proxy_address);
97 : 0 : username = g_proxy_address_get_username (proxy_address);
98 : 0 : password = g_proxy_address_get_password (proxy_address);
99 : :
100 : 0 : request = g_string_new (NULL);
101 : :
102 : 0 : g_string_append_printf (request,
103 : : "CONNECT %s:%i HTTP/1.0\r\n"
104 : : "Host: %s:%i\r\n"
105 : : "Proxy-Connection: keep-alive\r\n"
106 : : "User-Agent: GLib/%i.%i\r\n",
107 : : ascii_hostname, port,
108 : : ascii_hostname, port,
109 : : GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION);
110 : 0 : g_free (ascii_hostname);
111 : :
112 : 0 : if (username != NULL && password != NULL)
113 : : {
114 : : gchar *cred;
115 : : gchar *base64_cred;
116 : :
117 : 0 : if (has_cred)
118 : 0 : *has_cred = TRUE;
119 : :
120 : 0 : cred = g_strdup_printf ("%s:%s", username, password);
121 : 0 : base64_cred = g_base64_encode ((guchar *) cred, strlen (cred));
122 : 0 : g_free (cred);
123 : 0 : g_string_append_printf (request,
124 : : "Proxy-Authorization: Basic %s\r\n",
125 : : base64_cred);
126 : 0 : g_free (base64_cred);
127 : : }
128 : :
129 : 0 : g_string_append (request, "\r\n");
130 : :
131 : 0 : return g_string_free (request, FALSE);
132 : : }
133 : :
134 : : static gboolean
135 : 0 : check_reply (const gchar *buffer,
136 : : gboolean has_cred,
137 : : GError **error)
138 : : {
139 : : gint err_code;
140 : 0 : const gchar *ptr = buffer + 7;
141 : :
142 : 0 : if (strncmp (buffer, "HTTP/1.", 7) != 0 || (*ptr != '0' && *ptr != '1'))
143 : : {
144 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
145 : : _("Bad HTTP proxy reply"));
146 : 0 : return FALSE;
147 : : }
148 : :
149 : 0 : ptr++;
150 : 0 : while (*ptr == ' ')
151 : 0 : ptr++;
152 : :
153 : 0 : err_code = atoi (ptr);
154 : :
155 : 0 : if (err_code < 200 || err_code >= 300)
156 : : {
157 : 0 : switch (err_code)
158 : : {
159 : 0 : case 403:
160 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_NOT_ALLOWED,
161 : : _("HTTP proxy connection not allowed"));
162 : 0 : break;
163 : 0 : case 407:
164 : 0 : if (has_cred)
165 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_AUTH_FAILED,
166 : : _("HTTP proxy authentication failed"));
167 : : else
168 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_NEED_AUTH,
169 : : _("HTTP proxy authentication required"));
170 : 0 : break;
171 : 0 : default:
172 : 0 : g_set_error (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
173 : : _("HTTP proxy connection failed: %i"), err_code);
174 : : }
175 : :
176 : 0 : return FALSE;
177 : : }
178 : :
179 : 0 : return TRUE;
180 : : }
181 : :
182 : : #define HTTP_END_MARKER "\r\n\r\n"
183 : :
184 : : static GIOStream *
185 : 0 : g_http_proxy_connect (GProxy *proxy,
186 : : GIOStream *io_stream,
187 : : GProxyAddress *proxy_address,
188 : : GCancellable *cancellable,
189 : : GError **error)
190 : : {
191 : : GInputStream *in;
192 : : GOutputStream *out;
193 : 0 : gchar *buffer = NULL;
194 : : gsize buffer_length;
195 : : gsize bytes_read;
196 : : gboolean has_cred;
197 : 0 : GIOStream *tlsconn = NULL;
198 : :
199 : 0 : if (G_IS_HTTPS_PROXY (proxy))
200 : : {
201 : 0 : tlsconn = g_tls_client_connection_new (io_stream,
202 : 0 : G_SOCKET_CONNECTABLE (proxy_address),
203 : : error);
204 : 0 : if (!tlsconn)
205 : 0 : goto error;
206 : :
207 : : #ifdef DEBUG
208 : : {
209 : : GTlsCertificateFlags tls_validation_flags = G_TLS_CERTIFICATE_VALIDATE_ALL;
210 : :
211 : : tls_validation_flags &= ~(G_TLS_CERTIFICATE_UNKNOWN_CA | G_TLS_CERTIFICATE_BAD_IDENTITY);
212 : : g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (tlsconn),
213 : : tls_validation_flags);
214 : : }
215 : : #endif
216 : :
217 : 0 : if (!g_tls_connection_handshake (G_TLS_CONNECTION (tlsconn), cancellable, error))
218 : 0 : goto error;
219 : :
220 : 0 : io_stream = tlsconn;
221 : : }
222 : :
223 : 0 : in = g_io_stream_get_input_stream (io_stream);
224 : 0 : out = g_io_stream_get_output_stream (io_stream);
225 : :
226 : 0 : buffer = create_request (proxy_address, &has_cred, error);
227 : 0 : if (!buffer)
228 : 0 : goto error;
229 : 0 : if (!g_output_stream_write_all (out, buffer, strlen (buffer), NULL,
230 : : cancellable, error))
231 : 0 : goto error;
232 : :
233 : 0 : g_free (buffer);
234 : :
235 : 0 : bytes_read = 0;
236 : 0 : buffer_length = 1024;
237 : 0 : buffer = g_malloc (buffer_length);
238 : :
239 : : /* Read byte-by-byte instead of using GDataInputStream
240 : : * since we do not want to read beyond the end marker
241 : : */
242 : : do
243 : 0 : {
244 : : gssize signed_nread;
245 : : gsize nread;
246 : :
247 : : signed_nread =
248 : 0 : g_input_stream_read (in, buffer + bytes_read, 1, cancellable, error);
249 : 0 : if (signed_nread == -1)
250 : 0 : goto error;
251 : :
252 : 0 : nread = signed_nread;
253 : 0 : if (nread == 0)
254 : 0 : break;
255 : :
256 : 0 : ++bytes_read;
257 : :
258 : 0 : if (bytes_read == buffer_length)
259 : : {
260 : : /* HTTP specifications does not defines any upper limit for
261 : : * headers. But, the most usual size used seems to be 8KB.
262 : : * Yet, the biggest we found was Tomcat's HTTP headers whose
263 : : * size is 48K. So, for a reasonable error margin, let's accept
264 : : * a header with a twice as large size but no more: 96KB */
265 : 0 : if (buffer_length > 98304)
266 : : {
267 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
268 : : _("HTTP proxy response too big"));
269 : 0 : goto error;
270 : : }
271 : 0 : buffer_length = 2 * buffer_length;
272 : 0 : buffer = g_realloc (buffer, buffer_length);
273 : : }
274 : :
275 : 0 : *(buffer + bytes_read) = '\0';
276 : :
277 : 0 : if (g_str_has_suffix (buffer, HTTP_END_MARKER))
278 : 0 : break;
279 : : }
280 : : while (TRUE);
281 : :
282 : 0 : if (bytes_read == 0)
283 : : {
284 : 0 : g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
285 : : _("HTTP proxy server closed connection unexpectedly."));
286 : 0 : goto error;
287 : : }
288 : :
289 : 0 : if (!check_reply (buffer, has_cred, error))
290 : 0 : goto error;
291 : :
292 : 0 : g_free (buffer);
293 : :
294 : 0 : g_object_ref (io_stream);
295 : 0 : g_clear_object (&tlsconn);
296 : :
297 : 0 : return io_stream;
298 : :
299 : 0 : error:
300 : 0 : g_clear_object (&tlsconn);
301 : 0 : g_free (buffer);
302 : 0 : return NULL;
303 : : }
304 : :
305 : : typedef struct
306 : : {
307 : : GIOStream *io_stream;
308 : : GProxyAddress *proxy_address;
309 : : } ConnectAsyncData;
310 : :
311 : : static void
312 : 0 : free_connect_data (ConnectAsyncData *data)
313 : : {
314 : 0 : g_object_unref (data->io_stream);
315 : 0 : g_object_unref (data->proxy_address);
316 : 0 : g_slice_free (ConnectAsyncData, data);
317 : 0 : }
318 : :
319 : : static void
320 : 0 : connect_thread (GTask *task,
321 : : gpointer source_object,
322 : : gpointer task_data,
323 : : GCancellable *cancellable)
324 : : {
325 : 0 : GProxy *proxy = source_object;
326 : 0 : ConnectAsyncData *data = task_data;
327 : : GIOStream *res;
328 : 0 : GError *error = NULL;
329 : :
330 : 0 : res = g_http_proxy_connect (proxy, data->io_stream, data->proxy_address,
331 : : cancellable, &error);
332 : :
333 : 0 : if (res == NULL)
334 : 0 : g_task_return_error (task, error);
335 : : else
336 : 0 : g_task_return_pointer (task, res, g_object_unref);
337 : 0 : }
338 : :
339 : : static void
340 : 0 : g_http_proxy_connect_async (GProxy *proxy,
341 : : GIOStream *io_stream,
342 : : GProxyAddress *proxy_address,
343 : : GCancellable *cancellable,
344 : : GAsyncReadyCallback callback,
345 : : gpointer user_data)
346 : : {
347 : : ConnectAsyncData *data;
348 : : GTask *task;
349 : :
350 : 0 : data = g_slice_new0 (ConnectAsyncData);
351 : 0 : data->io_stream = g_object_ref (io_stream);
352 : 0 : data->proxy_address = g_object_ref (proxy_address);
353 : :
354 : 0 : task = g_task_new (proxy, cancellable, callback, user_data);
355 : 0 : g_task_set_source_tag (task, g_http_proxy_connect_async);
356 : 0 : g_task_set_task_data (task, data, (GDestroyNotify) free_connect_data);
357 : :
358 : 0 : g_task_run_in_thread (task, connect_thread);
359 : 0 : g_object_unref (task);
360 : 0 : }
361 : :
362 : : static GIOStream *
363 : 0 : g_http_proxy_connect_finish (GProxy *proxy,
364 : : GAsyncResult *result,
365 : : GError **error)
366 : : {
367 : 0 : return g_task_propagate_pointer (G_TASK (result), error);
368 : : }
369 : :
370 : : static gboolean
371 : 0 : g_http_proxy_supports_hostname (GProxy *proxy)
372 : : {
373 : 0 : return TRUE;
374 : : }
375 : :
376 : : static void
377 : 0 : g_http_proxy_class_init (GHttpProxyClass *class)
378 : : {
379 : 0 : }
380 : :
381 : : static void
382 : 0 : g_http_proxy_iface_init (GProxyInterface *proxy_iface)
383 : : {
384 : 0 : proxy_iface->connect = g_http_proxy_connect;
385 : 0 : proxy_iface->connect_async = g_http_proxy_connect_async;
386 : 0 : proxy_iface->connect_finish = g_http_proxy_connect_finish;
387 : 0 : proxy_iface->supports_hostname = g_http_proxy_supports_hostname;
388 : 0 : }
389 : :
390 : : struct _GHttpsProxy
391 : : {
392 : : GHttpProxy parent;
393 : : };
394 : :
395 : : struct _GHttpsProxyClass
396 : : {
397 : : GHttpProxyClass parent_class;
398 : : };
399 : :
400 : : #define g_https_proxy_get_type _g_https_proxy_get_type
401 : 238 : G_DEFINE_TYPE_WITH_CODE (GHttpsProxy, g_https_proxy, G_TYPE_HTTP_PROXY,
402 : : G_IMPLEMENT_INTERFACE (G_TYPE_PROXY,
403 : : g_http_proxy_iface_init)
404 : : _g_io_modules_ensure_extension_points_registered ();
405 : : g_io_extension_point_implement (G_PROXY_EXTENSION_POINT_NAME,
406 : : g_define_type_id,
407 : : "https",
408 : : 0))
409 : :
410 : : static void
411 : 0 : g_https_proxy_init (GHttpsProxy *proxy)
412 : : {
413 : 0 : }
414 : :
415 : : static void
416 : 0 : g_https_proxy_class_init (GHttpsProxyClass *class)
417 : : {
418 : 0 : }
|