aboutsummaryrefslogtreecommitdiffstats
path: root/proxy
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commit55f4e4a5ec657a017e3bf75299ad71fd1c968dd3 (patch)
tree550ce922ea0e125ac6a9738210ce2939bf2fe901 /proxy
parent413f05aaf54fa08c0ae7e997327a4f4a473c0a8d (diff)
downloadexternal_qemu-55f4e4a5ec657a017e3bf75299ad71fd1c968dd3.zip
external_qemu-55f4e4a5ec657a017e3bf75299ad71fd1c968dd3.tar.gz
external_qemu-55f4e4a5ec657a017e3bf75299ad71fd1c968dd3.tar.bz2
Initial Contribution
Diffstat (limited to 'proxy')
-rw-r--r--proxy/proxy_common.c502
-rw-r--r--proxy/proxy_common.h79
-rw-r--r--proxy/proxy_http.c371
-rw-r--r--proxy/proxy_http.h24
-rw-r--r--proxy/proxy_int.h145
5 files changed, 1121 insertions, 0 deletions
diff --git a/proxy/proxy_common.c b/proxy/proxy_common.c
new file mode 100644
index 0000000..c5762dc
--- /dev/null
+++ b/proxy/proxy_common.c
@@ -0,0 +1,502 @@
+/* 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_int.h"
+#include "sockets.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include "vl.h"
+
+int proxy_log = 0;
+
+void
+proxy_LOG(const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+}
+
+void
+proxy_set_verbose(int mode)
+{
+ proxy_log = mode;
+}
+
+/** Global connection list
+ **/
+
+static ProxyConnection s_connections[1];
+
+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;
+ }
+}
+
+
+void
+proxy_connection_init( ProxyConnection* conn,
+ int socket,
+ struct sockaddr_in* address,
+ ProxyService* service )
+{
+ conn->socket = socket;
+ conn->address = address[0];
+ conn->service = service;
+ conn->next = NULL;
+
+ {
+ uint32_t ip = ntohl(address->sin_addr.s_addr);
+ uint16_t port = ntohs(address->sin_port);
+ int type = socket_get_type(socket);
+
+ snprintf( conn->name, sizeof(conn->name),
+ "%s:%d.%d.%d.%d:%d(%d)",
+ (type == SOCK_STREAM) ? "tcp" : "udp",
+ (ip >> 24) & 255, (ip >> 16) & 255,
+ (ip >> 8) & 255, ip & 255, port,
+ socket );
+
+ /* just in case */
+ conn->name[sizeof(conn->name)-1] = 0;
+ }
+
+ conn->buffer_pos = 0;
+ conn->buffer_len = 0;
+ conn->buffer = conn->buffer0;
+}
+
+void
+proxy_connection_done( ProxyConnection* conn )
+{
+ if (conn->buffer != conn->buffer0) {
+ qemu_free(conn->buffer);
+ }
+}
+
+
+int
+proxy_connection_send( ProxyConnection* conn )
+{
+ int result = -1;
+ int fd = conn->socket;
+ int avail = conn->buffer_len - conn->buffer_pos;
+
+ if (proxy_log) {
+ PROXY_LOG("%s: sending %d bytes:\n", conn->name, avail );
+ hex_dump( conn->buffer + conn->buffer_pos, avail, ">> " );
+ }
+
+ while (avail > 0) {
+ int n = send(fd, conn->buffer + conn->buffer_pos, avail, 0);
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
+ return 0;
+ PROXY_LOG("%s: error: %s\n", conn->name, strerror(errno));
+ return -1;
+ }
+ conn->buffer_pos += n;
+ avail -= n;
+ }
+ return 1;
+}
+
+int
+proxy_connection_receive( ProxyConnection* conn )
+{
+ int result = -1;
+ int fd = conn->socket;
+ int avail = conn->buffer_len - conn->buffer_pos;
+
+ while (avail > 0) {
+ int n = recv(fd, conn->buffer + conn->buffer_pos, avail, 0);
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
+ return 0;
+ PROXY_LOG("%s: error: %s\n", conn->name, strerror(errno));
+ return -1;
+ }
+
+ if (proxy_log) {
+ PROXY_LOG("%s: received %d bytes:\n", conn->name, n );
+ hex_dump( conn->buffer + conn->buffer_pos, n, ">> " );
+ }
+
+ conn->buffer_pos += n;
+ avail -= n;
+ }
+ return 1;
+}
+
+int
+proxy_connection_receive_line( ProxyConnection* conn )
+{
+ int result = -1;
+ int fd = conn->socket;
+
+ for (;;) {
+ char c;
+ int n = recv(fd, &c, 1, 0);
+ if (n == 0) {
+ PROXY_LOG("%s: disconnected from server\n", conn->name );
+ return -1;
+ }
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EWOULDBLOCK || errno == EAGAIN) {
+ PROXY_LOG("%s: blocked\n", conn->name);
+ return 0;
+ }
+ PROXY_LOG("%s: error: %s\n", conn->name, strerror(errno));
+ return -1;
+ }
+
+ 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;
+
+ 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;
+ }
+ }
+}
+
+
+
+static void
+proxy_connection_insert( ProxyConnection* conn, ProxyConnection* after )
+{
+ conn->next = after->next;
+ after->next->prev = conn;
+ after->next = conn;
+ conn->prev = after;
+}
+
+static void
+proxy_connection_remove( ProxyConnection* conn )
+{
+ conn->prev->next = conn->next;
+ conn->next->prev = conn->prev;
+
+ conn->next = conn->prev = conn;
+}
+
+/** Global service list
+ **/
+
+#define MAX_SERVICES 4
+
+static ProxyService* s_services[ MAX_SERVICES ];
+static int s_num_services;
+static int s_init;
+
+static void proxy_manager_atexit( void );
+
+static void
+proxy_manager_init(void)
+{
+ s_init = 1;
+ s_connections->next = s_connections;
+ s_connections->prev = s_connections;
+ atexit( proxy_manager_atexit );
+}
+
+
+extern int
+proxy_manager_add_service( ProxyService* service )
+{
+ if (!service || s_num_services >= MAX_SERVICES)
+ return -1;
+
+ if (!s_init)
+ proxy_manager_init();
+
+ s_services[s_num_services++] = service;
+ return 0;
+}
+
+
+extern void
+proxy_manager_atexit( void )
+{
+ ProxyConnection* conn = s_connections->next;
+ int n;
+
+ /* free all proxy connections */
+ while (conn != s_connections) {
+ ProxyConnection* next = conn->next;
+ conn->service->conn_free( conn );
+ conn = next;
+ }
+ conn->next = conn;
+ conn->prev = conn;
+
+ /* free all proxy services */
+ for (n = s_num_services; n-- > 0;) {
+ ProxyService* service = s_services[n];
+ service->serv_free( service->opaque );
+ }
+ s_num_services = 0;
+}
+
+
+void
+proxy_connection_free( ProxyConnection* conn,
+ ProxyEvent event )
+{
+ if (conn) {
+ int fd = conn->socket;
+
+ proxy_connection_remove(conn);
+
+ if (event != PROXY_EVENT_NONE)
+ conn->ev_func( conn->ev_opaque, event );
+
+ conn->service->conn_free(conn);
+ }
+}
+
+
+int
+proxy_manager_add( int socket,
+ struct sockaddr_in* address,
+ void* ev_opaque,
+ ProxyEventFunc ev_func )
+{
+ int n;
+
+ if (!s_init) {
+ 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,
+ address );
+ if (conn != NULL) {
+ conn->ev_opaque = ev_opaque;
+ conn->ev_func = ev_func;
+ proxy_connection_insert(conn, s_connections->prev);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+
+/* 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
+ */
+void
+proxy_manager_del( int socket )
+{
+ ProxyConnection* conn = s_connections->next;
+ for ( ; conn != s_connections; conn = conn->next ) {
+ if (conn->socket == socket) {
+ int fd = conn->socket;
+ proxy_connection_remove(conn);
+ conn->service->conn_free(conn);
+ socket_close(fd);
+ return;
+ }
+ }
+}
+
+/* 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;
+
+ 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;
+
+ 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 );
+ }
+ }
+}
+
+/* this function is called to act on proxified connection sockets when network events arrive */
+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;
+
+ 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;
+
+ if (flags != 0) {
+ conn->service->conn_poll( conn, flags );
+ }
+ conn = next;
+ }
+}
+
+
+int
+proxy_base64_encode( const char* src, int srclen,
+ char* dst, int dstlen )
+{
+ static const char cb64[64]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ const char* srcend = src + srclen;
+ int result = 0;
+
+ while (src+3 <= srcend && result+4 <= dstlen)
+ {
+ dst[result+0] = cb64[ src[0] >> 2 ];
+ dst[result+1] = cb64[ ((src[0] & 3) << 4) | ((src[1] & 0xf0) >> 4) ];
+ dst[result+2] = cb64[ ((src[1] & 0xf) << 2) | ((src[2] & 0xc0) >> 6) ];
+ dst[result+3] = cb64[ src[2] & 0x3f ];
+ src += 3;
+ result += 4;
+ }
+
+ if (src < srcend) {
+ unsigned char in[4];
+
+ if (result+4 > dstlen)
+ return -1;
+
+ in[0] = src[0];
+ in[1] = src+1 < srcend ? src[1] : 0;
+ in[2] = src+2 < srcend ? src[2] : 0;
+
+ dst[result+0] = cb64[ in[0] >> 2 ];
+ dst[result+1] = cb64[ ((in[0] & 3) << 4) | ((in[1] & 0xf0) >> 4) ];
+ dst[result+2] = (unsigned char) (src+1 < srcend ? cb64[ ((in[1] & 0xf) << 2) | ((in[2] & 0xc0) >> 6) ] : '=');
+ dst[result+3] = (unsigned char) (src+2 < srcend ? cb64[ in[2] & 0x3f ] : '=');
+ result += 4;
+ }
+ return result;
+}
+
+int
+proxy_resolve_server( struct sockaddr_in* addr,
+ const char* servername,
+ int servernamelen,
+ int serverport )
+{
+ char name0[64], *name = name0;
+ int result = -1;
+ struct hostent* host;
+
+ if (servernamelen < 0)
+ servernamelen = strlen(servername);
+
+ memset(addr, 0, sizeof(*addr));
+ addr->sin_family = AF_INET;
+ addr->sin_port = htons(serverport);
+
+ if (servernamelen >= sizeof(name0)) {
+ name = qemu_malloc(servernamelen+1);
+ if (name == NULL)
+ return -1;
+ }
+
+ memcpy(name, servername, servernamelen);
+ name[servernamelen] = 0;
+
+ host = gethostbyname(name);
+ if (host == NULL) {
+ PROXY_LOG("%s: can't resolve proxy server name '%s'\n",
+ __FUNCTION__, name);
+ goto Exit;
+ }
+
+ 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);
+ }
+ result = 0;
+
+Exit:
+ if (name != name0)
+ qemu_free(name);
+
+ return result;
+}
+
+
diff --git a/proxy/proxy_common.h b/proxy/proxy_common.h
new file mode 100644
index 0000000..54889cf
--- /dev/null
+++ b/proxy/proxy_common.h
@@ -0,0 +1,79 @@
+/* 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_COMMON_H_
+#define _PROXY_COMMON_H_
+
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <sys/select.h>
+#include <netinet/in.h>
+#endif
+
+/* types and definitions used by all proxy connections */
+
+typedef enum {
+ PROXY_EVENT_NONE,
+ PROXY_EVENT_CONNECTED,
+ PROXY_EVENT_CONNECTION_REFUSED,
+ PROXY_EVENT_SERVER_ERROR
+} ProxyEvent;
+
+/* event can't be NONE when this callback is called */
+typedef void (*ProxyEventFunc)( void* opaque, ProxyEvent event );
+
+extern void proxy_set_verbose(int mode);
+
+
+typedef enum {
+ PROXY_OPTION_AUTH_USERNAME = 1,
+ PROXY_OPTION_AUTH_PASSWORD,
+
+ PROXY_OPTION_HTTP_NOCACHE = 100,
+ PROXY_OPTION_HTTP_KEEPALIVE,
+ PROXY_OPTION_HTTP_USER_AGENT,
+
+ PROXY_OPTION_MAX
+
+} ProxyOptionType;
+
+
+typedef struct {
+ ProxyOptionType type;
+ const char* string;
+ int string_len;
+} ProxyOption;
+
+
+/* add a new proxified socket connection to the manager's list. the event function
+ * will be called when the connection is established or refused.
+ *
+ * only IPv4 is supported at the moment, since our slirp code cannot handle IPv6
+ *
+ * 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 );
+
+/* 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 );
+
+/* 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);
+
+/* 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 );
+
+#endif /* END */
diff --git a/proxy/proxy_http.c b/proxy/proxy_http.c
new file mode 100644
index 0000000..83982a7
--- /dev/null
+++ b/proxy/proxy_http.c
@@ -0,0 +1,371 @@
+/* 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_int.h"
+#include "proxy_http.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__);
+ if (service->footer != service->footer0)
+ qemu_free(service->footer);
+ qemu_free(service);
+}
+
+
+static ProxyConnection*
+http_service_connect( HttpService* service,
+ int socket,
+ struct sockaddr_in* address )
+{
+ HttpConnection* conn = qemu_mallocz(sizeof(*conn));
+ int sock_type = socket_get_type(socket);
+
+ /* the HTTP proxy can only handle TCP connections */
+ if (sock_type != SOCK_STREAM)
+ return NULL;
+
+ /* if the client tries to directly connect to the proxy, let it do so */
+ if (address->sin_addr.s_addr == service->server_addr.sin_addr.s_addr &&
+ 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;
+ }
+
+ return conn->root;
+}
+
+
+int
+proxy_http_setup( const char* servername,
+ int servernamelen,
+ int serverport,
+ int num_options,
+ const ProxyOption* options )
+{
+ HttpService* service;
+ struct sockaddr_in server_addr;
+ const ProxyOption* opt_nocache = NULL;
+ const ProxyOption* opt_keepalive = NULL;
+ const ProxyOption* opt_auth_user = NULL;
+ const ProxyOption* opt_auth_pass = NULL;
+ const ProxyOption* opt_user_agent = NULL;
+
+ if (servernamelen < 0)
+ servernamelen = strlen(servername);
+
+ PROXY_LOG( "%s: creating http proxy service connecting to: %.*s:%d\n",
+ __FUNCTION__, servernamelen, servername, serverport );
+
+ /* resolve server address */
+ if (proxy_resolve_server(&server_addr, servername,
+ servernamelen, serverport) < 0)
+ {
+ return -1;
+ }
+
+ /* create service object */
+ service = qemu_mallocz(sizeof(*service));
+ if (service == NULL) {
+ PROXY_LOG("%s: not enough memory to allocate new proxy service\n", __FUNCTION__);
+ return -1;
+ }
+
+ service->server_addr = server_addr;
+
+ /* parse options */
+ {
+ const ProxyOption* opt = options;
+ const ProxyOption* end = opt + num_options;
+
+ for ( ; opt < end; opt++ ) {
+ switch (opt->type) {
+ case PROXY_OPTION_HTTP_NOCACHE: opt_nocache = opt; break;
+ case PROXY_OPTION_HTTP_KEEPALIVE: opt_keepalive = opt; break;
+ case PROXY_OPTION_AUTH_USERNAME: opt_auth_user = opt; break;
+ case PROXY_OPTION_AUTH_PASSWORD: opt_auth_pass = opt; break;
+ case PROXY_OPTION_HTTP_USER_AGENT: opt_user_agent = opt; break;
+ default: ;
+ }
+ }
+ }
+
+ /* prepare footer */
+ {
+ int wlen;
+ char* p = service->footer0;
+ char* end = p + sizeof(service->footer0);
+
+ /* no-cache */
+ if (opt_nocache) {
+ p += snprintf(p, end-p, "Pragma: no-cache\r\nCache-Control: no-cache\r\n");
+ if (p >= end) goto FooterOverflow;
+ }
+ /* keep-alive */
+ if (opt_keepalive) {
+ p += snprintf(p, end-p, "Connection: Keep-Alive\r\nProxy-Connection: Keep-Alive\r\n");
+ if (p >= end) goto FooterOverflow;
+ }
+ /* authentication */
+ if (opt_auth_user && opt_auth_pass) {
+ char user_pass[256];
+ char encoded[512];
+ int uplen;
+
+ uplen = snprintf( user_pass, sizeof(user_pass), "%.*s:%.*s",
+ opt_auth_user->string_len, opt_auth_user->string,
+ opt_auth_pass->string_len, opt_auth_pass->string );
+
+ if (uplen >= sizeof(user_pass)) goto FooterOverflow;
+
+ 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);
+ goto FooterOverflow;
+ }
+
+ p += snprintf(p, end-p, "Proxy-authorization: Basic %.*s\r\n", wlen, encoded);
+ if (p >= end) goto FooterOverflow;
+ }
+ /* user agent */
+ if (opt_user_agent) {
+ p += snprintf(p, end-p, "User-Agent: %.*s\r\n",
+ opt_user_agent->string_len,
+ opt_user_agent->string);
+ if (p >= end) goto FooterOverflow;
+ }
+
+ p += snprintf(p, end-p, "\r\n");
+
+ if (p >= end) {
+ FooterOverflow:
+ PROXY_LOG( "%s: buffer overflow when creating connection footer\n",
+ __FUNCTION__);
+ http_service_free(service);
+ return -1;
+ }
+
+ service->footer = service->footer0;
+ service->footer_len = (p - service->footer);
+ }
+
+ PROXY_LOG( "%s: creating HTTP Proxy Service Footer is (len=%d):\n'%.*s'\n",
+ __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;
+
+ if (proxy_manager_add_service( service->root ) < 0) {
+ http_service_free(service);
+ return -1;
+ }
+ return 0;
+}
+
diff --git a/proxy/proxy_http.h b/proxy/proxy_http.h
new file mode 100644
index 0000000..a2e2917
--- /dev/null
+++ b/proxy/proxy_http.h
@@ -0,0 +1,24 @@
+/* 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_H
+#define _PROXY_HTTP_H
+
+#include "proxy_common.h"
+
+extern int
+proxy_http_setup( const char* servername,
+ int servernamelen,
+ int serverport,
+ int num_options,
+ const ProxyOption* options );
+
+#endif /* END */
diff --git a/proxy/proxy_int.h b/proxy/proxy_int.h
new file mode 100644
index 0000000..9d91169
--- /dev/null
+++ b/proxy/proxy_int.h
@@ -0,0 +1,145 @@
+/* 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_INT_H
+#define _PROXY_INT_H
+
+#include "proxy_common.h"
+#include "sockets.h"
+
+extern int proxy_log;
+
+extern void
+proxy_LOG(const char* fmt, ...);
+
+#define PROXY_LOG(...) \
+ do { if (proxy_log) proxy_LOG(__VA_ARGS__); } while (0)
+
+
+/* sockets proxy manager internals */
+
+typedef struct ProxyConnection ProxyConnection;
+typedef struct ProxyService ProxyService;
+
+
+/* root ProxyConnection object */
+struct ProxyConnection {
+ int socket;
+ struct sockaddr_in address; /* for debugging */
+ ProxyConnection* next;
+ ProxyConnection* prev;
+ ProxyEventFunc ev_func;
+ void* ev_opaque;
+ ProxyService* service;
+
+ /* 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 */
+};
+
+
+
+extern void
+proxy_connection_init( ProxyConnection* conn,
+ int socket,
+ struct sockaddr_in* address,
+ ProxyService* service );
+
+extern void
+proxy_connection_done( ProxyConnection* conn );
+
+extern void
+proxy_connection_free( ProxyConnection* conn,
+ 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
+ */
+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 int
+proxy_connection_receive( ProxyConnection* conn );
+
+/* tries to receive a line of text from the proxy
+ * 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 );
+
+/* base64 encode a source string, returns size of encoded result,
+ * or -1 if there was not enough room in the destination buffer
+ */
+extern int
+proxy_base64_encode( const char* src, int srclen,
+ char* dst, int dstlen );
+
+extern int
+proxy_resolve_server( struct sockaddr_in* addr,
+ const char* servername,
+ int servernamelen,
+ int serverport );
+
+/* 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,
+ 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
+proxy_manager_add_service( ProxyService* service );
+
+
+#endif /* _PROXY_INT_H */
+