blob: 6c43036631faf32fb88587856476ce066769c7e8 [file] [log] [blame]
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001/* 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 <stdio.h>
14#include <string.h>
15#include "qemu-common.h"
16#include "android/utils/system.h" /* strsep */
17
18/* this implements a transparent HTTP rewriting proxy
19 *
20 * this is needed because the HTTP spec mandates that
21 * any query made to a proxy uses an absolute URI as
22 * in:
23 *
24 * GET http://www.example.com/index.html HTTP/1.1
25 *
26 * while the Android browser will think it's talking to
27 * a normal web server and will issue a:
28 *
29 * GET /index.html HTTP/1.1
30 * Host: www.example.com
31 *
32 * what we do here is thus the following:
33 *
34 * - read the request header
35 * - rewrite the request's URI to use absolute URI
36 * - send the rewritten header to the proxy
37 * - then read the rest of the request, and tunnel it to the
38 * proxy as well
39 * - read the answer as-is and send it back to the system
40 *
41 * this sounds all easy, but the rules for computing the
42 * sizes of HTTP Message Bodies makes the implementation
43 * a *bit* funky.
44 */
45
46/* define D_ACTIVE to 1 to dump additionnal debugging
47 * info when -debug-proxy is used. These are only needed
48 * when debugging the proxy code.
49 */
50#define D_ACTIVE 1
51
52#if D_ACTIVE
53# define D(...) PROXY_LOG(__VA_ARGS__)
54#else
55# define D(...) ((void)0)
56#endif
57
58
59/** *************************************************************
60 **
61 ** HTTP HEADERS
62 **
63 **/
64
David Turner47356942009-04-05 14:23:06 -070065/* HttpHeader is a simple structure used to hold a (key,value)
66 * pair in a linked list.
67 */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080068typedef struct HttpHeader {
69 struct HttpHeader* next;
70 const char* key;
71 const char* value;
72} HttpHeader;
73
74static void
75http_header_free( HttpHeader* h )
76{
77 if (h) {
David 'Digit' Turneraa8236d2014-01-10 17:02:29 +010078 g_free((char*)h->value);
79 g_free(h);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080080 }
81}
82
83static int
84http_header_append( HttpHeader* h, const char* value )
85{
86 int old = strlen(h->value);
87 int new = strlen(value);
88 char* s = realloc((char*)h->value, old+new+1);
89 if (s == NULL)
90 return -1;
91 memcpy(s + old, value, new+1);
92 h->value = (const char*)s;
93 return 0;
94}
95
96static HttpHeader*
97http_header_alloc( const char* key, const char* value )
98{
99 int len = strlen(key)+1;
100 HttpHeader* h = malloc(sizeof(*h) + len+1);
101 if (h) {
102 h->next = NULL;
103 h->key = (const char*)(h+1);
104 memcpy( (char*)h->key, key, len );
David 'Digit' Turneraa8236d2014-01-10 17:02:29 +0100105 h->value = g_strdup(value);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800106 }
107 return h;
108}
109
David Turner47356942009-04-05 14:23:06 -0700110/** *************************************************************
111 **
112 ** HTTP HEADERS LIST
113 **
114 **/
115
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800116typedef struct {
117 HttpHeader* first;
118 HttpHeader* last;
119} HttpHeaderList;
120
121static void
122http_header_list_init( HttpHeaderList* l )
123{
124 l->first = l->last = NULL;
125}
126
127static void
128http_header_list_done( HttpHeaderList* l )
129{
130 while (l->first) {
131 HttpHeader* h = l->first;
132 l->first = h->next;
133 http_header_free(h);
134 }
135 l->last = NULL;
136}
137
138static void
139http_header_list_add( HttpHeaderList* l,
140 HttpHeader* h )
141{
142 if (!l->first) {
143 l->first = h;
144 } else {
145 l->last->next = h;
146 }
147 h->next = NULL;
148 l->last = h;
149}
150
151static const char*
152http_header_list_find( HttpHeaderList* l,
153 const char* key )
154{
155 HttpHeader* h;
156 for (h = l->first; h; h = h->next)
157 if (!strcasecmp(h->key, key))
158 return h->value;
159
160 return NULL;
161}
162
163/** *************************************************************
164 **
165 ** HTTP REQUEST AND REPLY
166 **
167 **/
168
169typedef enum {
170 HTTP_REQUEST_UNSUPPORTED = 0,
171 HTTP_REQUEST_GET,
172 HTTP_REQUEST_HEAD,
173 HTTP_REQUEST_POST,
174 HTTP_REQUEST_PUT,
175 HTTP_REQUEST_DELETE,
176} HttpRequestType;
177
David Turner47356942009-04-05 14:23:06 -0700178/* HttpRequest is used both to store information about a specific
179 * request and the corresponding reply
180 */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800181typedef struct {
David Turner47356942009-04-05 14:23:06 -0700182 HttpRequestType req_type; /* request type */
183 char* req_method; /* "GET", "POST", "HEAD", etc... */
184 char* req_uri; /* the request URI */
185 char* req_version; /* "HTTP/1.0" or "HTTP/1.1" */
186 char* rep_version; /* reply version string */
187 int rep_code; /* reply code as decimal */
188 char* rep_readable; /* human-friendly reply/error message */
189 HttpHeaderList headers[1]; /* headers */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800190} HttpRequest;
191
192
193static HttpRequest*
194http_request_alloc( const char* method,
195 const char* uri,
196 const char* version )
197{
198 HttpRequest* r = malloc(sizeof(*r));
199
David 'Digit' Turneraa8236d2014-01-10 17:02:29 +0100200 r->req_method = g_strdup(method);
201 r->req_uri = g_strdup(uri);
202 r->req_version = g_strdup(version);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800203 r->rep_version = NULL;
204 r->rep_code = -1;
205 r->rep_readable = NULL;
206
207 if (!strcmp(method,"GET")) {
208 r->req_type = HTTP_REQUEST_GET;
209 } else if (!strcmp(method,"POST")) {
210 r->req_type = HTTP_REQUEST_POST;
211 } else if (!strcmp(method,"HEAD")) {
212 r->req_type = HTTP_REQUEST_HEAD;
213 } else if (!strcmp(method,"PUT")) {
214 r->req_type = HTTP_REQUEST_PUT;
215 } else if (!strcmp(method,"DELETE")) {
216 r->req_type = HTTP_REQUEST_DELETE;
217 } else
218 r->req_type = HTTP_REQUEST_UNSUPPORTED;
219
220 http_header_list_init(r->headers);
221 return r;
222}
223
224static void
225http_request_replace_uri( HttpRequest* r,
226 const char* uri )
227{
228 const char* old = r->req_uri;
David 'Digit' Turneraa8236d2014-01-10 17:02:29 +0100229 r->req_uri = g_strdup(uri);
230 g_free((char*)old);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800231}
232
233static void
234http_request_free( HttpRequest* r )
235{
236 if (r) {
237 http_header_list_done(r->headers);
238
David 'Digit' Turneraa8236d2014-01-10 17:02:29 +0100239 g_free(r->req_method);
240 g_free(r->req_uri);
241 g_free(r->req_version);
242 g_free(r->rep_version);
243 g_free(r->rep_readable);
244 g_free(r);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800245 }
246}
247
248static char*
249http_request_find_header( HttpRequest* r,
250 const char* key )
251{
252 return (char*)http_header_list_find(r->headers, key);
253}
254
255
256static int
257http_request_add_header( HttpRequest* r,
258 const char* key,
259 const char* value )
260{
261 HttpHeader* h = http_header_alloc(key,value);
262 if (h) {
263 http_header_list_add(r->headers, h);
264 return 0;
265 }
266 return -1;
267}
268
269static int
270http_request_add_to_last_header( HttpRequest* r,
271 const char* line )
272{
273 if (r->headers->last) {
274 return http_header_append( r->headers->last, line );
275 } else {
276 return -1;
277 }
278}
279
280static int
281http_request_set_reply( HttpRequest* r,
282 const char* version,
283 const char* code,
284 const char* readable )
285{
286 if (strcmp(version,"HTTP/1.0") && strcmp(version,"HTTP/1.1")) {
287 PROXY_LOG("%s: bad reply protocol: %s", __FUNCTION__, version);
288 return -1;
289 }
290 r->rep_code = atoi(code);
291 if (r->rep_code == 0) {
292 PROXY_LOG("%s: bad reply code: %d", __FUNCTION__, code);
293 return -1;
294 }
295
David 'Digit' Turneraa8236d2014-01-10 17:02:29 +0100296 r->rep_version = g_strdup(version);
297 r->rep_readable = g_strdup(readable);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800298
299 /* reset the list of headers */
300 http_header_list_done(r->headers);
301 return 0;
302}
303
304/** *************************************************************
305 **
306 ** REWRITER CONNECTION
307 **
308 **/
309
310typedef enum {
311 STATE_CONNECTING = 0,
312 STATE_CREATE_SOCKET_PAIR,
313 STATE_REQUEST_FIRST_LINE,
314 STATE_REQUEST_HEADERS,
315 STATE_REQUEST_SEND,
316 STATE_REQUEST_BODY,
317 STATE_REPLY_FIRST_LINE,
318 STATE_REPLY_HEADERS,
319 STATE_REPLY_SEND,
320 STATE_REPLY_BODY,
321} ConnectionState;
322
323/* root->socket is connected to the proxy server. while
324 * slirp_fd is connected to the slirp code through a
325 * socket_pair() we created for this specific purpose.
326 */
327
328typedef enum {
329 BODY_NONE = 0,
330 BODY_KNOWN_LENGTH,
331 BODY_UNTIL_CLOSE,
332 BODY_CHUNKED,
333 BODY_MODE_MAX
334} BodyMode;
335
336static const char* const body_mode_str[BODY_MODE_MAX] = {
337 "NONE", "KNOWN_LENGTH", "UNTIL_CLOSE", "CHUNKED"
338};
339
David 'Digit' Turnerf9c98df2011-10-11 04:11:20 +0200340enum {
341 CHUNK_HEADER, // Waiting for a chunk header + CR LF
342 CHUNK_DATA, // Waiting for chunk data
343 CHUNK_DATA_END, // Waiting for the CR LF after the chunk data
344 CHUNK_TRAILER // Waiting for the chunk trailer + CR LF
345};
346
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800347typedef struct {
348 ProxyConnection root[1];
349 int slirp_fd;
350 ConnectionState state;
351 HttpRequest* request;
352 BodyMode body_mode;
353 int64_t body_length;
354 int64_t body_total;
355 int64_t body_sent;
356 int64_t chunk_length;
357 int64_t chunk_total;
David 'Digit' Turnerf9c98df2011-10-11 04:11:20 +0200358 int chunk_state;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800359 char body_has_data;
360 char body_is_full;
361 char body_is_closed;
362 char parse_chunk_header;
363 char parse_chunk_trailer;
364} RewriteConnection;
365
366
367static void
368rewrite_connection_free( ProxyConnection* root )
369{
370 RewriteConnection* conn = (RewriteConnection*)root;
371
372 if (conn->slirp_fd >= 0) {
373 socket_close(conn->slirp_fd);
374 conn->slirp_fd = -1;
375 }
376 http_request_free(conn->request);
377 proxy_connection_done(root);
David 'Digit' Turneraa8236d2014-01-10 17:02:29 +0100378 g_free(conn);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800379}
380
381
382static int
383rewrite_connection_init( RewriteConnection* conn )
384{
385 HttpService* service = (HttpService*) conn->root->service;
386 ProxyConnection* root = conn->root;
387
388 conn->slirp_fd = -1;
389 conn->state = STATE_CONNECTING;
390
391 if (socket_connect( root->socket, &service->server_addr ) < 0) {
David 'Digit' Turnerce0f4b02010-03-25 11:11:29 -0700392 if (errno == EINPROGRESS || errno == EWOULDBLOCK || errno == EAGAIN) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800393 PROXY_LOG("%s: connecting", conn->root->name);
394 }
395 else {
396 PROXY_LOG("%s: cannot connect to proxy: %s", root->name, errno_str);
397 return -1;
398 }
399 }
400 else {
401 PROXY_LOG("%s: immediate connection", root->name);
402 conn->state = STATE_CREATE_SOCKET_PAIR;
403 }
404 return 0;
405}
406
407static int
408rewrite_connection_create_sockets( RewriteConnection* conn )
409{
410 /* immediate connection to the proxy. now create a socket
411 * pair and send a 'success' event to slirp */
412 int slirp_1;
413 ProxyConnection* root = conn->root;
414
415 if (socket_pair( &slirp_1, &conn->slirp_fd ) < 0) {
416 PROXY_LOG("%s: coult not create socket pair: %s",
417 root->name, errno_str);
418 return -1;
419 }
420
421 root->ev_func( root->ev_opaque, slirp_1, PROXY_EVENT_CONNECTED );
422 conn->state = STATE_REQUEST_FIRST_LINE;
423 return 0;
424}
425
426
427/* read the first line of a given HTTP request. returns -1/0/+1 */
428static DataStatus
429rewrite_connection_read_request( RewriteConnection* conn )
430{
431 ProxyConnection* root = conn->root;
432 DataStatus ret;
433
434 ret = proxy_connection_receive_line(root, conn->slirp_fd);
435 if (ret == DATA_COMPLETED) {
436 /* now parse the first line to see if we can handle it */
437 char* line = root->str->s;
438 char* method;
439 char* uri;
440 char* version;
441 char* p = line;
442
443 method = strsep(&p, " ");
444 if (p == NULL) {
David 'Digit' Turnerc0052462014-02-25 18:39:29 +0100445 PROXY_LOG("%s: can't parse method in '%'",
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800446 root->name, line);
447 return DATA_ERROR;
448 }
449 uri = strsep(&p, " ");
450 if (p == NULL) {
451 PROXY_LOG( "%s: can't parse URI in '%s'",
452 root->name, line);
453 return DATA_ERROR;
454 }
455 version = strsep(&p, " ");
456 if (p != NULL) {
457 PROXY_LOG( "%s: extra data after version in '%s'",
458 root->name, line);
459 return DATA_ERROR;
460 }
461 if (conn->request)
462 http_request_free(conn->request);
463
464 conn->request = http_request_alloc( method, uri, version );
465 if (!conn->request)
466 return DATA_ERROR;
467
468 proxy_connection_rewind(root);
469 }
470 return ret;
471}
472
473
474static DataStatus
475rewrite_connection_read_reply( RewriteConnection* conn )
476{
477 ProxyConnection* root = conn->root;
478 DataStatus ret;
479
480 ret = proxy_connection_receive_line( root, root->socket );
481 if (ret == DATA_COMPLETED) {
482 HttpRequest* request = conn->request;
483
484 char* line = stralloc_cstr( root->str );
485 char* p = line;
486 char* protocol;
487 char* number;
488 char* readable;
489
490 protocol = strsep(&p, " ");
491 if (p == NULL) {
492 PROXY_LOG("%s: can't parse response protocol: '%s'",
493 root->name, line);
494 return DATA_ERROR;
495 }
496 number = strsep(&p, " ");
497 if (p == NULL) {
498 PROXY_LOG("%s: can't parse response number: '%s'",
499 root->name, line);
500 return DATA_ERROR;
501 }
502 readable = p;
503
504 if (http_request_set_reply(request, protocol, number, readable) < 0)
505 return DATA_ERROR;
506
507 proxy_connection_rewind(root);
508 }
509 return ret;
510}
511
512
513static DataStatus
514rewrite_connection_read_headers( RewriteConnection* conn,
515 int fd )
516{
517 int ret;
518 ProxyConnection* root = conn->root;
519
520 for (;;) {
521 char* line;
522 stralloc_t* str = root->str;
523
524 ret = proxy_connection_receive_line(root, fd);
525 if (ret != DATA_COMPLETED)
526 break;
527
528 str->n = 0;
529 line = str->s;
530
531 if (line[0] == 0) {
532 /* an empty line means the end of headers */
533 ret = 1;
534 break;
535 }
536
537 /* it this a continuation ? */
538 if (line[0] == ' ' || line[0] == '\t') {
539 ret = http_request_add_to_last_header( conn->request, line );
540 }
541 else {
542 char* key;
543 char* value;
544
545 value = line;
546 key = strsep(&value, ":");
547 if (value == NULL) {
548 PROXY_LOG("%s: can't parse header '%s'", root->name, line);
549 ret = -1;
550 break;
551 }
552 value += strspn(value, " ");
553 if (http_request_add_header(conn->request, key, value) < 0)
554 ret = -1;
555 }
556 if (ret == DATA_ERROR)
557 break;
558 }
559 return ret;
560}
561
562static int
563rewrite_connection_rewrite_request( RewriteConnection* conn )
564{
565 ProxyConnection* root = conn->root;
566 HttpService* service = (HttpService*) root->service;
567 HttpRequest* r = conn->request;
568 stralloc_t* str = root->str;
569 HttpHeader* h;
570
571 proxy_connection_rewind(conn->root);
572
573 /* only rewrite the URI if it is not absolute */
574 if (r->req_uri[0] == '/') {
575 char* host = http_request_find_header(r, "Host");
576 if (host == NULL) {
577 PROXY_LOG("%s: uh oh, not Host: in request ?", root->name);
578 } else {
579 /* now create new URI */
580 stralloc_add_str(str, "http://");
581 stralloc_add_str(str, host);
582 stralloc_add_str(str, r->req_uri);
583 http_request_replace_uri(r, stralloc_cstr(str));
584 proxy_connection_rewind(root);
585 }
586 }
587
588 stralloc_format( str, "%s %s %s\r\n", r->req_method, r->req_uri, r->req_version );
589 for (h = r->headers->first; h; h = h->next) {
590 stralloc_add_format( str, "%s: %s\r\n", h->key, h->value );
591 }
592 /* add the service's footer - includes final \r\n */
593 stralloc_add_bytes( str, service->footer, service->footer_len );
594
595 return 0;
596}
597
598static int
599rewrite_connection_rewrite_reply( RewriteConnection* conn )
600{
601 HttpRequest* r = conn->request;
602 ProxyConnection* root = conn->root;
603 stralloc_t* str = root->str;
604 HttpHeader* h;
605
606 proxy_connection_rewind(root);
607 stralloc_format(str, "%s %d %s\r\n", r->rep_version, r->rep_code, r->rep_readable);
608 for (h = r->headers->first; h; h = h->next) {
609 stralloc_add_format(str, "%s: %s\r\n", h->key, h->value);
610 }
611 stralloc_add_str(str, "\r\n");
612
613 return 0;
614}
615
616
617static int
618rewrite_connection_get_body_length( RewriteConnection* conn,
619 int is_request )
620{
621 HttpRequest* r = conn->request;
622 ProxyConnection* root = conn->root;
623 char* content_length;
624 char* transfer_encoding;
625
626 conn->body_mode = BODY_NONE;
627 conn->body_length = 0;
628 conn->body_total = 0;
629 conn->body_sent = 0;
630 conn->body_is_closed = 0;
631 conn->body_is_full = 0;
632 conn->body_has_data = 0;
633
634 proxy_connection_rewind(root);
635
636 if (is_request) {
637 /* only POST and PUT should have a body */
638 if (r->req_type != HTTP_REQUEST_POST &&
639 r->req_type != HTTP_REQUEST_PUT)
640 {
641 return 0;
642 }
643 } else {
644 /* HTTP 1.1 Section 4.3 Message Body states that HEAD requests must not have
645 * a message body, as well as any 1xx, 204 and 304 replies */
646 if (r->req_type == HTTP_REQUEST_HEAD || r->rep_code/100 == 1 ||
647 r->rep_code == 204 || r->rep_code == 304)
648 return 0;
649 }
650
651 content_length = http_request_find_header(r, "Content-Length");
652 if (content_length != NULL) {
653 char* end;
654 int64_t body_len = strtoll( content_length, &end, 10 );
655 if (*end != '\0' || *content_length == '\0' || body_len < 0) {
656 PROXY_LOG("%s: bad content length: %s", root->name, content_length);
657 return DATA_ERROR;
658 }
659 if (body_len > 0) {
660 conn->body_mode = BODY_KNOWN_LENGTH;
661 conn->body_length = body_len;
662 }
663 } else {
David Turner47356942009-04-05 14:23:06 -0700664 transfer_encoding = http_request_find_header(r, "Transfer-Encoding");
665 if (transfer_encoding && !strcasecmp(transfer_encoding, "Chunked")) {
666 conn->body_mode = BODY_CHUNKED;
667 conn->parse_chunk_header = 0;
668 conn->parse_chunk_trailer = 0;
669 conn->chunk_length = -1;
670 conn->chunk_total = 0;
David 'Digit' Turnerf9c98df2011-10-11 04:11:20 +0200671 conn->chunk_state = CHUNK_HEADER;
David Turner47356942009-04-05 14:23:06 -0700672 }
673 }
674 if (conn->body_mode == BODY_NONE) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800675 char* connection = http_request_find_header(r, "Proxy-Connection");
676
677 if (!connection)
678 connection = http_request_find_header(r, "Connection");
679
680 if (!connection || strcasecmp(connection, "Close")) {
681 /* hum, we can't support this at all */
682 PROXY_LOG("%s: can't determine content length, and client wants"
683 " to keep connection opened",
684 root->name);
685 return -1;
686 }
687 /* a negative value means that the data ends when the client
688 * disconnects the connection.
689 */
690 conn->body_mode = BODY_UNTIL_CLOSE;
691 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800692 D("%s: body_length=%lld body_mode=%s",
David 'Digit' Turnerc0052462014-02-25 18:39:29 +0100693 root->name, conn->body_length,
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800694 body_mode_str[conn->body_mode]);
695
696 proxy_connection_rewind(root);
697 return 0;
698}
699
700#define MAX_BODY_BUFFER 65536
701
702static DataStatus
703rewrite_connection_read_body( RewriteConnection* conn, int fd )
704{
705 ProxyConnection* root = conn->root;
706 stralloc_t* str = root->str;
707 int wanted = 0, current, avail;
708 DataStatus ret;
709
710 if (conn->body_is_closed) {
711 return DATA_NEED_MORE;
712 }
713
714 /* first, determine how many bytes we want to read. */
715 switch (conn->body_mode) {
716 case BODY_NONE:
717 D("%s: INTERNAL ERROR: SHOULDN'T BE THERE", root->name);
718 return DATA_COMPLETED;
719
720 case BODY_KNOWN_LENGTH:
721 {
722 if (conn->body_length == 0)
723 return DATA_COMPLETED;
724
725 if (conn->body_length > MAX_BODY_BUFFER)
726 wanted = MAX_BODY_BUFFER;
727 else
728 wanted = (int)conn->body_length;
729 }
730 break;
731
732 case BODY_UNTIL_CLOSE:
733 wanted = MAX_BODY_BUFFER;
734 break;
735
736 case BODY_CHUNKED:
David 'Digit' Turnerf9c98df2011-10-11 04:11:20 +0200737 if (conn->chunk_state == CHUNK_DATA_END) {
738 /* We're waiting for the CR LF after the chunk data */
739 ret = proxy_connection_receive_line(root, fd);
740 if (ret != DATA_COMPLETED)
741 return ret;
742
743 if (str->s[0] != 0) { /* this should be an empty line */
744 PROXY_LOG("%s: invalid chunk data end: '%s'",
745 root->name, str->s);
746 return DATA_ERROR;
747 }
748 /* proxy_connection_receive_line() did remove the
749 * trailing \r\n, but we must preserve it when we
750 * send the chunk size end to the proxy.
751 */
752 stralloc_add_str(root->str, "\r\n");
753 conn->chunk_state = CHUNK_HEADER;
754 /* fall-through */
755 }
756
757 if (conn->chunk_state == CHUNK_HEADER) {
758 char* line;
759 char* end;
760 long long length;
761 /* Ensure that the previous chunk was flushed before
762 * accepting a new header */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800763 if (!conn->parse_chunk_header) {
764 if (conn->body_has_data)
765 return DATA_NEED_MORE;
766 D("%s: waiting chunk header", root->name);
767 conn->parse_chunk_header = 1;
768 }
769 ret = proxy_connection_receive_line(root, fd);
David 'Digit' Turnerf9c98df2011-10-11 04:11:20 +0200770 if (ret != DATA_COMPLETED) {
771 return ret;
772 }
773 conn->parse_chunk_header = 0;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800774
David 'Digit' Turnerf9c98df2011-10-11 04:11:20 +0200775 line = str->s;
776 length = strtoll(line, &end, 16);
777 if (line[0] == ' ' || (end[0] != '\0' && end[0] != ';')) {
778 PROXY_LOG("%s: invalid chunk header: %s",
779 root->name, line);
780 return DATA_ERROR;
781 }
782 if (length < 0) {
783 PROXY_LOG("%s: invalid chunk length %lld",
784 root->name, length);
785 return DATA_ERROR;
786 }
787 /* proxy_connection_receive_line() did remove the
788 * trailing \r\n, but we must preserve it when we
789 * send the chunk size to the proxy.
790 */
791 stralloc_add_str(root->str, "\r\n");
David 'Digit' Turner1fee27e2010-07-23 12:45:33 -0700792
David 'Digit' Turnerf9c98df2011-10-11 04:11:20 +0200793 conn->chunk_length = length;
794 conn->chunk_total = 0;
795 conn->chunk_state = CHUNK_DATA;
796 if (length == 0) {
797 /* the last chunk, no we need to add the trailer */
798 conn->chunk_state = CHUNK_TRAILER;
799 conn->parse_chunk_trailer = 0;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800800 }
801 }
802
David 'Digit' Turnerf9c98df2011-10-11 04:11:20 +0200803 if (conn->chunk_state == CHUNK_TRAILER) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800804 /* ensure that 'str' is flushed before reading the trailer */
805 if (!conn->parse_chunk_trailer) {
806 if (conn->body_has_data)
807 return DATA_NEED_MORE;
808 conn->parse_chunk_trailer = 1;
809 }
810 ret = rewrite_connection_read_headers(conn, fd);
811 if (ret == DATA_COMPLETED) {
812 conn->body_is_closed = 1;
813 }
814 return ret;
815 }
816
817 /* if we get here, body_length > 0 */
818 if (conn->chunk_length > MAX_BODY_BUFFER)
819 wanted = MAX_BODY_BUFFER;
820 else
821 wanted = (int)conn->chunk_length;
822 break;
823
824 default:
825 ;
826 }
827
828 /* we don't want more than MAX_BODY_BUFFER bytes in the
829 * buffer we used to pass the body */
830 current = str->n;
831 avail = MAX_BODY_BUFFER - current;
832 if (avail <= 0) {
833 /* wait for some flush */
834 conn->body_is_full = 1;
David 'Digit' Turnerc0052462014-02-25 18:39:29 +0100835 D("%s: waiting to flush %d bytes",
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800836 root->name, current);
837 return DATA_NEED_MORE;
838 }
839
840 if (wanted > avail)
841 wanted = avail;
842
843 ret = proxy_connection_receive(root, fd, wanted);
844 conn->body_has_data = (str->n > 0);
845 conn->body_is_full = (str->n == MAX_BODY_BUFFER);
846
847 if (ret == DATA_ERROR) {
848 if (conn->body_mode == BODY_UNTIL_CLOSE) {
849 /* a disconnection here is normal and signals the
850 * end of the body */
851 conn->body_total += root->str_recv;
David 'Digit' Turnerc0052462014-02-25 18:39:29 +0100852 D("%s: body completed by close (%lld bytes)",
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800853 root->name, conn->body_total);
854 conn->body_is_closed = 1;
855 ret = DATA_COMPLETED;
856 }
857 } else {
858 avail = root->str_recv;
859 ret = DATA_NEED_MORE; /* we're not really done yet */
860
861 switch (conn->body_mode) {
862 case BODY_CHUNKED:
863 conn->chunk_total += avail;
864 conn->chunk_length -= avail;
865
866 if (conn->chunk_length == 0) {
David 'Digit' Turnerc0052462014-02-25 18:39:29 +0100867 D("%s: chunk completed (%lld bytes)",
David 'Digit' Turnerf9c98df2011-10-11 04:11:20 +0200868 root->name, conn->chunk_total);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800869 conn->body_total += conn->chunk_total;
870 conn->chunk_total = 0;
871 conn->chunk_length = -1;
David 'Digit' Turnerf9c98df2011-10-11 04:11:20 +0200872 conn->chunk_state = CHUNK_DATA;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800873 }
874 break;
875
876 case BODY_KNOWN_LENGTH:
877 conn->body_length -= avail;
878 conn->body_total += avail;
879
880 if (conn->body_length == 0) {
David 'Digit' Turnerc0052462014-02-25 18:39:29 +0100881 D("%s: body completed (%lld bytes)",
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800882 root->name, conn->body_total);
883 conn->body_is_closed = 1;
884 ret = DATA_COMPLETED;
885 }
886 break;
887
888 case BODY_UNTIL_CLOSE:
889 conn->body_total += avail;
890 break;
891
892 default:
893 ;
894 }
895 }
896 return ret;
897}
898
899static DataStatus
900rewrite_connection_send_body( RewriteConnection* conn, int fd )
901{
902 ProxyConnection* root = conn->root;
903 stralloc_t* str = root->str;
904 DataStatus ret = DATA_NEED_MORE;
905
906 if (conn->body_has_data) {
907 ret = proxy_connection_send(root, fd);
908 if (ret != DATA_ERROR) {
909 int pos = root->str_pos;
910
911 memmove(str->s, str->s+pos, str->n-pos);
912 str->n -= pos;
913 root->str_pos = 0;
914 conn->body_is_full = (str->n == MAX_BODY_BUFFER);
915 conn->body_has_data = (str->n > 0);
916 conn->body_sent += root->str_sent;
917
918 /* ensure that we return DATA_COMPLETED only when
919 * we have sent everything, and there is no more
920 * body pieces to read */
921 if (ret == DATA_COMPLETED) {
922 if (!conn->body_is_closed || conn->body_has_data)
923 ret = DATA_NEED_MORE;
924 else {
925 D("%s: sent all body (%lld bytes)",
926 root->name, conn->body_sent);
927 }
928 }
929 D("%s: sent closed=%d data=%d n=%d ret=%d",
930 root->name, conn->body_is_closed,
931 conn->body_has_data, str->n,
932 ret);
933 }
934 }
935 return ret;
936}
937
938
939static void
940rewrite_connection_select( ProxyConnection* root,
941 ProxySelect* sel )
942{
943 RewriteConnection* conn = (RewriteConnection*)root;
944 int slirp = conn->slirp_fd;
945 int proxy = root->socket;
946
947 switch (conn->state) {
948 case STATE_CONNECTING:
949 case STATE_CREATE_SOCKET_PAIR:
950 /* try to connect to the proxy server */
951 proxy_select_set( sel, proxy, PROXY_SELECT_WRITE );
952 break;
953
954 case STATE_REQUEST_FIRST_LINE:
955 case STATE_REQUEST_HEADERS:
956 proxy_select_set( sel, slirp, PROXY_SELECT_READ );
957 break;
958
959 case STATE_REQUEST_SEND:
960 proxy_select_set( sel, proxy, PROXY_SELECT_WRITE );
961 break;
962
963 case STATE_REQUEST_BODY:
964 if (!conn->body_is_closed && !conn->body_is_full)
965 proxy_select_set( sel, slirp, PROXY_SELECT_READ );
966
967 if (conn->body_has_data)
968 proxy_select_set( sel, proxy, PROXY_SELECT_WRITE );
969 break;
970
971 case STATE_REPLY_FIRST_LINE:
972 case STATE_REPLY_HEADERS:
973 proxy_select_set( sel, proxy, PROXY_SELECT_READ );
974 break;
975
976 case STATE_REPLY_SEND:
977 proxy_select_set( sel, slirp, PROXY_SELECT_WRITE );
978 break;
979
980 case STATE_REPLY_BODY:
981 if (conn->body_has_data)
982 proxy_select_set( sel, slirp, PROXY_SELECT_WRITE );
983
984 if (!conn->body_is_closed && !conn->body_is_full)
985 proxy_select_set( sel, proxy, PROXY_SELECT_READ );
986 break;
987 default:
988 ;
989 };
990}
991
992static void
993rewrite_connection_poll( ProxyConnection* root,
994 ProxySelect* sel )
995{
996 RewriteConnection* conn = (RewriteConnection*)root;
997
998 int slirp = conn->slirp_fd;
999 int proxy = root->socket;
1000 int has_slirp = proxy_select_poll(sel, slirp);
1001 int has_proxy = proxy_select_poll(sel, proxy);
1002 DataStatus ret = DATA_NEED_MORE;
1003
1004 switch (conn->state) {
1005 case STATE_CONNECTING:
1006 if (has_proxy) {
1007 PROXY_LOG("%s: connected to proxy", root->name);
1008 conn->state = STATE_CREATE_SOCKET_PAIR;
1009 }
1010 break;
1011
1012 case STATE_CREATE_SOCKET_PAIR:
1013 if (has_proxy) {
1014 if (rewrite_connection_create_sockets(conn) < 0) {
1015 ret = DATA_ERROR;
1016 } else {
1017 D("%s: socket pair created", root->name);
1018 conn->state = STATE_REQUEST_FIRST_LINE;
1019 }
1020 }
1021 break;
1022
1023 case STATE_REQUEST_FIRST_LINE:
1024 if (has_slirp) {
1025 ret = rewrite_connection_read_request(conn);
1026 if (ret == DATA_COMPLETED) {
1027 PROXY_LOG("%s: request first line ok", root->name);
1028 conn->state = STATE_REQUEST_HEADERS;
1029 }
1030 }
1031 break;
1032
1033 case STATE_REQUEST_HEADERS:
1034 if (has_slirp) {
1035 ret = rewrite_connection_read_headers(conn, slirp);
1036 if (ret == DATA_COMPLETED) {
1037 PROXY_LOG("%s: request headers ok", root->name);
1038 if (rewrite_connection_rewrite_request(conn) < 0)
1039 ret = DATA_ERROR;
1040 else
1041 conn->state = STATE_REQUEST_SEND;
1042 }
1043 }
1044 break;
1045
1046 case STATE_REQUEST_SEND:
1047 if (has_proxy) {
1048 ret = proxy_connection_send(root, proxy);
1049 if (ret == DATA_COMPLETED) {
1050 if (rewrite_connection_get_body_length(conn, 1) < 0) {
1051 ret = DATA_ERROR;
1052 } else if (conn->body_mode != BODY_NONE) {
1053 PROXY_LOG("%s: request sent, waiting for body",
1054 root->name);
1055 conn->state = STATE_REQUEST_BODY;
1056 } else {
1057 PROXY_LOG("%s: request sent, waiting for reply",
1058 root->name);
1059 conn->state = STATE_REPLY_FIRST_LINE;
1060 }
1061 }
1062 }
1063 break;
1064
1065 case STATE_REQUEST_BODY:
1066 if (has_slirp) {
1067 ret = rewrite_connection_read_body(conn, slirp);
1068 }
1069 if (ret != DATA_ERROR && has_proxy) {
1070 ret = rewrite_connection_send_body(conn, proxy);
1071 if (ret == DATA_COMPLETED) {
1072 PROXY_LOG("%s: request body ok, waiting for reply",
1073 root->name);
1074 conn->state = STATE_REPLY_FIRST_LINE;
1075 }
1076 }
1077 break;
1078
1079 case STATE_REPLY_FIRST_LINE:
1080 if (has_proxy) {
1081 ret = rewrite_connection_read_reply(conn);
1082 if (ret == DATA_COMPLETED) {
1083 PROXY_LOG("%s: reply first line ok", root->name);
1084 conn->state = STATE_REPLY_HEADERS;
1085 }
1086 }
1087 break;
1088
1089 case STATE_REPLY_HEADERS:
1090 if (has_proxy) {
1091 ret = rewrite_connection_read_headers(conn, proxy);
1092 if (ret == DATA_COMPLETED) {
1093 PROXY_LOG("%s: reply headers ok", root->name);
1094 if (rewrite_connection_rewrite_reply(conn) < 0)
1095 ret = DATA_ERROR;
1096 else
1097 conn->state = STATE_REPLY_SEND;
1098 }
1099 }
1100 break;
1101
1102 case STATE_REPLY_SEND:
1103 if (has_slirp) {
1104 ret = proxy_connection_send(conn->root, slirp);
1105 if (ret == DATA_COMPLETED) {
1106 if (rewrite_connection_get_body_length(conn, 0) < 0) {
1107 ret = DATA_ERROR;
1108 } else if (conn->body_mode != BODY_NONE) {
1109 PROXY_LOG("%s: reply sent, waiting for body",
1110 root->name);
1111 conn->state = STATE_REPLY_BODY;
1112 } else {
1113 PROXY_LOG("%s: reply sent, looping to waiting request",
1114 root->name);
1115 conn->state = STATE_REQUEST_FIRST_LINE;
1116 }
1117 }
1118 }
1119 break;
1120
1121 case STATE_REPLY_BODY:
1122 if (has_proxy) {
1123 ret = rewrite_connection_read_body(conn, proxy);
1124 }
1125 if (ret != DATA_ERROR && has_slirp) {
1126 ret = rewrite_connection_send_body(conn, slirp);
1127 if (ret == DATA_COMPLETED) {
1128 if (conn->body_mode == BODY_UNTIL_CLOSE) {
1129 PROXY_LOG("%s: closing connection", root->name);
1130 ret = DATA_ERROR;
1131 } else {
1132 PROXY_LOG("%s: reply body ok, looping to waiting request",
1133 root->name);
1134 conn->state = STATE_REQUEST_FIRST_LINE;
1135 }
1136 }
1137 }
1138 break;
1139
1140 default:
1141 ;
1142 }
1143 if (ret == DATA_ERROR)
1144 proxy_connection_free(root, 0, PROXY_EVENT_NONE);
1145
1146 return;
1147}
1148
1149
1150ProxyConnection*
1151http_rewriter_connect( HttpService* service,
1152 SockAddress* address )
1153{
1154 RewriteConnection* conn;
1155 int s;
1156
David 'Digit' Turner9b98dbd2010-07-30 15:35:00 -07001157 s = socket_create(address->family, SOCKET_STREAM );
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001158 if (s < 0)
1159 return NULL;
1160
David 'Digit' Turneraa8236d2014-01-10 17:02:29 +01001161 conn = g_malloc0(sizeof(*conn));
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001162 if (conn == NULL) {
1163 socket_close(s);
1164 return NULL;
1165 }
1166
1167 proxy_connection_init( conn->root, s, address, service->root,
1168 rewrite_connection_free,
1169 rewrite_connection_select,
1170 rewrite_connection_poll );
1171
1172 if ( rewrite_connection_init( conn ) < 0 ) {
1173 rewrite_connection_free( conn->root );
1174 return NULL;
1175 }
1176
1177 return conn->root;
1178}