aboutsummaryrefslogtreecommitdiffstats
path: root/proxy
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:04:49 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:04:49 -0800
commitdf7881f07f53b041dc0568be8528e9dbb74994cc (patch)
tree1b3e036f7df4241bf0c2d527b73198c50e1d7891 /proxy
parent55f4e4a5ec657a017e3bf75299ad71fd1c968dd3 (diff)
downloadexternal_qemu-df7881f07f53b041dc0568be8528e9dbb74994cc.zip
external_qemu-df7881f07f53b041dc0568be8528e9dbb74994cc.tar.gz
external_qemu-df7881f07f53b041dc0568be8528e9dbb74994cc.tar.bz2
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'proxy')
-rw-r--r--proxy/proxy_common.c345
-rw-r--r--proxy/proxy_common.h18
-rw-r--r--proxy/proxy_http.c235
-rw-r--r--proxy/proxy_http_connector.c213
-rw-r--r--proxy/proxy_http_int.h38
-rw-r--r--proxy/proxy_http_rewriter.c1132
-rw-r--r--proxy/proxy_int.h146
7 files changed, 1727 insertions, 400 deletions
diff --git a/proxy/proxy_common.c b/proxy/proxy_common.c
index c5762dc..0e45481 100644
--- a/proxy/proxy_common.c
+++ b/proxy/proxy_common.c
@@ -26,6 +26,7 @@ proxy_LOG(const char* fmt, ...)
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
+ fprintf(stderr, "\n");
}
void
@@ -39,49 +40,39 @@ proxy_set_verbose(int mode)
static ProxyConnection s_connections[1];
+#define MAX_HEX_DUMP 512
+
static void
hex_dump( void* base, int size, const char* prefix )
{
- uint8_t* p = (uint8_t*)base;
- const int max_count = 16;
-
- while (size > 0) {
- int count = size > max_count ? max_count : size;
- int n;
- const char* space = prefix;
-
- for (n = 0; n < count; n++) {
- proxy_LOG( "%s%02x", space, p[n] );
- space = " ";
- }
-
- proxy_LOG( "%-*s", 4 + 3*(max_count-n), "" );
-
- for (n = 0; n < count; n++) {
- int c = p[n];
-
- if (c < 32 || c > 127)
- c = '.';
- proxy_LOG( "%c", c );
- }
- proxy_LOG( "\n" );
- size -= count;
- p += count;
- }
+ STRALLOC_DEFINE(s);
+ if (size > MAX_HEX_DUMP)
+ size = MAX_HEX_DUMP;
+ stralloc_add_hexdump(s, base, size, prefix);
+ proxy_LOG( "%s", stralloc_cstr(s) );
+ stralloc_reset(s);
}
-
void
-proxy_connection_init( ProxyConnection* conn,
- int socket,
- struct sockaddr_in* address,
- ProxyService* service )
+proxy_connection_init( ProxyConnection* conn,
+ int socket,
+ struct sockaddr_in* address,
+ ProxyService* service,
+ ProxyConnectionFreeFunc conn_free,
+ ProxyConnectionSelectFunc conn_select,
+ ProxyConnectionPollFunc conn_poll )
{
conn->socket = socket;
conn->address = address[0];
conn->service = service;
conn->next = NULL;
+ conn->conn_free = conn_free;
+ conn->conn_select = conn_select;
+ conn->conn_poll = conn_poll;
+
+ socket_set_nonblock(socket);
+
{
uint32_t ip = ntohl(address->sin_addr.s_addr);
uint16_t port = ntohs(address->sin_port);
@@ -98,122 +89,154 @@ proxy_connection_init( ProxyConnection* conn,
conn->name[sizeof(conn->name)-1] = 0;
}
- conn->buffer_pos = 0;
- conn->buffer_len = 0;
- conn->buffer = conn->buffer0;
+ stralloc_reset(conn->str);
+ conn->str_pos = 0;
}
void
proxy_connection_done( ProxyConnection* conn )
{
- if (conn->buffer != conn->buffer0) {
- qemu_free(conn->buffer);
+ stralloc_reset( conn->str );
+ if (conn->socket >= 0) {
+ socket_close(conn->socket);
+ conn->socket = -1;
}
}
-int
-proxy_connection_send( ProxyConnection* conn )
+void
+proxy_connection_rewind( ProxyConnection* conn )
{
- int result = -1;
- int fd = conn->socket;
- int avail = conn->buffer_len - conn->buffer_pos;
+ stralloc_t* str = conn->str;
+
+ /* only keep a small buffer in the heap */
+ conn->str_pos = 0;
+ str->n = 0;
+ if (str->a > 1024)
+ stralloc_reset(str);
+}
+
+DataStatus
+proxy_connection_send( ProxyConnection* conn, int fd )
+{
+ stralloc_t* str = conn->str;
+ int avail = str->n - conn->str_pos;
+
+ conn->str_sent = 0;
+
+ if (avail <= 0)
+ return 1;
if (proxy_log) {
- PROXY_LOG("%s: sending %d bytes:\n", conn->name, avail );
- hex_dump( conn->buffer + conn->buffer_pos, avail, ">> " );
+ PROXY_LOG("%s: sending %d bytes:", conn->name, avail );
+ hex_dump( str->s + conn->str_pos, avail, ">> " );
}
while (avail > 0) {
- int n = send(fd, conn->buffer + conn->buffer_pos, avail, 0);
+ int n = send(fd, str->s + conn->str_pos, avail, 0);
+ if (n == 0) {
+ PROXY_LOG("%s: connection reset by peer (send)",
+ conn->name);
+ return DATA_ERROR;
+ }
if (n < 0) {
- if (errno == EINTR)
+ if (socket_errno == EINTR)
continue;
- if (errno == EWOULDBLOCK || errno == EAGAIN)
- return 0;
- PROXY_LOG("%s: error: %s\n", conn->name, strerror(errno));
- return -1;
+
+ if (socket_errno == EWOULDBLOCK || socket_errno == EAGAIN)
+ return DATA_NEED_MORE;
+
+ PROXY_LOG("%s: error: %s", conn->name, socket_errstr());
+ return DATA_ERROR;
}
- conn->buffer_pos += n;
- avail -= n;
+ conn->str_pos += n;
+ conn->str_sent += n;
+ avail -= n;
}
- return 1;
+
+ proxy_connection_rewind(conn);
+ return DATA_COMPLETED;
}
-int
-proxy_connection_receive( ProxyConnection* conn )
+
+DataStatus
+proxy_connection_receive( ProxyConnection* conn, int fd, int wanted )
{
- int result = -1;
- int fd = conn->socket;
- int avail = conn->buffer_len - conn->buffer_pos;
+ stralloc_t* str = conn->str;
- while (avail > 0) {
- int n = recv(fd, conn->buffer + conn->buffer_pos, avail, 0);
+ conn->str_recv = 0;
+
+ while (wanted > 0) {
+ int n;
+
+ stralloc_readyplus( str, wanted );
+ n = recv(fd, str->s + str->n, wanted, 0);
+ if (n == 0) {
+ PROXY_LOG("%s: connection reset by peer (receive)",
+ conn->name);
+ return DATA_ERROR;
+ }
if (n < 0) {
- if (errno == EINTR)
+ if (socket_errno == EINTR)
continue;
- if (errno == EWOULDBLOCK || errno == EAGAIN)
- return 0;
- PROXY_LOG("%s: error: %s\n", conn->name, strerror(errno));
- return -1;
+
+ if (socket_errno == EWOULDBLOCK || socket_errno == EAGAIN)
+ return DATA_NEED_MORE;
+
+ PROXY_LOG("%s: error: %s", conn->name, socket_errstr());
+ return DATA_ERROR;
}
if (proxy_log) {
- PROXY_LOG("%s: received %d bytes:\n", conn->name, n );
- hex_dump( conn->buffer + conn->buffer_pos, n, ">> " );
+ PROXY_LOG("%s: received %d bytes:", conn->name, n );
+ hex_dump( str->s + str->n, n, "<< " );
}
- conn->buffer_pos += n;
- avail -= n;
+ str->n += n;
+ wanted -= n;
+ conn->str_recv += n;
}
- return 1;
+ return DATA_COMPLETED;
}
-int
-proxy_connection_receive_line( ProxyConnection* conn )
+
+DataStatus
+proxy_connection_receive_line( ProxyConnection* conn, int fd )
{
- int result = -1;
- int fd = conn->socket;
+ stralloc_t* str = conn->str;
for (;;) {
char c;
int n = recv(fd, &c, 1, 0);
if (n == 0) {
- PROXY_LOG("%s: disconnected from server\n", conn->name );
- return -1;
+ PROXY_LOG("%s: disconnected from server", conn->name );
+ return DATA_ERROR;
}
if (n < 0) {
- if (errno == EINTR)
+ if (socket_errno == EINTR)
continue;
- if (errno == EWOULDBLOCK || errno == EAGAIN) {
- PROXY_LOG("%s: blocked\n", conn->name);
- return 0;
+
+ if (socket_errno == EWOULDBLOCK || socket_errno == EAGAIN) {
+ PROXY_LOG("%s: blocked", conn->name);
+ return DATA_NEED_MORE;
}
- PROXY_LOG("%s: error: %s\n", conn->name, strerror(errno));
- return -1;
+ PROXY_LOG("%s: error: %s", conn->name, socket_errstr());
+ return DATA_ERROR;
}
+ stralloc_add_c(str, c);
if (c == '\n') {
- if (conn->buffer_pos > 0 && conn->buffer[conn->buffer_pos-1] == '\r')
- conn->buffer_pos -= 1;
-
- conn->buffer[conn->buffer_pos] = 0;
+ str->s[--str->n] = 0;
+ if (str->n > 0 && str->s[str->n-1] == '\r')
+ str->s[--str->n] = 0;
- PROXY_LOG("%s: received '%.*s'\n", conn->name,
- conn->buffer_pos, conn->buffer);
- return 1;
- }
-
- conn->buffer[ conn->buffer_pos++ ] = c;
- if (conn->buffer_pos == conn->buffer_len) {
- PROXY_LOG("%s: line received from proxy is too long\n", conn->name);
- return -1;
+ PROXY_LOG("%s: received '%s'", conn->name,
+ quote_bytes(str->s, str->n));
+ return DATA_COMPLETED;
}
}
}
-
-
static void
proxy_connection_insert( ProxyConnection* conn, ProxyConnection* after )
{
@@ -276,7 +299,7 @@ proxy_manager_atexit( void )
/* free all proxy connections */
while (conn != s_connections) {
ProxyConnection* next = conn->next;
- conn->service->conn_free( conn );
+ conn->conn_free( conn );
conn = next;
}
conn->next = conn;
@@ -293,6 +316,7 @@ proxy_manager_atexit( void )
void
proxy_connection_free( ProxyConnection* conn,
+ int keep_alive,
ProxyEvent event )
{
if (conn) {
@@ -301,18 +325,21 @@ proxy_connection_free( ProxyConnection* conn,
proxy_connection_remove(conn);
if (event != PROXY_EVENT_NONE)
- conn->ev_func( conn->ev_opaque, event );
+ conn->ev_func( conn->ev_opaque, fd, event );
- conn->service->conn_free(conn);
+ if (keep_alive)
+ conn->socket = -1;
+
+ conn->conn_free(conn);
}
}
int
-proxy_manager_add( int socket,
- struct sockaddr_in* address,
- void* ev_opaque,
- ProxyEventFunc ev_func )
+proxy_manager_add( struct sockaddr_in* address,
+ int sock_type,
+ ProxyEventFunc ev_func,
+ void* ev_opaque )
{
int n;
@@ -320,16 +347,14 @@ proxy_manager_add( int socket,
proxy_manager_init();
}
- socket_set_nonblock(socket);
-
for (n = 0; n < s_num_services; n++) {
ProxyService* service = s_services[n];
ProxyConnection* conn = service->serv_connect( service->opaque,
- socket,
+ sock_type,
address );
if (conn != NULL) {
- conn->ev_opaque = ev_opaque;
conn->ev_func = ev_func;
+ conn->ev_opaque = ev_opaque;
proxy_connection_insert(conn, s_connections->prev);
return 0;
}
@@ -343,50 +368,83 @@ proxy_manager_add( int socket,
* the connection accept/refusal occured
*/
void
-proxy_manager_del( int socket )
+proxy_manager_del( void* ev_opaque )
{
ProxyConnection* conn = s_connections->next;
for ( ; conn != s_connections; conn = conn->next ) {
- if (conn->socket == socket) {
- int fd = conn->socket;
+ if (conn->ev_opaque == ev_opaque) {
proxy_connection_remove(conn);
- conn->service->conn_free(conn);
- socket_close(fd);
+ conn->conn_free(conn);
return;
}
}
}
+void
+proxy_select_set( ProxySelect* sel,
+ int fd,
+ unsigned flags )
+{
+ if (fd < 0 || !flags)
+ return;
+
+ if (*sel->pcount < fd+1)
+ *sel->pcount = fd+1;
+
+ if (flags & PROXY_SELECT_READ) {
+ FD_SET( fd, sel->reads );
+ } else {
+ FD_CLR( fd, sel->reads );
+ }
+ if (flags & PROXY_SELECT_WRITE) {
+ FD_SET( fd, sel->writes );
+ } else {
+ FD_CLR( fd, sel->writes );
+ }
+ if (flags & PROXY_SELECT_ERROR) {
+ FD_SET( fd, sel->errors );
+ } else {
+ FD_CLR( fd, sel->errors );
+ }
+}
+
+unsigned
+proxy_select_poll( ProxySelect* sel, int fd )
+{
+ unsigned flags = 0;
+
+ if (fd >= 0) {
+ if ( FD_ISSET(fd, sel->reads) )
+ flags |= PROXY_SELECT_READ;
+ if ( FD_ISSET(fd, sel->writes) )
+ flags |= PROXY_SELECT_WRITE;
+ if ( FD_ISSET(fd, sel->errors) )
+ flags |= PROXY_SELECT_ERROR;
+ }
+ return flags;
+}
+
/* this function is called to update the select file descriptor sets
* with those of the proxified connection sockets that are currently managed */
void
proxy_manager_select_fill( int *pcount, fd_set* read_fds, fd_set* write_fds, fd_set* err_fds)
{
ProxyConnection* conn;
+ ProxySelect sel[1];
if (!s_init)
proxy_manager_init();
- conn = s_connections->next;
- for ( ; conn != s_connections; conn = conn->next ) {
- unsigned flags = conn->service->conn_select(conn);
- int fd = conn->socket;
-
- if (!flags)
- continue;
+ sel->pcount = pcount;
+ sel->reads = read_fds;
+ sel->writes = write_fds;
+ sel->errors = err_fds;
- if (*pcount < fd+1)
- *pcount = fd+1;
-
- if (flags & PROXY_SELECT_READ) {
- FD_SET( fd, read_fds );
- }
- if (flags & PROXY_SELECT_WRITE) {
- FD_SET( fd, write_fds );
- }
- if (flags & PROXY_SELECT_ERROR) {
- FD_SET( fd, err_fds );
- }
+ conn = s_connections->next;
+ while (conn != s_connections) {
+ ProxyConnection* next = conn->next;
+ conn->conn_select(conn, sel);
+ conn = next;
}
}
@@ -395,21 +453,16 @@ void
proxy_manager_poll( fd_set* read_fds, fd_set* write_fds, fd_set* err_fds )
{
ProxyConnection* conn = s_connections->next;
- while (conn != s_connections) {
- ProxyConnection* next = conn->next;
- int fd = conn->socket;
- unsigned flags = 0;
+ ProxySelect sel[1];
- if ( FD_ISSET(fd, read_fds) )
- flags |= PROXY_SELECT_READ;
- if ( FD_ISSET(fd, write_fds) )
- flags |= PROXY_SELECT_WRITE;
- if ( FD_ISSET(fd, err_fds) )
- flags |= PROXY_SELECT_ERROR;
+ sel->pcount = NULL;
+ sel->reads = read_fds;
+ sel->writes = write_fds;
+ sel->errors = err_fds;
- if (flags != 0) {
- conn->service->conn_poll( conn, flags );
- }
+ while (conn != s_connections) {
+ ProxyConnection* next = conn->next;
+ conn->conn_poll( conn, sel );
conn = next;
}
}
@@ -480,7 +533,7 @@ proxy_resolve_server( struct sockaddr_in* addr,
host = gethostbyname(name);
if (host == NULL) {
- PROXY_LOG("%s: can't resolve proxy server name '%s'\n",
+ PROXY_LOG("%s: can't resolve proxy server name '%s'",
__FUNCTION__, name);
goto Exit;
}
@@ -488,7 +541,7 @@ proxy_resolve_server( struct sockaddr_in* addr,
addr->sin_addr = *(struct in_addr*)host->h_addr;
{
uint32_t a = ntohl(addr->sin_addr.s_addr);
- PROXY_LOG("server name '%s' resolved to %d.%d.%d.%d\n", name, (a>>24)&255, (a>>16)&255,(a>>8)&255,a&255);
+ PROXY_LOG("server name '%s' resolved to %d.%d.%d.%d", name, (a>>24)&255, (a>>16)&255,(a>>8)&255,a&255);
}
result = 0;
diff --git a/proxy/proxy_common.h b/proxy/proxy_common.h
index 54889cf..57f224d 100644
--- a/proxy/proxy_common.h
+++ b/proxy/proxy_common.h
@@ -29,7 +29,7 @@ typedef enum {
} ProxyEvent;
/* event can't be NONE when this callback is called */
-typedef void (*ProxyEventFunc)( void* opaque, ProxyEvent event );
+typedef void (*ProxyEventFunc)( void* opaque, int fd, ProxyEvent event );
extern void proxy_set_verbose(int mode);
@@ -61,19 +61,27 @@ typedef struct {
*
* returns 0 on success, or -1 if there is no proxy service for this type of connection
*/
-extern int proxy_manager_add( int socket, struct sockaddr_in* address, void* ev_opaque, ProxyEventFunc ev_func );
+extern int proxy_manager_add( struct sockaddr_in* address,
+ int sock_type,
+ ProxyEventFunc ev_func,
+ void* ev_opaque );
/* remove an on-going proxified socket connection from the manager's list.
* this is only necessary when the socket connection must be canceled before
* the connection accept/refusal occured
*/
-extern void proxy_manager_del( int socket );
+extern void proxy_manager_del( void* ev_opaque );
/* this function is called to update the select file descriptor sets
* with those of the proxified connection sockets that are currently managed */
-extern void proxy_manager_select_fill( int *pcount, fd_set* read_fds, fd_set* write_fds, fd_set* err_fds);
+extern void proxy_manager_select_fill( int *pcount,
+ fd_set* read_fds,
+ fd_set* write_fds,
+ fd_set* err_fds);
/* this function is called to act on proxified connection sockets when network events arrive */
-extern void proxy_manager_poll( fd_set* read_fds, fd_set* write_fds, fd_set* err_fds );
+extern void proxy_manager_poll( fd_set* read_fds,
+ fd_set* write_fds,
+ fd_set* err_fds );
#endif /* END */
diff --git a/proxy/proxy_http.c b/proxy/proxy_http.c
index 83982a7..c3d663c 100644
--- a/proxy/proxy_http.c
+++ b/proxy/proxy_http.c
@@ -10,200 +10,18 @@
** GNU General Public License for more details.
*/
#include "proxy_int.h"
-#include "proxy_http.h"
+#include "proxy_http_int.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "vl.h"
-typedef enum {
- HTTP_NONE = 0,
- HTTP_CONNECTING, /* connecting to the server */
- HTTP_SEND_HEADER, /* connected, sending header to the server */
- HTTP_RECEIVE_ANSWER_LINE1,
- HTTP_RECEIVE_ANSWER_LINE2 /* connected, reading server's answer */
-} HttpConnectionState;
-
-
-typedef struct {
- ProxyConnection root[1];
- HttpConnectionState state;
-} HttpConnection;
-
-
-typedef struct {
- ProxyService root[1];
- struct sockaddr_in server_addr; /* server address and port */
- char* footer; /* the footer contains the static parts of the */
- int footer_len; /* connection header, we generate it only once */
- char footer0[512];
-} HttpService;
-
-
-static void
-http_connection_free( HttpConnection* conn )
-{
- proxy_connection_done(conn->root);
- qemu_free(conn);
-}
-
-
#define HTTP_VERSION "1.1"
-static int
-http_connection_init( HttpConnection* conn )
-{
- HttpService* service = (HttpService*) conn->root->service;
- ProxyConnection* root = conn->root;
- char* p = root->buffer0;
- char* end = p + sizeof(root->buffer0);
- int wlen, ret;
- uint32_t address = ntohl(conn->root->address.sin_addr.s_addr);
- int port = ntohs(conn->root->address.sin_port);
-
- root->buffer_pos = 0;
- root->buffer = p;
-
- p += snprintf(p, end-p, "CONNECT %d.%d.%d.%d:%d HTTP/" HTTP_VERSION "\r\n",
- (address >> 24) & 0xff, (address >> 16) & 0xff,
- (address >> 8) & 0xff, address & 0xff, port);
- if (p >= end) goto Overflow;
-
- p += snprintf(p, end-p, "%.*s", service->footer_len, service->footer);
-
- if (p >= end) {
- Overflow:
- PROXY_LOG("%s: buffer overflow in proxy connection header\n", root->name);
- return -1;
- }
-
- root->buffer_len = (p - root->buffer);
-
- ret = connect( root->socket,
- (struct sockaddr*) &service->server_addr,
- sizeof(service->server_addr) );
- if (ret == 0) {
- /* immediate connection ?? */
- conn->state = HTTP_SEND_HEADER;
- PROXY_LOG("%s: immediate connection\n", root->name);
- }
- else {
- if (socket_errno == EINPROGRESS || socket_errno == EWOULDBLOCK) {
- conn->state = HTTP_CONNECTING;
- PROXY_LOG("%s: connecting\n", conn->root->name);
- }
- else {
- PROXY_LOG("%s: cannot connect to proxy: %s\n", root->name, strerror(errno));
- return -1;
- }
- }
- return 0;
-}
-
-
-static unsigned
-http_connection_select( HttpConnection* conn )
-{
- unsigned flags;
-
- switch (conn->state) {
- case HTTP_RECEIVE_ANSWER_LINE1:
- case HTTP_RECEIVE_ANSWER_LINE2:
- flags = PROXY_SELECT_READ;
- break;
-
- case HTTP_CONNECTING:
- case HTTP_SEND_HEADER:
- flags = PROXY_SELECT_WRITE;
- break;
-
- default:
- flags = 0;
- };
- return flags;
-}
-
-static void
-http_connection_poll( HttpConnection* conn,
- unsigned flags )
-{
- int ret;
- ProxyConnection* root = conn->root;
-
- switch (conn->state)
- {
- case HTTP_CONNECTING:
- PROXY_LOG("%s: connected to http proxy, sending header\n", root->name);
- conn->state = HTTP_SEND_HEADER;
- break;
-
- case HTTP_SEND_HEADER:
- {
- int ret = proxy_connection_send(root);
-
- if (ret < 0) {
- proxy_connection_free( root, PROXY_EVENT_SERVER_ERROR );
- return;
- }
- if (ret == 0)
- return;
-
- root->buffer_len = sizeof(root->buffer0);
- root->buffer_pos = 0;
- conn->state = HTTP_RECEIVE_ANSWER_LINE1;
- PROXY_LOG("%s: header sent, receiving first answer line\n", root->name);
- }
- break;
-
- case HTTP_RECEIVE_ANSWER_LINE1:
- case HTTP_RECEIVE_ANSWER_LINE2:
- {
- int ret = proxy_connection_receive_line(root);
-
- if (ret < 0) {
- proxy_connection_free( root, PROXY_EVENT_SERVER_ERROR );
- return;
- }
- if (ret == 0)
- return;
-
- if (conn->state == HTTP_RECEIVE_ANSWER_LINE1) {
- int http1, http2, codenum;
-
- if ( sscanf(root->buffer, "HTTP/%d.%d %d", &http1, &http2, &codenum) != 3 ) {
- PROXY_LOG( "%s: invalid answer from proxy: '%s'\n",
- root->name, root->buffer );
- proxy_connection_free( root, PROXY_EVENT_SERVER_ERROR );
- return;
- }
-
- /* success is 2xx */
- if (codenum/2 != 100) {
- PROXY_LOG( "%s: connection refused, error=%d\n",
- root->name, codenum );
- proxy_connection_free( root, PROXY_EVENT_CONNECTION_REFUSED );
- return;
- }
- PROXY_LOG("%s: receiving second answer line\n", root->name);
- conn->state = HTTP_RECEIVE_ANSWER_LINE2;
- root->buffer_pos = 0;
- } else {
- /* ok, we're connected */
- PROXY_LOG("%s: connection succeeded\n", root->name);
- proxy_connection_free( root, PROXY_EVENT_CONNECTED );
- }
- }
- break;
-
- default:
- PROXY_LOG("%s: invalid state for read event: %d\n", root->name, conn->state);
- }
-}
-
static void
http_service_free( HttpService* service )
{
- PROXY_LOG("%s\n", __FUNCTION__);
+ PROXY_LOG("%s", __FUNCTION__);
if (service->footer != service->footer0)
qemu_free(service->footer);
qemu_free(service);
@@ -212,11 +30,11 @@ http_service_free( HttpService* service )
static ProxyConnection*
http_service_connect( HttpService* service,
- int socket,
+ int sock_type,
struct sockaddr_in* address )
{
- HttpConnection* conn = qemu_mallocz(sizeof(*conn));
- int sock_type = socket_get_type(socket);
+ uint32_t addr;
+ int port;
/* the HTTP proxy can only handle TCP connections */
if (sock_type != SOCK_STREAM)
@@ -227,14 +45,25 @@ http_service_connect( HttpService* service,
address->sin_port == service->server_addr.sin_port)
return NULL;
- proxy_connection_init( conn->root, socket, address, service->root );
-
- if ( http_connection_init( conn ) < 0 ) {
- http_connection_free( conn );
- return NULL;
+ addr = ntohl(address->sin_addr.s_addr);
+ port = ntohs(address->sin_port);
+
+ PROXY_LOG("%s: trying to connect to %d.%d.%d.%d on port %d",
+ __FUNCTION__,
+ (addr >> 24) & 255,
+ (addr >> 16) & 255,
+ (addr >> 8) & 255,
+ addr & 255,
+ port );
+
+ if (port == 80) {
+ /* use the rewriter for HTTP */
+ PROXY_LOG("%s: using HTTP rewriter", __FUNCTION__);
+ return http_rewriter_connect(service, address);
+ } else {
+ PROXY_LOG("%s: using HTTP rewriter", __FUNCTION__);
+ return http_connector_connect(service, address);
}
-
- return conn->root;
}
@@ -256,7 +85,7 @@ proxy_http_setup( const char* servername,
if (servernamelen < 0)
servernamelen = strlen(servername);
- PROXY_LOG( "%s: creating http proxy service connecting to: %.*s:%d\n",
+ PROXY_LOG( "%s: creating http proxy service connecting to: %.*s:%d",
__FUNCTION__, servernamelen, servername, serverport );
/* resolve server address */
@@ -269,7 +98,7 @@ proxy_http_setup( const char* servername,
/* create service object */
service = qemu_mallocz(sizeof(*service));
if (service == NULL) {
- PROXY_LOG("%s: not enough memory to allocate new proxy service\n", __FUNCTION__);
+ PROXY_LOG("%s: not enough memory to allocate new proxy service", __FUNCTION__);
return -1;
}
@@ -322,7 +151,7 @@ proxy_http_setup( const char* servername,
wlen = proxy_base64_encode(user_pass, uplen, encoded, (int)sizeof(encoded));
if (wlen < 0) {
- PROXY_LOG( "could not base64 encode '%.*s'\n", uplen, user_pass);
+ PROXY_LOG( "could not base64 encode '%.*s'", uplen, user_pass);
goto FooterOverflow;
}
@@ -341,7 +170,7 @@ proxy_http_setup( const char* servername,
if (p >= end) {
FooterOverflow:
- PROXY_LOG( "%s: buffer overflow when creating connection footer\n",
+ PROXY_LOG( "%s: buffer overflow when creating connection footer",
__FUNCTION__);
http_service_free(service);
return -1;
@@ -351,18 +180,16 @@ proxy_http_setup( const char* servername,
service->footer_len = (p - service->footer);
}
- PROXY_LOG( "%s: creating HTTP Proxy Service Footer is (len=%d):\n'%.*s'\n",
+ PROXY_LOG( "%s: creating HTTP Proxy Service Footer is (len=%d):\n'%.*s'",
__FUNCTION__, service->footer_len,
service->footer_len, service->footer );
service->root->opaque = service;
- service->root->serv_free = (ProxyServiceFreeFunc) http_service_free;
- service->root->serv_connect = (ProxyServiceConnectFunc) http_service_connect;
- service->root->conn_free = (ProxyConnectionFreeFunc) http_connection_free;
- service->root->conn_select = (ProxyConnectionSelectFunc) http_connection_select;
- service->root->conn_poll = (ProxyConnectionPollFunc) http_connection_poll;
+ service->root->serv_free = (ProxyServiceFreeFunc) http_service_free;
+ service->root->serv_connect = (ProxyServiceConnectFunc) http_service_connect;
if (proxy_manager_add_service( service->root ) < 0) {
+ PROXY_LOG("%s: could not register service ?", __FUNCTION__);
http_service_free(service);
return -1;
}
diff --git a/proxy/proxy_http_connector.c b/proxy/proxy_http_connector.c
new file mode 100644
index 0000000..1b2ba3e
--- /dev/null
+++ b/proxy/proxy_http_connector.c
@@ -0,0 +1,213 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+*/
+#include "proxy_http_int.h"
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include "vl.h"
+
+/* A HttpConnector implements a non-HTTP proxied connection
+ * through the CONNECT method. Many firewalls are configured
+ * to reject these for port 80, so these connections should
+ * use a HttpRewriter instead.
+ */
+
+typedef enum {
+ STATE_NONE = 0,
+ STATE_CONNECTING, /* connecting to the server */
+ STATE_SEND_HEADER, /* connected, sending header to the server */
+ STATE_RECEIVE_ANSWER_LINE1,
+ STATE_RECEIVE_ANSWER_LINE2 /* connected, reading server's answer */
+} ConnectorState;
+
+typedef struct Connection {
+ ProxyConnection root[1];
+ ConnectorState state;
+} Connection;
+
+
+static void
+connection_free( ProxyConnection* root )
+{
+ proxy_connection_done(root);
+ qemu_free(root);
+}
+
+
+
+#define HTTP_VERSION "1.1"
+
+static int
+connection_init( Connection* conn )
+{
+ HttpService* service = (HttpService*) conn->root->service;
+ ProxyConnection* root = conn->root;
+ stralloc_t* str = root->str;
+ int ret;
+ uint32_t address = ntohl(root->address.sin_addr.s_addr);
+ int port = ntohs(root->address.sin_port);
+
+ proxy_connection_rewind(root);
+ stralloc_add_format(str, "CONNECT %d.%d.%d.%d:%d HTTP/" HTTP_VERSION "\r\n",
+ (address >> 24) & 0xff, (address >> 16) & 0xff,
+ (address >> 8) & 0xff, address & 0xff, port);
+
+ stralloc_add_bytes(str, service->footer, service->footer_len);
+
+ do {
+ ret = connect( root->socket,
+ (struct sockaddr*) &service->server_addr,
+ sizeof(service->server_addr) );
+ } while (ret < 0 && socket_errno == EINTR);
+
+ if (ret == 0) {
+ /* immediate connection ?? */
+ conn->state = STATE_SEND_HEADER;
+ PROXY_LOG("%s: immediate connection", root->name);
+ }
+ else {
+ if (socket_errno == EINPROGRESS || socket_errno == EWOULDBLOCK) {
+ conn->state = STATE_CONNECTING;
+ PROXY_LOG("%s: connecting", root->name);
+ }
+ else {
+ PROXY_LOG("%s: cannot connect to proxy: %s", root->name, socket_errstr());
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+static void
+connection_select( ProxyConnection* root,
+ ProxySelect* sel )
+{
+ unsigned flags;
+ Connection* conn = (Connection*)root;
+
+ switch (conn->state) {
+ case STATE_RECEIVE_ANSWER_LINE1:
+ case STATE_RECEIVE_ANSWER_LINE2:
+ flags = PROXY_SELECT_READ;
+ break;
+
+ case STATE_CONNECTING:
+ case STATE_SEND_HEADER:
+ flags = PROXY_SELECT_WRITE;
+ break;
+
+ default:
+ flags = 0;
+ };
+ proxy_select_set(sel, root->socket, flags);
+}
+
+static void
+connection_poll( ProxyConnection* root,
+ ProxySelect* sel )
+{
+ DataStatus ret = DATA_NEED_MORE;
+ Connection* conn = (Connection*)root;
+ int fd = root->socket;
+
+ if (!proxy_select_poll(sel, fd))
+ return;
+
+ switch (conn->state)
+ {
+ case STATE_CONNECTING:
+ PROXY_LOG("%s: connected to http proxy, sending header", root->name);
+ conn->state = STATE_SEND_HEADER;
+ break;
+
+ case STATE_SEND_HEADER:
+ ret = proxy_connection_send(root, fd);
+ if (ret == DATA_COMPLETED) {
+ conn->state = STATE_RECEIVE_ANSWER_LINE1;
+ PROXY_LOG("%s: header sent, receiving first answer line", root->name);
+ }
+ break;
+
+ case STATE_RECEIVE_ANSWER_LINE1:
+ case STATE_RECEIVE_ANSWER_LINE2:
+ ret = proxy_connection_receive_line(root, root->socket);
+ if (ret == DATA_COMPLETED) {
+ if (conn->state == STATE_RECEIVE_ANSWER_LINE1) {
+ int http1, http2, codenum;
+ const char* line = root->str->s;
+
+ if ( sscanf(line, "HTTP/%d.%d %d", &http1, &http2, &codenum) != 3 ) {
+ PROXY_LOG( "%s: invalid answer from proxy: '%s'",
+ root->name, line );
+ ret = DATA_ERROR;
+ break;
+ }
+
+ /* success is 2xx */
+ if (codenum/2 != 100) {
+ PROXY_LOG( "%s: connection refused, error=%d",
+ root->name, codenum );
+ proxy_connection_free( root, 0, PROXY_EVENT_CONNECTION_REFUSED );
+ return;
+ }
+ PROXY_LOG("%s: receiving second answer line", root->name);
+ conn->state = STATE_RECEIVE_ANSWER_LINE2;
+ proxy_connection_rewind(root);
+ } else {
+ /* ok, we're connected */
+ PROXY_LOG("%s: connection succeeded", root->name);
+ proxy_connection_free( root, 1, PROXY_EVENT_CONNECTED );
+ }
+ }
+ break;
+
+ default:
+ PROXY_LOG("%s: invalid state for read event: %d", root->name, conn->state);
+ }
+
+ if (ret == DATA_ERROR) {
+ proxy_connection_free( root, 0, PROXY_EVENT_SERVER_ERROR );
+ }
+}
+
+
+
+ProxyConnection*
+http_connector_connect( HttpService* service,
+ struct sockaddr_in* address )
+{
+ Connection* conn;
+ int s;
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0)
+ return NULL;
+
+ conn = qemu_mallocz(sizeof(*conn));
+ if (conn == NULL) {
+ socket_close(s);
+ return NULL;
+ }
+
+ proxy_connection_init( conn->root, s, address, service->root,
+ connection_free,
+ connection_select,
+ connection_poll );
+
+ if ( connection_init( conn ) < 0 ) {
+ connection_free( conn->root );
+ return NULL;
+ }
+
+ return conn->root;
+}
diff --git a/proxy/proxy_http_int.h b/proxy/proxy_http_int.h
new file mode 100644
index 0000000..d0a6bc0
--- /dev/null
+++ b/proxy/proxy_http_int.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+*/
+#ifndef _PROXY_HTTP_INT_H
+#define _PROXY_HTTP_INT_H
+
+#include "proxy_http.h"
+#include "proxy_int.h"
+
+/* the HttpService object */
+typedef struct HttpService {
+ ProxyService root[1];
+ struct sockaddr_in server_addr; /* server address and port */
+ char* footer; /* the footer contains the static parts of the */
+ int footer_len; /* connection header, we generate it only once */
+ char footer0[512];
+} HttpService;
+
+/* create a CONNECT connection (for port != 80) */
+extern ProxyConnection* http_connector_connect(
+ HttpService* service,
+ struct sockaddr_in* address );
+
+/* create a HTTP rewriting connection (for port == 80) */
+extern ProxyConnection* http_rewriter_connect(
+ HttpService* service,
+ struct sockaddr_in* address );
+
+
+#endif /* _PROXY_HTTP_INT_H */
diff --git a/proxy/proxy_http_rewriter.c b/proxy/proxy_http_rewriter.c
new file mode 100644
index 0000000..3e98557
--- /dev/null
+++ b/proxy/proxy_http_rewriter.c
@@ -0,0 +1,1132 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+** GNU General Public License for more details.
+*/
+#include "proxy_http_int.h"
+#include "android_utils.h"
+#include <stdio.h>
+#include <string.h>
+#include "vl.h"
+
+/* this implements a transparent HTTP rewriting proxy
+ *
+ * this is needed because the HTTP spec mandates that
+ * any query made to a proxy uses an absolute URI as
+ * in:
+ *
+ * GET http://www.example.com/index.html HTTP/1.1
+ *
+ * while the Android browser will think it's talking to
+ * a normal web server and will issue a:
+ *
+ * GET /index.html HTTP/1.1
+ * Host: www.example.com
+ *
+ * what we do here is thus the following:
+ *
+ * - read the request header
+ * - rewrite the request's URI to use absolute URI
+ * - send the rewritten header to the proxy
+ * - then read the rest of the request, and tunnel it to the
+ * proxy as well
+ * - read the answer as-is and send it back to the system
+ *
+ * this sounds all easy, but the rules for computing the
+ * sizes of HTTP Message Bodies makes the implementation
+ * a *bit* funky.
+ */
+
+/* define D_ACTIVE to 1 to dump additionnal debugging
+ * info when -debug-proxy is used. These are only needed
+ * when debugging the proxy code.
+ */
+#define D_ACTIVE 1
+
+#if D_ACTIVE
+# define D(...) PROXY_LOG(__VA_ARGS__)
+#else
+# define D(...) ((void)0)
+#endif
+
+
+/** *************************************************************
+ **
+ ** HTTP HEADERS
+ **
+ **/
+
+typedef struct HttpHeader {
+ struct HttpHeader* next;
+ const char* key;
+ const char* value;
+} HttpHeader;
+
+static void
+http_header_free( HttpHeader* h )
+{
+ if (h) {
+ qemu_free((char*)h->value);
+ qemu_free(h);
+ }
+}
+
+static int
+http_header_append( HttpHeader* h, const char* value )
+{
+ int old = strlen(h->value);
+ int new = strlen(value);
+ char* s = realloc((char*)h->value, old+new+1);
+ if (s == NULL)
+ return -1;
+ memcpy(s + old, value, new+1);
+ h->value = (const char*)s;
+ return 0;
+}
+
+static HttpHeader*
+http_header_alloc( const char* key, const char* value )
+{
+ int len = strlen(key)+1;
+ HttpHeader* h = malloc(sizeof(*h) + len+1);
+ if (h) {
+ h->next = NULL;
+ h->key = (const char*)(h+1);
+ memcpy( (char*)h->key, key, len );
+ h->value = qemu_strdup(value);
+ }
+ return h;
+}
+
+typedef struct {
+ HttpHeader* first;
+ HttpHeader* last;
+} HttpHeaderList;
+
+static void
+http_header_list_init( HttpHeaderList* l )
+{
+ l->first = l->last = NULL;
+}
+
+static void
+http_header_list_done( HttpHeaderList* l )
+{
+ while (l->first) {
+ HttpHeader* h = l->first;
+ l->first = h->next;
+ http_header_free(h);
+ }
+ l->last = NULL;
+}
+
+static void
+http_header_list_add( HttpHeaderList* l,
+ HttpHeader* h )
+{
+ if (!l->first) {
+ l->first = h;
+ } else {
+ l->last->next = h;
+ }
+ h->next = NULL;
+ l->last = h;
+}
+
+static const char*
+http_header_list_find( HttpHeaderList* l,
+ const char* key )
+{
+ HttpHeader* h;
+ for (h = l->first; h; h = h->next)
+ if (!strcasecmp(h->key, key))
+ return h->value;
+
+ return NULL;
+}
+
+/** *************************************************************
+ **
+ ** HTTP REQUEST AND REPLY
+ **
+ **/
+
+typedef enum {
+ HTTP_REQUEST_UNSUPPORTED = 0,
+ HTTP_REQUEST_GET,
+ HTTP_REQUEST_HEAD,
+ HTTP_REQUEST_POST,
+ HTTP_REQUEST_PUT,
+ HTTP_REQUEST_DELETE,
+} HttpRequestType;
+
+typedef struct {
+ HttpRequestType req_type;
+ char* req_method;
+ char* req_uri;
+ char* req_version;
+ char* rep_version;
+ int rep_code;
+ char* rep_readable;
+ HttpHeaderList headers[1];
+} HttpRequest;
+
+
+static HttpRequest*
+http_request_alloc( const char* method,
+ const char* uri,
+ const char* version )
+{
+ HttpRequest* r = malloc(sizeof(*r));
+
+ r->req_method = qemu_strdup(method);
+ r->req_uri = qemu_strdup(uri);
+ r->req_version = qemu_strdup(version);
+ r->rep_version = NULL;
+ r->rep_code = -1;
+ r->rep_readable = NULL;
+
+ if (!strcmp(method,"GET")) {
+ r->req_type = HTTP_REQUEST_GET;
+ } else if (!strcmp(method,"POST")) {
+ r->req_type = HTTP_REQUEST_POST;
+ } else if (!strcmp(method,"HEAD")) {
+ r->req_type = HTTP_REQUEST_HEAD;
+ } else if (!strcmp(method,"PUT")) {
+ r->req_type = HTTP_REQUEST_PUT;
+ } else if (!strcmp(method,"DELETE")) {
+ r->req_type = HTTP_REQUEST_DELETE;
+ } else
+ r->req_type = HTTP_REQUEST_UNSUPPORTED;
+
+ http_header_list_init(r->headers);
+ return r;
+}
+
+static void
+http_request_replace_uri( HttpRequest* r,
+ const char* uri )
+{
+ const char* old = r->req_uri;
+ r->req_uri = qemu_strdup(uri);
+ qemu_free((char*)old);
+}
+
+static void
+http_request_free( HttpRequest* r )
+{
+ if (r) {
+ http_header_list_done(r->headers);
+
+ qemu_free(r->req_method);
+ qemu_free(r->req_uri);
+ qemu_free(r->req_version);
+ qemu_free(r->rep_version);
+ qemu_free(r->rep_readable);
+ qemu_free(r);
+ }
+}
+
+static char*
+http_request_find_header( HttpRequest* r,
+ const char* key )
+{
+ return (char*)http_header_list_find(r->headers, key);
+}
+
+
+static int
+http_request_add_header( HttpRequest* r,
+ const char* key,
+ const char* value )
+{
+ HttpHeader* h = http_header_alloc(key,value);
+ if (h) {
+ http_header_list_add(r->headers, h);
+ return 0;
+ }
+ return -1;
+}
+
+static int
+http_request_add_to_last_header( HttpRequest* r,
+ const char* line )
+{
+ if (r->headers->last) {
+ return http_header_append( r->headers->last, line );
+ } else {
+ return -1;
+ }
+}
+
+static int
+http_request_set_reply( HttpRequest* r,
+ const char* version,
+ const char* code,
+ const char* readable )
+{
+ if (strcmp(version,"HTTP/1.0") && strcmp(version,"HTTP/1.1")) {
+ PROXY_LOG("%s: bad reply protocol: %s", __FUNCTION__, version);
+ return -1;
+ }
+ r->rep_code = atoi(code);
+ if (r->rep_code == 0) {
+ PROXY_LOG("%s: bad reply code: %d", __FUNCTION__, code);
+ return -1;
+ }
+
+ r->rep_version = qemu_strdup(version);
+ r->rep_readable = qemu_strdup(readable);
+
+ /* reset the list of headers */
+ http_header_list_done(r->headers);
+ return 0;
+}
+
+/** *************************************************************
+ **
+ ** REWRITER CONNECTION
+ **
+ **/
+
+typedef enum {
+ STATE_CONNECTING = 0,
+ STATE_CREATE_SOCKET_PAIR,
+ STATE_REQUEST_FIRST_LINE,
+ STATE_REQUEST_HEADERS,
+ STATE_REQUEST_SEND,
+ STATE_REQUEST_BODY,
+ STATE_REPLY_FIRST_LINE,
+ STATE_REPLY_HEADERS,
+ STATE_REPLY_SEND,
+ STATE_REPLY_BODY,
+} ConnectionState;
+
+/* root->socket is connected to the proxy server. while
+ * slirp_fd is connected to the slirp code through a
+ * socket_pair() we created for this specific purpose.
+ */
+
+typedef enum {
+ BODY_NONE = 0,
+ BODY_KNOWN_LENGTH,
+ BODY_UNTIL_CLOSE,
+ BODY_CHUNKED,
+ BODY_MODE_MAX
+} BodyMode;
+
+static const char* const body_mode_str[BODY_MODE_MAX] = {
+ "NONE", "KNOWN_LENGTH", "UNTIL_CLOSE", "CHUNKED"
+};
+
+typedef struct {
+ ProxyConnection root[1];
+ int slirp_fd;
+ ConnectionState state;
+ HttpRequest* request;
+ BodyMode body_mode;
+ int64_t body_length;
+ int64_t body_total;
+ int64_t body_sent;
+ int64_t chunk_length;
+ int64_t chunk_total;
+ char body_has_data;
+ char body_is_full;
+ char body_is_closed;
+ char parse_chunk_header;
+ char parse_chunk_trailer;
+} RewriteConnection;
+
+
+static void
+rewrite_connection_free( ProxyConnection* root )
+{
+ RewriteConnection* conn = (RewriteConnection*)root;
+
+ if (conn->slirp_fd >= 0) {
+ socket_close(conn->slirp_fd);
+ conn->slirp_fd = -1;
+ }
+ http_request_free(conn->request);
+ proxy_connection_done(root);
+ qemu_free(conn);
+}
+
+
+static int
+rewrite_connection_init( RewriteConnection* conn )
+{
+ HttpService* service = (HttpService*) conn->root->service;
+ ProxyConnection* root = conn->root;
+ int ret;
+
+ conn->slirp_fd = -1;
+ conn->state = STATE_CONNECTING;
+
+ do {
+ ret = connect( root->socket,
+ (struct sockaddr*) &service->server_addr,
+ sizeof(service->server_addr) );
+ } while (ret < 0 && socket_errno == EINTR);
+
+ if (ret < 0) {
+ if (socket_errno == EINPROGRESS || socket_errno == EWOULDBLOCK) {
+ PROXY_LOG("%s: connecting", conn->root->name);
+ }
+ else {
+ PROXY_LOG("%s: cannot connect to proxy: %s", root->name, socket_errstr());
+ return -1;
+ }
+ }
+ else {
+ PROXY_LOG("%s: immediate connection", root->name);
+ conn->state = STATE_CREATE_SOCKET_PAIR;
+ }
+ return 0;
+}
+
+static int
+rewrite_connection_create_sockets( RewriteConnection* conn )
+{
+ /* immediate connection to the proxy. now create a socket
+ * pair and send a 'success' event to slirp */
+ int slirp_1;
+ ProxyConnection* root = conn->root;
+
+ if (socket_pair( &slirp_1, &conn->slirp_fd ) < 0) {
+ PROXY_LOG("%s: coult not create socket pair: %s",
+ root->name, socket_errstr());
+ return -1;
+ }
+
+ root->ev_func( root->ev_opaque, slirp_1, PROXY_EVENT_CONNECTED );
+ conn->state = STATE_REQUEST_FIRST_LINE;
+ return 0;
+}
+
+
+/* read the first line of a given HTTP request. returns -1/0/+1 */
+static DataStatus
+rewrite_connection_read_request( RewriteConnection* conn )
+{
+ ProxyConnection* root = conn->root;
+ DataStatus ret;
+
+ ret = proxy_connection_receive_line(root, conn->slirp_fd);
+ if (ret == DATA_COMPLETED) {
+ /* now parse the first line to see if we can handle it */
+ char* line = root->str->s;
+ char* method;
+ char* uri;
+ char* version;
+ char* p = line;
+
+ method = strsep(&p, " ");
+ if (p == NULL) {
+ PROXY_LOG("%s: can't parse method in '%'",
+ root->name, line);
+ return DATA_ERROR;
+ }
+ uri = strsep(&p, " ");
+ if (p == NULL) {
+ PROXY_LOG( "%s: can't parse URI in '%s'",
+ root->name, line);
+ return DATA_ERROR;
+ }
+ version = strsep(&p, " ");
+ if (p != NULL) {
+ PROXY_LOG( "%s: extra data after version in '%s'",
+ root->name, line);
+ return DATA_ERROR;
+ }
+ if (conn->request)
+ http_request_free(conn->request);
+
+ conn->request = http_request_alloc( method, uri, version );
+ if (!conn->request)
+ return DATA_ERROR;
+
+ proxy_connection_rewind(root);
+ }
+ return ret;
+}
+
+
+static DataStatus
+rewrite_connection_read_reply( RewriteConnection* conn )
+{
+ ProxyConnection* root = conn->root;
+ DataStatus ret;
+
+ ret = proxy_connection_receive_line( root, root->socket );
+ if (ret == DATA_COMPLETED) {
+ HttpRequest* request = conn->request;
+
+ char* line = stralloc_cstr( root->str );
+ char* p = line;
+ char* protocol;
+ char* number;
+ char* readable;
+
+ protocol = strsep(&p, " ");
+ if (p == NULL) {
+ PROXY_LOG("%s: can't parse response protocol: '%s'",
+ root->name, line);
+ return DATA_ERROR;
+ }
+ number = strsep(&p, " ");
+ if (p == NULL) {
+ PROXY_LOG("%s: can't parse response number: '%s'",
+ root->name, line);
+ return DATA_ERROR;
+ }
+ readable = p;
+
+ if (http_request_set_reply(request, protocol, number, readable) < 0)
+ return DATA_ERROR;
+
+ proxy_connection_rewind(root);
+ }
+ return ret;
+}
+
+
+static DataStatus
+rewrite_connection_read_headers( RewriteConnection* conn,
+ int fd )
+{
+ int ret;
+ ProxyConnection* root = conn->root;
+
+ for (;;) {
+ char* line;
+ stralloc_t* str = root->str;
+
+ ret = proxy_connection_receive_line(root, fd);
+ if (ret != DATA_COMPLETED)
+ break;
+
+ str->n = 0;
+ line = str->s;
+
+ if (line[0] == 0) {
+ /* an empty line means the end of headers */
+ ret = 1;
+ break;
+ }
+
+ /* it this a continuation ? */
+ if (line[0] == ' ' || line[0] == '\t') {
+ ret = http_request_add_to_last_header( conn->request, line );
+ }
+ else {
+ char* key;
+ char* value;
+
+ value = line;
+ key = strsep(&value, ":");
+ if (value == NULL) {
+ PROXY_LOG("%s: can't parse header '%s'", root->name, line);
+ ret = -1;
+ break;
+ }
+ value += strspn(value, " ");
+ if (http_request_add_header(conn->request, key, value) < 0)
+ ret = -1;
+ }
+ if (ret == DATA_ERROR)
+ break;
+ }
+ return ret;
+}
+
+static int
+rewrite_connection_rewrite_request( RewriteConnection* conn )
+{
+ ProxyConnection* root = conn->root;
+ HttpService* service = (HttpService*) root->service;
+ HttpRequest* r = conn->request;
+ stralloc_t* str = root->str;
+ HttpHeader* h;
+
+ proxy_connection_rewind(conn->root);
+
+ /* only rewrite the URI if it is not absolute */
+ if (r->req_uri[0] == '/') {
+ char* host = http_request_find_header(r, "Host");
+ if (host == NULL) {
+ PROXY_LOG("%s: uh oh, not Host: in request ?", root->name);
+ } else {
+ /* now create new URI */
+ stralloc_add_str(str, "http://");
+ stralloc_add_str(str, host);
+ stralloc_add_str(str, r->req_uri);
+ http_request_replace_uri(r, stralloc_cstr(str));
+ proxy_connection_rewind(root);
+ }
+ }
+
+ stralloc_format( str, "%s %s %s\r\n", r->req_method, r->req_uri, r->req_version );
+ for (h = r->headers->first; h; h = h->next) {
+ stralloc_add_format( str, "%s: %s\r\n", h->key, h->value );
+ }
+ /* add the service's footer - includes final \r\n */
+ stralloc_add_bytes( str, service->footer, service->footer_len );
+
+ return 0;
+}
+
+static int
+rewrite_connection_rewrite_reply( RewriteConnection* conn )
+{
+ HttpRequest* r = conn->request;
+ ProxyConnection* root = conn->root;
+ stralloc_t* str = root->str;
+ HttpHeader* h;
+
+ proxy_connection_rewind(root);
+ stralloc_format(str, "%s %d %s\r\n", r->rep_version, r->rep_code, r->rep_readable);
+ for (h = r->headers->first; h; h = h->next) {
+ stralloc_add_format(str, "%s: %s\r\n", h->key, h->value);
+ }
+ stralloc_add_str(str, "\r\n");
+
+ return 0;
+}
+
+
+static int
+rewrite_connection_get_body_length( RewriteConnection* conn,
+ int is_request )
+{
+ HttpRequest* r = conn->request;
+ ProxyConnection* root = conn->root;
+ char* content_length;
+ char* transfer_encoding;
+
+ conn->body_mode = BODY_NONE;
+ conn->body_length = 0;
+ conn->body_total = 0;
+ conn->body_sent = 0;
+ conn->body_is_closed = 0;
+ conn->body_is_full = 0;
+ conn->body_has_data = 0;
+
+ proxy_connection_rewind(root);
+
+ if (is_request) {
+ /* only POST and PUT should have a body */
+ if (r->req_type != HTTP_REQUEST_POST &&
+ r->req_type != HTTP_REQUEST_PUT)
+ {
+ return 0;
+ }
+ } else {
+ /* HTTP 1.1 Section 4.3 Message Body states that HEAD requests must not have
+ * a message body, as well as any 1xx, 204 and 304 replies */
+ if (r->req_type == HTTP_REQUEST_HEAD || r->rep_code/100 == 1 ||
+ r->rep_code == 204 || r->rep_code == 304)
+ return 0;
+ }
+
+ content_length = http_request_find_header(r, "Content-Length");
+ if (content_length != NULL) {
+ char* end;
+ int64_t body_len = strtoll( content_length, &end, 10 );
+ if (*end != '\0' || *content_length == '\0' || body_len < 0) {
+ PROXY_LOG("%s: bad content length: %s", root->name, content_length);
+ return DATA_ERROR;
+ }
+ if (body_len > 0) {
+ conn->body_mode = BODY_KNOWN_LENGTH;
+ conn->body_length = body_len;
+ }
+ } else {
+ char* connection = http_request_find_header(r, "Proxy-Connection");
+
+ if (!connection)
+ connection = http_request_find_header(r, "Connection");
+
+ if (!connection || strcasecmp(connection, "Close")) {
+ /* hum, we can't support this at all */
+ PROXY_LOG("%s: can't determine content length, and client wants"
+ " to keep connection opened",
+ root->name);
+ return -1;
+ }
+ /* a negative value means that the data ends when the client
+ * disconnects the connection.
+ */
+ conn->body_mode = BODY_UNTIL_CLOSE;
+ }
+ transfer_encoding = http_request_find_header(r, "Transfer-Encoding");
+ if (transfer_encoding && !strcasecmp(transfer_encoding, "Chunked")) {
+ conn->body_mode = BODY_CHUNKED;
+ conn->parse_chunk_header = 0;
+ conn->parse_chunk_trailer = 0;
+ conn->chunk_length = -1;
+ conn->chunk_total = 0;
+ }
+ D("%s: body_length=%lld body_mode=%s",
+ root->name, conn->body_length,
+ body_mode_str[conn->body_mode]);
+
+ proxy_connection_rewind(root);
+ return 0;
+}
+
+#define MAX_BODY_BUFFER 65536
+
+static DataStatus
+rewrite_connection_read_body( RewriteConnection* conn, int fd )
+{
+ ProxyConnection* root = conn->root;
+ stralloc_t* str = root->str;
+ int wanted = 0, current, avail;
+ DataStatus ret;
+
+ if (conn->body_is_closed) {
+ return DATA_NEED_MORE;
+ }
+
+ /* first, determine how many bytes we want to read. */
+ switch (conn->body_mode) {
+ case BODY_NONE:
+ D("%s: INTERNAL ERROR: SHOULDN'T BE THERE", root->name);
+ return DATA_COMPLETED;
+
+ case BODY_KNOWN_LENGTH:
+ {
+ if (conn->body_length == 0)
+ return DATA_COMPLETED;
+
+ if (conn->body_length > MAX_BODY_BUFFER)
+ wanted = MAX_BODY_BUFFER;
+ else
+ wanted = (int)conn->body_length;
+ }
+ break;
+
+ case BODY_UNTIL_CLOSE:
+ wanted = MAX_BODY_BUFFER;
+ break;
+
+ case BODY_CHUNKED:
+ if (conn->chunk_length < 0) {
+ /* chunk_length < 0 means we need to read a chunk header */
+ /* ensure that 'str' is flushed before doing this */
+ if (!conn->parse_chunk_header) {
+ if (conn->body_has_data)
+ return DATA_NEED_MORE;
+ D("%s: waiting chunk header", root->name);
+ conn->parse_chunk_header = 1;
+ }
+ ret = proxy_connection_receive_line(root, fd);
+ if (ret == DATA_COMPLETED) {
+ char* line = str->s;
+ char* end;
+ long long length;
+
+ length = strtoll(line, &end, 16);
+ if (line[0] == ' ' || (end[0] != '\0' && end[0] != ';')) {
+ PROXY_LOG("%s: invalid chunk header: %s",
+ root->name, line);
+ return DATA_ERROR;
+ }
+ if (length < 0) {
+ PROXY_LOG("%s: invalid chunk length %lld",
+ root->name, length);
+ return DATA_ERROR;
+ }
+ conn->chunk_length = length;
+ conn->chunk_total = 0;
+ if (length == 0) {
+ /* the last chunk, no we need to add the trailer */
+ conn->parse_chunk_trailer = 0;
+ }
+ conn->parse_chunk_header = 0;
+ }
+ }
+
+ if (conn->chunk_length == 0) {
+ /* chunk_length == 0 means we're reading the chunk trailer */
+ /* ensure that 'str' is flushed before reading the trailer */
+ if (!conn->parse_chunk_trailer) {
+ if (conn->body_has_data)
+ return DATA_NEED_MORE;
+ conn->parse_chunk_trailer = 1;
+ }
+ ret = rewrite_connection_read_headers(conn, fd);
+ if (ret == DATA_COMPLETED) {
+ conn->body_is_closed = 1;
+ }
+ return ret;
+ }
+
+ /* if we get here, body_length > 0 */
+ if (conn->chunk_length > MAX_BODY_BUFFER)
+ wanted = MAX_BODY_BUFFER;
+ else
+ wanted = (int)conn->chunk_length;
+ break;
+
+ default:
+ ;
+ }
+
+ /* we don't want more than MAX_BODY_BUFFER bytes in the
+ * buffer we used to pass the body */
+ current = str->n;
+ avail = MAX_BODY_BUFFER - current;
+ if (avail <= 0) {
+ /* wait for some flush */
+ conn->body_is_full = 1;
+ D("%s: waiting to flush %d bytes",
+ root->name, current);
+ return DATA_NEED_MORE;
+ }
+
+ if (wanted > avail)
+ wanted = avail;
+
+ ret = proxy_connection_receive(root, fd, wanted);
+ conn->body_has_data = (str->n > 0);
+ conn->body_is_full = (str->n == MAX_BODY_BUFFER);
+
+ if (ret == DATA_ERROR) {
+ if (conn->body_mode == BODY_UNTIL_CLOSE) {
+ /* a disconnection here is normal and signals the
+ * end of the body */
+ conn->body_total += root->str_recv;
+ D("%s: body completed by close (%lld bytes)",
+ root->name, conn->body_total);
+ conn->body_is_closed = 1;
+ ret = DATA_COMPLETED;
+ }
+ } else {
+ avail = root->str_recv;
+ ret = DATA_NEED_MORE; /* we're not really done yet */
+
+ switch (conn->body_mode) {
+ case BODY_CHUNKED:
+ conn->chunk_total += avail;
+ conn->chunk_length -= avail;
+
+ if (conn->chunk_length == 0) {
+ D("%s: chunk completed (%lld bytes)",
+ root->name, conn->chunk_length);
+ conn->body_total += conn->chunk_total;
+ conn->chunk_total = 0;
+ conn->chunk_length = -1;
+ }
+ break;
+
+ case BODY_KNOWN_LENGTH:
+ conn->body_length -= avail;
+ conn->body_total += avail;
+
+ if (conn->body_length == 0) {
+ D("%s: body completed (%lld bytes)",
+ root->name, conn->body_total);
+ conn->body_is_closed = 1;
+ ret = DATA_COMPLETED;
+ }
+ break;
+
+ case BODY_UNTIL_CLOSE:
+ conn->body_total += avail;
+ break;
+
+ default:
+ ;
+ }
+ }
+ return ret;
+}
+
+static DataStatus
+rewrite_connection_send_body( RewriteConnection* conn, int fd )
+{
+ ProxyConnection* root = conn->root;
+ stralloc_t* str = root->str;
+ DataStatus ret = DATA_NEED_MORE;
+
+ if (conn->body_has_data) {
+ ret = proxy_connection_send(root, fd);
+ if (ret != DATA_ERROR) {
+ int pos = root->str_pos;
+
+ memmove(str->s, str->s+pos, str->n-pos);
+ str->n -= pos;
+ root->str_pos = 0;
+ conn->body_is_full = (str->n == MAX_BODY_BUFFER);
+ conn->body_has_data = (str->n > 0);
+ conn->body_sent += root->str_sent;
+
+ /* ensure that we return DATA_COMPLETED only when
+ * we have sent everything, and there is no more
+ * body pieces to read */
+ if (ret == DATA_COMPLETED) {
+ if (!conn->body_is_closed || conn->body_has_data)
+ ret = DATA_NEED_MORE;
+ else {
+ D("%s: sent all body (%lld bytes)",
+ root->name, conn->body_sent);
+ }
+ }
+ D("%s: sent closed=%d data=%d n=%d ret=%d",
+ root->name, conn->body_is_closed,
+ conn->body_has_data, str->n,
+ ret);
+ }
+ }
+ return ret;
+}
+
+
+static void
+rewrite_connection_select( ProxyConnection* root,
+ ProxySelect* sel )
+{
+ RewriteConnection* conn = (RewriteConnection*)root;
+ int slirp = conn->slirp_fd;
+ int proxy = root->socket;
+
+ switch (conn->state) {
+ case STATE_CONNECTING:
+ case STATE_CREATE_SOCKET_PAIR:
+ /* try to connect to the proxy server */
+ proxy_select_set( sel, proxy, PROXY_SELECT_WRITE );
+ break;
+
+ case STATE_REQUEST_FIRST_LINE:
+ case STATE_REQUEST_HEADERS:
+ proxy_select_set( sel, slirp, PROXY_SELECT_READ );
+ break;
+
+ case STATE_REQUEST_SEND:
+ proxy_select_set( sel, proxy, PROXY_SELECT_WRITE );
+ break;
+
+ case STATE_REQUEST_BODY:
+ if (!conn->body_is_closed && !conn->body_is_full)
+ proxy_select_set( sel, slirp, PROXY_SELECT_READ );
+
+ if (conn->body_has_data)
+ proxy_select_set( sel, proxy, PROXY_SELECT_WRITE );
+ break;
+
+ case STATE_REPLY_FIRST_LINE:
+ case STATE_REPLY_HEADERS:
+ proxy_select_set( sel, proxy, PROXY_SELECT_READ );
+ break;
+
+ case STATE_REPLY_SEND:
+ proxy_select_set( sel, slirp, PROXY_SELECT_WRITE );
+ break;
+
+ case STATE_REPLY_BODY:
+ if (conn->body_has_data)
+ proxy_select_set( sel, slirp, PROXY_SELECT_WRITE );
+
+ if (!conn->body_is_closed && !conn->body_is_full)
+ proxy_select_set( sel, proxy, PROXY_SELECT_READ );
+ break;
+ default:
+ ;
+ };
+}
+
+static void
+rewrite_connection_poll( ProxyConnection* root,
+ ProxySelect* sel )
+{
+ RewriteConnection* conn = (RewriteConnection*)root;
+
+ int slirp = conn->slirp_fd;
+ int proxy = root->socket;
+ int has_slirp = proxy_select_poll(sel, slirp);
+ int has_proxy = proxy_select_poll(sel, proxy);
+ DataStatus ret = DATA_NEED_MORE;
+
+ switch (conn->state) {
+ case STATE_CONNECTING:
+ if (has_proxy) {
+ PROXY_LOG("%s: connected to proxy", root->name);
+ conn->state = STATE_CREATE_SOCKET_PAIR;
+ }
+ break;
+
+ case STATE_CREATE_SOCKET_PAIR:
+ if (has_proxy) {
+ if (rewrite_connection_create_sockets(conn) < 0) {
+ ret = DATA_ERROR;
+ } else {
+ D("%s: socket pair created", root->name);
+ conn->state = STATE_REQUEST_FIRST_LINE;
+ }
+ }
+ break;
+
+ case STATE_REQUEST_FIRST_LINE:
+ if (has_slirp) {
+ ret = rewrite_connection_read_request(conn);
+ if (ret == DATA_COMPLETED) {
+ PROXY_LOG("%s: request first line ok", root->name);
+ conn->state = STATE_REQUEST_HEADERS;
+ }
+ }
+ break;
+
+ case STATE_REQUEST_HEADERS:
+ if (has_slirp) {
+ ret = rewrite_connection_read_headers(conn, slirp);
+ if (ret == DATA_COMPLETED) {
+ PROXY_LOG("%s: request headers ok", root->name);
+ if (rewrite_connection_rewrite_request(conn) < 0)
+ ret = DATA_ERROR;
+ else
+ conn->state = STATE_REQUEST_SEND;
+ }
+ }
+ break;
+
+ case STATE_REQUEST_SEND:
+ if (has_proxy) {
+ ret = proxy_connection_send(root, proxy);
+ if (ret == DATA_COMPLETED) {
+ if (rewrite_connection_get_body_length(conn, 1) < 0) {
+ ret = DATA_ERROR;
+ } else if (conn->body_mode != BODY_NONE) {
+ PROXY_LOG("%s: request sent, waiting for body",
+ root->name);
+ conn->state = STATE_REQUEST_BODY;
+ } else {
+ PROXY_LOG("%s: request sent, waiting for reply",
+ root->name);
+ conn->state = STATE_REPLY_FIRST_LINE;
+ }
+ }
+ }
+ break;
+
+ case STATE_REQUEST_BODY:
+ if (has_slirp) {
+ ret = rewrite_connection_read_body(conn, slirp);
+ }
+ if (ret != DATA_ERROR && has_proxy) {
+ ret = rewrite_connection_send_body(conn, proxy);
+ if (ret == DATA_COMPLETED) {
+ PROXY_LOG("%s: request body ok, waiting for reply",
+ root->name);
+ conn->state = STATE_REPLY_FIRST_LINE;
+ }
+ }
+ break;
+
+ case STATE_REPLY_FIRST_LINE:
+ if (has_proxy) {
+ ret = rewrite_connection_read_reply(conn);
+ if (ret == DATA_COMPLETED) {
+ PROXY_LOG("%s: reply first line ok", root->name);
+ conn->state = STATE_REPLY_HEADERS;
+ }
+ }
+ break;
+
+ case STATE_REPLY_HEADERS:
+ if (has_proxy) {
+ ret = rewrite_connection_read_headers(conn, proxy);
+ if (ret == DATA_COMPLETED) {
+ PROXY_LOG("%s: reply headers ok", root->name);
+ if (rewrite_connection_rewrite_reply(conn) < 0)
+ ret = DATA_ERROR;
+ else
+ conn->state = STATE_REPLY_SEND;
+ }
+ }
+ break;
+
+ case STATE_REPLY_SEND:
+ if (has_slirp) {
+ ret = proxy_connection_send(conn->root, slirp);
+ if (ret == DATA_COMPLETED) {
+ if (rewrite_connection_get_body_length(conn, 0) < 0) {
+ ret = DATA_ERROR;
+ } else if (conn->body_mode != BODY_NONE) {
+ PROXY_LOG("%s: reply sent, waiting for body",
+ root->name);
+ conn->state = STATE_REPLY_BODY;
+ } else {
+ PROXY_LOG("%s: reply sent, looping to waiting request",
+ root->name);
+ conn->state = STATE_REQUEST_FIRST_LINE;
+ }
+ }
+ }
+ break;
+
+ case STATE_REPLY_BODY:
+ if (has_proxy) {
+ ret = rewrite_connection_read_body(conn, proxy);
+ }
+ if (ret != DATA_ERROR && has_slirp) {
+ ret = rewrite_connection_send_body(conn, slirp);
+ if (ret == DATA_COMPLETED) {
+ if (conn->body_mode == BODY_UNTIL_CLOSE) {
+ PROXY_LOG("%s: closing connection", root->name);
+ ret = DATA_ERROR;
+ } else {
+ PROXY_LOG("%s: reply body ok, looping to waiting request",
+ root->name);
+ conn->state = STATE_REQUEST_FIRST_LINE;
+ }
+ }
+ }
+ break;
+
+ default:
+ ;
+ }
+ if (ret == DATA_ERROR)
+ proxy_connection_free(root, 0, PROXY_EVENT_NONE);
+
+ return;
+}
+
+
+ProxyConnection*
+http_rewriter_connect( HttpService* service,
+ struct sockaddr_in* address )
+{
+ RewriteConnection* conn;
+ int s;
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0)
+ return NULL;
+
+ conn = qemu_mallocz(sizeof(*conn));
+ if (conn == NULL) {
+ socket_close(s);
+ return NULL;
+ }
+
+ proxy_connection_init( conn->root, s, address, service->root,
+ rewrite_connection_free,
+ rewrite_connection_select,
+ rewrite_connection_poll );
+
+ if ( rewrite_connection_init( conn ) < 0 ) {
+ rewrite_connection_free( conn->root );
+ return NULL;
+ }
+
+ return conn->root;
+}
diff --git a/proxy/proxy_int.h b/proxy/proxy_int.h
index 9d91169..e71d9d6 100644
--- a/proxy/proxy_int.h
+++ b/proxy/proxy_int.h
@@ -14,6 +14,7 @@
#include "proxy_common.h"
#include "sockets.h"
+#include "android_utils.h"
extern int proxy_log;
@@ -24,11 +25,44 @@ proxy_LOG(const char* fmt, ...);
do { if (proxy_log) proxy_LOG(__VA_ARGS__); } while (0)
+/* ProxySelect is used to handle events */
+
+enum {
+ PROXY_SELECT_READ = (1 << 0),
+ PROXY_SELECT_WRITE = (1 << 1),
+ PROXY_SELECT_ERROR = (1 << 2)
+};
+
+typedef struct {
+ int* pcount;
+ fd_set* reads;
+ fd_set* writes;
+ fd_set* errors;
+} ProxySelect;
+
+extern void proxy_select_set( ProxySelect* sel,
+ int fd,
+ unsigned flags );
+
+extern unsigned proxy_select_poll( ProxySelect* sel, int fd );
+
+
/* sockets proxy manager internals */
typedef struct ProxyConnection ProxyConnection;
typedef struct ProxyService ProxyService;
+/* free a given proxified connection */
+typedef void (*ProxyConnectionFreeFunc) ( ProxyConnection* conn );
+
+/* modify the ProxySelect to tell which events to listen to */
+typedef void (*ProxyConnectionSelectFunc) ( ProxyConnection* conn,
+ ProxySelect* sel );
+
+/* action a proxy connection when select() returns certain events for its socket */
+typedef void (*ProxyConnectionPollFunc) ( ProxyConnection* conn,
+ ProxySelect* sel );
+
/* root ProxyConnection object */
struct ProxyConnection {
@@ -42,51 +76,93 @@ struct ProxyConnection {
/* the following is useful for all types of services */
char name[64]; /* for debugging purposes */
- int buffer_pos;
- int buffer_len;
- char* buffer;
- char buffer0[ 1024 ];
- /* rest of data depend on ProxyService */
+ stralloc_t str[1]; /* network buffer (dynamic) */
+ int str_pos; /* see proxy_connection_send() */
+ int str_sent; /* see proxy_connection_send() */
+ int str_recv; /* see proxy_connection_receive() */
+
+ /* connection methods */
+ ProxyConnectionFreeFunc conn_free;
+ ProxyConnectionSelectFunc conn_select;
+ ProxyConnectionPollFunc conn_poll;
+
+ /* rest of data depend on exact implementation */
};
extern void
-proxy_connection_init( ProxyConnection* conn,
- int socket,
- struct sockaddr_in* address,
- ProxyService* service );
+proxy_connection_init( ProxyConnection* conn,
+ int socket,
+ struct sockaddr_in* address,
+ ProxyService* service,
+ ProxyConnectionFreeFunc conn_free,
+ ProxyConnectionSelectFunc conn_select,
+ ProxyConnectionPollFunc conn_poll );
extern void
proxy_connection_done( ProxyConnection* conn );
+/* free the proxy connection object. this will also
+ * close the corresponding socket unless the
+ * 'keep_alive' flag is set to TRUE.
+ */
extern void
proxy_connection_free( ProxyConnection* conn,
+ int keep_alive,
ProxyEvent event );
-/* tries to send data from the connection's buffer to the proxy.
- * returns 1 when all data has been sent (i.e. buffer_pos == buffer_len),
- * 0 if there is still some data to send, or -1 in case of error
+/* status of data transfer operations */
+typedef enum {
+ DATA_ERROR = -1,
+ DATA_NEED_MORE = 0,
+ DATA_COMPLETED = 1
+} DataStatus;
+
+/* try to send data from the connection's buffer to a socket.
+ * starting from offset conn->str_pos in the buffer
+ *
+ * returns DATA_COMPLETED if everything could be written
+ * returns DATA_ERROR for a socket disconnection or error
+ * returns DATA_NEED_MORE if all data could not be sent.
+ *
+ * on exit, conn->str_sent contains the number of bytes
+ * that were really sent. conn->str_pos will be incremented
+ * by conn->str_sent as well.
+ *
+ * note that in case of success (DATA_COMPLETED), this also
+ * performs a proxy_connection_rewind which sets conn->str_pos
+ * to 0.
*/
-extern int
-proxy_connection_send( ProxyConnection* conn );
-
-/* tries to receive data from the connection's buffer from the proxy
- * returns 1 when all data has been received (buffer_pos == buffer_len)
- * returns 0 if there is still some data to receive
- * returns -1 in case of error
+extern DataStatus
+proxy_connection_send( ProxyConnection* conn, int fd );
+
+/* try to read 'wanted' bytes into conn->str from a socket
+ *
+ * returns DATA_COMPLETED if all bytes could be read
+ * returns DATA_NEED_MORE if not all bytes could be read
+ * returns DATA_ERROR in case of socket disconnection or error
+ *
+ * on exit, the amount of data received is in conn->str_recv
*/
-extern int
-proxy_connection_receive( ProxyConnection* conn );
+extern DataStatus
+proxy_connection_receive( ProxyConnection* conn, int fd, int wanted );
-/* tries to receive a line of text from the proxy
+/* tries to receive a line of text from the proxy.
+ * when an entire line is read, the trailing \r\n is stripped
+ * and replaced by a terminating zero. str->n will be the
+ * lenght of the line, exclusing the terminating zero.
* returns 1 when a line has been received
* returns 0 if there is still some data to receive
* returns -1 in case of error
*/
-extern int
-proxy_connection_receive_line( ProxyConnection* conn );
+extern DataStatus
+proxy_connection_receive_line( ProxyConnection* conn, int fd );
+
+/* rewind the string buffer for a new operation */
+extern void
+proxy_connection_rewind( ProxyConnection* conn );
/* base64 encode a source string, returns size of encoded result,
* or -1 if there was not enough room in the destination buffer
@@ -103,38 +179,19 @@ proxy_resolve_server( struct sockaddr_in* addr,
/* a ProxyService is really a proxy server and associated options */
-enum {
- PROXY_SELECT_READ = (1 << 0),
- PROXY_SELECT_WRITE = (1 << 1),
- PROXY_SELECT_ERROR = (1 << 2)
-};
-
/* destroy a given proxy service */
typedef void (*ProxyServiceFreeFunc) ( void* opaque );
/* tries to create a new proxified connection, returns NULL if the service can't
* handle this address */
typedef ProxyConnection* (*ProxyServiceConnectFunc)( void* opaque,
- int socket,
+ int socket_type,
struct sockaddr_in* address );
-/* free a given proxified connection */
-typedef void (*ProxyConnectionFreeFunc) ( ProxyConnection* conn );
-
-/* return flags corresponding to the select() events to wait to a proxified connection */
-typedef unsigned (*ProxyConnectionSelectFunc) ( ProxyConnection* conn );
-
-/* action a proxy connection when select() returns certain events for its socket */
-typedef void (*ProxyConnectionPollFunc) ( ProxyConnection* conn,
- unsigned select_flags );
-
struct ProxyService {
void* opaque;
ProxyServiceFreeFunc serv_free;
ProxyServiceConnectFunc serv_connect;
- ProxyConnectionFreeFunc conn_free;
- ProxyConnectionSelectFunc conn_select;
- ProxyConnectionPollFunc conn_poll;
};
extern int
@@ -142,4 +199,3 @@ proxy_manager_add_service( ProxyService* service );
#endif /* _PROXY_INT_H */
-