diff options
| author | haiqing <haiqing.bai@windriver.com> | 2020-03-27 10:38:05 +0800 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2020-04-17 08:29:02 +0100 |
| commit | a41085d1a63cc629cfb9b4fb1fc30ec43d8e56a7 (patch) | |
| tree | d328c78409d5984eeb08be1aab803769288419dc | |
| parent | eb029c9ac0f5820c05912760a95388827a6d3f0d (diff) | |
| download | poky-a41085d1a63cc629cfb9b4fb1fc30ec43d8e56a7.tar.gz | |
glib-2.0: fix CVE-2020-6750
GSocketClient in GNOME GLib through 2.62.4 may occasionally connect directly
to a target address instead of connecting via a proxy server when configured
to do so, because the proxy_addr field is mishandled. This bug is timing-dependent
and may occur only sporadically depending on network delays. The greatest security
relevance is in use cases where a proxy is used to help with privacy/anonymity,
even though there is no technical barrier to a direct connection.
(From OE-Core rev: 29ed9fc7341cc3db716115aef1a6910fdb893145)
Signed-off-by: Haiqing Bai <Haiqing.Bai@windriver.com>
Signed-off-by: Anuj Mittal <anuj.mittal@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
| -rw-r--r-- | meta/recipes-core/glib-2.0/glib-2.0/CVE-2020-6750.patch | 741 | ||||
| -rw-r--r-- | meta/recipes-core/glib-2.0/glib-2.0_2.60.7.bb | 1 |
2 files changed, 742 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 @@ | |||
| 1 | From 747f2c646f5a86ac58ad59be08036e81388e971d Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Patrick Griffis <tingping@tingping.se> | ||
| 3 | Date: Thu, 23 Jan 2020 19:58:41 -0800 | ||
| 4 | Subject: [PATCH] Refactor g_socket_client_connect_async() | ||
| 5 | |||
| 6 | This 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 | |||
| 36 | Closes #1995 | ||
| 37 | |||
| 38 | CVE: CVE-2020-6750 | ||
| 39 | |||
| 40 | Upstream-Status: Backport [ https://gitlab.gnome.org/GNOME/glib.git; | ||
| 41 | commit=2722620e3291b930a3a228100d7c0e07b69534e3 ] | ||
| 42 | |||
| 43 | Signed-off-by: Haiqing Bai <Haiqing.Bai@windriver.com> | ||
| 44 | --- | ||
| 45 | gio/gsocketclient.c | 459 ++++++++++++++++++++++++++++---------------- | ||
| 46 | 1 file changed, 296 insertions(+), 163 deletions(-) | ||
| 47 | |||
| 48 | diff --git a/gio/gsocketclient.c b/gio/gsocketclient.c | ||
| 49 | index 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 | -- | ||
| 740 | 2.23.0 | ||
| 741 | |||
diff --git a/meta/recipes-core/glib-2.0/glib-2.0_2.60.7.bb b/meta/recipes-core/glib-2.0/glib-2.0_2.60.7.bb index 5aefa6ad8b..5be81a8f31 100644 --- a/meta/recipes-core/glib-2.0/glib-2.0_2.60.7.bb +++ b/meta/recipes-core/glib-2.0/glib-2.0_2.60.7.bb | |||
| @@ -16,6 +16,7 @@ SRC_URI = "${GNOME_MIRROR}/glib/${SHRT_VER}/glib-${PV}.tar.xz \ | |||
| 16 | file://0001-Do-not-write-bindir-into-pkg-config-files.patch \ | 16 | file://0001-Do-not-write-bindir-into-pkg-config-files.patch \ |
| 17 | file://0001-meson.build-do-not-hardcode-linux-as-the-host-system.patch \ | 17 | file://0001-meson.build-do-not-hardcode-linux-as-the-host-system.patch \ |
| 18 | file://0001-meson-do-a-build-time-check-for-strlcpy-before-attem.patch \ | 18 | file://0001-meson-do-a-build-time-check-for-strlcpy-before-attem.patch \ |
| 19 | file://CVE-2020-6750.patch \ | ||
| 19 | " | 20 | " |
| 20 | 21 | ||
| 21 | SRC_URI_append_class-native = " file://relocate-modules.patch" | 22 | SRC_URI_append_class-native = " file://relocate-modules.patch" |
