/* 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_int.h"
#include "qemu-common.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
#define HTTP_VERSION "1.1"
static void
http_service_free( HttpService* service )
{
PROXY_LOG("%s", __FUNCTION__);
if (service->footer != service->footer0)
qemu_free(service->footer);
qemu_free(service);
}
static ProxyConnection*
http_service_connect( HttpService* service,
SocketType sock_type,
SockAddress* address )
{
/* the HTTP proxy can only handle TCP connections */
if (sock_type != SOCKET_STREAM)
return NULL;
/* if the client tries to directly connect to the proxy, let it do so */
if (sock_address_equal( address, &service->server_addr ))
return NULL;
PROXY_LOG("%s: trying to connect to %s",
__FUNCTION__, sock_address_to_string(address));
if (sock_address_get_port(address) == 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);
}
}
int
proxy_http_setup( const char* servername,
int servernamelen,
int serverport,
int num_options,
const ProxyOption* options )
{
HttpService* service;
SockAddress 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",
__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", __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'", 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",
__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'",
__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;
if (proxy_manager_add_service( service->root ) < 0) {
PROXY_LOG("%s: could not register service ?", __FUNCTION__);
http_service_free(service);
return -1;
}
return 0;
}