summaryrefslogtreecommitdiffstats
path: root/meta/recipes-core/glib-2.0/glib-2.0/CVE-2020-6750.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-core/glib-2.0/glib-2.0/CVE-2020-6750.patch')
-rw-r--r--meta/recipes-core/glib-2.0/glib-2.0/CVE-2020-6750.patch741
1 files changed, 741 insertions, 0 deletions
diff --git a/meta/recipes-core/glib-2.0/glib-2.0/CVE-2020-6750.patch b/meta/recipes-core/glib-2.0/glib-2.0/CVE-2020-6750.patch
new file mode 100644
index 0000000000..6db3934978
--- /dev/null
+++ b/meta/recipes-core/glib-2.0/glib-2.0/CVE-2020-6750.patch
@@ -0,0 +1,741 @@
1From 747f2c646f5a86ac58ad59be08036e81388e971d Mon Sep 17 00:00:00 2001
2From: Patrick Griffis <tingping@tingping.se>
3Date: Thu, 23 Jan 2020 19:58:41 -0800
4Subject: [PATCH] Refactor g_socket_client_connect_async()
5
6This is a fairly large refactoring. The highlights are:
7
8- Removing in-progress connections/addresses from GSocketClientAsyncConnectData:
9
10 This caused issues where multiple ConnectionAttempt's would step over eachother
11 and modify shared state causing bugs like accidentally bypassing a set proxy.
12
13 Fixes #1871
14 Fixes #1989
15 Fixes #1902
16
17- Cancelling address enumeration on error/completion
18
19- Queuing successful TCP connections and doing application layer work serially:
20
21 This is more in the spirit of Happy Eyeballs but it also greatly simplifies
22 the flow of connection handling so fewer tasks are happening in parallel
23 when they don't need to be.
24
25 The behavior also should more closely match that of g_socket_client_connect().
26
27- Better track the state of address enumeration:
28
29 Previously we were over eager to treat enumeration finishing as an error.
30
31 Fixes #1872
32 See also #1982
33
34- Add more detailed documentation and logging.
35
36Closes #1995
37
38CVE: CVE-2020-6750
39
40Upstream-Status: Backport [ https://gitlab.gnome.org/GNOME/glib.git;
41commit=2722620e3291b930a3a228100d7c0e07b69534e3 ]
42
43Signed-off-by: Haiqing Bai <Haiqing.Bai@windriver.com>
44---
45 gio/gsocketclient.c | 459 ++++++++++++++++++++++++++++----------------
46 1 file changed, 296 insertions(+), 163 deletions(-)
47
48diff --git a/gio/gsocketclient.c b/gio/gsocketclient.c
49index 81767c0..b1d5f6c 100644
50--- a/gio/gsocketclient.c
51+++ b/gio/gsocketclient.c
52@@ -1332,13 +1332,15 @@ typedef struct
53
54 GSocketConnectable *connectable;
55 GSocketAddressEnumerator *enumerator;
56- GProxyAddress *proxy_addr;
57- GSocket *socket;
58- GIOStream *connection;
59+ GCancellable *enumeration_cancellable;
60
61 GSList *connection_attempts;
62+ GSList *successful_connections;
63 GError *last_error;
64
65+ gboolean enumerated_at_least_once;
66+ gboolean enumeration_completed;
67+ gboolean connection_in_progress;
68 gboolean completed;
69 } GSocketClientAsyncConnectData;
70
71@@ -1350,10 +1352,9 @@ g_socket_client_async_connect_data_free (GSocketClientAsyncConnectData *data)
72 data->task = NULL;
73 g_clear_object (&data->connectable);
74 g_clear_object (&data->enumerator);
75- g_clear_object (&data->proxy_addr);
76- g_clear_object (&data->socket);
77- g_clear_object (&data->connection);
78+ g_clear_object (&data->enumeration_cancellable);
79 g_slist_free_full (data->connection_attempts, connection_attempt_unref);
80+ g_slist_free_full (data->successful_connections, connection_attempt_unref);
81
82 g_clear_error (&data->last_error);
83
84@@ -1365,6 +1366,7 @@ typedef struct
85 GSocketAddress *address;
86 GSocket *socket;
87 GIOStream *connection;
88+ GProxyAddress *proxy_addr;
89 GSocketClientAsyncConnectData *data; /* unowned */
90 GSource *timeout_source;
91 GCancellable *cancellable;
92@@ -1396,6 +1398,7 @@ connection_attempt_unref (gpointer pointer)
93 g_clear_object (&attempt->socket);
94 g_clear_object (&attempt->connection);
95 g_clear_object (&attempt->cancellable);
96+ g_clear_object (&attempt->proxy_addr);
97 if (attempt->timeout_source)
98 {
99 g_source_destroy (attempt->timeout_source);
100@@ -1413,37 +1416,59 @@ connection_attempt_remove (ConnectionAttempt *attempt)
101 }
102
103 static void
104-g_socket_client_async_connect_complete (GSocketClientAsyncConnectData *data)
105+cancel_all_attempts (GSocketClientAsyncConnectData *data)
106 {
107- g_assert (data->connection);
108+ GSList *l;
109
110- if (!G_IS_SOCKET_CONNECTION (data->connection))
111+ for (l = data->connection_attempts; l; l = g_slist_next (l))
112 {
113- GSocketConnection *wrapper_connection;
114-
115- wrapper_connection = g_tcp_wrapper_connection_new (data->connection, data->socket);
116- g_object_unref (data->connection);
117- data->connection = (GIOStream *)wrapper_connection;
118+ ConnectionAttempt *attempt_entry = l->data;
119+ g_cancellable_cancel (attempt_entry->cancellable);
120+ connection_attempt_unref (attempt_entry);
121 }
122+ g_slist_free (data->connection_attempts);
123+ data->connection_attempts = NULL;
124
125- if (!data->completed)
126+ g_slist_free_full (data->successful_connections, connection_attempt_unref);
127+ data->successful_connections = NULL;
128+
129+ g_cancellable_cancel (data->enumeration_cancellable);
130+}
131+
132+static void
133+g_socket_client_async_connect_complete (ConnectionAttempt *attempt)
134+{
135+ GSocketClientAsyncConnectData *data = attempt->data;
136+ GError *error = NULL;
137+ g_assert (attempt->connection);
138+ g_assert (!data->completed);
139+
140+ if (!G_IS_SOCKET_CONNECTION (attempt->connection))
141 {
142- GError *error = NULL;
143+ GSocketConnection *wrapper_connection;
144
145- if (g_cancellable_set_error_if_cancelled (g_task_get_cancellable (data->task), &error))
146- {
147- g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_COMPLETE, data->connectable, NULL);
148- g_task_return_error (data->task, g_steal_pointer (&error));
149- }
150- else
151- {
152- g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_COMPLETE, data->connectable, data->connection);
153- g_task_return_pointer (data->task, g_steal_pointer (&data->connection), g_object_unref);
154- }
155+ wrapper_connection = g_tcp_wrapper_connection_new (attempt->connection, attempt->socket);
156+ g_object_unref (attempt->connection);
157+ attempt->connection = (GIOStream *)wrapper_connection;
158+ }
159
160- data->completed = TRUE;
161+ data->completed = TRUE;
162+ cancel_all_attempts (data);
163+
164+ if (g_cancellable_set_error_if_cancelled (g_task_get_cancellable (data->task), &error))
165+ {
166+ g_debug ("GSocketClient: Connection cancelled!");
167+ g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_COMPLETE, data->connectable, NULL);
168+ g_task_return_error (data->task, g_steal_pointer (&error));
169+ }
170+ else
171+ {
172+ g_debug ("GSocketClient: Connection successful!");
173+ g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_COMPLETE, data->connectable, attempt->connection);
174+ g_task_return_pointer (data->task, g_steal_pointer (&attempt->connection), g_object_unref);
175 }
176
177+ connection_attempt_unref (attempt);
178 g_object_unref (data->task);
179 }
180
181@@ -1465,59 +1490,63 @@ static void
182 enumerator_next_async (GSocketClientAsyncConnectData *data,
183 gboolean add_task_ref)
184 {
185- /* We need to cleanup the state */
186- g_clear_object (&data->socket);
187- g_clear_object (&data->proxy_addr);
188- g_clear_object (&data->connection);
189-
190 /* Each enumeration takes a ref. This arg just avoids repeated unrefs when
191 an enumeration starts another enumeration */
192 if (add_task_ref)
193 g_object_ref (data->task);
194
195 g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_RESOLVING, data->connectable, NULL);
196+ g_debug ("GSocketClient: Starting new address enumeration");
197 g_socket_address_enumerator_next_async (data->enumerator,
198- g_task_get_cancellable (data->task),
199+ data->enumeration_cancellable,
200 g_socket_client_enumerator_callback,
201 data);
202 }
203
204+static void try_next_connection_or_finish (GSocketClientAsyncConnectData *, gboolean);
205+
206 static void
207 g_socket_client_tls_handshake_callback (GObject *object,
208 GAsyncResult *result,
209 gpointer user_data)
210 {
211- GSocketClientAsyncConnectData *data = user_data;
212+ ConnectionAttempt *attempt = user_data;
213+ GSocketClientAsyncConnectData *data = attempt->data;
214
215 if (g_tls_connection_handshake_finish (G_TLS_CONNECTION (object),
216 result,
217 &data->last_error))
218 {
219- g_object_unref (data->connection);
220- data->connection = G_IO_STREAM (object);
221+ g_object_unref (attempt->connection);
222+ attempt->connection = G_IO_STREAM (object);
223
224- g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_TLS_HANDSHAKED, data->connectable, data->connection);
225- g_socket_client_async_connect_complete (data);
226+ g_debug ("GSocketClient: TLS handshake succeeded");
227+ g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_TLS_HANDSHAKED, data->connectable, attempt->connection);
228+ g_socket_client_async_connect_complete (attempt);
229 }
230 else
231 {
232 g_object_unref (object);
233- enumerator_next_async (data, FALSE);
234+ connection_attempt_unref (attempt);
235+ g_debug ("GSocketClient: TLS handshake failed: %s", data->last_error->message);
236+ try_next_connection_or_finish (data, TRUE);
237 }
238 }
239
240 static void
241-g_socket_client_tls_handshake (GSocketClientAsyncConnectData *data)
242+g_socket_client_tls_handshake (ConnectionAttempt *attempt)
243 {
244+ GSocketClientAsyncConnectData *data = attempt->data;
245 GIOStream *tlsconn;
246
247 if (!data->client->priv->tls)
248 {
249- g_socket_client_async_connect_complete (data);
250+ g_socket_client_async_connect_complete (attempt);
251 return;
252 }
253
254- tlsconn = g_tls_client_connection_new (data->connection,
255+ g_debug ("GSocketClient: Starting TLS handshake");
256+ tlsconn = g_tls_client_connection_new (attempt->connection,
257 data->connectable,
258 &data->last_error);
259 if (tlsconn)
260@@ -1529,11 +1558,12 @@ g_socket_client_tls_handshake (GSocketClientAsyncConnectData *data)
261 G_PRIORITY_DEFAULT,
262 g_task_get_cancellable (data->task),
263 g_socket_client_tls_handshake_callback,
264- data);
265+ attempt);
266 }
267 else
268 {
269- enumerator_next_async (data, FALSE);
270+ connection_attempt_unref (attempt);
271+ try_next_connection_or_finish (data, TRUE);
272 }
273 }
274
275@@ -1542,23 +1572,38 @@ g_socket_client_proxy_connect_callback (GObject *object,
276 GAsyncResult *result,
277 gpointer user_data)
278 {
279- GSocketClientAsyncConnectData *data = user_data;
280+ ConnectionAttempt *attempt = user_data;
281+ GSocketClientAsyncConnectData *data = attempt->data;
282
283- g_object_unref (data->connection);
284- data->connection = g_proxy_connect_finish (G_PROXY (object),
285- result,
286- &data->last_error);
287- if (data->connection)
288+ g_object_unref (attempt->connection);
289+ attempt->connection = g_proxy_connect_finish (G_PROXY (object),
290+ result,
291+ &data->last_error);
292+ if (attempt->connection)
293 {
294- g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_PROXY_NEGOTIATED, data->connectable, data->connection);
295+ g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_PROXY_NEGOTIATED, data->connectable, attempt->connection);
296 }
297 else
298 {
299- enumerator_next_async (data, FALSE);
300+ connection_attempt_unref (attempt);
301+ try_next_connection_or_finish (data, TRUE);
302 return;
303 }
304
305- g_socket_client_tls_handshake (data);
306+ g_socket_client_tls_handshake (attempt);
307+}
308+
309+static void
310+complete_connection_with_error (GSocketClientAsyncConnectData *data,
311+ GError *error)
312+{
313+ g_debug ("GSocketClient: Connection failed: %s", error->message);
314+ g_assert (!data->completed);
315+
316+ g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_COMPLETE, data->connectable, NULL);
317+ data->completed = TRUE;
318+ cancel_all_attempts (data);
319+ g_task_return_error (data->task, error);
320 }
321
322 static gboolean
323@@ -1572,15 +1617,114 @@ task_completed_or_cancelled (GSocketClientAsyncConnectData *data)
324 return TRUE;
325 else if (g_cancellable_set_error_if_cancelled (cancellable, &error))
326 {
327- g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_COMPLETE, data->connectable, NULL);
328- g_task_return_error (task, g_steal_pointer (&error));
329- data->completed = TRUE;
330+ complete_connection_with_error (data, g_steal_pointer (&error));
331 return TRUE;
332 }
333 else
334 return FALSE;
335 }
336
337+static gboolean
338+try_next_successful_connection (GSocketClientAsyncConnectData *data)
339+{
340+ ConnectionAttempt *attempt;
341+ const gchar *protocol;
342+ GProxy *proxy;
343+
344+ if (data->connection_in_progress)
345+ return FALSE;
346+
347+ g_assert (data->successful_connections != NULL);
348+ attempt = data->successful_connections->data;
349+ g_assert (attempt != NULL);
350+ data->successful_connections = g_slist_remove (data->successful_connections, attempt);
351+ data->connection_in_progress = TRUE;
352+
353+ g_debug ("GSocketClient: Starting application layer connection");
354+
355+ if (!attempt->proxy_addr)
356+ {
357+ g_socket_client_tls_handshake (g_steal_pointer (&attempt));
358+ return TRUE;
359+ }
360+
361+ protocol = g_proxy_address_get_protocol (attempt->proxy_addr);
362+
363+ /* The connection should not be anything other than TCP,
364+ * but let's put a safety guard in case
365+ */
366+ if (!G_IS_TCP_CONNECTION (attempt->connection))
367+ {
368+ g_critical ("Trying to proxy over non-TCP connection, this is "
369+ "most likely a bug in GLib IO library.");
370+
371+ g_set_error_literal (&data->last_error,
372+ G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
373+ _("Proxying over a non-TCP connection is not supported."));
374+ }
375+ else if (g_hash_table_contains (data->client->priv->app_proxies, protocol))
376+ {
377+ /* Simply complete the connection, we don't want to do TLS handshake
378+ * as the application proxy handling may need proxy handshake first */
379+ g_socket_client_async_connect_complete (g_steal_pointer (&attempt));
380+ return TRUE;
381+ }
382+ else if ((proxy = g_proxy_get_default_for_protocol (protocol)))
383+ {
384+ GIOStream *connection = attempt->connection;
385+ GProxyAddress *proxy_addr = attempt->proxy_addr;
386+
387+ g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_PROXY_NEGOTIATING, data->connectable, attempt->connection);
388+ g_debug ("GSocketClient: Starting proxy connection");
389+ g_proxy_connect_async (proxy,
390+ connection,
391+ proxy_addr,
392+ g_task_get_cancellable (data->task),
393+ g_socket_client_proxy_connect_callback,
394+ g_steal_pointer (&attempt));
395+ g_object_unref (proxy);
396+ return TRUE;
397+ }
398+ else
399+ {
400+ g_clear_error (&data->last_error);
401+
402+ g_set_error (&data->last_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
403+ _("Proxy protocol ā€œ%sā€ is not supported."),
404+ protocol);
405+ }
406+
407+ data->connection_in_progress = FALSE;
408+ g_clear_pointer (&attempt, connection_attempt_unref);
409+ return FALSE; /* All non-return paths are failures */
410+}
411+
412+static void
413+try_next_connection_or_finish (GSocketClientAsyncConnectData *data,
414+ gboolean end_current_connection)
415+{
416+ if (end_current_connection)
417+ data->connection_in_progress = FALSE;
418+
419+ if (data->connection_in_progress)
420+ return;
421+
422+ /* Keep trying successful connections until one works, each iteration pops one */
423+ while (data->successful_connections)
424+ {
425+ if (try_next_successful_connection (data))
426+ return;
427+ }
428+
429+ if (!data->enumeration_completed)
430+ {
431+ enumerator_next_async (data, FALSE);
432+ return;
433+ }
434+
435+ complete_connection_with_error (data, data->last_error);
436+}
437+
438 static void
439 g_socket_client_connected_callback (GObject *source,
440 GAsyncResult *result,
441@@ -1588,10 +1732,7 @@ g_socket_client_connected_callback (GObject *source,
442 {
443 ConnectionAttempt *attempt = user_data;
444 GSocketClientAsyncConnectData *data = attempt->data;
445- GSList *l;
446 GError *error = NULL;
447- GProxy *proxy;
448- const gchar *protocol;
449
450 if (task_completed_or_cancelled (data) || g_cancellable_is_cancelled (attempt->cancellable))
451 {
452@@ -1613,11 +1754,12 @@ g_socket_client_connected_callback (GObject *source,
453 {
454 clarify_connect_error (error, data->connectable, attempt->address);
455 set_last_error (data, error);
456+ g_debug ("GSocketClient: Connection attempt failed: %s", error->message);
457 connection_attempt_remove (attempt);
458- enumerator_next_async (data, FALSE);
459 connection_attempt_unref (attempt);
460+ try_next_connection_or_finish (data, FALSE);
461 }
462- else
463+ else /* Silently ignore cancelled attempts */
464 {
465 g_clear_error (&error);
466 g_object_unref (data->task);
467@@ -1627,74 +1769,21 @@ g_socket_client_connected_callback (GObject *source,
468 return;
469 }
470
471- data->socket = g_steal_pointer (&attempt->socket);
472- data->connection = g_steal_pointer (&attempt->connection);
473-
474- for (l = data->connection_attempts; l; l = g_slist_next (l))
475- {
476- ConnectionAttempt *attempt_entry = l->data;
477- g_cancellable_cancel (attempt_entry->cancellable);
478- connection_attempt_unref (attempt_entry);
479- }
480- g_slist_free (data->connection_attempts);
481- data->connection_attempts = NULL;
482- connection_attempt_unref (attempt);
483-
484- g_socket_connection_set_cached_remote_address ((GSocketConnection*)data->connection, NULL);
485- g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_CONNECTED, data->connectable, data->connection);
486+ g_socket_connection_set_cached_remote_address ((GSocketConnection*)attempt->connection, NULL);
487+ g_debug ("GSocketClient: TCP connection successful");
488+ g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_CONNECTED, data->connectable, attempt->connection);
489
490 /* wrong, but backward compatible */
491- g_socket_set_blocking (data->socket, TRUE);
492+ g_socket_set_blocking (attempt->socket, TRUE);
493
494- if (!data->proxy_addr)
495- {
496- g_socket_client_tls_handshake (data);
497- return;
498- }
499-
500- protocol = g_proxy_address_get_protocol (data->proxy_addr);
501-
502- /* The connection should not be anything other than TCP,
503- * but let's put a safety guard in case
504+ /* This ends the parallel "happy eyeballs" portion of connecting.
505+ Now that we have a successful tcp connection we will attempt to connect
506+ at the TLS/Proxy layer. If those layers fail we will move on to the next
507+ connection.
508 */
509- if (!G_IS_TCP_CONNECTION (data->connection))
510- {
511- g_critical ("Trying to proxy over non-TCP connection, this is "
512- "most likely a bug in GLib IO library.");
513-
514- g_set_error_literal (&data->last_error,
515- G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
516- _("Proxying over a non-TCP connection is not supported."));
517-
518- enumerator_next_async (data, FALSE);
519- }
520- else if (g_hash_table_contains (data->client->priv->app_proxies, protocol))
521- {
522- /* Simply complete the connection, we don't want to do TLS handshake
523- * as the application proxy handling may need proxy handshake first */
524- g_socket_client_async_connect_complete (data);
525- }
526- else if ((proxy = g_proxy_get_default_for_protocol (protocol)))
527- {
528- g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_PROXY_NEGOTIATING, data->connectable, data->connection);
529- g_proxy_connect_async (proxy,
530- data->connection,
531- data->proxy_addr,
532- g_task_get_cancellable (data->task),
533- g_socket_client_proxy_connect_callback,
534- data);
535- g_object_unref (proxy);
536- }
537- else
538- {
539- g_clear_error (&data->last_error);
540-
541- g_set_error (&data->last_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
542- _("Proxy protocol ā€œ%sā€ is not supported."),
543- protocol);
544-
545- enumerator_next_async (data, FALSE);
546- }
547+ connection_attempt_remove (attempt);
548+ data->successful_connections = g_slist_append (data->successful_connections, g_steal_pointer (&attempt));
549+ try_next_connection_or_finish (data, FALSE);
550 }
551
552 static gboolean
553@@ -1702,7 +1791,11 @@ on_connection_attempt_timeout (gpointer data)
554 {
555 ConnectionAttempt *attempt = data;
556
557- enumerator_next_async (attempt->data, TRUE);
558+ if (!attempt->data->enumeration_completed)
559+ {
560+ g_debug ("GSocketClient: Timeout reached, trying another enumeration");
561+ enumerator_next_async (attempt->data, TRUE);
562+ }
563
564 g_clear_pointer (&attempt->timeout_source, g_source_unref);
565 return G_SOURCE_REMOVE;
566@@ -1712,9 +1805,9 @@ static void
567 on_connection_cancelled (GCancellable *cancellable,
568 gpointer data)
569 {
570- GCancellable *attempt_cancellable = data;
571+ GCancellable *linked_cancellable = G_CANCELLABLE (data);
572
573- g_cancellable_cancel (attempt_cancellable);
574+ g_cancellable_cancel (linked_cancellable);
575 }
576
577 static void
578@@ -1738,39 +1831,49 @@ g_socket_client_enumerator_callback (GObject *object,
579 result, &error);
580 if (address == NULL)
581 {
582- if (data->connection_attempts)
583+ if (G_UNLIKELY (data->enumeration_completed))
584+ return;
585+
586+ data->enumeration_completed = TRUE;
587+ g_debug ("GSocketClient: Address enumeration completed (out of addresses)");
588+
589+ /* As per API docs: We only care about error if its the first call,
590+ after that the enumerator is done.
591+
592+ Note that we don't care about cancellation errors because
593+ task_completed_or_cancelled() above should handle that.
594+
595+ If this fails and nothing is in progress then we will complete task here.
596+ */
597+ if ((data->enumerated_at_least_once && !data->connection_attempts && !data->connection_in_progress) ||
598+ !data->enumerated_at_least_once)
599 {
600- g_object_unref (data->task);
601- return;
602+ g_debug ("GSocketClient: Address enumeration failed: %s", error ? error->message : NULL);
603+ if (data->last_error)
604+ {
605+ g_clear_error (&error);
606+ error = data->last_error;
607+ data->last_error = NULL;
608+ }
609+ else if (!error)
610+ {
611+ g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
612+ _("Unknown error on connect"));
613+ }
614+
615+ complete_connection_with_error (data, error);
616 }
617
618- g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_COMPLETE, data->connectable, NULL);
619- data->completed = TRUE;
620- if (!error)
621- {
622- if (data->last_error)
623- {
624- error = data->last_error;
625- data->last_error = NULL;
626- }
627- else
628- {
629- g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
630- _("Unknown error on connect"));
631- }
632- }
633- g_task_return_error (data->task, error);
634+ /* Enumeration should never trigger again, drop our ref */
635 g_object_unref (data->task);
636 return;
637 }
638
639+ data->enumerated_at_least_once = TRUE;
640+ g_debug ("GSocketClient: Address enumeration succeeded");
641 g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_RESOLVED,
642 data->connectable, NULL);
643
644- if (G_IS_PROXY_ADDRESS (address) &&
645- data->client->priv->enable_proxy)
646- data->proxy_addr = g_object_ref (G_PROXY_ADDRESS (address));
647-
648 g_clear_error (&data->last_error);
649
650 socket = create_socket (data->client, address, &data->last_error);
651@@ -1788,6 +1891,10 @@ g_socket_client_enumerator_callback (GObject *object,
652 attempt->cancellable = g_cancellable_new ();
653 attempt->connection = (GIOStream *)g_socket_connection_factory_create_connection (socket);
654 attempt->timeout_source = g_timeout_source_new (HAPPY_EYEBALLS_CONNECTION_ATTEMPT_TIMEOUT_MS);
655+
656+ if (G_IS_PROXY_ADDRESS (address) && data->client->priv->enable_proxy)
657+ attempt->proxy_addr = g_object_ref (G_PROXY_ADDRESS (address));
658+
659 g_source_set_callback (attempt->timeout_source, on_connection_attempt_timeout, attempt, NULL);
660 g_source_attach (attempt->timeout_source, g_main_context_get_thread_default ());
661 data->connection_attempts = g_slist_append (data->connection_attempts, attempt);
662@@ -1797,6 +1904,7 @@ g_socket_client_enumerator_callback (GObject *object,
663 g_object_ref (attempt->cancellable), g_object_unref);
664
665 g_socket_connection_set_cached_remote_address ((GSocketConnection *)attempt->connection, address);
666+ g_debug ("GSocketClient: Starting TCP connection attempt");
667 g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_CONNECTING, data->connectable, attempt->connection);
668 g_socket_connection_connect_async (G_SOCKET_CONNECTION (attempt->connection),
669 address,
670@@ -1849,24 +1957,48 @@ g_socket_client_connect_async (GSocketClient *client,
671 else
672 data->enumerator = g_socket_connectable_enumerate (connectable);
673
674- /* The flow and ownership here isn't quite obvious:
675- - The task starts an async attempt to connect.
676- - Each attempt holds a single ref on task.
677- - Each attempt may create new attempts by timing out (not a failure) so
678- there are multiple attempts happening in parallel.
679- - Upon failure an attempt will start a new attempt that steals its ref
680- until there are no more attempts left and it drops its ref.
681- - Upon success it will cancel all other attempts and continue on
682- to the rest of the connection (tls, proxies, etc) which do not
683- happen in parallel and at the very end drop its ref.
684- - Upon cancellation an attempt drops its ref.
685- */
686+ /* This function tries to match the behavior of g_socket_client_connect ()
687+ which is simple enough but much of it is done in parallel to be as responsive
688+ as possible as per Happy Eyeballs (RFC 8305). This complicates flow quite a
689+ bit but we can describe it in 3 sections:
690+
691+ Firstly we have address enumeration (DNS):
692+ - This may be triggered multiple times by enumerator_next_async().
693+ - It also has its own cancellable (data->enumeration_cancellable).
694+ - Enumeration is done lazily because GNetworkAddressAddressEnumerator
695+ also does work in parallel and may lazily add new addresses.
696+ - If the first enumeration errors then the task errors. Otherwise all enumerations
697+ will potentially be used (until task or enumeration is cancelled).
698+
699+ Then we start attempting connections (TCP):
700+ - Each connection is independent and kept in a ConnectionAttempt object.
701+ - They each hold a ref on the main task and have their own cancellable.
702+ - Multiple attempts may happen in parallel as per Happy Eyeballs.
703+ - Upon failure or timeouts more connection attempts are made.
704+ - If no connections succeed the task errors.
705+ - Upon success they are kept in a list of successful connections.
706+
707+ Lastly we connect at the application layer (TLS, Proxies):
708+ - These are done in serial.
709+ - The reasoning here is that Happy Eyeballs is about making bad connections responsive
710+ at the IP/TCP layers. Issues at the application layer are generally not due to
711+ connectivity issues but rather misconfiguration.
712+ - Upon failure it will try the next TCP connection until it runs out and
713+ the task errors.
714+ - Upon success it cancels everything remaining (enumeration and connections)
715+ and returns the connection.
716+ */
717
718 data->task = g_task_new (client, cancellable, callback, user_data);
719 g_task_set_check_cancellable (data->task, FALSE); /* We handle this manually */
720 g_task_set_source_tag (data->task, g_socket_client_connect_async);
721 g_task_set_task_data (data->task, data, (GDestroyNotify)g_socket_client_async_connect_data_free);
722
723+ data->enumeration_cancellable = g_cancellable_new ();
724+ if (cancellable)
725+ g_cancellable_connect (cancellable, G_CALLBACK (on_connection_cancelled),
726+ g_object_ref (data->enumeration_cancellable), g_object_unref);
727+
728 enumerator_next_async (data, FALSE);
729 }
730
731@@ -1985,6 +2117,7 @@ g_socket_client_connect_to_uri_async (GSocketClient *client,
732 }
733 else
734 {
735+ g_debug("g_socket_client_connect_to_uri_async");
736 g_socket_client_connect_async (client,
737 connectable, cancellable,
738 callback, user_data);
739--
7402.23.0
741