diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | 55f4e4a5ec657a017e3bf75299ad71fd1c968dd3 (patch) | |
tree | 550ce922ea0e125ac6a9738210ce2939bf2fe901 /proxy | |
parent | 413f05aaf54fa08c0ae7e997327a4f4a473c0a8d (diff) | |
download | external_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.c | 502 | ||||
-rw-r--r-- | proxy/proxy_common.h | 79 | ||||
-rw-r--r-- | proxy/proxy_http.c | 371 | ||||
-rw-r--r-- | proxy/proxy_http.h | 24 | ||||
-rw-r--r-- | proxy/proxy_int.h | 145 |
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 */ + |