LCOV - code coverage report
Current view: top level - gio - ghttpproxy.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 1.4 % 143 2
Test Date: 2024-11-26 05:23:01 Functions: 21.1 % 19 4
Branches: - 0 0

             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 : }
        

Generated by: LCOV version 2.0-1