2008-05-24 16:05:49 +08:00
|
|
|
/* tinyproxy - A fast light-weight HTTP proxy
|
|
|
|
* Copyright (C) 1998 Steven Young <sdyoung@miranda.org>
|
|
|
|
* Copyright (C) 1999-2005 Robert James Kaes <rjkaes@users.sourceforge.net>
|
|
|
|
* Copyright (C) 2000 Chris Lightfoot <chris@ex-parrot.com>
|
|
|
|
* Copyright (C) 2002 Petr Lampa <lampa@fit.vutbr.cz>
|
2000-02-17 01:32:49 +08:00
|
|
|
*
|
2008-05-24 16:05:49 +08:00
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* This is where all the work in tinyproxy is actually done. Incoming
|
2002-05-27 02:55:19 +08:00
|
|
|
* connections have a new child created for them. The child then
|
2000-09-12 08:04:42 +08:00
|
|
|
* processes the headers from the client, the response from the server,
|
|
|
|
* and then relays the bytes between the two.
|
2000-02-17 01:32:49 +08:00
|
|
|
*/
|
|
|
|
|
2009-08-07 06:12:53 +08:00
|
|
|
#include "main.h"
|
2000-09-12 08:04:42 +08:00
|
|
|
|
|
|
|
#include "acl.h"
|
|
|
|
#include "anonymous.h"
|
2000-02-17 01:32:49 +08:00
|
|
|
#include "buffer.h"
|
2001-10-26 00:58:50 +08:00
|
|
|
#include "conns.h"
|
2000-02-17 01:32:49 +08:00
|
|
|
#include "filter.h"
|
2002-04-08 05:35:59 +08:00
|
|
|
#include "hashmap.h"
|
2002-05-24 02:24:46 +08:00
|
|
|
#include "heap.h"
|
2008-05-24 16:17:14 +08:00
|
|
|
#include "html-error.h"
|
2000-09-12 08:04:42 +08:00
|
|
|
#include "log.h"
|
2002-05-24 02:24:46 +08:00
|
|
|
#include "network.h"
|
2000-09-12 08:04:42 +08:00
|
|
|
#include "reqs.h"
|
|
|
|
#include "sock.h"
|
|
|
|
#include "stats.h"
|
2002-05-24 02:24:46 +08:00
|
|
|
#include "text.h"
|
2000-09-12 08:04:42 +08:00
|
|
|
#include "utils.h"
|
2002-04-26 02:58:08 +08:00
|
|
|
#include "vector.h"
|
2008-03-09 09:33:54 +08:00
|
|
|
#include "reverse-proxy.h"
|
2008-06-09 06:50:23 +08:00
|
|
|
#include "transparent-proxy.h"
|
2000-02-17 01:32:49 +08:00
|
|
|
|
2001-12-18 03:10:56 +08:00
|
|
|
/*
|
|
|
|
* Maximum length of a HTTP line
|
|
|
|
*/
|
2001-12-19 13:13:40 +08:00
|
|
|
#define HTTP_LINE_LENGTH (MAXBUFFSIZE / 6)
|
2001-12-18 03:10:56 +08:00
|
|
|
|
2003-05-06 00:46:05 +08:00
|
|
|
|
2001-12-17 08:11:32 +08:00
|
|
|
/*
|
|
|
|
* Macro to help test if the Upstream proxy supported is compiled in and
|
|
|
|
* enabled.
|
|
|
|
*/
|
|
|
|
#ifdef UPSTREAM_SUPPORT
|
2003-05-30 03:44:00 +08:00
|
|
|
# define UPSTREAM_CONFIGURED() (config.upstream_list != NULL)
|
|
|
|
# define UPSTREAM_HOST(host) upstream_get(host)
|
2001-12-17 08:11:32 +08:00
|
|
|
#else
|
|
|
|
# define UPSTREAM_CONFIGURED() (0)
|
2003-05-30 03:44:00 +08:00
|
|
|
# define UPSTREAM_HOST(host) (NULL)
|
2001-12-17 08:11:32 +08:00
|
|
|
#endif
|
|
|
|
|
2001-12-20 12:48:32 +08:00
|
|
|
/*
|
|
|
|
* Codify the test for the carriage return and new line characters.
|
|
|
|
*/
|
2009-08-07 07:00:38 +08:00
|
|
|
#define CHECK_CRLF(header, len) \
|
|
|
|
(((len) == 1 && header[0] == '\n') || \
|
|
|
|
((len) == 2 && header[0] == '\r' && header[1] == '\n'))
|
2001-12-20 12:48:32 +08:00
|
|
|
|
2009-08-06 09:23:42 +08:00
|
|
|
/*
|
|
|
|
* Codify the test for header fields folded over multiple lines.
|
|
|
|
*/
|
2009-08-07 07:00:38 +08:00
|
|
|
#define CHECK_LWS(header, len) \
|
|
|
|
((len) > 0 && (header[0] == ' ' || header[0] == '\t'))
|
2009-08-06 09:23:42 +08:00
|
|
|
|
2002-04-13 01:00:42 +08:00
|
|
|
/*
|
|
|
|
* This is a global variable which stores which ports are allowed by
|
|
|
|
* the CONNECT method. It's a security thing.
|
|
|
|
*/
|
|
|
|
static vector_t ports_allowed_by_connect = NULL;
|
|
|
|
|
2003-05-30 03:44:00 +08:00
|
|
|
|
2002-04-13 01:00:42 +08:00
|
|
|
/*
|
|
|
|
* Now, this routine adds a "port" to the list. It also creates the list if
|
|
|
|
* it hasn't already by done.
|
|
|
|
*/
|
|
|
|
void
|
2008-12-01 23:01:11 +08:00
|
|
|
add_connect_port_allowed (int port)
|
2002-04-13 01:00:42 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
if (!ports_allowed_by_connect)
|
|
|
|
{
|
|
|
|
ports_allowed_by_connect = vector_create ();
|
|
|
|
if (!ports_allowed_by_connect)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
log_message (LOG_WARNING,
|
|
|
|
"Could not create a list of allowed CONNECT ports");
|
|
|
|
return;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
log_message (LOG_INFO, "Adding Port [%d] to the list allowed by CONNECT",
|
2008-12-08 21:39:44 +08:00
|
|
|
port);
|
2008-12-01 23:01:11 +08:00
|
|
|
vector_append (ports_allowed_by_connect, (void **) &port, sizeof (port));
|
2002-04-13 01:00:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This routine checks to see if a port is allowed in the CONNECT method.
|
|
|
|
*
|
|
|
|
* Returns: 1 if allowed
|
|
|
|
* 0 if denied
|
|
|
|
*/
|
|
|
|
static int
|
2008-12-01 23:01:11 +08:00
|
|
|
check_allowed_connect_ports (int port)
|
2002-04-13 01:00:42 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
size_t i;
|
|
|
|
int *data;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A port list is REQUIRED for a CONNECT request to function
|
|
|
|
* properly. This closes a potential security hole.
|
|
|
|
*/
|
|
|
|
if (!ports_allowed_by_connect)
|
|
|
|
return 0;
|
|
|
|
|
2009-08-10 05:01:32 +08:00
|
|
|
for (i = 0; i != (size_t)vector_length (ports_allowed_by_connect); ++i)
|
2008-12-01 23:01:11 +08:00
|
|
|
{
|
2009-08-10 05:02:54 +08:00
|
|
|
data = (int *)vector_getentry (ports_allowed_by_connect, i, NULL);
|
2008-12-01 23:01:11 +08:00
|
|
|
if (data && *data == port)
|
2008-12-08 21:39:44 +08:00
|
|
|
return 1;
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2002-04-13 01:00:42 +08:00
|
|
|
}
|
|
|
|
|
2000-02-17 01:32:49 +08:00
|
|
|
/*
|
2001-09-14 12:56:29 +08:00
|
|
|
* Read in the first line from the client (the request line for HTTP
|
|
|
|
* connections. The request line is allocated from the heap, but it must
|
|
|
|
* be freed in another function.
|
2000-02-17 01:32:49 +08:00
|
|
|
*/
|
2002-04-08 05:35:59 +08:00
|
|
|
static int
|
2008-12-01 23:01:11 +08:00
|
|
|
read_request_line (struct conn_s *connptr)
|
2000-02-17 01:32:49 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
ssize_t len;
|
|
|
|
|
|
|
|
retry:
|
|
|
|
len = readline (connptr->client_fd, &connptr->request_line);
|
|
|
|
if (len <= 0)
|
|
|
|
{
|
|
|
|
log_message (LOG_ERR,
|
2008-12-08 21:39:44 +08:00
|
|
|
"read_request_line: Client (file descriptor: %d) "
|
|
|
|
"closed socket before read.", connptr->client_fd);
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Strip the new line and carriage return from the string.
|
|
|
|
*/
|
|
|
|
if (chomp (connptr->request_line, len) == len)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If the number of characters removed is the same as the
|
|
|
|
* length then it was a blank line. Free the buffer and
|
|
|
|
* try again (since we're looking for a request line.)
|
|
|
|
*/
|
|
|
|
safefree (connptr->request_line);
|
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
|
|
|
|
log_message (LOG_CONN, "Request (file descriptor %d): %s",
|
2008-12-08 21:39:44 +08:00
|
|
|
connptr->client_fd, connptr->request_line);
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
return 0;
|
2001-09-14 12:56:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2003-05-30 03:44:00 +08:00
|
|
|
* Free all the memory allocated in a request.
|
2001-09-14 12:56:29 +08:00
|
|
|
*/
|
2001-11-22 08:31:10 +08:00
|
|
|
static void
|
2008-12-01 23:01:11 +08:00
|
|
|
free_request_struct (struct request_s *request)
|
2001-09-17 04:10:19 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
if (!request)
|
|
|
|
return;
|
2001-10-23 00:08:29 +08:00
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
safefree (request->method);
|
|
|
|
safefree (request->protocol);
|
2001-09-17 04:10:19 +08:00
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
if (request->host)
|
|
|
|
safefree (request->host);
|
|
|
|
if (request->path)
|
|
|
|
safefree (request->path);
|
2001-09-17 04:10:19 +08:00
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
safefree (request);
|
2001-09-17 04:10:19 +08:00
|
|
|
}
|
|
|
|
|
2002-12-05 01:36:48 +08:00
|
|
|
/*
|
|
|
|
* Take a host string and if there is a username/password part, strip
|
|
|
|
* it off.
|
|
|
|
*/
|
|
|
|
static void
|
2008-12-01 23:01:11 +08:00
|
|
|
strip_username_password (char *host)
|
2002-12-05 01:36:48 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
char *p;
|
|
|
|
|
|
|
|
assert (host);
|
|
|
|
assert (strlen (host) > 0);
|
|
|
|
|
|
|
|
if ((p = strchr (host, '@')) == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Move the pointer past the "@" and then copy from that point
|
|
|
|
* until the NUL to the beginning of the host buffer.
|
|
|
|
*/
|
|
|
|
p++;
|
|
|
|
while (*p)
|
|
|
|
*host++ = *p++;
|
|
|
|
*host = '\0';
|
2002-12-05 01:36:48 +08:00
|
|
|
}
|
|
|
|
|
2004-02-05 03:57:40 +08:00
|
|
|
/*
|
|
|
|
* Take a host string and if there is a port part, strip
|
|
|
|
* it off and set proper port variable i.e. for www.host.com:8001
|
|
|
|
*/
|
|
|
|
static int
|
2008-12-01 23:01:11 +08:00
|
|
|
strip_return_port (char *host)
|
2004-02-05 03:57:40 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
char *ptr1;
|
|
|
|
int port;
|
2004-02-05 03:57:40 +08:00
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
ptr1 = strchr (host, ':');
|
|
|
|
if (ptr1 == NULL)
|
|
|
|
return 0;
|
2004-02-05 03:57:40 +08:00
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
*ptr1++ = '\0';
|
2008-12-08 21:39:44 +08:00
|
|
|
if (sscanf (ptr1, "%d", &port) != 1) /* one conversion required */
|
2008-12-01 23:01:11 +08:00
|
|
|
return 0;
|
|
|
|
return port;
|
2004-02-05 03:57:40 +08:00
|
|
|
}
|
|
|
|
|
2001-09-14 12:56:29 +08:00
|
|
|
/*
|
2002-11-30 03:25:59 +08:00
|
|
|
* Pull the information out of the URL line. This will handle both HTTP
|
|
|
|
* and FTP (proxied) URLs.
|
2001-09-14 12:56:29 +08:00
|
|
|
*/
|
2001-11-22 08:31:10 +08:00
|
|
|
static int
|
2008-12-01 23:01:11 +08:00
|
|
|
extract_http_url (const char *url, struct request_s *request)
|
2001-09-14 12:56:29 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
char *p;
|
|
|
|
int len;
|
|
|
|
int port;
|
|
|
|
|
|
|
|
/* Split the URL on the slash to separate host from path */
|
|
|
|
p = strchr (url, '/');
|
|
|
|
if (p != NULL)
|
|
|
|
{
|
|
|
|
len = p - url;
|
2009-08-10 05:04:27 +08:00
|
|
|
request->host = (char *)safemalloc (len + 1);
|
2008-12-01 23:01:11 +08:00
|
|
|
memcpy (request->host, url, len);
|
|
|
|
request->host[len] = '\0';
|
|
|
|
request->path = safestrdup (p);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
request->host = safestrdup (url);
|
|
|
|
request->path = safestrdup ("/");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!request->host || !request->path)
|
|
|
|
goto ERROR_EXIT;
|
|
|
|
|
|
|
|
/* Remove the username/password if they're present */
|
|
|
|
strip_username_password (request->host);
|
|
|
|
|
|
|
|
/* Find a proper port in www.site.com:8001 URLs */
|
|
|
|
port = strip_return_port (request->host);
|
|
|
|
request->port = (port != 0) ? port : HTTP_PORT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ERROR_EXIT:
|
|
|
|
if (request->host)
|
|
|
|
safefree (request->host);
|
|
|
|
if (request->path)
|
|
|
|
safefree (request->path);
|
|
|
|
|
|
|
|
return -1;
|
2001-09-14 12:56:29 +08:00
|
|
|
}
|
2000-02-17 01:32:49 +08:00
|
|
|
|
2001-09-14 12:56:29 +08:00
|
|
|
/*
|
|
|
|
* Extract the URL from a SSL connection.
|
|
|
|
*/
|
2001-11-22 08:31:10 +08:00
|
|
|
static int
|
2008-12-01 23:01:11 +08:00
|
|
|
extract_ssl_url (const char *url, struct request_s *request)
|
2001-09-14 12:56:29 +08:00
|
|
|
{
|
2009-08-10 05:05:28 +08:00
|
|
|
request->host = (char *)safemalloc (strlen (url) + 1);
|
2008-12-01 23:01:11 +08:00
|
|
|
if (!request->host)
|
|
|
|
return -1;
|
2005-08-15 11:54:31 +08:00
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
if (sscanf (url, "%[^:]:%hu", request->host, &request->port) == 2);
|
|
|
|
else if (sscanf (url, "%s", request->host) == 1)
|
|
|
|
request->port = HTTP_PORT_SSL;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
log_message (LOG_ERR, "extract_ssl_url: Can't parse URL.");
|
2005-08-15 11:54:31 +08:00
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
safefree (request->host);
|
|
|
|
return -1;
|
|
|
|
}
|
2001-09-14 12:56:29 +08:00
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
/* Remove the username/password if they're present */
|
|
|
|
strip_username_password (request->host);
|
2002-12-05 01:36:48 +08:00
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
return 0;
|
2001-09-14 12:56:29 +08:00
|
|
|
}
|
|
|
|
|
2003-04-17 02:11:58 +08:00
|
|
|
|
2003-05-30 03:44:00 +08:00
|
|
|
#ifdef UPSTREAM_SUPPORT
|
|
|
|
/*
|
|
|
|
* Add an entry to the upstream list
|
|
|
|
*/
|
|
|
|
void
|
2008-12-01 23:01:11 +08:00
|
|
|
upstream_add (const char *host, int port, const char *domain)
|
2003-05-30 03:44:00 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
char *ptr;
|
2009-08-10 05:06:31 +08:00
|
|
|
struct upstream *up = (struct upstream *)safemalloc(sizeof (struct upstream));
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
if (!up)
|
|
|
|
{
|
|
|
|
log_message (LOG_ERR, "Unable to allocate memory in upstream_add()");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
up->host = up->domain = NULL;
|
|
|
|
up->ip = up->mask = 0;
|
|
|
|
|
|
|
|
if (domain == NULL)
|
|
|
|
{
|
|
|
|
if (!host || host[0] == '\0' || port < 1)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
log_message (LOG_WARNING,
|
|
|
|
"Nonsense upstream rule: invalid host or port");
|
|
|
|
goto upstream_cleanup;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
up->host = safestrdup (host);
|
|
|
|
up->port = port;
|
|
|
|
|
|
|
|
log_message (LOG_INFO, "Added upstream %s:%d for [default]",
|
2008-12-08 21:39:44 +08:00
|
|
|
host, port);
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
|
|
|
else if (host == NULL)
|
|
|
|
{
|
|
|
|
if (!domain || domain[0] == '\0')
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
log_message (LOG_WARNING,
|
|
|
|
"Nonsense no-upstream rule: empty domain");
|
|
|
|
goto upstream_cleanup;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
ptr = strchr (domain, '/');
|
|
|
|
if (ptr)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
struct in_addr addrstruct;
|
|
|
|
|
|
|
|
*ptr = '\0';
|
|
|
|
if (inet_aton (domain, &addrstruct) != 0)
|
|
|
|
{
|
|
|
|
up->ip = ntohl (addrstruct.s_addr);
|
|
|
|
*ptr++ = '/';
|
|
|
|
|
|
|
|
if (strchr (ptr, '.'))
|
|
|
|
{
|
|
|
|
if (inet_aton (ptr, &addrstruct) != 0)
|
|
|
|
up->mask = ntohl (addrstruct.s_addr);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
up->mask = ~((1 << (32 - atoi (ptr))) - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
else
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
up->domain = safestrdup (domain);
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
log_message (LOG_INFO, "Added no-upstream for %s", domain);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!host || host[0] == '\0' || port < 1 || !domain || domain == '\0')
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
log_message (LOG_WARNING,
|
|
|
|
"Nonsense upstream rule: invalid parameters");
|
|
|
|
goto upstream_cleanup;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
up->host = safestrdup (host);
|
|
|
|
up->port = port;
|
|
|
|
up->domain = safestrdup (domain);
|
|
|
|
|
|
|
|
log_message (LOG_INFO, "Added upstream %s:%d for %s",
|
2008-12-08 21:39:44 +08:00
|
|
|
host, port, domain);
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!up->domain && !up->ip)
|
2008-12-08 21:39:44 +08:00
|
|
|
{ /* always add default to end */
|
2008-12-01 23:01:11 +08:00
|
|
|
struct upstream *tmp = config.upstream_list;
|
|
|
|
|
|
|
|
while (tmp)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
if (!tmp->domain && !tmp->ip)
|
|
|
|
{
|
|
|
|
log_message (LOG_WARNING, "Duplicate default upstream");
|
|
|
|
goto upstream_cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!tmp->next)
|
|
|
|
{
|
|
|
|
up->next = NULL;
|
|
|
|
tmp->next = up;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp = tmp->next;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
up->next = config.upstream_list;
|
|
|
|
config.upstream_list = up;
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
upstream_cleanup:
|
|
|
|
safefree (up->host);
|
|
|
|
safefree (up->domain);
|
|
|
|
safefree (up);
|
|
|
|
|
|
|
|
return;
|
2003-05-30 03:44:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if a host is in the upstream list
|
|
|
|
*/
|
|
|
|
static struct upstream *
|
2008-12-01 23:01:11 +08:00
|
|
|
upstream_get (char *host)
|
2003-05-30 03:44:00 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
struct upstream *up = config.upstream_list;
|
|
|
|
|
|
|
|
in_addr_t my_ip = INADDR_NONE;
|
|
|
|
|
|
|
|
while (up)
|
|
|
|
{
|
|
|
|
if (up->domain)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
if (strcasecmp (host, up->domain) == 0)
|
|
|
|
break; /* exact match */
|
2008-12-01 23:01:11 +08:00
|
|
|
|
2008-12-08 21:39:44 +08:00
|
|
|
if (up->domain[0] == '.')
|
|
|
|
{
|
|
|
|
char *dot = strchr (host, '.');
|
2008-12-01 23:01:11 +08:00
|
|
|
|
2008-12-08 21:39:44 +08:00
|
|
|
if (!dot && !up->domain[1])
|
|
|
|
break; /* local host matches "." */
|
2008-12-01 23:01:11 +08:00
|
|
|
|
2008-12-08 21:39:44 +08:00
|
|
|
while (dot && strcasecmp (dot, up->domain))
|
|
|
|
dot = strchr (dot + 1, '.');
|
2008-12-01 23:01:11 +08:00
|
|
|
|
2008-12-08 21:39:44 +08:00
|
|
|
if (dot)
|
|
|
|
break; /* subdomain match */
|
|
|
|
}
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
else if (up->ip)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
if (my_ip == INADDR_NONE)
|
|
|
|
my_ip = ntohl (inet_addr (host));
|
2008-12-01 23:01:11 +08:00
|
|
|
|
2008-12-08 21:39:44 +08:00
|
|
|
if ((my_ip & up->mask) == up->ip)
|
|
|
|
break;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
else
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
break; /* No domain or IP, default upstream */
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
up = up->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (up && (!up->host || !up->port))
|
|
|
|
up = NULL;
|
|
|
|
|
|
|
|
if (up)
|
|
|
|
log_message (LOG_INFO, "Found proxy %s:%d for %s",
|
2008-12-08 21:39:44 +08:00
|
|
|
up->host, up->port, host);
|
2008-12-01 23:01:11 +08:00
|
|
|
else
|
|
|
|
log_message (LOG_INFO, "No proxy for %s", host);
|
|
|
|
|
|
|
|
return up;
|
2003-05-30 03:44:00 +08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2001-12-19 13:13:40 +08:00
|
|
|
/*
|
|
|
|
* Create a connection for HTTP connections.
|
|
|
|
*/
|
|
|
|
static int
|
2008-12-01 23:01:11 +08:00
|
|
|
establish_http_connection (struct conn_s *connptr, struct request_s *request)
|
2001-12-19 13:13:40 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
char portbuff[7];
|
|
|
|
|
|
|
|
/* Build a port string if it's not a standard port */
|
|
|
|
if (request->port != HTTP_PORT && request->port != HTTP_PORT_SSL)
|
|
|
|
snprintf (portbuff, 7, ":%u", request->port);
|
|
|
|
else
|
|
|
|
portbuff[0] = '\0';
|
|
|
|
|
|
|
|
return write_message (connptr->server_fd,
|
2008-12-08 21:39:44 +08:00
|
|
|
"%s %s HTTP/1.0\r\n"
|
|
|
|
"Host: %s%s\r\n"
|
|
|
|
"Connection: close\r\n",
|
|
|
|
request->method, request->path,
|
|
|
|
request->host, portbuff);
|
2001-09-14 12:56:29 +08:00
|
|
|
}
|
2000-02-17 01:32:49 +08:00
|
|
|
|
2001-09-16 05:26:14 +08:00
|
|
|
/*
|
2004-01-27 03:11:52 +08:00
|
|
|
* These two defines are for the SSL tunnelling.
|
2001-09-16 05:26:14 +08:00
|
|
|
*/
|
2002-04-16 11:20:43 +08:00
|
|
|
#define SSL_CONNECTION_RESPONSE "HTTP/1.0 200 Connection established"
|
|
|
|
#define PROXY_AGENT "Proxy-agent: " PACKAGE "/" VERSION
|
2001-09-16 05:26:14 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Send the appropriate response to the client to establish a SSL
|
|
|
|
* connection.
|
|
|
|
*/
|
2001-11-22 08:31:10 +08:00
|
|
|
static inline int
|
2008-12-01 23:01:11 +08:00
|
|
|
send_ssl_response (struct conn_s *connptr)
|
2001-09-16 05:26:14 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
return write_message (connptr->client_fd,
|
2008-12-08 21:39:44 +08:00
|
|
|
"%s\r\n"
|
|
|
|
"%s\r\n"
|
|
|
|
"\r\n", SSL_CONNECTION_RESPONSE, PROXY_AGENT);
|
2001-09-16 05:26:14 +08:00
|
|
|
}
|
|
|
|
|
2001-09-14 12:56:29 +08:00
|
|
|
/*
|
|
|
|
* Break the request line apart and figure out where to connect and
|
|
|
|
* build a new request line. Finally connect to the remote server.
|
|
|
|
*/
|
2001-11-22 08:31:10 +08:00
|
|
|
static struct request_s *
|
2008-12-01 23:01:11 +08:00
|
|
|
process_request (struct conn_s *connptr, hashmap_t hashofheaders)
|
2001-09-14 12:56:29 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
char *url;
|
|
|
|
struct request_s *request;
|
|
|
|
int ret;
|
|
|
|
size_t request_len;
|
|
|
|
|
|
|
|
/* NULL out all the fields so frees don't cause segfaults. */
|
|
|
|
request = safecalloc (1, sizeof (struct request_s));
|
|
|
|
if (!request)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
request_len = strlen (connptr->request_line) + 1;
|
|
|
|
|
|
|
|
request->method = safemalloc (request_len);
|
|
|
|
url = safemalloc (request_len);
|
|
|
|
request->protocol = safemalloc (request_len);
|
|
|
|
|
|
|
|
if (!request->method || !url || !request->protocol)
|
|
|
|
{
|
|
|
|
safefree (url);
|
|
|
|
free_request_struct (request);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = sscanf (connptr->request_line, "%[^ ] %[^ ] %[^ ]",
|
2008-12-08 21:39:44 +08:00
|
|
|
request->method, url, request->protocol);
|
2008-12-01 23:01:11 +08:00
|
|
|
if (ret == 2 && !strcasecmp (request->method, "GET"))
|
|
|
|
{
|
|
|
|
request->protocol[0] = 0;
|
|
|
|
|
|
|
|
/* Indicate that this is a HTTP/0.9 GET request */
|
|
|
|
connptr->protocol.major = 0;
|
|
|
|
connptr->protocol.minor = 9;
|
|
|
|
}
|
|
|
|
else if (ret == 3 && !strncasecmp (request->protocol, "HTTP/", 5))
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Break apart the protocol and update the connection
|
|
|
|
* structure.
|
|
|
|
*/
|
|
|
|
ret = sscanf (request->protocol + 5, "%u.%u",
|
2008-12-08 21:39:44 +08:00
|
|
|
&connptr->protocol.major, &connptr->protocol.minor);
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the conversion doesn't succeed, drop down below and
|
|
|
|
* send the error to the user.
|
|
|
|
*/
|
|
|
|
if (ret != 2)
|
2008-12-08 21:39:44 +08:00
|
|
|
goto BAD_REQUEST_ERROR;
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
BAD_REQUEST_ERROR:
|
|
|
|
log_message (LOG_ERR,
|
2008-12-08 21:39:44 +08:00
|
|
|
"process_request: Bad Request on file descriptor %d",
|
|
|
|
connptr->client_fd);
|
2008-12-01 23:01:11 +08:00
|
|
|
indicate_http_error (connptr, 400, "Bad Request",
|
2008-12-08 21:39:44 +08:00
|
|
|
"detail", "Request has an invalid format",
|
|
|
|
"url", url, NULL);
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
safefree (url);
|
|
|
|
free_request_struct (request);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!url)
|
|
|
|
{
|
|
|
|
log_message (LOG_ERR,
|
2008-12-08 21:39:44 +08:00
|
|
|
"process_request: Null URL on file descriptor %d",
|
|
|
|
connptr->client_fd);
|
2008-12-01 23:01:11 +08:00
|
|
|
indicate_http_error (connptr, 400, "Bad Request",
|
2008-12-08 21:39:44 +08:00
|
|
|
"detail", "Request has an empty URL",
|
|
|
|
"url", url, NULL);
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
safefree (url);
|
|
|
|
free_request_struct (request);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2004-01-27 03:11:52 +08:00
|
|
|
#ifdef REVERSE_SUPPORT
|
2008-12-01 23:01:11 +08:00
|
|
|
if (config.reversepath_list != NULL)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Rewrite the URL based on the reverse path. After calling
|
|
|
|
* reverse_rewrite_url "url" can be freed since we either
|
|
|
|
* have the newly rewritten URL, or something failed and
|
|
|
|
* we'll be closing anyway.
|
|
|
|
*/
|
|
|
|
char *reverse_url;
|
|
|
|
|
|
|
|
reverse_url = reverse_rewrite_url (connptr, hashofheaders, url);
|
|
|
|
safefree (url);
|
|
|
|
|
|
|
|
if (!reverse_url)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
free_request_struct (request);
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
else
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
url = reverse_url;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
2004-01-27 03:11:52 +08:00
|
|
|
#endif
|
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
if (strncasecmp (url, "http://", 7) == 0
|
|
|
|
|| (UPSTREAM_CONFIGURED () && strncasecmp (url, "ftp://", 6) == 0))
|
|
|
|
{
|
|
|
|
char *skipped_type = strstr (url, "//") + 2;
|
|
|
|
|
|
|
|
if (extract_http_url (skipped_type, request) < 0)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
indicate_http_error (connptr, 400, "Bad Request",
|
|
|
|
"detail", "Could not parse URL",
|
|
|
|
"url", url, NULL);
|
2008-12-01 23:01:11 +08:00
|
|
|
|
2008-12-08 21:39:44 +08:00
|
|
|
safefree (url);
|
|
|
|
free_request_struct (request);
|
2008-12-01 23:01:11 +08:00
|
|
|
|
2008-12-08 21:39:44 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
|
|
|
else if (strcmp (request->method, "CONNECT") == 0)
|
|
|
|
{
|
|
|
|
if (extract_ssl_url (url, request) < 0)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
indicate_http_error (connptr, 400, "Bad Request",
|
|
|
|
"detail", "Could not parse URL",
|
|
|
|
"url", url, NULL);
|
2008-12-01 23:01:11 +08:00
|
|
|
|
2008-12-08 21:39:44 +08:00
|
|
|
safefree (url);
|
|
|
|
free_request_struct (request);
|
2008-12-01 23:01:11 +08:00
|
|
|
|
2008-12-08 21:39:44 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
/* Verify that the port in the CONNECT method is allowed */
|
|
|
|
if (!check_allowed_connect_ports (request->port))
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
indicate_http_error (connptr, 403, "Access violation",
|
|
|
|
"detail",
|
|
|
|
"The CONNECT method not allowed "
|
|
|
|
"with the port you tried to use.",
|
|
|
|
"url", url, NULL);
|
|
|
|
log_message (LOG_INFO,
|
|
|
|
"Refused CONNECT method on port %d", request->port);
|
2008-12-01 23:01:11 +08:00
|
|
|
|
2008-12-08 21:39:44 +08:00
|
|
|
safefree (url);
|
|
|
|
free_request_struct (request);
|
2008-12-01 23:01:11 +08:00
|
|
|
|
2008-12-08 21:39:44 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
connptr->connect_method = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2002-06-07 04:32:30 +08:00
|
|
|
#ifdef TRANSPARENT_PROXY
|
2008-12-01 23:01:11 +08:00
|
|
|
if (!do_transparent_proxy
|
2008-12-08 21:39:44 +08:00
|
|
|
(connptr, hashofheaders, request, &config, url))
|
|
|
|
{
|
|
|
|
safefree (url);
|
|
|
|
free_request_struct (request);
|
|
|
|
return NULL;
|
|
|
|
}
|
2002-06-07 04:32:30 +08:00
|
|
|
#else
|
2008-12-01 23:01:11 +08:00
|
|
|
indicate_http_error (connptr, 501, "Not Implemented",
|
2008-12-08 21:39:44 +08:00
|
|
|
"detail",
|
|
|
|
"Unknown method or unsupported protocol.", "url",
|
|
|
|
url, NULL);
|
2008-12-01 23:01:11 +08:00
|
|
|
log_message (LOG_INFO, "Unknown method (%s) or protocol (%s)",
|
2008-12-08 21:39:44 +08:00
|
|
|
request->method, url);
|
2008-12-01 23:01:11 +08:00
|
|
|
safefree (url);
|
|
|
|
free_request_struct (request);
|
|
|
|
return NULL;
|
|
|
|
|
2002-06-07 04:32:30 +08:00
|
|
|
#endif
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
2000-02-17 01:32:49 +08:00
|
|
|
|
2001-09-14 12:56:29 +08:00
|
|
|
#ifdef FILTER_ENABLE
|
2008-12-01 23:01:11 +08:00
|
|
|
/*
|
|
|
|
* Filter restricted domains/urls
|
|
|
|
*/
|
|
|
|
if (config.filter)
|
|
|
|
{
|
|
|
|
if (config.filter_url)
|
2008-12-08 21:39:44 +08:00
|
|
|
ret = filter_url (url);
|
2008-12-01 23:01:11 +08:00
|
|
|
else
|
2008-12-08 21:39:44 +08:00
|
|
|
ret = filter_domain (request->host);
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
if (ret)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
update_stats (STAT_DENIED);
|
|
|
|
|
|
|
|
if (config.filter_url)
|
|
|
|
log_message (LOG_NOTICE,
|
|
|
|
"Proxying refused on filtered url \"%s\"", url);
|
|
|
|
else
|
|
|
|
log_message (LOG_NOTICE,
|
|
|
|
"Proxying refused on filtered domain \"%s\"",
|
|
|
|
request->host);
|
|
|
|
|
|
|
|
indicate_http_error (connptr, 403, "Filtered",
|
|
|
|
"detail",
|
|
|
|
"The request you made has been filtered",
|
|
|
|
"url", url, NULL);
|
|
|
|
|
|
|
|
safefree (url);
|
|
|
|
free_request_struct (request);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
2001-09-14 12:56:29 +08:00
|
|
|
#endif
|
2000-02-17 01:32:49 +08:00
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
safefree (url);
|
2002-05-27 10:00:22 +08:00
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
/*
|
|
|
|
* Check to see if they're requesting the stat host
|
|
|
|
*/
|
|
|
|
if (config.stathost && strcmp (config.stathost, request->host) == 0)
|
|
|
|
{
|
|
|
|
log_message (LOG_NOTICE, "Request for the stathost.");
|
|
|
|
connptr->show_stats = TRUE;
|
2001-09-16 05:26:14 +08:00
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
free_request_struct (request);
|
|
|
|
return NULL;
|
|
|
|
}
|
2001-09-14 12:56:29 +08:00
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
return request;
|
2000-02-17 01:32:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2000-09-12 08:04:42 +08:00
|
|
|
* pull_client_data is used to pull across any client data (like in a
|
|
|
|
* POST) which needs to be handled before an error can be reported, or
|
|
|
|
* server headers can be processed.
|
|
|
|
* - rjkaes
|
2000-02-17 01:32:49 +08:00
|
|
|
*/
|
2001-11-22 08:31:10 +08:00
|
|
|
static int
|
2008-12-01 23:01:11 +08:00
|
|
|
pull_client_data (struct conn_s *connptr, long int length)
|
2000-02-17 01:32:49 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
char *buffer;
|
|
|
|
ssize_t len;
|
|
|
|
|
|
|
|
buffer = safemalloc (min (MAXBUFFSIZE, length));
|
|
|
|
if (!buffer)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
len = safe_read (connptr->client_fd, buffer, min (MAXBUFFSIZE, length));
|
|
|
|
if (len <= 0)
|
2008-12-08 21:39:44 +08:00
|
|
|
goto ERROR_EXIT;
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
if (!connptr->error_variables)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
if (safe_write (connptr->server_fd, buffer, len) < 0)
|
|
|
|
goto ERROR_EXIT;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
length -= len;
|
|
|
|
}
|
|
|
|
while (length > 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* BUG FIX: Internet Explorer will leave two bytes (carriage
|
|
|
|
* return and line feed) at the end of a POST message. These
|
|
|
|
* need to be eaten for tinyproxy to work correctly.
|
|
|
|
*/
|
|
|
|
socket_nonblocking (connptr->client_fd);
|
|
|
|
len = recv (connptr->client_fd, buffer, 2, MSG_PEEK);
|
|
|
|
socket_blocking (connptr->client_fd);
|
|
|
|
|
|
|
|
if (len < 0 && errno != EAGAIN)
|
|
|
|
goto ERROR_EXIT;
|
|
|
|
|
|
|
|
if (len == 2 && CHECK_CRLF (buffer, len))
|
|
|
|
read (connptr->client_fd, buffer, 2);
|
|
|
|
|
|
|
|
safefree (buffer);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ERROR_EXIT:
|
|
|
|
safefree (buffer);
|
|
|
|
return -1;
|
2000-02-17 01:32:49 +08:00
|
|
|
}
|
|
|
|
|
2000-09-12 08:04:42 +08:00
|
|
|
#ifdef XTINYPROXY_ENABLE
|
2000-02-17 01:32:49 +08:00
|
|
|
/*
|
2000-09-12 08:04:42 +08:00
|
|
|
* Add the X-Tinyproxy header to the collection of headers being sent to
|
|
|
|
* the server.
|
|
|
|
* -rjkaes
|
2000-02-17 01:32:49 +08:00
|
|
|
*/
|
2004-08-13 03:57:15 +08:00
|
|
|
static inline int
|
2008-12-01 23:01:11 +08:00
|
|
|
add_xtinyproxy_header (struct conn_s *connptr)
|
2000-02-17 01:32:49 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
assert (connptr && connptr->server_fd >= 0);
|
|
|
|
return write_message (connptr->server_fd,
|
2008-12-08 21:39:44 +08:00
|
|
|
"X-Tinyproxy: %s\r\n", connptr->client_ip_addr);
|
2000-02-17 01:32:49 +08:00
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
#endif /* XTINYPROXY */
|
2000-02-17 01:32:49 +08:00
|
|
|
|
2002-04-08 05:35:59 +08:00
|
|
|
/*
|
|
|
|
* Take a complete header line and break it apart (into a key and the data.)
|
|
|
|
* Now insert this information into the hashmap for the connection so it
|
|
|
|
* can be retrieved and manipulated later.
|
|
|
|
*/
|
|
|
|
static inline int
|
2008-12-01 23:01:11 +08:00
|
|
|
add_header_to_connection (hashmap_t hashofheaders, char *header, size_t len)
|
2002-04-08 05:35:59 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
char *sep;
|
2002-04-08 05:35:59 +08:00
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
/* Get rid of the new line and return at the end */
|
|
|
|
len -= chomp (header, len);
|
2002-04-08 05:35:59 +08:00
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
sep = strchr (header, ':');
|
|
|
|
if (!sep)
|
|
|
|
return -1;
|
2002-04-08 05:35:59 +08:00
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
/* Blank out colons, spaces, and tabs. */
|
|
|
|
while (*sep == ':' || *sep == ' ' || *sep == '\t')
|
|
|
|
*sep++ = '\0';
|
2002-04-16 11:20:43 +08:00
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
/* Calculate the new length of just the data */
|
|
|
|
len -= sep - header - 1;
|
2002-04-26 02:58:08 +08:00
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
return hashmap_insert (hashofheaders, header, sep, len);
|
2002-04-08 05:35:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read all the headers from the stream
|
|
|
|
*/
|
|
|
|
static int
|
2008-12-01 23:01:11 +08:00
|
|
|
get_all_headers (int fd, hashmap_t hashofheaders)
|
2002-04-08 05:35:59 +08:00
|
|
|
{
|
2009-08-06 09:23:42 +08:00
|
|
|
char *line = NULL;
|
|
|
|
char *header = NULL;
|
|
|
|
char *tmp;
|
|
|
|
ssize_t linelen;
|
|
|
|
ssize_t len = 0;
|
2008-12-08 21:39:44 +08:00
|
|
|
unsigned int double_cgi = FALSE; /* boolean */
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
assert (fd >= 0);
|
|
|
|
assert (hashofheaders != NULL);
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
2009-08-06 09:23:42 +08:00
|
|
|
if ((linelen = readline (fd, &line)) <= 0)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
safefree (header);
|
2009-08-06 09:23:42 +08:00
|
|
|
safefree (line);
|
2008-12-08 21:39:44 +08:00
|
|
|
return -1;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
|
2009-08-06 09:23:42 +08:00
|
|
|
/*
|
|
|
|
* If we received a CR LF or a non-continuation line, then add
|
|
|
|
* the accumulated header field, if any, to the hashmap, and
|
|
|
|
* reset it.
|
|
|
|
*/
|
|
|
|
if (CHECK_CRLF (line, linelen) || !CHECK_LWS (line, linelen))
|
|
|
|
{
|
|
|
|
if (!double_cgi
|
|
|
|
&& len > 0
|
|
|
|
&& add_header_to_connection (hashofheaders, header, len) < 0)
|
|
|
|
{
|
|
|
|
safefree (header);
|
|
|
|
safefree (line);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = 0;
|
|
|
|
}
|
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
/*
|
|
|
|
* If we received just a CR LF on a line, the headers are
|
|
|
|
* finished.
|
|
|
|
*/
|
2009-08-06 09:23:42 +08:00
|
|
|
if (CHECK_CRLF (line, linelen))
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
safefree (header);
|
2009-08-06 09:23:42 +08:00
|
|
|
safefree (line);
|
2008-12-08 21:39:44 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* BUG FIX: The following code detects a "Double CGI"
|
|
|
|
* situation so that we can handle the nonconforming system.
|
|
|
|
* This problem was found when accessing cgi.ebay.com, and it
|
|
|
|
* turns out to be a wider spread problem as well.
|
|
|
|
*
|
|
|
|
* If "Double CGI" is in effect, duplicate headers are
|
|
|
|
* ignored.
|
|
|
|
*
|
|
|
|
* FIXME: Might need to change this to a more robust check.
|
|
|
|
*/
|
2009-08-06 09:23:42 +08:00
|
|
|
if (linelen >= 5 && strncasecmp (line, "HTTP/", 5) == 0)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
double_cgi = TRUE;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
|
2009-08-06 09:23:42 +08:00
|
|
|
/*
|
|
|
|
* Append the new line to the current header field.
|
|
|
|
*/
|
|
|
|
if ((tmp = saferealloc (header, len + linelen)) == NULL)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
safefree (header);
|
2009-08-06 09:23:42 +08:00
|
|
|
safefree (line);
|
2008-12-08 21:39:44 +08:00
|
|
|
return -1;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
|
2009-08-06 09:23:42 +08:00
|
|
|
header = tmp;
|
|
|
|
memcpy (header + len, line, linelen);
|
|
|
|
len += linelen;
|
|
|
|
|
|
|
|
safefree (line);
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
2002-04-08 05:35:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Extract the headers to remove. These headers were listed in the Connection
|
2002-04-26 02:58:08 +08:00
|
|
|
* and Proxy-Connection headers.
|
2002-04-08 05:35:59 +08:00
|
|
|
*/
|
|
|
|
static int
|
2008-12-01 23:01:11 +08:00
|
|
|
remove_connection_headers (hashmap_t hashofheaders)
|
2002-04-08 05:35:59 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
static char *headers[] = {
|
|
|
|
"connection",
|
|
|
|
"proxy-connection"
|
|
|
|
};
|
|
|
|
|
|
|
|
char *data;
|
|
|
|
char *ptr;
|
|
|
|
ssize_t len;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i != (sizeof (headers) / sizeof (char *)); ++i)
|
|
|
|
{
|
|
|
|
/* Look for the connection header. If it's not found, return. */
|
|
|
|
len = hashmap_entry_by_key (hashofheaders, headers[i], (void **) &data);
|
|
|
|
if (len <= 0)
|
2008-12-08 21:39:44 +08:00
|
|
|
return 0;
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Go through the data line and replace any special characters
|
|
|
|
* with a NULL.
|
|
|
|
*/
|
|
|
|
ptr = data;
|
|
|
|
while ((ptr = strpbrk (ptr, "()<>@,;:\\\"/[]?={} \t")))
|
2008-12-08 21:39:44 +08:00
|
|
|
*ptr++ = '\0';
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* All the tokens are separated by NULLs. Now go through the
|
|
|
|
* token and remove them from the hashofheaders.
|
|
|
|
*/
|
|
|
|
ptr = data;
|
|
|
|
while (ptr < data + len)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
hashmap_remove (hashofheaders, ptr);
|
2008-12-01 23:01:11 +08:00
|
|
|
|
2008-12-08 21:39:44 +08:00
|
|
|
/* Advance ptr to the next token */
|
|
|
|
ptr += strlen (ptr) + 1;
|
|
|
|
while (ptr < data + len && *ptr == '\0')
|
|
|
|
ptr++;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
/* Now remove the connection header it self. */
|
|
|
|
hashmap_remove (hashofheaders, headers[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2002-04-08 05:35:59 +08:00
|
|
|
}
|
|
|
|
|
2002-04-12 04:44:15 +08:00
|
|
|
/*
|
|
|
|
* If there is a Content-Length header, then return the value; otherwise, return
|
|
|
|
* a negative number.
|
|
|
|
*/
|
|
|
|
static long
|
2008-12-01 23:01:11 +08:00
|
|
|
get_content_length (hashmap_t hashofheaders)
|
2002-04-12 04:44:15 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
ssize_t len;
|
|
|
|
char *data;
|
|
|
|
long content_length = -1;
|
2002-04-12 04:44:15 +08:00
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
len =
|
|
|
|
hashmap_entry_by_key (hashofheaders, "content-length", (void **) &data);
|
|
|
|
if (len > 0)
|
|
|
|
content_length = atol (data);
|
2002-04-12 04:44:15 +08:00
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
return content_length;
|
2002-04-12 04:44:15 +08:00
|
|
|
}
|
|
|
|
|
2002-04-12 11:09:04 +08:00
|
|
|
/*
|
2003-06-21 01:02:13 +08:00
|
|
|
* Search for Via header in a hash of headers and either write a new Via
|
|
|
|
* header, or append our information to the end of an existing Via header.
|
2002-04-12 11:09:04 +08:00
|
|
|
*
|
|
|
|
* FIXME: Need to add code to "hide" our internal information for security
|
|
|
|
* purposes.
|
|
|
|
*/
|
2002-04-29 04:03:18 +08:00
|
|
|
static int
|
2008-12-01 23:01:11 +08:00
|
|
|
write_via_header (int fd, hashmap_t hashofheaders,
|
2008-12-08 21:39:44 +08:00
|
|
|
unsigned int major, unsigned int minor)
|
2002-04-12 11:09:04 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
ssize_t len;
|
|
|
|
char hostname[512];
|
|
|
|
char *data;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (config.via_proxy_name)
|
|
|
|
{
|
|
|
|
strlcpy (hostname, config.via_proxy_name, sizeof (hostname));
|
|
|
|
}
|
|
|
|
else if (gethostname (hostname, sizeof (hostname)) < 0)
|
|
|
|
{
|
|
|
|
strcpy (hostname, "unknown");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See if there is a "Via" header. If so, again we need to do a bit
|
|
|
|
* of processing.
|
|
|
|
*/
|
|
|
|
len = hashmap_entry_by_key (hashofheaders, "via", (void **) &data);
|
|
|
|
if (len > 0)
|
|
|
|
{
|
|
|
|
ret = write_message (fd,
|
2008-12-08 21:39:44 +08:00
|
|
|
"Via: %s, %hu.%hu %s (%s/%s)\r\n",
|
|
|
|
data, major, minor, hostname, PACKAGE, VERSION);
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
hashmap_remove (hashofheaders, "via");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ret = write_message (fd,
|
2008-12-08 21:39:44 +08:00
|
|
|
"Via: %hu.%hu %s (%s/%s)\r\n",
|
|
|
|
major, minor, hostname, PACKAGE, VERSION);
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2002-04-12 11:09:04 +08:00
|
|
|
}
|
|
|
|
|
2002-04-08 05:35:59 +08:00
|
|
|
/*
|
|
|
|
* Number of buckets to use internally in the hashmap.
|
|
|
|
*/
|
|
|
|
#define HEADER_BUCKETS 32
|
|
|
|
|
2000-02-17 01:32:49 +08:00
|
|
|
/*
|
2000-09-12 08:04:42 +08:00
|
|
|
* Here we loop through all the headers the client is sending. If we
|
|
|
|
* are running in anonymous mode, we will _only_ send the headers listed
|
|
|
|
* (plus a few which are required for various methods).
|
|
|
|
* - rjkaes
|
2000-02-17 01:32:49 +08:00
|
|
|
*/
|
2001-11-22 08:31:10 +08:00
|
|
|
static int
|
2008-12-01 23:01:11 +08:00
|
|
|
process_client_headers (struct conn_s *connptr, hashmap_t hashofheaders)
|
2000-02-17 01:32:49 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
static char *skipheaders[] = {
|
|
|
|
"host",
|
|
|
|
"keep-alive",
|
|
|
|
"proxy-connection",
|
|
|
|
"te",
|
|
|
|
"trailers",
|
|
|
|
"transfer-encoding",
|
|
|
|
"upgrade"
|
|
|
|
};
|
|
|
|
int i;
|
|
|
|
hashmap_iter iter;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
char *data, *header;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Don't send headers if there's already an error, if the request was
|
|
|
|
* a stats request, or if this was a CONNECT method (unless upstream
|
|
|
|
* proxy is in use.)
|
|
|
|
*/
|
|
|
|
if (connptr->server_fd == -1 || connptr->show_stats
|
|
|
|
|| (connptr->connect_method && (connptr->upstream_proxy == NULL)))
|
|
|
|
{
|
|
|
|
log_message (LOG_INFO, "Not sending client headers to remote machine");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See if there is a "Content-Length" header. If so, again we need
|
|
|
|
* to do a bit of processing.
|
|
|
|
*/
|
|
|
|
connptr->content_length.client = get_content_length (hashofheaders);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See if there is a "Connection" header. If so, we need to do a bit
|
|
|
|
* of processing. :)
|
|
|
|
*/
|
|
|
|
remove_connection_headers (hashofheaders);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Delete the headers listed in the skipheaders list
|
|
|
|
*/
|
|
|
|
for (i = 0; i != (sizeof (skipheaders) / sizeof (char *)); i++)
|
|
|
|
{
|
|
|
|
hashmap_remove (hashofheaders, skipheaders[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send, or add the Via header */
|
|
|
|
ret = write_via_header (connptr->server_fd, hashofheaders,
|
2008-12-08 21:39:44 +08:00
|
|
|
connptr->protocol.major, connptr->protocol.minor);
|
2008-12-01 23:01:11 +08:00
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
indicate_http_error (connptr, 503,
|
2008-12-08 21:39:44 +08:00
|
|
|
"Could not send data to remote server",
|
|
|
|
"detail",
|
|
|
|
"A network error occurred while "
|
|
|
|
"trying to write data to the remote web server.",
|
|
|
|
NULL);
|
2008-12-01 23:01:11 +08:00
|
|
|
goto PULL_CLIENT_DATA;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Output all the remaining headers to the remote machine.
|
|
|
|
*/
|
|
|
|
iter = hashmap_first (hashofheaders);
|
|
|
|
if (iter >= 0)
|
|
|
|
{
|
|
|
|
for (; !hashmap_is_end (hashofheaders, iter); ++iter)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
hashmap_return_entry (hashofheaders,
|
|
|
|
iter, &data, (void **) &header);
|
|
|
|
|
|
|
|
if (!is_anonymous_enabled () || anonymous_search (data) > 0)
|
|
|
|
{
|
|
|
|
ret =
|
|
|
|
write_message (connptr->server_fd,
|
|
|
|
"%s: %s\r\n", data, header);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
indicate_http_error (connptr, 503,
|
|
|
|
"Could not send data to remote server",
|
|
|
|
"detail",
|
|
|
|
"A network error occurred while "
|
|
|
|
"trying to write data to the "
|
|
|
|
"remote web server.", NULL);
|
|
|
|
goto PULL_CLIENT_DATA;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
2002-04-08 05:35:59 +08:00
|
|
|
#if defined(XTINYPROXY_ENABLE)
|
2008-12-01 23:01:11 +08:00
|
|
|
if (config.my_domain)
|
|
|
|
add_xtinyproxy_header (connptr);
|
2002-04-08 05:35:59 +08:00
|
|
|
#endif
|
2005-08-15 11:54:31 +08:00
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
/* Write the final "blank" line to signify the end of the headers */
|
|
|
|
if (safe_write (connptr->server_fd, "\r\n", 2) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Spin here pulling the data from the client.
|
|
|
|
*/
|
|
|
|
PULL_CLIENT_DATA:
|
|
|
|
if (connptr->content_length.client > 0)
|
|
|
|
return pull_client_data (connptr, connptr->content_length.client);
|
|
|
|
else
|
|
|
|
return ret;
|
2000-02-17 01:32:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2000-09-12 08:04:42 +08:00
|
|
|
* Loop through all the headers (including the response code) from the
|
|
|
|
* server.
|
2000-02-17 01:32:49 +08:00
|
|
|
*/
|
2001-11-22 08:31:10 +08:00
|
|
|
static int
|
2008-12-01 23:01:11 +08:00
|
|
|
process_server_headers (struct conn_s *connptr)
|
2000-02-17 01:32:49 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
static char *skipheaders[] = {
|
|
|
|
"keep-alive",
|
|
|
|
"proxy-authenticate",
|
|
|
|
"proxy-authorization",
|
|
|
|
"proxy-connection",
|
|
|
|
"transfer-encoding",
|
|
|
|
};
|
|
|
|
|
|
|
|
char *response_line;
|
|
|
|
|
|
|
|
hashmap_t hashofheaders;
|
|
|
|
hashmap_iter iter;
|
|
|
|
char *data, *header;
|
|
|
|
ssize_t len;
|
|
|
|
int i;
|
|
|
|
int ret;
|
2001-09-12 03:26:49 +08:00
|
|
|
|
2004-01-27 03:11:52 +08:00
|
|
|
#ifdef REVERSE_SUPPORT
|
2008-12-01 23:01:11 +08:00
|
|
|
struct reversepath *reverse = config.reversepath_list;
|
2004-01-27 03:11:52 +08:00
|
|
|
#endif
|
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
/* Get the response line from the remote server. */
|
|
|
|
retry:
|
|
|
|
len = readline (connptr->server_fd, &response_line);
|
|
|
|
if (len <= 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Strip the new line and character return from the string.
|
|
|
|
*/
|
|
|
|
if (chomp (response_line, len) == len)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If the number of characters removed is the same as the
|
|
|
|
* length then it was a blank line. Free the buffer and
|
|
|
|
* try again (since we're looking for a request line.)
|
|
|
|
*/
|
|
|
|
safefree (response_line);
|
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
|
|
|
|
hashofheaders = hashmap_create (HEADER_BUCKETS);
|
|
|
|
if (!hashofheaders)
|
|
|
|
{
|
|
|
|
safefree (response_line);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get all the headers from the remote server in a big hash
|
|
|
|
*/
|
|
|
|
if (get_all_headers (connptr->server_fd, hashofheaders) < 0)
|
|
|
|
{
|
|
|
|
log_message (LOG_WARNING,
|
2008-12-08 21:39:44 +08:00
|
|
|
"Could not retrieve all the headers from the remote server.");
|
2008-12-01 23:01:11 +08:00
|
|
|
hashmap_delete (hashofheaders);
|
|
|
|
safefree (response_line);
|
|
|
|
|
|
|
|
indicate_http_error (connptr, 503,
|
2008-12-08 21:39:44 +08:00
|
|
|
"Could not retrieve all the headers",
|
|
|
|
"detail",
|
|
|
|
PACKAGE " "
|
|
|
|
"was unable to retrieve and process headers from "
|
|
|
|
"the remote web server.", NULL);
|
2008-12-01 23:01:11 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* At this point we've received the response line and all the
|
|
|
|
* headers. However, if this is a simple HTTP/0.9 request we
|
|
|
|
* CAN NOT send any of that information back to the client.
|
|
|
|
* Instead we'll free all the memory and return.
|
|
|
|
*/
|
|
|
|
if (connptr->protocol.major < 1)
|
|
|
|
{
|
|
|
|
hashmap_delete (hashofheaders);
|
|
|
|
safefree (response_line);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send the saved response line first */
|
|
|
|
ret = write_message (connptr->client_fd, "%s\r\n", response_line);
|
|
|
|
safefree (response_line);
|
|
|
|
if (ret < 0)
|
|
|
|
goto ERROR_EXIT;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If there is a "Content-Length" header, retrieve the information
|
|
|
|
* from it for later use.
|
|
|
|
*/
|
|
|
|
connptr->content_length.server = get_content_length (hashofheaders);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See if there is a connection header. If so, we need to to a bit of
|
|
|
|
* processing.
|
|
|
|
*/
|
|
|
|
remove_connection_headers (hashofheaders);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Delete the headers listed in the skipheaders list
|
|
|
|
*/
|
|
|
|
for (i = 0; i != (sizeof (skipheaders) / sizeof (char *)); i++)
|
|
|
|
{
|
|
|
|
hashmap_remove (hashofheaders, skipheaders[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send, or add the Via header */
|
|
|
|
ret = write_via_header (connptr->client_fd, hashofheaders,
|
2008-12-08 21:39:44 +08:00
|
|
|
connptr->protocol.major, connptr->protocol.minor);
|
2008-12-01 23:01:11 +08:00
|
|
|
if (ret < 0)
|
|
|
|
goto ERROR_EXIT;
|
2002-04-12 04:44:15 +08:00
|
|
|
|
2004-01-27 03:11:52 +08:00
|
|
|
#ifdef REVERSE_SUPPORT
|
2008-12-01 23:01:11 +08:00
|
|
|
/* Write tracking cookie for the magical reverse proxy path hack */
|
|
|
|
if (config.reversemagic && connptr->reversepath)
|
|
|
|
{
|
|
|
|
ret = write_message (connptr->client_fd,
|
2008-12-08 21:39:44 +08:00
|
|
|
"Set-Cookie: " REVERSE_COOKIE
|
|
|
|
"=%s; path=/\r\n", connptr->reversepath);
|
2008-12-01 23:01:11 +08:00
|
|
|
if (ret < 0)
|
2008-12-08 21:39:44 +08:00
|
|
|
goto ERROR_EXIT;
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Rewrite the HTTP redirect if needed */
|
|
|
|
if (config.reversebaseurl &&
|
|
|
|
hashmap_entry_by_key (hashofheaders, "location", (void **) &header) > 0)
|
|
|
|
{
|
|
|
|
|
|
|
|
/* Look for a matching entry in the reversepath list */
|
|
|
|
while (reverse)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
if (strncasecmp (header,
|
|
|
|
reverse->url, (len = strlen (reverse->url))) == 0)
|
|
|
|
break;
|
|
|
|
reverse = reverse->next;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
if (reverse)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
ret =
|
|
|
|
write_message (connptr->client_fd,
|
|
|
|
"Location: %s%s%s\r\n",
|
|
|
|
config.reversebaseurl,
|
|
|
|
(reverse->path + 1), (header + len));
|
|
|
|
if (ret < 0)
|
|
|
|
goto ERROR_EXIT;
|
|
|
|
|
|
|
|
log_message (LOG_INFO,
|
|
|
|
"Rewriting HTTP redirect: %s -> %s%s%s",
|
|
|
|
header, config.reversebaseurl,
|
|
|
|
(reverse->path + 1), (header + len));
|
|
|
|
hashmap_remove (hashofheaders, "location");
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
2004-01-27 03:11:52 +08:00
|
|
|
#endif
|
|
|
|
|
2008-12-01 23:01:11 +08:00
|
|
|
/*
|
|
|
|
* All right, output all the remaining headers to the client.
|
|
|
|
*/
|
|
|
|
iter = hashmap_first (hashofheaders);
|
|
|
|
if (iter >= 0)
|
|
|
|
{
|
|
|
|
for (; !hashmap_is_end (hashofheaders, iter); ++iter)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
hashmap_return_entry (hashofheaders,
|
|
|
|
iter, &data, (void **) &header);
|
|
|
|
|
|
|
|
ret = write_message (connptr->client_fd,
|
|
|
|
"%s: %s\r\n", data, header);
|
|
|
|
if (ret < 0)
|
|
|
|
goto ERROR_EXIT;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
|
|
|
hashmap_delete (hashofheaders);
|
|
|
|
|
|
|
|
/* Write the final blank line to signify the end of the headers */
|
|
|
|
if (safe_write (connptr->client_fd, "\r\n", 2) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ERROR_EXIT:
|
|
|
|
hashmap_delete (hashofheaders);
|
|
|
|
return -1;
|
2000-02-17 01:32:49 +08:00
|
|
|
}
|
|
|
|
|
2000-09-12 08:04:42 +08:00
|
|
|
/*
|
|
|
|
* Switch the sockets into nonblocking mode and begin relaying the bytes
|
|
|
|
* between the two connections. We continue to use the buffering code
|
|
|
|
* since we want to be able to buffer a certain amount for slower
|
|
|
|
* connections (as this was the reason why I originally modified
|
|
|
|
* tinyproxy oh so long ago...)
|
|
|
|
* - rjkaes
|
|
|
|
*/
|
2001-11-22 08:31:10 +08:00
|
|
|
static void
|
2008-12-01 23:01:11 +08:00
|
|
|
relay_connection (struct conn_s *connptr)
|
2000-02-17 01:32:49 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
fd_set rset, wset;
|
|
|
|
struct timeval tv;
|
|
|
|
time_t last_access;
|
|
|
|
int ret;
|
|
|
|
double tdiff;
|
|
|
|
int maxfd = max (connptr->client_fd, connptr->server_fd) + 1;
|
|
|
|
ssize_t bytes_received;
|
|
|
|
|
|
|
|
socket_nonblocking (connptr->client_fd);
|
|
|
|
socket_nonblocking (connptr->server_fd);
|
|
|
|
|
|
|
|
last_access = time (NULL);
|
|
|
|
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
FD_ZERO (&rset);
|
|
|
|
FD_ZERO (&wset);
|
|
|
|
|
|
|
|
tv.tv_sec = config.idletimeout - difftime (time (NULL), last_access);
|
|
|
|
tv.tv_usec = 0;
|
|
|
|
|
|
|
|
if (buffer_size (connptr->sbuffer) > 0)
|
2008-12-08 21:39:44 +08:00
|
|
|
FD_SET (connptr->client_fd, &wset);
|
2008-12-01 23:01:11 +08:00
|
|
|
if (buffer_size (connptr->cbuffer) > 0)
|
2008-12-08 21:39:44 +08:00
|
|
|
FD_SET (connptr->server_fd, &wset);
|
2008-12-01 23:01:11 +08:00
|
|
|
if (buffer_size (connptr->sbuffer) < MAXBUFFSIZE)
|
2008-12-08 21:39:44 +08:00
|
|
|
FD_SET (connptr->server_fd, &rset);
|
2008-12-01 23:01:11 +08:00
|
|
|
if (buffer_size (connptr->cbuffer) < MAXBUFFSIZE)
|
2008-12-08 21:39:44 +08:00
|
|
|
FD_SET (connptr->client_fd, &rset);
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
ret = select (maxfd, &rset, &wset, NULL, &tv);
|
|
|
|
|
|
|
|
if (ret == 0)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
tdiff = difftime (time (NULL), last_access);
|
|
|
|
if (tdiff > config.idletimeout)
|
|
|
|
{
|
|
|
|
log_message (LOG_INFO,
|
|
|
|
"Idle Timeout (after select) as %g > %u.",
|
|
|
|
tdiff, config.idletimeout);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
else if (ret < 0)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
log_message (LOG_ERR,
|
|
|
|
"relay_connection: select() error \"%s\". "
|
|
|
|
"Closing connection (client_fd:%d, server_fd:%d)",
|
|
|
|
strerror (errno), connptr->client_fd,
|
|
|
|
connptr->server_fd);
|
|
|
|
return;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
else
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* All right, something was actually selected so mark it.
|
|
|
|
*/
|
|
|
|
last_access = time (NULL);
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
if (FD_ISSET (connptr->server_fd, &rset))
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
bytes_received = read_buffer (connptr->server_fd, connptr->sbuffer);
|
|
|
|
if (bytes_received < 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
connptr->content_length.server -= bytes_received;
|
|
|
|
if (connptr->content_length.server == 0)
|
|
|
|
break;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
if (FD_ISSET (connptr->client_fd, &rset)
|
2008-12-08 21:39:44 +08:00
|
|
|
&& read_buffer (connptr->client_fd, connptr->cbuffer) < 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
if (FD_ISSET (connptr->server_fd, &wset)
|
2008-12-08 21:39:44 +08:00
|
|
|
&& write_buffer (connptr->server_fd, connptr->cbuffer) < 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
if (FD_ISSET (connptr->client_fd, &wset)
|
2008-12-08 21:39:44 +08:00
|
|
|
&& write_buffer (connptr->client_fd, connptr->sbuffer) < 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Here the server has closed the connection... write the
|
|
|
|
* remainder to the client and then exit.
|
|
|
|
*/
|
|
|
|
socket_blocking (connptr->client_fd);
|
|
|
|
while (buffer_size (connptr->sbuffer) > 0)
|
|
|
|
{
|
|
|
|
if (write_buffer (connptr->client_fd, connptr->sbuffer) < 0)
|
2008-12-08 21:39:44 +08:00
|
|
|
break;
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
|
|
|
shutdown (connptr->client_fd, SHUT_WR);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to send any remaining data to the server if we can.
|
|
|
|
*/
|
|
|
|
socket_blocking (connptr->server_fd);
|
|
|
|
while (buffer_size (connptr->cbuffer) > 0)
|
|
|
|
{
|
|
|
|
if (write_buffer (connptr->server_fd, connptr->cbuffer) < 0)
|
2008-12-08 21:39:44 +08:00
|
|
|
break;
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
2000-02-17 01:32:49 +08:00
|
|
|
}
|
|
|
|
|
2001-10-20 02:03:49 +08:00
|
|
|
/*
|
|
|
|
* Establish a connection to the upstream proxy server.
|
|
|
|
*/
|
2001-11-22 08:31:10 +08:00
|
|
|
static int
|
2008-12-01 23:01:11 +08:00
|
|
|
connect_to_upstream (struct conn_s *connptr, struct request_s *request)
|
2001-10-20 02:03:49 +08:00
|
|
|
{
|
2003-01-28 02:42:18 +08:00
|
|
|
#ifndef UPSTREAM_SUPPORT
|
2008-12-01 23:01:11 +08:00
|
|
|
/*
|
|
|
|
* This function does nothing if upstream support was not compiled
|
|
|
|
* into tinyproxy.
|
|
|
|
*/
|
|
|
|
return -1;
|
2003-01-29 05:21:55 +08:00
|
|
|
#else
|
2008-12-01 23:01:11 +08:00
|
|
|
char *combined_string;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
struct upstream *cur_upstream = connptr->upstream_proxy;
|
|
|
|
|
|
|
|
if (!cur_upstream)
|
|
|
|
{
|
|
|
|
log_message (LOG_WARNING,
|
2008-12-08 21:39:44 +08:00
|
|
|
"No upstream proxy defined for %s.", request->host);
|
2008-12-01 23:01:11 +08:00
|
|
|
indicate_http_error (connptr, 404,
|
2008-12-08 21:39:44 +08:00
|
|
|
"Unable to connect to upstream proxy.");
|
2008-12-01 23:01:11 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
connptr->server_fd =
|
|
|
|
opensock (cur_upstream->host, cur_upstream->port,
|
2008-12-08 21:39:44 +08:00
|
|
|
connptr->server_ip_addr);
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
if (connptr->server_fd < 0)
|
|
|
|
{
|
|
|
|
log_message (LOG_WARNING, "Could not connect to upstream proxy.");
|
|
|
|
indicate_http_error (connptr, 404,
|
2008-12-08 21:39:44 +08:00
|
|
|
"Unable to connect to upstream proxy",
|
|
|
|
"detail",
|
|
|
|
"A network error occurred while trying to "
|
|
|
|
"connect to the upstream web proxy.", NULL);
|
2008-12-01 23:01:11 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
log_message (LOG_CONN,
|
2008-12-08 21:39:44 +08:00
|
|
|
"Established connection to upstream proxy \"%s\" "
|
|
|
|
"using file descriptor %d.",
|
|
|
|
cur_upstream->host, connptr->server_fd);
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We need to re-write the "path" part of the request so that we
|
|
|
|
* can reuse the establish_http_connection() function. It expects a
|
|
|
|
* method and path.
|
|
|
|
*/
|
|
|
|
if (connptr->connect_method)
|
|
|
|
{
|
|
|
|
len = strlen (request->host) + 7;
|
|
|
|
|
|
|
|
combined_string = safemalloc (len);
|
|
|
|
if (!combined_string)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
snprintf (combined_string, len, "%s:%d", request->host, request->port);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
len = strlen (request->host) + strlen (request->path) + 14;
|
|
|
|
combined_string = safemalloc (len);
|
|
|
|
if (!combined_string)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
snprintf (combined_string, len, "http://%s:%d%s", request->host,
|
2008-12-08 21:39:44 +08:00
|
|
|
request->port, request->path);
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (request->path)
|
|
|
|
safefree (request->path);
|
|
|
|
request->path = combined_string;
|
|
|
|
|
|
|
|
return establish_http_connection (connptr, request);
|
2003-01-29 05:21:55 +08:00
|
|
|
#endif
|
2001-10-20 02:03:49 +08:00
|
|
|
}
|
|
|
|
|
2000-02-17 01:32:49 +08:00
|
|
|
/*
|
2000-09-12 08:04:42 +08:00
|
|
|
* This is the main drive for each connection. As you can tell, for the
|
|
|
|
* first few steps we are using a blocking socket. If you remember the
|
|
|
|
* older tinyproxy code, this use to be a very confusing state machine.
|
|
|
|
* Well, no more! :) The sockets are only switched into nonblocking mode
|
|
|
|
* when we start the relay portion. This makes most of the original
|
|
|
|
* tinyproxy code, which was confusing, redundant. Hail progress.
|
|
|
|
* - rjkaes
|
2000-02-17 01:32:49 +08:00
|
|
|
*/
|
2001-11-22 08:31:10 +08:00
|
|
|
void
|
2008-12-01 23:01:11 +08:00
|
|
|
handle_connection (int fd)
|
2000-02-17 01:32:49 +08:00
|
|
|
{
|
2008-12-01 23:01:11 +08:00
|
|
|
struct conn_s *connptr;
|
|
|
|
struct request_s *request = NULL;
|
|
|
|
hashmap_t hashofheaders = NULL;
|
|
|
|
|
|
|
|
char sock_ipaddr[IP_LENGTH];
|
|
|
|
char peer_ipaddr[IP_LENGTH];
|
|
|
|
char peer_string[HOSTNAME_LENGTH];
|
|
|
|
|
|
|
|
getpeer_information (fd, peer_ipaddr, peer_string);
|
|
|
|
|
|
|
|
if (config.bindsame)
|
|
|
|
getsock_ip (fd, sock_ipaddr);
|
|
|
|
|
|
|
|
log_message (LOG_CONN, config.bindsame ?
|
2008-12-08 21:39:44 +08:00
|
|
|
"Connect (file descriptor %d): %s [%s] at [%s]" :
|
|
|
|
"Connect (file descriptor %d): %s [%s]",
|
|
|
|
fd, peer_string, peer_ipaddr, sock_ipaddr);
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
connptr = initialize_conn (fd, peer_ipaddr, peer_string,
|
2008-12-08 21:39:44 +08:00
|
|
|
config.bindsame ? sock_ipaddr : 0);
|
2008-12-01 23:01:11 +08:00
|
|
|
if (!connptr)
|
|
|
|
{
|
|
|
|
close (fd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-08-04 20:40:25 +08:00
|
|
|
if (check_acl (peer_ipaddr, peer_string) <= 0)
|
2008-12-01 23:01:11 +08:00
|
|
|
{
|
|
|
|
update_stats (STAT_DENIED);
|
|
|
|
indicate_http_error (connptr, 403, "Access denied",
|
2008-12-08 21:39:44 +08:00
|
|
|
"detail",
|
|
|
|
"The administrator of this proxy has not configured "
|
|
|
|
"it to service requests from your host.", NULL);
|
2008-12-01 23:01:11 +08:00
|
|
|
send_http_error_message (connptr);
|
|
|
|
destroy_conn (connptr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (read_request_line (connptr) < 0)
|
|
|
|
{
|
|
|
|
update_stats (STAT_BADCONN);
|
|
|
|
indicate_http_error (connptr, 408, "Timeout",
|
2008-12-08 21:39:44 +08:00
|
|
|
"detail",
|
|
|
|
"Server timeout waiting for the HTTP request "
|
|
|
|
"from the client.", NULL);
|
2008-12-01 23:01:11 +08:00
|
|
|
send_http_error_message (connptr);
|
|
|
|
destroy_conn (connptr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The "hashofheaders" store the client's headers.
|
|
|
|
*/
|
|
|
|
if (!(hashofheaders = hashmap_create (HEADER_BUCKETS)))
|
|
|
|
{
|
|
|
|
update_stats (STAT_BADCONN);
|
|
|
|
indicate_http_error (connptr, 503, "Internal error",
|
2008-12-08 21:39:44 +08:00
|
|
|
"detail",
|
|
|
|
"An internal server error occurred while processing "
|
|
|
|
"your request. Please contact the administrator.",
|
|
|
|
NULL);
|
2008-12-01 23:01:11 +08:00
|
|
|
send_http_error_message (connptr);
|
|
|
|
destroy_conn (connptr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get all the headers from the client in a big hash.
|
|
|
|
*/
|
|
|
|
if (get_all_headers (connptr->client_fd, hashofheaders) < 0)
|
|
|
|
{
|
|
|
|
log_message (LOG_WARNING,
|
2008-12-08 21:39:44 +08:00
|
|
|
"Could not retrieve all the headers from the client");
|
2008-12-01 23:01:11 +08:00
|
|
|
hashmap_delete (hashofheaders);
|
|
|
|
update_stats (STAT_BADCONN);
|
|
|
|
destroy_conn (connptr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
request = process_request (connptr, hashofheaders);
|
|
|
|
if (!request)
|
|
|
|
{
|
|
|
|
if (!connptr->error_variables && !connptr->show_stats)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
update_stats (STAT_BADCONN);
|
|
|
|
destroy_conn (connptr);
|
|
|
|
hashmap_delete (hashofheaders);
|
|
|
|
return;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
goto send_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
connptr->upstream_proxy = UPSTREAM_HOST (request->host);
|
|
|
|
if (connptr->upstream_proxy != NULL)
|
|
|
|
{
|
|
|
|
if (connect_to_upstream (connptr, request) < 0)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
goto send_error;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
connptr->server_fd = opensock (request->host, request->port,
|
2008-12-08 21:39:44 +08:00
|
|
|
connptr->server_ip_addr);
|
2008-12-01 23:01:11 +08:00
|
|
|
if (connptr->server_fd < 0)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
indicate_http_error (connptr, 500, "Unable to connect",
|
|
|
|
"detail",
|
|
|
|
PACKAGE " "
|
|
|
|
"was unable to connect to the remote web server.",
|
|
|
|
"error", strerror (errno), NULL);
|
|
|
|
goto send_error;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
log_message (LOG_CONN,
|
2008-12-08 21:39:44 +08:00
|
|
|
"Established connection to host \"%s\" using "
|
|
|
|
"file descriptor %d.", request->host, connptr->server_fd);
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
if (!connptr->connect_method)
|
2008-12-08 21:39:44 +08:00
|
|
|
establish_http_connection (connptr, request);
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
send_error:
|
|
|
|
free_request_struct (request);
|
|
|
|
|
|
|
|
if (process_client_headers (connptr, hashofheaders) < 0)
|
|
|
|
{
|
|
|
|
update_stats (STAT_BADCONN);
|
|
|
|
if (!connptr->error_variables)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
hashmap_delete (hashofheaders);
|
|
|
|
destroy_conn (connptr);
|
|
|
|
return;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
|
|
|
hashmap_delete (hashofheaders);
|
|
|
|
|
|
|
|
if (connptr->error_variables)
|
|
|
|
{
|
|
|
|
send_http_error_message (connptr);
|
|
|
|
destroy_conn (connptr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (connptr->show_stats)
|
|
|
|
{
|
|
|
|
showstats (connptr);
|
|
|
|
destroy_conn (connptr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!connptr->connect_method || (connptr->upstream_proxy != NULL))
|
|
|
|
{
|
|
|
|
if (process_server_headers (connptr) < 0)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
if (connptr->error_variables)
|
|
|
|
send_http_error_message (connptr);
|
|
|
|
|
|
|
|
update_stats (STAT_BADCONN);
|
|
|
|
destroy_conn (connptr);
|
|
|
|
return;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (send_ssl_response (connptr) < 0)
|
2008-12-08 21:39:44 +08:00
|
|
|
{
|
|
|
|
log_message (LOG_ERR,
|
|
|
|
"handle_connection: Could not send SSL greeting "
|
|
|
|
"to client.");
|
|
|
|
update_stats (STAT_BADCONN);
|
|
|
|
destroy_conn (connptr);
|
|
|
|
return;
|
|
|
|
}
|
2008-12-01 23:01:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
relay_connection (connptr);
|
|
|
|
|
|
|
|
log_message (LOG_INFO,
|
2008-12-08 21:39:44 +08:00
|
|
|
"Closed connection between local client (fd:%d) "
|
|
|
|
"and remote client (fd:%d)",
|
|
|
|
connptr->client_fd, connptr->server_fd);
|
2008-12-01 23:01:11 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* All done... close everything and go home... :)
|
|
|
|
*/
|
|
|
|
destroy_conn (connptr);
|
|
|
|
return;
|
2000-02-17 01:32:49 +08:00
|
|
|
}
|