aboutsummaryrefslogtreecommitdiffstats
path: root/proxy/proxy_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'proxy/proxy_common.c')
-rw-r--r--proxy/proxy_common.c532
1 files changed, 532 insertions, 0 deletions
diff --git a/proxy/proxy_common.c b/proxy/proxy_common.c
new file mode 100644
index 0000000..7794a62
--- /dev/null
+++ b/proxy/proxy_common.c
@@ -0,0 +1,532 @@
+/* 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 "android/utils/misc.h"
+#include "android/utils/system.h"
+#include <stdlib.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);
+ fprintf(stderr, "\n");
+}
+
+void
+proxy_set_verbose(int mode)
+{
+ proxy_log = mode;
+}
+
+/** Global connection list
+ **/
+
+static ProxyConnection s_connections[1];
+
+#define MAX_HEX_DUMP 512
+
+static void
+hex_dump( void* base, int size, const char* prefix )
+{
+ 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,
+ SockAddress* 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);
+
+ {
+ SocketType type = socket_get_type(socket);
+
+ snprintf( conn->name, sizeof(conn->name),
+ "%s:%s(%d)",
+ (type == SOCKET_STREAM) ? "tcp" : "udp",
+ sock_address_to_string(address), socket );
+
+ /* just in case */
+ conn->name[sizeof(conn->name)-1] = 0;
+ }
+
+ stralloc_reset(conn->str);
+ conn->str_pos = 0;
+}
+
+void
+proxy_connection_done( ProxyConnection* conn )
+{
+ stralloc_reset( conn->str );
+ if (conn->socket >= 0) {
+ socket_close(conn->socket);
+ conn->socket = -1;
+ }
+}
+
+
+void
+proxy_connection_rewind( ProxyConnection* conn )
+{
+ 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:", conn->name, avail );
+ hex_dump( str->s + conn->str_pos, avail, ">> " );
+ }
+
+ while (avail > 0) {
+ int n = socket_send(fd, str->s + conn->str_pos, avail);
+ if (n == 0) {
+ PROXY_LOG("%s: connection reset by peer (send)",
+ conn->name);
+ return DATA_ERROR;
+ }
+ if (n < 0) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
+ return DATA_NEED_MORE;
+
+ PROXY_LOG("%s: error: %s", conn->name, errno_str);
+ return DATA_ERROR;
+ }
+ conn->str_pos += n;
+ conn->str_sent += n;
+ avail -= n;
+ }
+
+ proxy_connection_rewind(conn);
+ return DATA_COMPLETED;
+}
+
+
+DataStatus
+proxy_connection_receive( ProxyConnection* conn, int fd, int wanted )
+{
+ stralloc_t* str = conn->str;
+
+ conn->str_recv = 0;
+
+ while (wanted > 0) {
+ int n;
+
+ stralloc_readyplus( str, wanted );
+ n = socket_recv(fd, str->s + str->n, wanted);
+ if (n == 0) {
+ PROXY_LOG("%s: connection reset by peer (receive)",
+ conn->name);
+ return DATA_ERROR;
+ }
+ if (n < 0) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
+ return DATA_NEED_MORE;
+
+ PROXY_LOG("%s: error: %s", conn->name, errno_str);
+ return DATA_ERROR;
+ }
+
+ if (proxy_log) {
+ PROXY_LOG("%s: received %d bytes:", conn->name, n );
+ hex_dump( str->s + str->n, n, "<< " );
+ }
+
+ str->n += n;
+ wanted -= n;
+ conn->str_recv += n;
+ }
+ return DATA_COMPLETED;
+}
+
+
+DataStatus
+proxy_connection_receive_line( ProxyConnection* conn, int fd )
+{
+ stralloc_t* str = conn->str;
+
+ for (;;) {
+ char c;
+ int n = socket_recv(fd, &c, 1);
+ if (n == 0) {
+ PROXY_LOG("%s: disconnected from server", conn->name );
+ return DATA_ERROR;
+ }
+ if (n < 0) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN) {
+ PROXY_LOG("%s: blocked", conn->name);
+ return DATA_NEED_MORE;
+ }
+ PROXY_LOG("%s: error: %s", conn->name, errno_str);
+ return DATA_ERROR;
+ }
+
+ stralloc_add_c(str, c);
+ if (c == '\n') {
+ 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'", conn->name,
+ quote_bytes(str->s, str->n));
+ return DATA_COMPLETED;
+ }
+ }
+}
+
+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->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,
+ int keep_alive,
+ ProxyEvent event )
+{
+ if (conn) {
+ int fd = conn->socket;
+
+ proxy_connection_remove(conn);
+
+ if (event != PROXY_EVENT_NONE)
+ conn->ev_func( conn->ev_opaque, fd, event );
+
+ if (keep_alive)
+ conn->socket = -1;
+
+ conn->conn_free(conn);
+ }
+}
+
+
+int
+proxy_manager_add( SockAddress* address,
+ SocketType sock_type,
+ ProxyEventFunc ev_func,
+ void* ev_opaque )
+{
+ int n;
+
+ if (!s_init) {
+ proxy_manager_init();
+ }
+
+ for (n = 0; n < s_num_services; n++) {
+ ProxyService* service = s_services[n];
+ ProxyConnection* conn = service->serv_connect( service->opaque,
+ sock_type,
+ address );
+ if (conn != NULL) {
+ conn->ev_func = ev_func;
+ conn->ev_opaque = ev_opaque;
+ 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( void* ev_opaque )
+{
+ ProxyConnection* conn = s_connections->next;
+ for ( ; conn != s_connections; conn = conn->next ) {
+ if (conn->ev_opaque == ev_opaque) {
+ proxy_connection_remove(conn);
+ 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();
+
+ sel->pcount = pcount;
+ sel->reads = read_fds;
+ sel->writes = write_fds;
+ sel->errors = err_fds;
+
+ conn = s_connections->next;
+ while (conn != s_connections) {
+ ProxyConnection* next = conn->next;
+ conn->conn_select(conn, sel);
+ conn = next;
+ }
+}
+
+/* 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;
+ ProxySelect sel[1];
+
+ sel->pcount = NULL;
+ sel->reads = read_fds;
+ sel->writes = write_fds;
+ sel->errors = err_fds;
+
+ while (conn != s_connections) {
+ ProxyConnection* next = conn->next;
+ conn->conn_poll( conn, sel );
+ 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( SockAddress* addr,
+ const char* servername,
+ int servernamelen,
+ int serverport )
+{
+ char name0[64], *name = name0;
+ int result = -1;
+
+ if (servernamelen < 0)
+ servernamelen = strlen(servername);
+
+ if (servernamelen >= sizeof(name0)) {
+ AARRAY_NEW(name, servernamelen+1);
+ }
+
+ memcpy(name, servername, servernamelen);
+ name[servernamelen] = 0;
+
+ if (sock_address_init_resolve( addr, name, serverport, 0 ) < 0) {
+ PROXY_LOG("%s: can't resolve proxy server name '%s'",
+ __FUNCTION__, name);
+ goto Exit;
+ }
+
+ PROXY_LOG("server name '%s' resolved to %s", name, sock_address_to_string(addr));
+ result = 0;
+
+Exit:
+ if (name != name0)
+ AFREE(name);
+
+ return result;
+}
+
+