proxy_http_connector.c revision df7881f07f53b041dc0568be8528e9dbb74994cc
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 "vl.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
24typedef 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
32typedef struct Connection {
33    ProxyConnection  root[1];
34    ConnectorState   state;
35} Connection;
36
37
38static void
39connection_free( ProxyConnection*  root )
40{
41    proxy_connection_done(root);
42    qemu_free(root);
43}
44
45
46
47#define  HTTP_VERSION  "1.1"
48
49static int
50connection_init( Connection*  conn )
51{
52    HttpService*      service = (HttpService*) conn->root->service;
53    ProxyConnection*  root    = conn->root;
54    stralloc_t*       str     = root->str;
55    int               ret;
56    uint32_t          address  = ntohl(root->address.sin_addr.s_addr);
57    int               port     = ntohs(root->address.sin_port);
58
59    proxy_connection_rewind(root);
60    stralloc_add_format(str, "CONNECT %d.%d.%d.%d:%d HTTP/" HTTP_VERSION "\r\n",
61                 (address >> 24) & 0xff, (address >> 16) & 0xff,
62                 (address >> 8)  & 0xff, address & 0xff, port);
63
64    stralloc_add_bytes(str, service->footer, service->footer_len);
65
66    do {
67        ret = connect( root->socket,
68                    (struct sockaddr*) &service->server_addr,
69                    sizeof(service->server_addr) );
70    } while (ret < 0 && socket_errno == EINTR);
71
72    if (ret == 0) {
73        /* immediate connection ?? */
74        conn->state = STATE_SEND_HEADER;
75        PROXY_LOG("%s: immediate connection", root->name);
76    }
77    else {
78        if (socket_errno == EINPROGRESS || socket_errno == EWOULDBLOCK) {
79            conn->state = STATE_CONNECTING;
80            PROXY_LOG("%s: connecting", root->name);
81        }
82        else {
83            PROXY_LOG("%s: cannot connect to proxy: %s", root->name, socket_errstr());
84            return -1;
85        }
86    }
87    return 0;
88}
89
90
91static void
92connection_select( ProxyConnection*   root,
93                   ProxySelect*       sel )
94{
95    unsigned     flags;
96    Connection*  conn = (Connection*)root;
97
98    switch (conn->state) {
99        case STATE_RECEIVE_ANSWER_LINE1:
100        case STATE_RECEIVE_ANSWER_LINE2:
101            flags = PROXY_SELECT_READ;
102            break;
103
104        case STATE_CONNECTING:
105        case STATE_SEND_HEADER:
106            flags = PROXY_SELECT_WRITE;
107            break;
108
109        default:
110            flags = 0;
111    };
112    proxy_select_set(sel, root->socket, flags);
113}
114
115static void
116connection_poll( ProxyConnection*   root,
117                 ProxySelect*       sel )
118{
119    DataStatus   ret  = DATA_NEED_MORE;
120    Connection*  conn = (Connection*)root;
121    int          fd   = root->socket;
122
123    if (!proxy_select_poll(sel, fd))
124        return;
125
126    switch (conn->state)
127    {
128        case STATE_CONNECTING:
129            PROXY_LOG("%s: connected to http proxy, sending header", root->name);
130            conn->state = STATE_SEND_HEADER;
131            break;
132
133        case STATE_SEND_HEADER:
134            ret = proxy_connection_send(root, fd);
135            if (ret == DATA_COMPLETED) {
136                conn->state = STATE_RECEIVE_ANSWER_LINE1;
137                PROXY_LOG("%s: header sent, receiving first answer line", root->name);
138            }
139            break;
140
141        case STATE_RECEIVE_ANSWER_LINE1:
142        case STATE_RECEIVE_ANSWER_LINE2:
143            ret = proxy_connection_receive_line(root, root->socket);
144            if (ret == DATA_COMPLETED) {
145                if (conn->state == STATE_RECEIVE_ANSWER_LINE1) {
146                    int  http1, http2, codenum;
147                    const char*  line = root->str->s;
148
149                    if ( sscanf(line, "HTTP/%d.%d %d", &http1, &http2, &codenum) != 3 ) {
150                        PROXY_LOG( "%s: invalid answer from proxy: '%s'",
151                                    root->name, line );
152                        ret = DATA_ERROR;
153                        break;
154                    }
155
156                    /* success is 2xx */
157                    if (codenum/2 != 100) {
158                        PROXY_LOG( "%s: connection refused, error=%d",
159                                    root->name, codenum );
160                        proxy_connection_free( root, 0, PROXY_EVENT_CONNECTION_REFUSED );
161                        return;
162                    }
163                    PROXY_LOG("%s: receiving second answer line", root->name);
164                    conn->state = STATE_RECEIVE_ANSWER_LINE2;
165                    proxy_connection_rewind(root);
166                } else {
167                    /* ok, we're connected */
168                    PROXY_LOG("%s: connection succeeded", root->name);
169                    proxy_connection_free( root, 1, PROXY_EVENT_CONNECTED );
170                }
171            }
172            break;
173
174        default:
175            PROXY_LOG("%s: invalid state for read event: %d", root->name, conn->state);
176    }
177
178    if (ret == DATA_ERROR) {
179        proxy_connection_free( root, 0, PROXY_EVENT_SERVER_ERROR );
180    }
181}
182
183
184
185ProxyConnection*
186http_connector_connect( HttpService*         service,
187                        struct sockaddr_in*  address )
188{
189    Connection*  conn;
190    int          s;
191
192    s = socket(AF_INET, SOCK_STREAM, 0);
193    if (s < 0)
194        return NULL;
195
196    conn = qemu_mallocz(sizeof(*conn));
197    if (conn == NULL) {
198        socket_close(s);
199        return NULL;
200    }
201
202    proxy_connection_init( conn->root, s, address, service->root,
203                           connection_free,
204                           connection_select,
205                           connection_poll );
206
207    if ( connection_init( conn ) < 0 ) {
208        connection_free( conn->root );
209        return NULL;
210    }
211
212    return conn->root;
213}
214