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