Home | History | Annotate | Download | only in proxy
      1 /* Copyright (C) 2007-2008 The Android Open Source Project
      2 **
      3 ** This software is licensed under the terms of the GNU General Public
      4 ** License version 2, as published by the Free Software Foundation, and
      5 ** may be copied, distributed, and modified under those terms.
      6 **
      7 ** This program is distributed in the hope that it will be useful,
      8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
      9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     10 ** GNU General Public License for more details.
     11 */
     12 #include "proxy_http_int.h"
     13 #include <errno.h>
     14 #include <stdio.h>
     15 #include <string.h>
     16 #include "qemu-common.h"
     17 
     18 /* A HttpConnector implements a non-HTTP proxied connection
     19  * through the CONNECT method. Many firewalls are configured
     20  * to reject these for port 80, so these connections should
     21  * use a HttpRewriter instead.
     22  */
     23 
     24 typedef enum {
     25     STATE_NONE = 0,
     26     STATE_CONNECTING,           /* connecting to the server */
     27     STATE_SEND_HEADER,          /* connected, sending header to the server */
     28     STATE_RECEIVE_ANSWER_LINE1,
     29     STATE_RECEIVE_ANSWER_LINE2  /* connected, reading server's answer */
     30 } ConnectorState;
     31 
     32 typedef struct Connection {
     33     ProxyConnection  root[1];
     34     ConnectorState   state;
     35 } Connection;
     36 
     37 
     38 static void
     39 connection_free( ProxyConnection*  root )
     40 {
     41     proxy_connection_done(root);
     42     qemu_free(root);
     43 }
     44 
     45 
     46 
     47 #define  HTTP_VERSION  "1.1"
     48 
     49 static int
     50 connection_init( Connection*  conn )
     51 {
     52     HttpService*      service = (HttpService*) conn->root->service;
     53     ProxyConnection*  root    = conn->root;
     54     stralloc_t*       str     = root->str;
     55 
     56     proxy_connection_rewind(root);
     57     stralloc_add_format(str, "CONNECT %s HTTP/" HTTP_VERSION "\r\n",
     58                         sock_address_to_string(&root->address));
     59 
     60     stralloc_add_bytes(str, service->footer, service->footer_len);
     61 
     62     if (!socket_connect( root->socket, &service->server_addr )) {
     63         /* immediate connection ?? */
     64         conn->state = STATE_SEND_HEADER;
     65         PROXY_LOG("%s: immediate connection", root->name);
     66     }
     67     else {
     68         if (errno == EINPROGRESS || errno == EWOULDBLOCK) {
     69             conn->state = STATE_CONNECTING;
     70             PROXY_LOG("%s: connecting", root->name);
     71         }
     72         else {
     73             PROXY_LOG("%s: cannot connect to proxy: %s", root->name, errno_str);
     74             return -1;
     75         }
     76     }
     77     return 0;
     78 }
     79 
     80 
     81 static void
     82 connection_select( ProxyConnection*   root,
     83                    ProxySelect*       sel )
     84 {
     85     unsigned     flags;
     86     Connection*  conn = (Connection*)root;
     87 
     88     switch (conn->state) {
     89         case STATE_RECEIVE_ANSWER_LINE1:
     90         case STATE_RECEIVE_ANSWER_LINE2:
     91             flags = PROXY_SELECT_READ;
     92             break;
     93 
     94         case STATE_CONNECTING:
     95         case STATE_SEND_HEADER:
     96             flags = PROXY_SELECT_WRITE;
     97             break;
     98 
     99         default:
    100             flags = 0;
    101     };
    102     proxy_select_set(sel, root->socket, flags);
    103 }
    104 
    105 static void
    106 connection_poll( ProxyConnection*   root,
    107                  ProxySelect*       sel )
    108 {
    109     DataStatus   ret  = DATA_NEED_MORE;
    110     Connection*  conn = (Connection*)root;
    111     int          fd   = root->socket;
    112 
    113     if (!proxy_select_poll(sel, fd))
    114         return;
    115 
    116     switch (conn->state)
    117     {
    118         case STATE_CONNECTING:
    119             PROXY_LOG("%s: connected to http proxy, sending header", root->name);
    120             conn->state = STATE_SEND_HEADER;
    121             break;
    122 
    123         case STATE_SEND_HEADER:
    124             ret = proxy_connection_send(root, fd);
    125             if (ret == DATA_COMPLETED) {
    126                 conn->state = STATE_RECEIVE_ANSWER_LINE1;
    127                 PROXY_LOG("%s: header sent, receiving first answer line", root->name);
    128             }
    129             break;
    130 
    131         case STATE_RECEIVE_ANSWER_LINE1:
    132         case STATE_RECEIVE_ANSWER_LINE2:
    133             ret = proxy_connection_receive_line(root, root->socket);
    134             if (ret == DATA_COMPLETED) {
    135                 if (conn->state == STATE_RECEIVE_ANSWER_LINE1) {
    136                     int  http1, http2, codenum;
    137                     const char*  line = root->str->s;
    138 
    139                     if ( sscanf(line, "HTTP/%d.%d %d", &http1, &http2, &codenum) != 3 ) {
    140                         PROXY_LOG( "%s: invalid answer from proxy: '%s'",
    141                                     root->name, line );
    142                         ret = DATA_ERROR;
    143                         break;
    144                     }
    145 
    146                     /* success is 2xx */
    147                     if (codenum/2 != 100) {
    148                         PROXY_LOG( "%s: connection refused, error=%d",
    149                                     root->name, codenum );
    150                         proxy_connection_free( root, 0, PROXY_EVENT_CONNECTION_REFUSED );
    151                         return;
    152                     }
    153                     PROXY_LOG("%s: receiving second answer line", root->name);
    154                     conn->state = STATE_RECEIVE_ANSWER_LINE2;
    155                     proxy_connection_rewind(root);
    156                 } else {
    157                     /* ok, we're connected */
    158                     PROXY_LOG("%s: connection succeeded", root->name);
    159                     proxy_connection_free( root, 1, PROXY_EVENT_CONNECTED );
    160                 }
    161             }
    162             break;
    163 
    164         default:
    165             PROXY_LOG("%s: invalid state for read event: %d", root->name, conn->state);
    166     }
    167 
    168     if (ret == DATA_ERROR) {
    169         proxy_connection_free( root, 0, PROXY_EVENT_SERVER_ERROR );
    170     }
    171 }
    172 
    173 
    174 
    175 ProxyConnection*
    176 http_connector_connect( HttpService*  service,
    177                         SockAddress*  address )
    178 {
    179     Connection*  conn;
    180     int          s;
    181 
    182     s = socket_create_inet( SOCKET_STREAM );
    183     if (s < 0)
    184         return NULL;
    185 
    186     conn = qemu_mallocz(sizeof(*conn));
    187     if (conn == NULL) {
    188         socket_close(s);
    189         return NULL;
    190     }
    191 
    192     proxy_connection_init( conn->root, s, address, service->root,
    193                            connection_free,
    194                            connection_select,
    195                            connection_poll );
    196 
    197     if ( connection_init( conn ) < 0 ) {
    198         connection_free( conn->root );
    199         return NULL;
    200     }
    201 
    202     return conn->root;
    203 }
    204