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