• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
connection_free(ProxyConnection * root)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
connection_init(Connection * conn)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
connection_select(ProxyConnection * root,ProxySelect * sel)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
connection_poll(ProxyConnection * root,ProxySelect * sel)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*
http_connector_connect(HttpService * service,SockAddress * address)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