Merge branch 'master' into log-obsolete-comment
This commit is contained in:
commit
709fa5ffc2
@ -176,37 +176,11 @@ The possible keywords and their descriptions are as follows:
|
||||
|
||||
*MaxClients*::
|
||||
|
||||
Tinyproxy creates one child process for each connected client.
|
||||
Tinyproxy creates one thread for each connected client.
|
||||
This options specifies the absolute highest number processes that
|
||||
will be created. With other words, only MaxClients clients can be
|
||||
connected to Tinyproxy simultaneously.
|
||||
|
||||
*MinSpareServers*::
|
||||
*MaxSpareServers*::
|
||||
|
||||
Tinyproxy always keeps a certain number of idle child processes
|
||||
so that it can handle new incoming client requests quickly.
|
||||
`MinSpareServer` and `MaxSpareServers` control the lower and upper
|
||||
limits for the number of spare processes. I.e. when the number of
|
||||
spare servers drops below `MinSpareServers` then Tinyproxy will
|
||||
start forking new spare processes in the background and when the
|
||||
number of spare processes exceeds `MaxSpareServers` then Tinyproxy
|
||||
will kill off extra processes.
|
||||
|
||||
*StartServers*::
|
||||
|
||||
The number of servers to start initially. This should usually be
|
||||
set to a value between MinSpareServers and MaxSpareServers.
|
||||
|
||||
*MaxRequestsPerChild*::
|
||||
|
||||
This limits the number of connections that a child process
|
||||
will handle before it is killed. The default value is `0`
|
||||
which disables this feature. This option is meant as an
|
||||
emergency measure in the case of problems with memory leakage.
|
||||
In that case, setting `MaxRequestsPerChild` to a value of e.g.
|
||||
1000, or 10000 can be useful.
|
||||
|
||||
*Allow*::
|
||||
*Deny*::
|
||||
|
||||
@ -222,6 +196,9 @@ The possible keywords and their descriptions are as follows:
|
||||
end of the client host name, i.e, this can be a full host name
|
||||
like `host.example.com` or a domain name like `.example.com` or
|
||||
even a top level domain name like `.com`.
|
||||
Note that by adding a rule using a host or domain name, a costly name
|
||||
lookup has to be done for every new connection, which could slow down
|
||||
the service considerably.
|
||||
|
||||
*AddHeader*::
|
||||
|
||||
|
@ -189,30 +189,6 @@ LogLevel Info
|
||||
#
|
||||
MaxClients 100
|
||||
|
||||
#
|
||||
# MinSpareServers/MaxSpareServers: These settings set the upper and
|
||||
# lower limit for the number of spare servers which should be available.
|
||||
#
|
||||
# If the number of spare servers falls below MinSpareServers then new
|
||||
# server processes will be spawned. If the number of servers exceeds
|
||||
# MaxSpareServers then the extras will be killed off.
|
||||
#
|
||||
MinSpareServers 5
|
||||
MaxSpareServers 20
|
||||
|
||||
#
|
||||
# StartServers: The number of servers to start initially.
|
||||
#
|
||||
StartServers 10
|
||||
|
||||
#
|
||||
# MaxRequestsPerChild: The number of connections a thread will handle
|
||||
# before it is killed. In practise this should be set to 0, which
|
||||
# disables thread reaping. If you do notice problems with memory
|
||||
# leakage, then set this to something like 10000.
|
||||
#
|
||||
MaxRequestsPerChild 0
|
||||
|
||||
#
|
||||
# Allow: Customization of authorization controls. If there are any
|
||||
# access control keywords then the default action is to DENY. Otherwise,
|
||||
@ -222,6 +198,7 @@ MaxRequestsPerChild 0
|
||||
# tested against the controls based on order.
|
||||
#
|
||||
Allow 127.0.0.1
|
||||
Allow ::1
|
||||
|
||||
# BasicAuth: HTTP "Basic Authentication" for accessing the proxy.
|
||||
# If there are any entries specified, access is only granted for authenticated
|
||||
|
@ -48,10 +48,12 @@ tinyproxy_SOURCES = \
|
||||
upstream.c upstream.h \
|
||||
basicauth.c basicauth.h \
|
||||
base64.c base64.h \
|
||||
sblist.c sblist.h \
|
||||
loop.c loop.h \
|
||||
connect-ports.c connect-ports.h
|
||||
|
||||
EXTRA_tinyproxy_SOURCES = filter.c filter.h \
|
||||
reverse-proxy.c reverse-proxy.h \
|
||||
transparent-proxy.c transparent-proxy.h
|
||||
tinyproxy_DEPENDENCIES = @ADDITIONAL_OBJECTS@
|
||||
tinyproxy_LDADD = @ADDITIONAL_OBJECTS@
|
||||
tinyproxy_LDADD = @ADDITIONAL_OBJECTS@ -lpthread
|
||||
|
28
src/acl.c
28
src/acl.c
@ -221,8 +221,8 @@ insert_acl (char *location, acl_access_t access_type, vector_t *access_list)
|
||||
* -1 if no tests match, so skip
|
||||
*/
|
||||
static int
|
||||
acl_string_processing (struct acl_s *acl,
|
||||
const char *ip_address, const char *string_address)
|
||||
acl_string_processing (struct acl_s *acl, const char *ip_address,
|
||||
union sockaddr_union *addr, char *string_addr)
|
||||
{
|
||||
int match;
|
||||
struct addrinfo hints, *res, *ressave;
|
||||
@ -231,7 +231,6 @@ acl_string_processing (struct acl_s *acl,
|
||||
|
||||
assert (acl && acl->type == ACL_STRING);
|
||||
assert (ip_address && strlen (ip_address) > 0);
|
||||
assert (string_address && strlen (string_address) > 0);
|
||||
|
||||
/*
|
||||
* If the first character of the ACL string is a period, we need to
|
||||
@ -267,7 +266,15 @@ acl_string_processing (struct acl_s *acl,
|
||||
}
|
||||
|
||||
STRING_TEST:
|
||||
test_length = strlen (string_address);
|
||||
if(string_addr[0] == 0) {
|
||||
/* only do costly hostname resolution when it is absolutely needed,
|
||||
and only once */
|
||||
if(getnameinfo ((void *) addr, sizeof (*addr),
|
||||
string_addr, HOSTNAME_LENGTH, NULL, 0, 0) != 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
test_length = strlen (string_addr);
|
||||
match_length = strlen (acl->address.string);
|
||||
|
||||
/*
|
||||
@ -278,7 +285,7 @@ STRING_TEST:
|
||||
return -1;
|
||||
|
||||
if (strcasecmp
|
||||
(string_address + (test_length - match_length),
|
||||
(string_addr + (test_length - match_length),
|
||||
acl->address.string) == 0) {
|
||||
if (acl->access == ACL_DENY)
|
||||
return 0;
|
||||
@ -329,15 +336,18 @@ static int check_numeric_acl (const struct acl_s *acl, const char *ip)
|
||||
* 1 if allowed
|
||||
* 0 if denied
|
||||
*/
|
||||
int check_acl (const char *ip, const char *host, vector_t access_list)
|
||||
int check_acl (const char *ip, union sockaddr_union *addr, vector_t access_list)
|
||||
{
|
||||
struct acl_s *acl;
|
||||
int perm = 0;
|
||||
size_t i;
|
||||
char string_addr[HOSTNAME_LENGTH];
|
||||
|
||||
assert (ip != NULL);
|
||||
assert (host != NULL);
|
||||
|
||||
string_addr[0] = 0;
|
||||
|
||||
/*
|
||||
* If there is no access list allow everything.
|
||||
*/
|
||||
@ -348,7 +358,7 @@ int check_acl (const char *ip, const char *host, vector_t access_list)
|
||||
acl = (struct acl_s *) vector_getentry (access_list, i, NULL);
|
||||
switch (acl->type) {
|
||||
case ACL_STRING:
|
||||
perm = acl_string_processing (acl, ip, host);
|
||||
perm = acl_string_processing (acl, ip, addr, string_addr);
|
||||
break;
|
||||
|
||||
case ACL_NUMERIC:
|
||||
@ -371,8 +381,8 @@ int check_acl (const char *ip, const char *host, vector_t access_list)
|
||||
/*
|
||||
* Deny all connections by default.
|
||||
*/
|
||||
log_message (LOG_NOTICE, "Unauthorized connection from \"%s\" [%s].",
|
||||
host, ip);
|
||||
log_message (LOG_NOTICE, "Unauthorized connection from \"%s\".",
|
||||
ip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -22,12 +22,13 @@
|
||||
#define TINYPROXY_ACL_H
|
||||
|
||||
#include "vector.h"
|
||||
#include "sock.h"
|
||||
|
||||
typedef enum { ACL_ALLOW, ACL_DENY } acl_access_t;
|
||||
|
||||
extern int insert_acl (char *location, acl_access_t access_type,
|
||||
vector_t *access_list);
|
||||
extern int check_acl (const char *ip_address, const char *string_address,
|
||||
extern int check_acl (const char *ip_address, union sockaddr_union *addr,
|
||||
vector_t access_list);
|
||||
extern void flush_access_list (vector_t access_list);
|
||||
|
||||
|
@ -28,21 +28,21 @@
|
||||
#include "log.h"
|
||||
#include "conf.h"
|
||||
|
||||
short int is_anonymous_enabled (void)
|
||||
short int is_anonymous_enabled (struct config_s *conf)
|
||||
{
|
||||
return (config.anonymous_map != NULL) ? 1 : 0;
|
||||
return (conf->anonymous_map != NULL) ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for the header. This function returns a positive value greater than
|
||||
* zero if the string was found, zero if it wasn't and negative upon error.
|
||||
*/
|
||||
int anonymous_search (const char *s)
|
||||
int anonymous_search (struct config_s *conf, const char *s)
|
||||
{
|
||||
assert (s != NULL);
|
||||
assert (config.anonymous_map != NULL);
|
||||
assert (conf->anonymous_map != NULL);
|
||||
|
||||
return hashmap_search (config.anonymous_map, s);
|
||||
return hashmap_search (conf->anonymous_map, s);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -51,23 +51,23 @@ int anonymous_search (const char *s)
|
||||
* Return -1 if there is an error, otherwise a 0 is returned if the insert was
|
||||
* successful.
|
||||
*/
|
||||
int anonymous_insert (const char *s)
|
||||
int anonymous_insert (struct config_s *conf, const char *s)
|
||||
{
|
||||
char data = 1;
|
||||
|
||||
assert (s != NULL);
|
||||
|
||||
if (!config.anonymous_map) {
|
||||
config.anonymous_map = hashmap_create (32);
|
||||
if (!config.anonymous_map)
|
||||
if (!conf->anonymous_map) {
|
||||
conf->anonymous_map = hashmap_create (32);
|
||||
if (!conf->anonymous_map)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hashmap_search (config.anonymous_map, s) > 0) {
|
||||
if (hashmap_search (conf->anonymous_map, s) > 0) {
|
||||
/* The key was already found, so return a positive number. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Insert the new key */
|
||||
return hashmap_insert (config.anonymous_map, s, &data, sizeof (data));
|
||||
return hashmap_insert (conf->anonymous_map, s, &data, sizeof (data));
|
||||
}
|
||||
|
@ -21,8 +21,8 @@
|
||||
#ifndef _TINYPROXY_ANONYMOUS_H_
|
||||
#define _TINYPROXY_ANONYMOUS_H_
|
||||
|
||||
extern short int is_anonymous_enabled (void);
|
||||
extern int anonymous_search (const char *s);
|
||||
extern int anonymous_insert (const char *s);
|
||||
extern short int is_anonymous_enabled (struct config_s *conf);
|
||||
extern int anonymous_search (struct config_s *conf, const char *s);
|
||||
extern int anonymous_insert (struct config_s *conf, const char *s);
|
||||
|
||||
#endif
|
||||
|
496
src/child.c
496
src/child.c
@ -31,184 +31,99 @@
|
||||
#include "sock.h"
|
||||
#include "utils.h"
|
||||
#include "conf.h"
|
||||
#include "sblist.h"
|
||||
#include "loop.h"
|
||||
#include <pthread.h>
|
||||
|
||||
static vector_t listen_fds;
|
||||
|
||||
/*
|
||||
* Stores the internal data needed for each child (connection)
|
||||
*/
|
||||
enum child_status_t { T_EMPTY, T_WAITING, T_CONNECTED };
|
||||
struct child_s {
|
||||
pid_t tid;
|
||||
unsigned int connects;
|
||||
enum child_status_t status;
|
||||
struct client {
|
||||
union sockaddr_union addr;
|
||||
int fd;
|
||||
};
|
||||
|
||||
/*
|
||||
* A pointer to an array of children. A certain number of children are
|
||||
* created when the program is started.
|
||||
*/
|
||||
static struct child_s *child_ptr;
|
||||
struct child {
|
||||
pthread_t thread;
|
||||
struct client client;
|
||||
volatile int done;
|
||||
};
|
||||
|
||||
static struct child_config_s {
|
||||
unsigned int maxclients, maxrequestsperchild;
|
||||
unsigned int maxspareservers, minspareservers, startservers;
|
||||
} child_config;
|
||||
|
||||
static unsigned int *servers_waiting; /* servers waiting for a connection */
|
||||
|
||||
/*
|
||||
* Lock/Unlock the "servers_waiting" variable so that two children cannot
|
||||
* modify it at the same time.
|
||||
*/
|
||||
#define SERVER_COUNT_LOCK() _child_lock_wait()
|
||||
#define SERVER_COUNT_UNLOCK() _child_lock_release()
|
||||
|
||||
/* START OF LOCKING SECTION */
|
||||
|
||||
/*
|
||||
* These variables are required for the locking mechanism. Also included
|
||||
* are the "private" functions for locking/unlocking.
|
||||
*/
|
||||
static struct flock lock_it, unlock_it;
|
||||
static int lock_fd = -1;
|
||||
|
||||
static void _child_lock_init (void)
|
||||
static void* child_thread(void* data)
|
||||
{
|
||||
char lock_file[] = "/tmp/tinyproxy.servers.lock.XXXXXX";
|
||||
|
||||
/* Only allow u+rw bits. This may be required for some versions
|
||||
* of glibc so that mkstemp() doesn't make us vulnerable.
|
||||
*/
|
||||
umask (0177);
|
||||
|
||||
lock_fd = mkstemp (lock_file);
|
||||
unlink (lock_file);
|
||||
|
||||
lock_it.l_type = F_WRLCK;
|
||||
lock_it.l_whence = SEEK_SET;
|
||||
lock_it.l_start = 0;
|
||||
lock_it.l_len = 0;
|
||||
|
||||
unlock_it.l_type = F_UNLCK;
|
||||
unlock_it.l_whence = SEEK_SET;
|
||||
unlock_it.l_start = 0;
|
||||
unlock_it.l_len = 0;
|
||||
struct child *c = data;
|
||||
handle_connection (c->client.fd, &c->client.addr);
|
||||
c->done = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void _child_lock_wait (void)
|
||||
static sblist *childs;
|
||||
|
||||
static void collect_threads(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
while ((rc = fcntl (lock_fd, F_SETLKW, &lock_it)) < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
else
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void _child_lock_release (void)
|
||||
{
|
||||
if (fcntl (lock_fd, F_SETLKW, &unlock_it) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
/* END OF LOCKING SECTION */
|
||||
|
||||
#define SERVER_INC() do { \
|
||||
SERVER_COUNT_LOCK(); \
|
||||
++(*servers_waiting); \
|
||||
DEBUG2("INC: servers_waiting: %d", *servers_waiting); \
|
||||
SERVER_COUNT_UNLOCK(); \
|
||||
} while (0)
|
||||
|
||||
#define SERVER_DEC() do { \
|
||||
SERVER_COUNT_LOCK(); \
|
||||
assert(*servers_waiting > 0); \
|
||||
--(*servers_waiting); \
|
||||
DEBUG2("DEC: servers_waiting: %d", *servers_waiting); \
|
||||
SERVER_COUNT_UNLOCK(); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Set the configuration values for the various child related settings.
|
||||
*/
|
||||
short int child_configure (child_config_t type, unsigned int val)
|
||||
{
|
||||
switch (type) {
|
||||
case CHILD_MAXCLIENTS:
|
||||
child_config.maxclients = val;
|
||||
break;
|
||||
case CHILD_MAXSPARESERVERS:
|
||||
child_config.maxspareservers = val;
|
||||
break;
|
||||
case CHILD_MINSPARESERVERS:
|
||||
child_config.minspareservers = val;
|
||||
break;
|
||||
case CHILD_STARTSERVERS:
|
||||
child_config.startservers = val;
|
||||
break;
|
||||
case CHILD_MAXREQUESTSPERCHILD:
|
||||
child_config.maxrequestsperchild = val;
|
||||
break;
|
||||
default:
|
||||
DEBUG2 ("Invalid type (%d)", type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* child signal handler for sighup
|
||||
*/
|
||||
static void child_sighup_handler (int sig)
|
||||
{
|
||||
if (sig == SIGHUP) {
|
||||
/*
|
||||
* Ignore the return value of reload_config for now.
|
||||
* This should actually be handled somehow...
|
||||
*/
|
||||
reload_config ();
|
||||
|
||||
#ifdef FILTER_ENABLE
|
||||
filter_reload ();
|
||||
#endif /* FILTER_ENABLE */
|
||||
size_t i;
|
||||
for (i = 0; i < sblist_getsize(childs); ) {
|
||||
struct child *c = *((struct child**)sblist_get(childs, i));
|
||||
if (c->done) {
|
||||
pthread_join(c->thread, 0);
|
||||
sblist_delete(childs, i);
|
||||
safefree(c);
|
||||
} else i++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the main (per child) loop.
|
||||
* This is the main loop accepting new connections.
|
||||
*/
|
||||
static void child_main (struct child_s *ptr)
|
||||
void child_main_loop (void)
|
||||
{
|
||||
int connfd;
|
||||
struct sockaddr *cliaddr;
|
||||
socklen_t clilen;
|
||||
union sockaddr_union cliaddr_storage;
|
||||
struct sockaddr *cliaddr = (void*) &cliaddr_storage;
|
||||
socklen_t clilen = sizeof(cliaddr_storage);
|
||||
fd_set rfds;
|
||||
int maxfd = 0;
|
||||
ssize_t i;
|
||||
int ret;
|
||||
int ret, listenfd, maxfd, was_full = 0;
|
||||
pthread_attr_t *attrp, attr;
|
||||
struct child *child;
|
||||
|
||||
cliaddr = (struct sockaddr *)
|
||||
safemalloc (sizeof(struct sockaddr_storage));
|
||||
if (!cliaddr) {
|
||||
log_message (LOG_CRIT,
|
||||
"Could not allocate memory for child address.");
|
||||
exit (0);
|
||||
}
|
||||
childs = sblist_new(sizeof (struct child*), config->maxclients);
|
||||
|
||||
ptr->connects = 0;
|
||||
srand(time(NULL));
|
||||
loop_records_init();
|
||||
|
||||
/*
|
||||
* We have to wait for connections on multiple fds,
|
||||
* so use select.
|
||||
*/
|
||||
while (!config.quit) {
|
||||
while (!config->quit) {
|
||||
|
||||
collect_threads();
|
||||
|
||||
if (sblist_getsize(childs) >= config->maxclients) {
|
||||
if (!was_full)
|
||||
log_message (LOG_NOTICE,
|
||||
"Maximum number of connections reached. "
|
||||
"Refusing new connections.");
|
||||
was_full = 1;
|
||||
usleep(16);
|
||||
continue;
|
||||
}
|
||||
|
||||
was_full = 0;
|
||||
listenfd = -1;
|
||||
maxfd = 0;
|
||||
|
||||
/* Handle log rotation if it was requested */
|
||||
if (received_sighup) {
|
||||
|
||||
reload_config (1);
|
||||
|
||||
#ifdef FILTER_ENABLE
|
||||
filter_reload ();
|
||||
#endif /* FILTER_ENABLE */
|
||||
|
||||
received_sighup = FALSE;
|
||||
}
|
||||
|
||||
int listenfd = -1;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
|
||||
@ -220,17 +135,13 @@ static void child_main (struct child_s *ptr)
|
||||
log_message(LOG_ERR, "Failed to set the listening "
|
||||
"socket %d to non-blocking: %s",
|
||||
fd, strerror(errno));
|
||||
exit(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
FD_SET(*fd, &rfds);
|
||||
maxfd = max(maxfd, *fd);
|
||||
}
|
||||
|
||||
ptr->status = T_WAITING;
|
||||
|
||||
clilen = sizeof(struct sockaddr_storage);
|
||||
|
||||
ret = select(maxfd + 1, &rfds, NULL, NULL, NULL);
|
||||
if (ret == -1) {
|
||||
if (errno == EINTR) {
|
||||
@ -238,7 +149,7 @@ static void child_main (struct child_s *ptr)
|
||||
}
|
||||
log_message (LOG_ERR, "error calling select: %s",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
continue;
|
||||
} else if (ret == 0) {
|
||||
log_message (LOG_WARNING, "Strange: select returned 0 "
|
||||
"but we did not specify a timeout...");
|
||||
@ -269,7 +180,7 @@ static void child_main (struct child_s *ptr)
|
||||
log_message(LOG_ERR, "Failed to set listening "
|
||||
"socket %d to blocking for accept: %s",
|
||||
listenfd, strerror(errno));
|
||||
exit(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -279,21 +190,6 @@ static void child_main (struct child_s *ptr)
|
||||
|
||||
connfd = accept (listenfd, cliaddr, &clilen);
|
||||
|
||||
#ifndef NDEBUG
|
||||
/*
|
||||
* Enable the TINYPROXY_DEBUG environment variable if you
|
||||
* want to use the GDB debugger.
|
||||
*/
|
||||
if (getenv ("TINYPROXY_DEBUG")) {
|
||||
/* Pause for 10 seconds to allow us to connect debugger */
|
||||
fprintf (stderr,
|
||||
"Process has accepted connection: %ld\n",
|
||||
(long int) ptr->tid);
|
||||
sleep (10);
|
||||
fprintf (stderr, "Continuing process: %ld\n",
|
||||
(long int) ptr->tid);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Make sure no error occurred...
|
||||
@ -305,223 +201,36 @@ static void child_main (struct child_s *ptr)
|
||||
continue;
|
||||
}
|
||||
|
||||
ptr->status = T_CONNECTED;
|
||||
|
||||
SERVER_DEC ();
|
||||
|
||||
handle_connection (connfd);
|
||||
ptr->connects++;
|
||||
|
||||
if (child_config.maxrequestsperchild != 0) {
|
||||
DEBUG2 ("%u connections so far...", ptr->connects);
|
||||
|
||||
if (ptr->connects == child_config.maxrequestsperchild) {
|
||||
log_message (LOG_NOTICE,
|
||||
"Child has reached MaxRequestsPerChild (%u). "
|
||||
"Killing child.", ptr->connects);
|
||||
break;
|
||||
}
|
||||
child = safemalloc(sizeof(struct child));
|
||||
if (!child) {
|
||||
oom:
|
||||
close(connfd);
|
||||
log_message (LOG_CRIT,
|
||||
"Could not allocate memory for child.");
|
||||
usleep(16); /* prevent 100% CPU usage in OOM situation */
|
||||
continue;
|
||||
}
|
||||
|
||||
SERVER_COUNT_LOCK ();
|
||||
if (*servers_waiting > child_config.maxspareservers) {
|
||||
/*
|
||||
* There are too many spare children, kill ourself
|
||||
* off.
|
||||
*/
|
||||
log_message (LOG_NOTICE,
|
||||
"Waiting servers (%d) exceeds MaxSpareServers (%d). "
|
||||
"Killing child.",
|
||||
*servers_waiting,
|
||||
child_config.maxspareservers);
|
||||
SERVER_COUNT_UNLOCK ();
|
||||
child->done = 0;
|
||||
|
||||
break;
|
||||
} else {
|
||||
SERVER_COUNT_UNLOCK ();
|
||||
if (!sblist_add(childs, &child)) {
|
||||
free(child);
|
||||
goto oom;
|
||||
}
|
||||
|
||||
SERVER_INC ();
|
||||
child->client.fd = connfd;
|
||||
memcpy(&child->client.addr, &cliaddr_storage, sizeof(cliaddr_storage));
|
||||
|
||||
attrp = 0;
|
||||
if (pthread_attr_init(&attr) == 0) {
|
||||
attrp = &attr;
|
||||
pthread_attr_setstacksize(attrp, 256*1024);
|
||||
}
|
||||
|
||||
ptr->status = T_EMPTY;
|
||||
|
||||
safefree (cliaddr);
|
||||
exit (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fork a child "child" (or in our case a process) and then start up the
|
||||
* child_main() function.
|
||||
*/
|
||||
static pid_t child_make (struct child_s *ptr)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
if ((pid = fork ()) > 0)
|
||||
return pid; /* parent */
|
||||
|
||||
/*
|
||||
* Reset the SIGNALS so that the child can be reaped.
|
||||
*/
|
||||
set_signal_handler (SIGCHLD, SIG_DFL);
|
||||
set_signal_handler (SIGTERM, SIG_DFL);
|
||||
set_signal_handler (SIGHUP, child_sighup_handler);
|
||||
|
||||
child_main (ptr); /* never returns */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a pool of children to handle incoming connections
|
||||
*/
|
||||
short int child_pool_create (void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/*
|
||||
* Make sure the number of MaxClients is not zero, since this
|
||||
* variable determines the size of the array created for children
|
||||
* later on.
|
||||
*/
|
||||
if (child_config.maxclients == 0) {
|
||||
log_message (LOG_ERR,
|
||||
"child_pool_create: \"MaxClients\" must be "
|
||||
"greater than zero.");
|
||||
return -1;
|
||||
}
|
||||
if (child_config.startservers == 0) {
|
||||
log_message (LOG_ERR,
|
||||
"child_pool_create: \"StartServers\" must be "
|
||||
"greater than zero.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
child_ptr =
|
||||
(struct child_s *) calloc_shared_memory (child_config.maxclients,
|
||||
sizeof (struct child_s));
|
||||
if (!child_ptr) {
|
||||
log_message (LOG_ERR,
|
||||
"Could not allocate memory for children.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
servers_waiting =
|
||||
(unsigned int *) malloc_shared_memory (sizeof (unsigned int));
|
||||
if (servers_waiting == MAP_FAILED) {
|
||||
log_message (LOG_ERR,
|
||||
"Could not allocate memory for child counting.");
|
||||
return -1;
|
||||
}
|
||||
*servers_waiting = 0;
|
||||
|
||||
/*
|
||||
* Create a "locking" file for use around the servers_waiting
|
||||
* variable.
|
||||
*/
|
||||
_child_lock_init ();
|
||||
|
||||
if (child_config.startservers > child_config.maxclients) {
|
||||
log_message (LOG_WARNING,
|
||||
"Can not start more than \"MaxClients\" servers. "
|
||||
"Starting %u servers instead.",
|
||||
child_config.maxclients);
|
||||
child_config.startservers = child_config.maxclients;
|
||||
}
|
||||
|
||||
for (i = 0; i != child_config.maxclients; i++) {
|
||||
child_ptr[i].status = T_EMPTY;
|
||||
child_ptr[i].connects = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i != child_config.startservers; i++) {
|
||||
DEBUG2 ("Trying to create child %d of %d", i + 1,
|
||||
child_config.startservers);
|
||||
child_ptr[i].status = T_WAITING;
|
||||
child_ptr[i].tid = child_make (&child_ptr[i]);
|
||||
|
||||
if (child_ptr[i].tid < 0) {
|
||||
log_message (LOG_WARNING,
|
||||
"Could not create child number %d of %d",
|
||||
i, child_config.startservers);
|
||||
return -1;
|
||||
} else {
|
||||
log_message (LOG_INFO,
|
||||
"Creating child number %d of %d ...",
|
||||
i + 1, child_config.startservers);
|
||||
|
||||
SERVER_INC ();
|
||||
}
|
||||
}
|
||||
|
||||
log_message (LOG_INFO, "Finished creating all children.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Keep the proper number of servers running. This is the birth of the
|
||||
* servers. It monitors this at least once a second.
|
||||
*/
|
||||
void child_main_loop (void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
while (1) {
|
||||
if (config.quit)
|
||||
return;
|
||||
|
||||
/* If there are not enough spare servers, create more */
|
||||
SERVER_COUNT_LOCK ();
|
||||
if (*servers_waiting < child_config.minspareservers) {
|
||||
log_message (LOG_NOTICE,
|
||||
"Waiting servers (%d) is less than MinSpareServers (%d). "
|
||||
"Creating new child.",
|
||||
*servers_waiting,
|
||||
child_config.minspareservers);
|
||||
|
||||
SERVER_COUNT_UNLOCK ();
|
||||
|
||||
for (i = 0; i != child_config.maxclients; i++) {
|
||||
if (child_ptr[i].status == T_EMPTY) {
|
||||
child_ptr[i].status = T_WAITING;
|
||||
child_ptr[i].tid =
|
||||
child_make (&child_ptr[i]);
|
||||
if (child_ptr[i].tid < 0) {
|
||||
log_message (LOG_NOTICE,
|
||||
"Could not create child");
|
||||
|
||||
child_ptr[i].status = T_EMPTY;
|
||||
break;
|
||||
}
|
||||
|
||||
SERVER_INC ();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
SERVER_COUNT_UNLOCK ();
|
||||
}
|
||||
|
||||
sleep (5);
|
||||
|
||||
/* Handle log rotation if it was requested */
|
||||
if (received_sighup) {
|
||||
/*
|
||||
* Ignore the return value of reload_config for now.
|
||||
* This should actually be handled somehow...
|
||||
*/
|
||||
reload_config ();
|
||||
|
||||
#ifdef FILTER_ENABLE
|
||||
filter_reload ();
|
||||
#endif /* FILTER_ENABLE */
|
||||
|
||||
/* propagate filter reload to all children */
|
||||
child_kill_children (SIGHUP);
|
||||
|
||||
received_sighup = FALSE;
|
||||
if (pthread_create(&child->thread, attrp, child_thread, child) != 0) {
|
||||
sblist_delete(childs, sblist_getsize(childs) -1);
|
||||
free(child);
|
||||
goto oom;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -531,13 +240,26 @@ void child_main_loop (void)
|
||||
*/
|
||||
void child_kill_children (int sig)
|
||||
{
|
||||
unsigned int i;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i != child_config.maxclients; i++) {
|
||||
if (child_ptr[i].status != T_EMPTY)
|
||||
kill (child_ptr[i].tid, sig);
|
||||
if (sig != SIGTERM) return;
|
||||
|
||||
for (i = 0; i < sblist_getsize(childs); i++) {
|
||||
struct child *c = *((struct child**)sblist_get(childs, i));
|
||||
if (!c->done) {
|
||||
/* interrupt blocking operations.
|
||||
this should cause the threads to shutdown orderly. */
|
||||
close(c->client.fd);
|
||||
}
|
||||
}
|
||||
usleep(16);
|
||||
collect_threads();
|
||||
if (sblist_getsize(childs) != 0)
|
||||
log_message (LOG_CRIT,
|
||||
"child_kill_children: %zu threads still alive!",
|
||||
sblist_getsize(childs)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
172
src/conf.c
172
src/conf.c
@ -27,7 +27,6 @@
|
||||
|
||||
#include "acl.h"
|
||||
#include "anonymous.h"
|
||||
#include "child.h"
|
||||
#include "filter.h"
|
||||
#include "heap.h"
|
||||
#include "html-error.h"
|
||||
@ -92,7 +91,8 @@
|
||||
* All configuration handling functions are REQUIRED to be defined
|
||||
* with the same function template as below.
|
||||
*/
|
||||
typedef int (*CONFFILE_HANDLER) (struct config_s *, const char *, regmatch_t[]);
|
||||
typedef int (*CONFFILE_HANDLER) (struct config_s *, const char *,
|
||||
unsigned long, regmatch_t[]);
|
||||
|
||||
/*
|
||||
* Define the pattern used by any directive handling function. The
|
||||
@ -107,7 +107,7 @@ typedef int (*CONFFILE_HANDLER) (struct config_s *, const char *, regmatch_t[]);
|
||||
*/
|
||||
#define HANDLE_FUNC(func) \
|
||||
int func(struct config_s* conf, const char* line, \
|
||||
regmatch_t match[])
|
||||
unsigned long lineno, regmatch_t match[])
|
||||
|
||||
/*
|
||||
* List all the handling functions. These are defined later, but they need
|
||||
@ -140,9 +140,7 @@ static HANDLE_FUNC (handle_listen);
|
||||
static HANDLE_FUNC (handle_logfile);
|
||||
static HANDLE_FUNC (handle_loglevel);
|
||||
static HANDLE_FUNC (handle_maxclients);
|
||||
static HANDLE_FUNC (handle_maxrequestsperchild);
|
||||
static HANDLE_FUNC (handle_maxspareservers);
|
||||
static HANDLE_FUNC (handle_minspareservers);
|
||||
static HANDLE_FUNC (handle_obsolete);
|
||||
static HANDLE_FUNC (handle_pidfile);
|
||||
static HANDLE_FUNC (handle_port);
|
||||
#ifdef REVERSE_SUPPORT
|
||||
@ -151,7 +149,6 @@ static HANDLE_FUNC (handle_reversemagic);
|
||||
static HANDLE_FUNC (handle_reverseonly);
|
||||
static HANDLE_FUNC (handle_reversepath);
|
||||
#endif
|
||||
static HANDLE_FUNC (handle_startservers);
|
||||
static HANDLE_FUNC (handle_statfile);
|
||||
static HANDLE_FUNC (handle_stathost);
|
||||
static HANDLE_FUNC (handle_syslog);
|
||||
@ -217,10 +214,10 @@ struct {
|
||||
/* integer arguments */
|
||||
STDCONF ("port", INT, handle_port),
|
||||
STDCONF ("maxclients", INT, handle_maxclients),
|
||||
STDCONF ("maxspareservers", INT, handle_maxspareservers),
|
||||
STDCONF ("minspareservers", INT, handle_minspareservers),
|
||||
STDCONF ("startservers", INT, handle_startservers),
|
||||
STDCONF ("maxrequestsperchild", INT, handle_maxrequestsperchild),
|
||||
STDCONF ("maxspareservers", INT, handle_obsolete),
|
||||
STDCONF ("minspareservers", INT, handle_obsolete),
|
||||
STDCONF ("startservers", INT, handle_obsolete),
|
||||
STDCONF ("maxrequestsperchild", INT, handle_obsolete),
|
||||
STDCONF ("timeout", INT, handle_timeout),
|
||||
STDCONF ("connectport", INT, handle_connectport),
|
||||
/* alphanumeric arguments */
|
||||
@ -290,7 +287,6 @@ free_added_headers (vector_t add_headers)
|
||||
|
||||
static void free_config (struct config_s *conf)
|
||||
{
|
||||
safefree (conf->config_file);
|
||||
safefree (conf->logf_name);
|
||||
safefree (conf->stathost);
|
||||
safefree (conf->user);
|
||||
@ -378,7 +374,8 @@ config_free_regex (void)
|
||||
* Returns 0 if a match was found and successfully processed; otherwise,
|
||||
* a negative number is returned.
|
||||
*/
|
||||
static int check_match (struct config_s *conf, const char *line)
|
||||
static int check_match (struct config_s *conf, const char *line,
|
||||
unsigned long lineno)
|
||||
{
|
||||
regmatch_t match[RE_MAX_MATCHES];
|
||||
unsigned int i;
|
||||
@ -389,7 +386,7 @@ static int check_match (struct config_s *conf, const char *line)
|
||||
assert (directives[i].cre);
|
||||
if (!regexec
|
||||
(directives[i].cre, line, RE_MAX_MATCHES, match, 0))
|
||||
return (*directives[i].handler) (conf, line, match);
|
||||
return (*directives[i].handler) (conf, line, lineno, match);
|
||||
}
|
||||
|
||||
return -1;
|
||||
@ -404,7 +401,7 @@ static int config_parse (struct config_s *conf, FILE * f)
|
||||
unsigned long lineno = 1;
|
||||
|
||||
while (fgets (buffer, sizeof (buffer), f)) {
|
||||
if (check_match (conf, buffer)) {
|
||||
if (check_match (conf, buffer, lineno)) {
|
||||
printf ("Syntax error on line %ld\n", lineno);
|
||||
return 1;
|
||||
}
|
||||
@ -444,113 +441,26 @@ done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void initialize_with_defaults (struct config_s *conf,
|
||||
struct config_s *defaults)
|
||||
static void initialize_config_defaults (struct config_s *conf)
|
||||
{
|
||||
if (defaults->logf_name) {
|
||||
conf->logf_name = safestrdup (defaults->logf_name);
|
||||
}
|
||||
memset (conf, 0, sizeof(*conf));
|
||||
|
||||
if (defaults->config_file) {
|
||||
conf->config_file = safestrdup (defaults->config_file);
|
||||
}
|
||||
|
||||
conf->syslog = defaults->syslog;
|
||||
conf->port = defaults->port;
|
||||
|
||||
if (defaults->stathost) {
|
||||
conf->stathost = safestrdup (defaults->stathost);
|
||||
}
|
||||
|
||||
conf->godaemon = defaults->godaemon;
|
||||
conf->quit = defaults->quit;
|
||||
|
||||
if (defaults->user) {
|
||||
conf->user = safestrdup (defaults->user);
|
||||
}
|
||||
|
||||
if (defaults->group) {
|
||||
conf->group = safestrdup (defaults->group);
|
||||
}
|
||||
|
||||
if (defaults->listen_addrs) {
|
||||
ssize_t i;
|
||||
|
||||
conf->listen_addrs = vector_create();
|
||||
for (i=0; i < vector_length(defaults->listen_addrs); i++) {
|
||||
char *addr;
|
||||
size_t size;
|
||||
addr = (char *)vector_getentry(defaults->listen_addrs,
|
||||
i, &size);
|
||||
vector_append(conf->listen_addrs, addr, size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef FILTER_ENABLE
|
||||
if (defaults->filter) {
|
||||
conf->filter = safestrdup (defaults->filter);
|
||||
}
|
||||
|
||||
conf->filter_url = defaults->filter_url;
|
||||
conf->filter_extended = defaults->filter_extended;
|
||||
conf->filter_casesensitive = defaults->filter_casesensitive;
|
||||
#endif /* FILTER_ENABLE */
|
||||
|
||||
#ifdef XTINYPROXY_ENABLE
|
||||
conf->add_xtinyproxy = defaults->add_xtinyproxy;
|
||||
#endif
|
||||
|
||||
#ifdef REVERSE_SUPPORT
|
||||
/* struct reversepath *reversepath_list; */
|
||||
conf->reverseonly = defaults->reverseonly;
|
||||
conf->reversemagic = defaults->reversemagic;
|
||||
|
||||
if (defaults->reversebaseurl) {
|
||||
conf->reversebaseurl = safestrdup (defaults->reversebaseurl);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef UPSTREAM_SUPPORT
|
||||
/* struct upstream *upstream_list; */
|
||||
#endif /* UPSTREAM_SUPPORT */
|
||||
|
||||
if (defaults->pidpath) {
|
||||
conf->pidpath = safestrdup (defaults->pidpath);
|
||||
}
|
||||
|
||||
conf->idletimeout = defaults->idletimeout;
|
||||
|
||||
if (defaults->bind_address) {
|
||||
conf->bind_address = safestrdup (defaults->bind_address);
|
||||
}
|
||||
|
||||
conf->bindsame = defaults->bindsame;
|
||||
|
||||
if (defaults->via_proxy_name) {
|
||||
conf->via_proxy_name = safestrdup (defaults->via_proxy_name);
|
||||
}
|
||||
|
||||
conf->disable_viaheader = defaults->disable_viaheader;
|
||||
|
||||
if (defaults->errorpage_undef) {
|
||||
conf->errorpage_undef = safestrdup (defaults->errorpage_undef);
|
||||
}
|
||||
|
||||
if (defaults->statpage) {
|
||||
conf->statpage = safestrdup (defaults->statpage);
|
||||
}
|
||||
|
||||
/* vector_t access_list; */
|
||||
/* vector_t connect_ports; */
|
||||
/* hashmap_t anonymous_map; */
|
||||
/*
|
||||
* Make sure the HTML error pages array is NULL to begin with.
|
||||
* (FIXME: Should have a better API for all this)
|
||||
*/
|
||||
conf->errorpages = NULL;
|
||||
conf->stathost = safestrdup (TINYPROXY_STATHOST);
|
||||
conf->idletimeout = MAX_IDLE_TIME;
|
||||
conf->logf_name = NULL;
|
||||
conf->pidpath = NULL;
|
||||
conf->maxclients = 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the configuration.
|
||||
*/
|
||||
int reload_config_file (const char *config_fname, struct config_s *conf,
|
||||
struct config_s *defaults)
|
||||
int reload_config_file (const char *config_fname, struct config_s *conf)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@ -558,7 +468,7 @@ int reload_config_file (const char *config_fname, struct config_s *conf,
|
||||
|
||||
free_config (conf);
|
||||
|
||||
initialize_with_defaults (conf, defaults);
|
||||
initialize_config_defaults (conf);
|
||||
|
||||
ret = load_config_file (config_fname, conf);
|
||||
if (ret != 0) {
|
||||
@ -714,7 +624,7 @@ static HANDLE_FUNC (handle_anonymous)
|
||||
if (!arg)
|
||||
return -1;
|
||||
|
||||
anonymous_insert (arg);
|
||||
anonymous_insert (conf, arg);
|
||||
safefree (arg);
|
||||
return 0;
|
||||
}
|
||||
@ -805,34 +715,14 @@ static HANDLE_FUNC (handle_port)
|
||||
|
||||
static HANDLE_FUNC (handle_maxclients)
|
||||
{
|
||||
child_configure (CHILD_MAXCLIENTS, get_long_arg (line, &match[2]));
|
||||
set_int_arg (&conf->maxclients, line, &match[2]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static HANDLE_FUNC (handle_maxspareservers)
|
||||
static HANDLE_FUNC (handle_obsolete)
|
||||
{
|
||||
child_configure (CHILD_MAXSPARESERVERS,
|
||||
get_long_arg (line, &match[2]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static HANDLE_FUNC (handle_minspareservers)
|
||||
{
|
||||
child_configure (CHILD_MINSPARESERVERS,
|
||||
get_long_arg (line, &match[2]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static HANDLE_FUNC (handle_startservers)
|
||||
{
|
||||
child_configure (CHILD_STARTSERVERS, get_long_arg (line, &match[2]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static HANDLE_FUNC (handle_maxrequestsperchild)
|
||||
{
|
||||
child_configure (CHILD_MAXREQUESTSPERCHILD,
|
||||
get_long_arg (line, &match[2]));
|
||||
fprintf (stderr, "WARNING: obsolete config item on line %lu\n",
|
||||
lineno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -39,12 +39,11 @@ typedef struct {
|
||||
struct config_s {
|
||||
vector_t basicauth_list;
|
||||
char *logf_name;
|
||||
char *config_file;
|
||||
unsigned int syslog; /* boolean */
|
||||
unsigned int port;
|
||||
char *stathost;
|
||||
unsigned int godaemon; /* boolean */
|
||||
unsigned int quit; /* boolean */
|
||||
unsigned int maxclients;
|
||||
char *user;
|
||||
char *group;
|
||||
vector_t listen_addrs;
|
||||
@ -113,8 +112,7 @@ struct config_s {
|
||||
vector_t add_headers;
|
||||
};
|
||||
|
||||
extern int reload_config_file (const char *config_fname, struct config_s *conf,
|
||||
struct config_s *defaults);
|
||||
extern int reload_config_file (const char *config_fname, struct config_s *conf);
|
||||
|
||||
int config_compile_regex (void);
|
||||
|
||||
|
@ -31,7 +31,6 @@
|
||||
#include "stats.h"
|
||||
|
||||
struct conn_s *initialize_conn (int client_fd, const char *ipaddr,
|
||||
const char *string_addr,
|
||||
const char *sock_ipaddr)
|
||||
{
|
||||
struct conn_s *connptr;
|
||||
@ -79,7 +78,6 @@ struct conn_s *initialize_conn (int client_fd, const char *ipaddr,
|
||||
connptr->server_ip_addr = (sock_ipaddr ?
|
||||
safestrdup (sock_ipaddr) : NULL);
|
||||
connptr->client_ip_addr = safestrdup (ipaddr);
|
||||
connptr->client_string_addr = safestrdup (string_addr);
|
||||
|
||||
connptr->upstream_proxy = NULL;
|
||||
|
||||
@ -134,8 +132,6 @@ void destroy_conn (struct conn_s *connptr)
|
||||
safefree (connptr->server_ip_addr);
|
||||
if (connptr->client_ip_addr)
|
||||
safefree (connptr->client_ip_addr);
|
||||
if (connptr->client_string_addr)
|
||||
safefree (connptr->client_string_addr);
|
||||
|
||||
#ifdef REVERSE_SUPPORT
|
||||
if (connptr->reversepath)
|
||||
|
@ -62,10 +62,9 @@ struct conn_s {
|
||||
char *server_ip_addr;
|
||||
|
||||
/*
|
||||
* Store the client's IP and hostname information
|
||||
* Store the client's IP information
|
||||
*/
|
||||
char *client_ip_addr;
|
||||
char *client_string_addr;
|
||||
|
||||
/*
|
||||
* Store the incoming request's HTTP protocol.
|
||||
@ -92,7 +91,6 @@ struct conn_s {
|
||||
* Functions for the creation and destruction of a connection structure.
|
||||
*/
|
||||
extern struct conn_s *initialize_conn (int client_fd, const char *ipaddr,
|
||||
const char *string_addr,
|
||||
const char *sock_ipaddr);
|
||||
extern void destroy_conn (struct conn_s *connptr);
|
||||
|
||||
|
10
src/filter.c
10
src/filter.c
@ -59,7 +59,7 @@ void filter_init (void)
|
||||
return;
|
||||
}
|
||||
|
||||
fd = fopen (config.filter, "r");
|
||||
fd = fopen (config->filter, "r");
|
||||
if (!fd) {
|
||||
return;
|
||||
}
|
||||
@ -67,9 +67,9 @@ void filter_init (void)
|
||||
p = NULL;
|
||||
|
||||
cflags = REG_NEWLINE | REG_NOSUB;
|
||||
if (config.filter_extended)
|
||||
if (config->filter_extended)
|
||||
cflags |= REG_EXTENDED;
|
||||
if (!config.filter_casesensitive)
|
||||
if (!config->filter_casesensitive)
|
||||
cflags |= REG_ICASE;
|
||||
|
||||
while (fgets (buf, FILTER_BUFFER_LEN, fd)) {
|
||||
@ -121,7 +121,7 @@ void filter_init (void)
|
||||
if (err != 0) {
|
||||
fprintf (stderr,
|
||||
"Bad regex in %s: %s\n",
|
||||
config.filter, p->pat);
|
||||
config->filter, p->pat);
|
||||
exit (EX_DATAERR);
|
||||
}
|
||||
}
|
||||
@ -157,7 +157,7 @@ void filter_destroy (void)
|
||||
*/
|
||||
void filter_reload (void)
|
||||
{
|
||||
if (config.filter) {
|
||||
if (config->filter) {
|
||||
log_message (LOG_NOTICE, "Re-reading filter file.");
|
||||
filter_destroy ();
|
||||
filter_init ();
|
||||
|
58
src/heap.c
58
src/heap.c
@ -97,61 +97,3 @@ char *debugging_strdup (const char *s, const char *file, unsigned long line)
|
||||
|
||||
#endif /* !NDEBUG */
|
||||
|
||||
/*
|
||||
* Allocate a block of memory in the "shared" memory region.
|
||||
*
|
||||
* FIXME: This uses the most basic (and slowest) means of creating a
|
||||
* shared memory location. It requires the use of a temporary file. We might
|
||||
* want to look into something like MM (Shared Memory Library) for a better
|
||||
* solution.
|
||||
*/
|
||||
void *malloc_shared_memory (size_t size)
|
||||
{
|
||||
int fd;
|
||||
void *ptr;
|
||||
char buffer[32];
|
||||
|
||||
static const char *shared_file = "/tmp/tinyproxy.shared.XXXXXX";
|
||||
|
||||
assert (size > 0);
|
||||
|
||||
strlcpy (buffer, shared_file, sizeof (buffer));
|
||||
|
||||
/* Only allow u+rw bits. This may be required for some versions
|
||||
* of glibc so that mkstemp() doesn't make us vulnerable.
|
||||
*/
|
||||
umask (0177);
|
||||
|
||||
if ((fd = mkstemp (buffer)) == -1)
|
||||
return MAP_FAILED;
|
||||
unlink (buffer);
|
||||
|
||||
if (ftruncate (fd, size) == -1)
|
||||
return MAP_FAILED;
|
||||
ptr = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a block of memory from the "shared" region an initialize it to
|
||||
* zero.
|
||||
*/
|
||||
void *calloc_shared_memory (size_t nmemb, size_t size)
|
||||
{
|
||||
void *ptr;
|
||||
long length;
|
||||
|
||||
assert (nmemb > 0);
|
||||
assert (size > 0);
|
||||
|
||||
length = nmemb * size;
|
||||
|
||||
ptr = malloc_shared_memory (length);
|
||||
if (ptr == MAP_FAILED)
|
||||
return ptr;
|
||||
|
||||
memset (ptr, 0, length);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
@ -52,10 +52,4 @@ extern char *debugging_strdup (const char *s, const char *file,
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Allocate memory from the "shared" region of memory.
|
||||
*/
|
||||
extern void *malloc_shared_memory (size_t size);
|
||||
extern void *calloc_shared_memory (size_t nmemb, size_t size);
|
||||
|
||||
#endif
|
||||
|
@ -41,13 +41,13 @@ int add_new_errorpage (char *filepath, unsigned int errornum)
|
||||
{
|
||||
char errornbuf[ERRORNUM_BUFSIZE];
|
||||
|
||||
config.errorpages = hashmap_create (ERRPAGES_BUCKETCOUNT);
|
||||
if (!config.errorpages)
|
||||
config->errorpages = hashmap_create (ERRPAGES_BUCKETCOUNT);
|
||||
if (!config->errorpages)
|
||||
return (-1);
|
||||
|
||||
snprintf (errornbuf, ERRORNUM_BUFSIZE, "%u", errornum);
|
||||
|
||||
if (hashmap_insert (config.errorpages, errornbuf,
|
||||
if (hashmap_insert (config->errorpages, errornbuf,
|
||||
filepath, strlen (filepath) + 1) < 0)
|
||||
return (-1);
|
||||
|
||||
@ -66,19 +66,19 @@ static char *get_html_file (unsigned int errornum)
|
||||
|
||||
assert (errornum >= 100 && errornum < 1000);
|
||||
|
||||
if (!config.errorpages)
|
||||
return (config.errorpage_undef);
|
||||
if (!config->errorpages)
|
||||
return (config->errorpage_undef);
|
||||
|
||||
snprintf (errornbuf, ERRORNUM_BUFSIZE, "%u", errornum);
|
||||
|
||||
result_iter = hashmap_find (config.errorpages, errornbuf);
|
||||
result_iter = hashmap_find (config->errorpages, errornbuf);
|
||||
|
||||
if (hashmap_is_end (config.errorpages, result_iter))
|
||||
return (config.errorpage_undef);
|
||||
if (hashmap_is_end (config->errorpages, result_iter))
|
||||
return (config->errorpage_undef);
|
||||
|
||||
if (hashmap_return_entry (config.errorpages, result_iter,
|
||||
if (hashmap_return_entry (config->errorpages, result_iter,
|
||||
&key, (void **) &val) < 0)
|
||||
return (config.errorpage_undef);
|
||||
return (config->errorpage_undef);
|
||||
|
||||
return (val);
|
||||
}
|
||||
@ -164,13 +164,17 @@ int send_http_headers (struct conn_s *connptr, int code, const char *message)
|
||||
"%s"
|
||||
"Connection: close\r\n" "\r\n";
|
||||
|
||||
const char auth_str[] =
|
||||
const char p_auth_str[] =
|
||||
"Proxy-Authenticate: Basic realm=\""
|
||||
PACKAGE_NAME "\"\r\n";
|
||||
|
||||
const char w_auth_str[] =
|
||||
"WWW-Authenticate: Basic realm=\""
|
||||
PACKAGE_NAME "\"\r\n";
|
||||
|
||||
/* according to rfc7235, the 407 error must be accompanied by
|
||||
a Proxy-Authenticate header field. */
|
||||
const char *add = code == 407 ? auth_str : "";
|
||||
const char *add = code == 407 ? p_auth_str : (code == 401 ? w_auth_str : "");
|
||||
|
||||
return (write_message (connptr->client_fd, headers,
|
||||
code, message, PACKAGE, VERSION,
|
||||
@ -258,7 +262,6 @@ int add_standard_vars (struct conn_s *connptr)
|
||||
ADD_VAR_RET ("cause", connptr->error_string);
|
||||
ADD_VAR_RET ("request", connptr->request_line);
|
||||
ADD_VAR_RET ("clientip", connptr->client_ip_addr);
|
||||
ADD_VAR_RET ("clienthost", connptr->client_string_addr);
|
||||
|
||||
/* The following value parts are all non-NULL and will
|
||||
* trigger warnings in ADD_VAR_RET(), so we use
|
||||
|
39
src/log.c
39
src/log.c
@ -29,6 +29,7 @@
|
||||
#include "utils.h"
|
||||
#include "vector.h"
|
||||
#include "conf.h"
|
||||
#include <pthread.h>
|
||||
|
||||
static const char *syslog_level[] = {
|
||||
NULL,
|
||||
@ -45,6 +46,8 @@ static const char *syslog_level[] = {
|
||||
#define TIME_LENGTH 16
|
||||
#define STRING_LENGTH 800
|
||||
|
||||
static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/*
|
||||
* Global file descriptor for the log file
|
||||
*/
|
||||
@ -71,10 +74,7 @@ static unsigned int logging_initialized = FALSE; /* boolean */
|
||||
int open_log_file (const char *log_file_name)
|
||||
{
|
||||
if (log_file_name == NULL) {
|
||||
if(config.godaemon == FALSE)
|
||||
log_file_fd = fileno(stdout);
|
||||
else
|
||||
log_file_fd = -1;
|
||||
} else {
|
||||
log_file_fd = create_file_safely (log_file_name, FALSE);
|
||||
}
|
||||
@ -129,7 +129,7 @@ void log_message (int level, const char *fmt, ...)
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (config.syslog && level == LOG_CONN)
|
||||
if (config && config->syslog && level == LOG_CONN)
|
||||
level = LOG_INFO;
|
||||
|
||||
va_start (args, fmt);
|
||||
@ -161,16 +161,18 @@ void log_message (int level, const char *fmt, ...)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(!config.syslog && log_file_fd == -1)
|
||||
if(!config->syslog && log_file_fd == -1)
|
||||
goto out;
|
||||
|
||||
if (config.syslog) {
|
||||
if (config->syslog) {
|
||||
pthread_mutex_lock(&log_mutex);
|
||||
#ifdef HAVE_VSYSLOG_H
|
||||
vsyslog (level, fmt, args);
|
||||
#else
|
||||
vsnprintf (str, STRING_LENGTH, fmt, args);
|
||||
syslog (level, "%s", str);
|
||||
#endif
|
||||
pthread_mutex_unlock(&log_mutex);
|
||||
} else {
|
||||
char *p;
|
||||
|
||||
@ -196,18 +198,24 @@ void log_message (int level, const char *fmt, ...)
|
||||
|
||||
assert (log_file_fd >= 0);
|
||||
|
||||
pthread_mutex_lock(&log_mutex);
|
||||
ret = write (log_file_fd, str, strlen (str));
|
||||
pthread_mutex_unlock(&log_mutex);
|
||||
|
||||
if (ret == -1) {
|
||||
config.syslog = TRUE;
|
||||
config->syslog = TRUE;
|
||||
|
||||
log_message(LOG_CRIT, "ERROR: Could not write to log "
|
||||
"file %s: %s.",
|
||||
config.logf_name, strerror(errno));
|
||||
config->logf_name, strerror(errno));
|
||||
log_message(LOG_CRIT,
|
||||
"Falling back to syslog logging");
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&log_mutex);
|
||||
fsync (log_file_fd);
|
||||
pthread_mutex_unlock(&log_mutex);
|
||||
|
||||
}
|
||||
|
||||
out:
|
||||
@ -261,26 +269,23 @@ static void send_stored_logs (void)
|
||||
*/
|
||||
int setup_logging (void)
|
||||
{
|
||||
if (!config.syslog) {
|
||||
if (open_log_file (config.logf_name) < 0) {
|
||||
if (!config->syslog) {
|
||||
if (open_log_file (config->logf_name) < 0) {
|
||||
/*
|
||||
* If opening the log file fails, we try
|
||||
* to fall back to syslog logging...
|
||||
*/
|
||||
config.syslog = TRUE;
|
||||
config->syslog = TRUE;
|
||||
|
||||
log_message (LOG_CRIT, "ERROR: Could not create log "
|
||||
"file %s: %s.",
|
||||
config.logf_name, strerror (errno));
|
||||
config->logf_name, strerror (errno));
|
||||
log_message (LOG_CRIT,
|
||||
"Falling back to syslog logging.");
|
||||
}
|
||||
}
|
||||
|
||||
if (config.syslog) {
|
||||
if (config.godaemon == TRUE)
|
||||
openlog ("tinyproxy", LOG_PID, LOG_DAEMON);
|
||||
else
|
||||
if (config->syslog) {
|
||||
openlog ("tinyproxy", LOG_PID, LOG_USER);
|
||||
}
|
||||
|
||||
@ -299,7 +304,7 @@ void shutdown_logging (void)
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.syslog) {
|
||||
if (config->syslog) {
|
||||
closelog ();
|
||||
} else {
|
||||
close_log_file ();
|
||||
|
76
src/loop.c
Normal file
76
src/loop.c
Normal file
@ -0,0 +1,76 @@
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#include "loop.h"
|
||||
#include "conf.h"
|
||||
#include "main.h"
|
||||
#include "sblist.h"
|
||||
#include "sock.h"
|
||||
|
||||
struct loop_record {
|
||||
union sockaddr_union addr;
|
||||
time_t tstamp;
|
||||
};
|
||||
|
||||
static sblist *loop_records;
|
||||
static pthread_mutex_t loop_records_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
void loop_records_init(void) {
|
||||
loop_records = sblist_new(sizeof (struct loop_record), 32);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void su_to_str(union sockaddr_union *addr, char *buf) {
|
||||
int af = addr->v4.sin_family;
|
||||
unsigned port = ntohs(af == AF_INET ? addr->v4.sin_port : addr->v6.sin6_port);
|
||||
char portb[32];
|
||||
sprintf(portb, ":%u", port);
|
||||
getpeer_information (addr, buf, 256);
|
||||
strcat(buf, portb);
|
||||
}
|
||||
#endif
|
||||
|
||||
void loop_records_add(union sockaddr_union *addr) {
|
||||
time_t now =time(0);
|
||||
struct loop_record rec;
|
||||
pthread_mutex_lock(&loop_records_lock);
|
||||
rec.tstamp = now;
|
||||
rec.addr = *addr;
|
||||
sblist_add(loop_records, &rec);
|
||||
pthread_mutex_unlock(&loop_records_lock);
|
||||
}
|
||||
|
||||
#define TIMEOUT_SECS 15
|
||||
|
||||
int connection_loops (union sockaddr_union *addr) {
|
||||
int ret = 0, af, our_af = addr->v4.sin_family;
|
||||
void *ipdata, *our_ipdata = our_af == AF_INET ? (void*)&addr->v4.sin_addr.s_addr : (void*)&addr->v6.sin6_addr.s6_addr;
|
||||
size_t i, cmp_len = our_af == AF_INET ? sizeof(addr->v4.sin_addr.s_addr) : sizeof(addr->v6.sin6_addr.s6_addr);
|
||||
unsigned port, our_port = ntohs(our_af == AF_INET ? addr->v4.sin_port : addr->v6.sin6_port);
|
||||
time_t now = time(0);
|
||||
|
||||
pthread_mutex_lock(&loop_records_lock);
|
||||
for (i = 0; i < sblist_getsize(loop_records); ) {
|
||||
struct loop_record *rec = sblist_get(loop_records, i);
|
||||
|
||||
if (rec->tstamp + TIMEOUT_SECS < now) {
|
||||
sblist_delete(loop_records, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
af = rec->addr.v4.sin_family;
|
||||
if (af != our_af) goto next;
|
||||
port = ntohs(af == AF_INET ? rec->addr.v4.sin_port : rec->addr.v6.sin6_port);
|
||||
if (port != our_port) goto next;
|
||||
ipdata = af == AF_INET ? (void*)&rec->addr.v4.sin_addr.s_addr : (void*)&rec->addr.v6.sin6_addr.s6_addr;
|
||||
if (!memcmp(ipdata, our_ipdata, cmp_len)) {
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
next:
|
||||
i++;
|
||||
}
|
||||
pthread_mutex_unlock(&loop_records_lock);
|
||||
return ret;
|
||||
}
|
||||
|
11
src/loop.h
Normal file
11
src/loop.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef LOOP_H
|
||||
#define LOOP_H
|
||||
|
||||
#include "sock.h"
|
||||
|
||||
void loop_records_init(void);
|
||||
void loop_records_add(union sockaddr_union *addr);
|
||||
int connection_loops (union sockaddr_union *addr);
|
||||
|
||||
#endif
|
||||
|
185
src/main.c
185
src/main.c
@ -47,10 +47,18 @@
|
||||
/*
|
||||
* Global Structures
|
||||
*/
|
||||
struct config_s config;
|
||||
struct config_s config_defaults;
|
||||
struct config_s *config;
|
||||
static struct config_s configs[2];
|
||||
static const char* config_file;
|
||||
unsigned int received_sighup = FALSE; /* boolean */
|
||||
|
||||
static struct config_s*
|
||||
get_next_config(void)
|
||||
{
|
||||
if (config == &configs[0]) return &configs[1];
|
||||
return &configs[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle a signal
|
||||
*/
|
||||
@ -65,8 +73,9 @@ takesig (int sig)
|
||||
received_sighup = TRUE;
|
||||
break;
|
||||
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
config.quit = TRUE;
|
||||
config->quit = TRUE;
|
||||
break;
|
||||
|
||||
case SIGCHLD:
|
||||
@ -161,52 +170,6 @@ get_id (char *str)
|
||||
return atoi (str);
|
||||
}
|
||||
|
||||
/**
|
||||
* process_cmdline:
|
||||
* @argc: argc as passed to main()
|
||||
* @argv: argv as passed to main()
|
||||
*
|
||||
* This function parses command line arguments.
|
||||
**/
|
||||
static void
|
||||
process_cmdline (int argc, char **argv, struct config_s *conf)
|
||||
{
|
||||
int opt;
|
||||
|
||||
while ((opt = getopt (argc, argv, "c:vdh")) != EOF) {
|
||||
switch (opt) {
|
||||
case 'v':
|
||||
display_version ();
|
||||
exit (EX_OK);
|
||||
|
||||
case 'd':
|
||||
conf->godaemon = FALSE;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
if (conf->config_file != NULL) {
|
||||
safefree (conf->config_file);
|
||||
}
|
||||
conf->config_file = safestrdup (optarg);
|
||||
if (!conf->config_file) {
|
||||
fprintf (stderr,
|
||||
"%s: Could not allocate memory.\n",
|
||||
argv[0]);
|
||||
exit (EX_SOFTWARE);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
display_usage ();
|
||||
exit (EX_OK);
|
||||
|
||||
default:
|
||||
display_usage ();
|
||||
exit (EX_USAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* change_user:
|
||||
* @program: The name of the program. Pass argv[0] here.
|
||||
@ -218,16 +181,16 @@ process_cmdline (int argc, char **argv, struct config_s *conf)
|
||||
static void
|
||||
change_user (const char *program)
|
||||
{
|
||||
if (config.group && strlen (config.group) > 0) {
|
||||
int gid = get_id (config.group);
|
||||
if (config->group && strlen (config->group) > 0) {
|
||||
int gid = get_id (config->group);
|
||||
|
||||
if (gid < 0) {
|
||||
struct group *thisgroup = getgrnam (config.group);
|
||||
struct group *thisgroup = getgrnam (config->group);
|
||||
|
||||
if (!thisgroup) {
|
||||
fprintf (stderr,
|
||||
"%s: Unable to find group \"%s\".\n",
|
||||
program, config.group);
|
||||
program, config->group);
|
||||
exit (EX_NOUSER);
|
||||
}
|
||||
|
||||
@ -237,7 +200,7 @@ change_user (const char *program)
|
||||
if (setgid (gid) < 0) {
|
||||
fprintf (stderr,
|
||||
"%s: Unable to change to group \"%s\".\n",
|
||||
program, config.group);
|
||||
program, config->group);
|
||||
exit (EX_NOPERM);
|
||||
}
|
||||
|
||||
@ -252,19 +215,19 @@ change_user (const char *program)
|
||||
#endif
|
||||
|
||||
log_message (LOG_INFO, "Now running as group \"%s\".",
|
||||
config.group);
|
||||
config->group);
|
||||
}
|
||||
|
||||
if (config.user && strlen (config.user) > 0) {
|
||||
int uid = get_id (config.user);
|
||||
if (config->user && strlen (config->user) > 0) {
|
||||
int uid = get_id (config->user);
|
||||
|
||||
if (uid < 0) {
|
||||
struct passwd *thisuser = getpwnam (config.user);
|
||||
struct passwd *thisuser = getpwnam (config->user);
|
||||
|
||||
if (!thisuser) {
|
||||
fprintf (stderr,
|
||||
"%s: Unable to find user \"%s\".\n",
|
||||
program, config.user);
|
||||
program, config->user);
|
||||
exit (EX_NOUSER);
|
||||
}
|
||||
|
||||
@ -274,53 +237,35 @@ change_user (const char *program)
|
||||
if (setuid (uid) < 0) {
|
||||
fprintf (stderr,
|
||||
"%s: Unable to change to user \"%s\".\n",
|
||||
program, config.user);
|
||||
program, config->user);
|
||||
exit (EX_NOPERM);
|
||||
}
|
||||
|
||||
log_message (LOG_INFO, "Now running as user \"%s\".",
|
||||
config.user);
|
||||
config->user);
|
||||
}
|
||||
}
|
||||
|
||||
static void initialize_config_defaults (struct config_s *conf)
|
||||
{
|
||||
memset (conf, 0, sizeof(*conf));
|
||||
|
||||
conf->config_file = safestrdup (SYSCONFDIR "/tinyproxy.conf");
|
||||
if (!conf->config_file) {
|
||||
fprintf (stderr, PACKAGE ": Could not allocate memory.\n");
|
||||
exit (EX_SOFTWARE);
|
||||
}
|
||||
conf->godaemon = TRUE;
|
||||
/*
|
||||
* Make sure the HTML error pages array is NULL to begin with.
|
||||
* (FIXME: Should have a better API for all this)
|
||||
*/
|
||||
conf->errorpages = NULL;
|
||||
conf->stathost = safestrdup (TINYPROXY_STATHOST);
|
||||
conf->idletimeout = MAX_IDLE_TIME;
|
||||
conf->logf_name = NULL;
|
||||
conf->pidpath = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* convenience wrapper around reload_config_file
|
||||
* that also re-initializes logging.
|
||||
*/
|
||||
int reload_config (void)
|
||||
int reload_config (int reload_logging)
|
||||
{
|
||||
int ret;
|
||||
struct config_s *c_next = get_next_config();
|
||||
|
||||
shutdown_logging ();
|
||||
if (reload_logging) shutdown_logging ();
|
||||
|
||||
ret = reload_config_file (config_file, c_next);
|
||||
|
||||
ret = reload_config_file (config_defaults.config_file, &config,
|
||||
&config_defaults);
|
||||
if (ret != 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = setup_logging ();
|
||||
config = c_next;
|
||||
|
||||
if (reload_logging) ret = setup_logging ();
|
||||
|
||||
done:
|
||||
return ret;
|
||||
@ -329,6 +274,10 @@ done:
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int opt, daemonized = TRUE;
|
||||
|
||||
srand(time(NULL)); /* for hashmap seeds */
|
||||
|
||||
/* Only allow u+rw bits. This may be required for some versions
|
||||
* of glibc so that mkstemp() doesn't make us vulnerable.
|
||||
*/
|
||||
@ -340,12 +289,33 @@ main (int argc, char **argv)
|
||||
exit (EX_SOFTWARE);
|
||||
}
|
||||
|
||||
initialize_config_defaults (&config_defaults);
|
||||
process_cmdline (argc, argv, &config_defaults);
|
||||
config_file = SYSCONFDIR "/tinyproxy.conf";
|
||||
|
||||
if (reload_config_file (config_defaults.config_file,
|
||||
&config,
|
||||
&config_defaults)) {
|
||||
while ((opt = getopt (argc, argv, "c:vdh")) != EOF) {
|
||||
switch (opt) {
|
||||
case 'v':
|
||||
display_version ();
|
||||
exit (EX_OK);
|
||||
|
||||
case 'd':
|
||||
daemonized = FALSE;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
config_file = optarg;
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
display_usage ();
|
||||
exit (EX_OK);
|
||||
|
||||
default:
|
||||
display_usage ();
|
||||
exit (EX_USAGE);
|
||||
}
|
||||
}
|
||||
|
||||
if (reload_config(0)) {
|
||||
exit (EX_SOFTWARE);
|
||||
}
|
||||
|
||||
@ -355,13 +325,13 @@ main (int argc, char **argv)
|
||||
* in the list of allowed headers, since it is required in a
|
||||
* HTTP/1.0 request. Also add the Content-Type header since it
|
||||
* goes hand in hand with Content-Length. */
|
||||
if (is_anonymous_enabled ()) {
|
||||
anonymous_insert ("Content-Length");
|
||||
anonymous_insert ("Content-Type");
|
||||
if (is_anonymous_enabled (config)) {
|
||||
anonymous_insert (config, "Content-Length");
|
||||
anonymous_insert (config, "Content-Type");
|
||||
}
|
||||
|
||||
if (config.godaemon == TRUE) {
|
||||
if (!config.syslog && config.logf_name == NULL)
|
||||
if (daemonized == TRUE) {
|
||||
if (!config->syslog && config->logf_name == NULL)
|
||||
fprintf(stderr, "WARNING: logging deactivated "
|
||||
"(can't log to stdout when daemonized)\n");
|
||||
|
||||
@ -375,20 +345,20 @@ main (int argc, char **argv)
|
||||
}
|
||||
|
||||
#ifdef FILTER_ENABLE
|
||||
if (config.filter)
|
||||
if (config->filter)
|
||||
filter_init ();
|
||||
#endif /* FILTER_ENABLE */
|
||||
|
||||
/* Start listening on the selected port. */
|
||||
if (child_listening_sockets(config.listen_addrs, config.port) < 0) {
|
||||
if (child_listening_sockets(config->listen_addrs, config->port) < 0) {
|
||||
fprintf (stderr, "%s: Could not create listening sockets.\n",
|
||||
argv[0]);
|
||||
exit (EX_OSERR);
|
||||
}
|
||||
|
||||
/* Create pid file before we drop privileges */
|
||||
if (config.pidpath) {
|
||||
if (pidfile_create (config.pidpath) < 0) {
|
||||
if (config->pidpath) {
|
||||
if (pidfile_create (config->pidpath) < 0) {
|
||||
fprintf (stderr, "%s: Could not create PID file.\n",
|
||||
argv[0]);
|
||||
exit (EX_OSERR);
|
||||
@ -407,13 +377,6 @@ main (int argc, char **argv)
|
||||
exit (EX_SOFTWARE);
|
||||
}
|
||||
|
||||
if (child_pool_create () < 0) {
|
||||
fprintf (stderr,
|
||||
"%s: Could not create the pool of children.\n",
|
||||
argv[0]);
|
||||
exit (EX_SOFTWARE);
|
||||
}
|
||||
|
||||
/* These signals are only for the parent process. */
|
||||
log_message (LOG_INFO, "Setting the various signals.");
|
||||
|
||||
@ -446,14 +409,14 @@ main (int argc, char **argv)
|
||||
child_close_sock ();
|
||||
|
||||
/* Remove the PID file */
|
||||
if (config.pidpath != NULL && unlink (config.pidpath) < 0) {
|
||||
if (config->pidpath != NULL && unlink (config->pidpath) < 0) {
|
||||
log_message (LOG_WARNING,
|
||||
"Could not remove PID file \"%s\": %s.",
|
||||
config.pidpath, strerror (errno));
|
||||
config->pidpath, strerror (errno));
|
||||
}
|
||||
|
||||
#ifdef FILTER_ENABLE
|
||||
if (config.filter)
|
||||
if (config->filter)
|
||||
filter_destroy ();
|
||||
#endif /* FILTER_ENABLE */
|
||||
|
||||
|
@ -29,9 +29,9 @@
|
||||
#define MAX_IDLE_TIME (60 * 10) /* 10 minutes of no activity */
|
||||
|
||||
/* Global Structures used in the program */
|
||||
extern struct config_s config;
|
||||
extern struct config_s *config;
|
||||
extern unsigned int received_sighup; /* boolean */
|
||||
|
||||
extern int reload_config (void);
|
||||
extern int reload_config (int reload_logging);
|
||||
|
||||
#endif /* __MAIN_H__ */
|
||||
|
106
src/reqs.c
106
src/reqs.c
@ -49,6 +49,7 @@
|
||||
#include "connect-ports.h"
|
||||
#include "conf.h"
|
||||
#include "basicauth.h"
|
||||
#include "loop.h"
|
||||
|
||||
/*
|
||||
* Maximum length of a HTTP line
|
||||
@ -60,8 +61,8 @@
|
||||
* enabled.
|
||||
*/
|
||||
#ifdef UPSTREAM_SUPPORT
|
||||
# define UPSTREAM_CONFIGURED() (config.upstream_list != NULL)
|
||||
# define UPSTREAM_HOST(host) upstream_get(host, config.upstream_list)
|
||||
# define UPSTREAM_CONFIGURED() (config->upstream_list != NULL)
|
||||
# define UPSTREAM_HOST(host) upstream_get(host, config->upstream_list)
|
||||
# define UPSTREAM_IS_HTTP(conn) (conn->upstream_proxy != NULL && conn->upstream_proxy->type == PT_HTTP)
|
||||
#else
|
||||
# define UPSTREAM_CONFIGURED() (0)
|
||||
@ -372,7 +373,7 @@ BAD_REQUEST_ERROR:
|
||||
}
|
||||
|
||||
#ifdef REVERSE_SUPPORT
|
||||
if (config.reversepath_list != NULL) {
|
||||
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
|
||||
@ -386,7 +387,7 @@ BAD_REQUEST_ERROR:
|
||||
if (reverse_url != NULL) {
|
||||
safefree (url);
|
||||
url = reverse_url;
|
||||
} else if (config.reverseonly) {
|
||||
} else if (config->reverseonly) {
|
||||
log_message (LOG_ERR,
|
||||
"Bad request, no mapping for '%s' found",
|
||||
url);
|
||||
@ -419,7 +420,7 @@ BAD_REQUEST_ERROR:
|
||||
|
||||
/* Verify that the port in the CONNECT method is allowed */
|
||||
if (!check_allowed_connect_ports (request->port,
|
||||
config.connect_ports))
|
||||
config->connect_ports))
|
||||
{
|
||||
indicate_http_error (connptr, 403, "Access violation",
|
||||
"detail",
|
||||
@ -436,7 +437,7 @@ BAD_REQUEST_ERROR:
|
||||
} else {
|
||||
#ifdef TRANSPARENT_PROXY
|
||||
if (!do_transparent_proxy
|
||||
(connptr, hashofheaders, request, &config, &url)) {
|
||||
(connptr, hashofheaders, request, config, &url)) {
|
||||
goto fail;
|
||||
}
|
||||
#else
|
||||
@ -454,8 +455,8 @@ BAD_REQUEST_ERROR:
|
||||
/*
|
||||
* Filter restricted domains/urls
|
||||
*/
|
||||
if (config.filter) {
|
||||
if (config.filter_url)
|
||||
if (config->filter) {
|
||||
if (config->filter_url)
|
||||
ret = filter_url (url);
|
||||
else
|
||||
ret = filter_domain (request->host);
|
||||
@ -463,7 +464,7 @@ BAD_REQUEST_ERROR:
|
||||
if (ret) {
|
||||
update_stats (STAT_DENIED);
|
||||
|
||||
if (config.filter_url)
|
||||
if (config->filter_url)
|
||||
log_message (LOG_NOTICE,
|
||||
"Proxying refused on filtered url \"%s\"",
|
||||
url);
|
||||
@ -485,7 +486,7 @@ BAD_REQUEST_ERROR:
|
||||
/*
|
||||
* Check to see if they're requesting the stat host
|
||||
*/
|
||||
if (config.stathost && strcmp (config.stathost, request->host) == 0) {
|
||||
if (config->stathost && strcmp (config->stathost, request->host) == 0) {
|
||||
log_message (LOG_NOTICE, "Request for the stathost.");
|
||||
connptr->show_stats = TRUE;
|
||||
goto fail;
|
||||
@ -803,13 +804,13 @@ write_via_header (int fd, hashmap_t hashofheaders,
|
||||
char *data;
|
||||
int ret;
|
||||
|
||||
if (config.disable_viaheader) {
|
||||
if (config->disable_viaheader) {
|
||||
ret = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (config.via_proxy_name) {
|
||||
strlcpy (hostname, config.via_proxy_name, sizeof (hostname));
|
||||
if (config->via_proxy_name) {
|
||||
strlcpy (hostname, config->via_proxy_name, sizeof (hostname));
|
||||
} else if (gethostname (hostname, sizeof (hostname)) < 0) {
|
||||
strlcpy (hostname, "unknown", 512);
|
||||
}
|
||||
@ -918,8 +919,8 @@ process_client_headers (struct conn_s *connptr, hashmap_t hashofheaders)
|
||||
hashmap_return_entry (hashofheaders,
|
||||
iter, &data, (void **) &header);
|
||||
|
||||
if (!is_anonymous_enabled ()
|
||||
|| anonymous_search (data) > 0) {
|
||||
if (!is_anonymous_enabled (config)
|
||||
|| anonymous_search (config, data) > 0) {
|
||||
ret =
|
||||
write_message (connptr->server_fd,
|
||||
"%s: %s\r\n", data, header);
|
||||
@ -937,7 +938,7 @@ process_client_headers (struct conn_s *connptr, hashmap_t hashofheaders)
|
||||
}
|
||||
}
|
||||
#if defined(XTINYPROXY_ENABLE)
|
||||
if (config.add_xtinyproxy)
|
||||
if (config->add_xtinyproxy)
|
||||
add_xtinyproxy_header (connptr);
|
||||
#endif
|
||||
|
||||
@ -980,7 +981,7 @@ static int process_server_headers (struct conn_s *connptr)
|
||||
int ret;
|
||||
|
||||
#ifdef REVERSE_SUPPORT
|
||||
struct reversepath *reverse = config.reversepath_list;
|
||||
struct reversepath *reverse = config->reversepath_list;
|
||||
#endif
|
||||
|
||||
/* Get the response line from the remote server. */
|
||||
@ -1072,7 +1073,7 @@ retry:
|
||||
|
||||
#ifdef REVERSE_SUPPORT
|
||||
/* Write tracking cookie for the magical reverse proxy path hack */
|
||||
if (config.reversemagic && connptr->reversepath) {
|
||||
if (config->reversemagic && connptr->reversepath) {
|
||||
ret = write_message (connptr->client_fd,
|
||||
"Set-Cookie: " REVERSE_COOKIE
|
||||
"=%s; path=/\r\n", connptr->reversepath);
|
||||
@ -1081,7 +1082,7 @@ retry:
|
||||
}
|
||||
|
||||
/* Rewrite the HTTP redirect if needed */
|
||||
if (config.reversebaseurl &&
|
||||
if (config->reversebaseurl &&
|
||||
hashmap_entry_by_key (hashofheaders, "location",
|
||||
(void **) &header) > 0) {
|
||||
|
||||
@ -1099,14 +1100,14 @@ retry:
|
||||
ret =
|
||||
write_message (connptr->client_fd,
|
||||
"Location: %s%s%s\r\n",
|
||||
config.reversebaseurl,
|
||||
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,
|
||||
header, config->reversebaseurl,
|
||||
(reverse->path + 1), (header + len));
|
||||
hashmap_remove (hashofheaders, "location");
|
||||
}
|
||||
@ -1180,7 +1181,7 @@ static void relay_connection (struct conn_s *connptr)
|
||||
FD_ZERO (&wset);
|
||||
|
||||
tv.tv_sec =
|
||||
config.idletimeout - difftime (time (NULL), last_access);
|
||||
config->idletimeout - difftime (time (NULL), last_access);
|
||||
tv.tv_usec = 0;
|
||||
|
||||
if (buffer_size (connptr->sbuffer) > 0)
|
||||
@ -1196,10 +1197,10 @@ static void relay_connection (struct conn_s *connptr)
|
||||
|
||||
if (ret == 0) {
|
||||
tdiff = difftime (time (NULL), last_access);
|
||||
if (tdiff > config.idletimeout) {
|
||||
if (tdiff > config->idletimeout) {
|
||||
log_message (LOG_INFO,
|
||||
"Idle Timeout (after select) as %g > %u.",
|
||||
tdiff, config.idletimeout);
|
||||
tdiff, config->idletimeout);
|
||||
return;
|
||||
} else {
|
||||
continue;
|
||||
@ -1533,7 +1534,7 @@ get_request_entity(struct conn_s *connptr)
|
||||
* tinyproxy code, which was confusing, redundant. Hail progress.
|
||||
* - rjkaes
|
||||
*/
|
||||
void handle_connection (int fd)
|
||||
void handle_connection (int fd, union sockaddr_union* addr)
|
||||
{
|
||||
ssize_t i;
|
||||
struct conn_s *connptr;
|
||||
@ -1542,26 +1543,39 @@ void handle_connection (int fd)
|
||||
|
||||
char sock_ipaddr[IP_LENGTH];
|
||||
char peer_ipaddr[IP_LENGTH];
|
||||
char peer_string[HOSTNAME_LENGTH];
|
||||
|
||||
getpeer_information (fd, peer_ipaddr, peer_string);
|
||||
getpeer_information (addr, peer_ipaddr, sizeof(peer_ipaddr));
|
||||
|
||||
if (config.bindsame)
|
||||
if (config->bindsame)
|
||||
getsock_ip (fd, sock_ipaddr);
|
||||
|
||||
log_message (LOG_CONN, config.bindsame ?
|
||||
"Connect (file descriptor %d): %s [%s] at [%s]" :
|
||||
"Connect (file descriptor %d): %s [%s]",
|
||||
fd, peer_string, peer_ipaddr, sock_ipaddr);
|
||||
log_message (LOG_CONN, config->bindsame ?
|
||||
"Connect (file descriptor %d): %s at [%s]" :
|
||||
"Connect (file descriptor %d): %s",
|
||||
fd, peer_ipaddr, sock_ipaddr);
|
||||
|
||||
connptr = initialize_conn (fd, peer_ipaddr, peer_string,
|
||||
config.bindsame ? sock_ipaddr : NULL);
|
||||
connptr = initialize_conn (fd, peer_ipaddr,
|
||||
config->bindsame ? sock_ipaddr : NULL);
|
||||
if (!connptr) {
|
||||
close (fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (check_acl (peer_ipaddr, peer_string, config.access_list) <= 0) {
|
||||
if (connection_loops (addr)) {
|
||||
log_message (LOG_CONN,
|
||||
"Prevented endless loop (file descriptor %d): %s",
|
||||
fd, peer_ipaddr);
|
||||
|
||||
indicate_http_error(connptr, 400, "Bad Request",
|
||||
"detail",
|
||||
"You tried to connect to the "
|
||||
"machine the proxy is running on",
|
||||
NULL);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
if (check_acl (peer_ipaddr, addr, config->access_list) <= 0) {
|
||||
update_stats (STAT_DENIED);
|
||||
indicate_http_error (connptr, 403, "Access denied",
|
||||
"detail",
|
||||
@ -1608,14 +1622,25 @@ void handle_connection (int fd)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (config.basicauth_list != NULL) {
|
||||
if (config->basicauth_list != NULL) {
|
||||
ssize_t len;
|
||||
char *authstring;
|
||||
int failure = 1;
|
||||
int failure = 1, stathost_connect = 0;
|
||||
len = hashmap_entry_by_key (hashofheaders, "proxy-authorization",
|
||||
(void **) &authstring);
|
||||
|
||||
if (len == 0 && config->stathost) {
|
||||
len = hashmap_entry_by_key (hashofheaders, "host",
|
||||
(void **) &authstring);
|
||||
if (len && !strncmp(authstring, config->stathost, strlen(config->stathost))) {
|
||||
len = hashmap_entry_by_key (hashofheaders, "authorization",
|
||||
(void **) &authstring);
|
||||
stathost_connect = 1;
|
||||
} else len = 0;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
if (stathost_connect) goto e401;
|
||||
update_stats (STAT_DENIED);
|
||||
indicate_http_error (connptr, 407, "Proxy Authentication Required",
|
||||
"detail",
|
||||
@ -1626,9 +1651,10 @@ void handle_connection (int fd)
|
||||
if ( /* currently only "basic" auth supported */
|
||||
(strncmp(authstring, "Basic ", 6) == 0 ||
|
||||
strncmp(authstring, "basic ", 6) == 0) &&
|
||||
basicauth_check (config.basicauth_list, authstring + 6) == 1)
|
||||
basicauth_check (config->basicauth_list, authstring + 6) == 1)
|
||||
failure = 0;
|
||||
if(failure) {
|
||||
e401:
|
||||
update_stats (STAT_DENIED);
|
||||
indicate_http_error (connptr, 401, "Unauthorized",
|
||||
"detail",
|
||||
@ -1644,9 +1670,9 @@ void handle_connection (int fd)
|
||||
* Add any user-specified headers (AddHeader directive) to the
|
||||
* outgoing HTTP request.
|
||||
*/
|
||||
for (i = 0; i < vector_length (config.add_headers); i++) {
|
||||
for (i = 0; i < vector_length (config->add_headers); i++) {
|
||||
http_header_t *header = (http_header_t *)
|
||||
vector_getentry (config.add_headers, i, NULL);
|
||||
vector_getentry (config->add_headers, i, NULL);
|
||||
|
||||
hashmap_insert (hashofheaders,
|
||||
header->name,
|
||||
|
@ -23,6 +23,7 @@
|
||||
#define _TINYPROXY_REQS_H_
|
||||
|
||||
#include "common.h"
|
||||
#include "sock.h"
|
||||
|
||||
/*
|
||||
* Port constants for HTTP (80) and SSL (443)
|
||||
@ -43,6 +44,6 @@ struct request_s {
|
||||
char *path;
|
||||
};
|
||||
|
||||
extern void handle_connection (int fd);
|
||||
extern void handle_connection (int fd, union sockaddr_union* addr);
|
||||
|
||||
#endif
|
||||
|
@ -122,14 +122,14 @@ char *reverse_rewrite_url (struct conn_s *connptr, hashmap_t hashofheaders,
|
||||
/* Reverse requests always start with a slash */
|
||||
if (*url == '/') {
|
||||
/* First try locating the reverse mapping by request url */
|
||||
reverse = reversepath_get (url, config.reversepath_list);
|
||||
reverse = reversepath_get (url, config->reversepath_list);
|
||||
if (reverse) {
|
||||
rewrite_url = (char *)
|
||||
safemalloc (strlen (url) + strlen (reverse->url) +
|
||||
1);
|
||||
strcpy (rewrite_url, reverse->url);
|
||||
strcat (rewrite_url, url + strlen (reverse->path));
|
||||
} else if (config.reversemagic
|
||||
} else if (config->reversemagic
|
||||
&& hashmap_entry_by_key (hashofheaders,
|
||||
"cookie",
|
||||
(void **) &cookie) > 0) {
|
||||
@ -139,7 +139,7 @@ char *reverse_rewrite_url (struct conn_s *connptr, hashmap_t hashofheaders,
|
||||
&& (reverse =
|
||||
reversepath_get (cookieval +
|
||||
strlen (REVERSE_COOKIE) + 1,
|
||||
config.reversepath_list)))
|
||||
config->reversepath_list)))
|
||||
{
|
||||
|
||||
rewrite_url = (char *) safemalloc
|
||||
@ -163,7 +163,7 @@ char *reverse_rewrite_url (struct conn_s *connptr, hashmap_t hashofheaders,
|
||||
log_message (LOG_CONN, "Rewriting URL: %s -> %s", url, rewrite_url);
|
||||
|
||||
/* Store reverse path so that the magical tracking cookie can be set */
|
||||
if (config.reversemagic && reverse)
|
||||
if (config->reversemagic && reverse)
|
||||
connptr->reversepath = safestrdup (reverse->path);
|
||||
|
||||
return rewrite_url;
|
||||
|
80
src/sblist.c
Normal file
80
src/sblist.c
Normal file
@ -0,0 +1,80 @@
|
||||
#undef _POSIX_C_SOURCE
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "sblist.h"
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#define MY_PAGE_SIZE 4096
|
||||
|
||||
sblist* sblist_new(size_t itemsize, size_t blockitems) {
|
||||
sblist* ret = (sblist*) malloc(sizeof(sblist));
|
||||
sblist_init(ret, itemsize, blockitems);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sblist_clear(sblist* l) {
|
||||
l->items = NULL;
|
||||
l->capa = 0;
|
||||
l->count = 0;
|
||||
}
|
||||
|
||||
void sblist_init(sblist* l, size_t itemsize, size_t blockitems) {
|
||||
if(l) {
|
||||
l->blockitems = blockitems ? blockitems : MY_PAGE_SIZE / itemsize;
|
||||
l->itemsize = itemsize;
|
||||
sblist_clear(l);
|
||||
}
|
||||
}
|
||||
|
||||
void sblist_free_items(sblist* l) {
|
||||
if(l) {
|
||||
if(l->items) free(l->items);
|
||||
sblist_clear(l);
|
||||
}
|
||||
}
|
||||
|
||||
void sblist_free(sblist* l) {
|
||||
if(l) {
|
||||
sblist_free_items(l);
|
||||
free(l);
|
||||
}
|
||||
}
|
||||
|
||||
char* sblist_item_from_index(sblist* l, size_t idx) {
|
||||
return l->items + (idx * l->itemsize);
|
||||
}
|
||||
|
||||
void* sblist_get(sblist* l, size_t item) {
|
||||
if(item < l->count) return (void*) sblist_item_from_index(l, item);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int sblist_set(sblist* l, void* item, size_t pos) {
|
||||
if(pos >= l->count) return 0;
|
||||
memcpy(sblist_item_from_index(l, pos), item, l->itemsize);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sblist_grow_if_needed(sblist* l) {
|
||||
char* temp;
|
||||
if(l->count == l->capa) {
|
||||
temp = realloc(l->items, (l->capa + l->blockitems) * l->itemsize);
|
||||
if(!temp) return 0;
|
||||
l->capa += l->blockitems;
|
||||
l->items = temp;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sblist_add(sblist* l, void* item) {
|
||||
if(!sblist_grow_if_needed(l)) return 0;
|
||||
l->count++;
|
||||
return sblist_set(l, item, l->count - 1);
|
||||
}
|
||||
|
||||
void sblist_delete(sblist* l, size_t item) {
|
||||
if (l->count && item < l->count) {
|
||||
memmove(sblist_item_from_index(l, item), sblist_item_from_index(l, item + 1), (sblist_getsize(l) - (item + 1)) * l->itemsize);
|
||||
l->count--;
|
||||
}
|
||||
}
|
92
src/sblist.h
Normal file
92
src/sblist.h
Normal file
@ -0,0 +1,92 @@
|
||||
#ifndef SBLIST_H
|
||||
#define SBLIST_H
|
||||
|
||||
/* this file is part of libulz, as of commit 8ab361a27743aaf025323ee43b8b8876dc054fdd
|
||||
modified for direct inclusion in tinyproxy, and for this purpose released under
|
||||
the license of tinyproxy. */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
/*
|
||||
* simple buffer list.
|
||||
*
|
||||
* this thing here is basically a generic dynamic array
|
||||
* will realloc after every blockitems inserts
|
||||
* can store items of any size.
|
||||
*
|
||||
* so think of it as a by-value list, as opposed to a typical by-ref list.
|
||||
* you typically use it by having some struct on the stack, and pass a pointer
|
||||
* to sblist_add, which will copy the contents into its internal memory.
|
||||
*
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
size_t itemsize;
|
||||
size_t blockitems;
|
||||
size_t count;
|
||||
size_t capa;
|
||||
char* items;
|
||||
} sblist;
|
||||
|
||||
#define sblist_getsize(X) ((X)->count)
|
||||
#define sblist_get_count(X) ((X)->count)
|
||||
#define sblist_empty(X) ((X)->count == 0)
|
||||
|
||||
/* for dynamic style */
|
||||
sblist* sblist_new(size_t itemsize, size_t blockitems);
|
||||
void sblist_free(sblist* l);
|
||||
|
||||
/*for static style*/
|
||||
void sblist_init(sblist* l, size_t itemsize, size_t blockitems);
|
||||
void sblist_free_items(sblist* l);
|
||||
|
||||
/* accessors */
|
||||
void* sblist_get(sblist* l, size_t item);
|
||||
/* returns 1 on success, 0 on OOM */
|
||||
int sblist_add(sblist* l, void* item);
|
||||
int sblist_set(sblist* l, void* item, size_t pos);
|
||||
void sblist_delete(sblist* l, size_t item);
|
||||
char* sblist_item_from_index(sblist* l, size_t idx);
|
||||
int sblist_grow_if_needed(sblist* l);
|
||||
int sblist_insert(sblist* l, void* item, size_t pos);
|
||||
/* same as sblist_add, but returns list index of new item, or -1 */
|
||||
size_t sblist_addi(sblist* l, void* item);
|
||||
void sblist_sort(sblist *l, int (*compar)(const void *, const void *));
|
||||
/* insert element into presorted list, returns listindex of new entry or -1*/
|
||||
size_t sblist_insert_sorted(sblist* l, void* o, int (*compar)(const void *, const void *));
|
||||
|
||||
#ifndef __COUNTER__
|
||||
#define __COUNTER__ __LINE__
|
||||
#endif
|
||||
|
||||
#define __sblist_concat_impl( x, y ) x##y
|
||||
#define __sblist_macro_concat( x, y ) __sblist_concat_impl( x, y )
|
||||
#define __sblist_iterator_name __sblist_macro_concat(sblist_iterator, __COUNTER__)
|
||||
|
||||
/* use with custom iterator variable */
|
||||
#define sblist_iter_counter(LIST, ITER, PTR) \
|
||||
for(size_t ITER = 0; (PTR = sblist_get(LIST, ITER)), ITER < sblist_getsize(LIST); ITER++)
|
||||
|
||||
/* use with custom iterator variable, which is predeclared */
|
||||
#define sblist_iter_counter2(LIST, ITER, PTR) \
|
||||
for(ITER = 0; (PTR = sblist_get(LIST, ITER)), ITER < sblist_getsize(LIST); ITER++)
|
||||
|
||||
/* use with custom iterator variable, which is predeclared and signed */
|
||||
/* useful for a loop which can delete items from the list, and then decrease the iterator var. */
|
||||
#define sblist_iter_counter2s(LIST, ITER, PTR) \
|
||||
for(ITER = 0; (PTR = sblist_get(LIST, ITER)), ITER < (ssize_t) sblist_getsize(LIST); ITER++)
|
||||
|
||||
|
||||
/* uses "magic" iterator variable */
|
||||
#define sblist_iter(LIST, PTR) sblist_iter_counter(LIST, __sblist_iterator_name, PTR)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
82
src/sock.c
82
src/sock.c
@ -33,6 +33,18 @@
|
||||
#include "sock.h"
|
||||
#include "text.h"
|
||||
#include "conf.h"
|
||||
#include "loop.h"
|
||||
|
||||
/*
|
||||
* Return a human readable error for getaddrinfo() and getnameinfo().
|
||||
*/
|
||||
static const char * get_gai_error (int n)
|
||||
{
|
||||
if (n == EAI_SYSTEM)
|
||||
return strerror (errno);
|
||||
else
|
||||
return gai_strerror (n);
|
||||
}
|
||||
|
||||
/*
|
||||
* Bind the given socket to the supplied address. The socket is
|
||||
@ -43,6 +55,7 @@ static int
|
||||
bind_socket (int sockfd, const char *addr, int family)
|
||||
{
|
||||
struct addrinfo hints, *res, *ressave;
|
||||
int n;
|
||||
|
||||
assert (sockfd >= 0);
|
||||
assert (addr != NULL && strlen (addr) != 0);
|
||||
@ -51,9 +64,13 @@ bind_socket (int sockfd, const char *addr, int family)
|
||||
hints.ai_family = family;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
/* The local port it not important */
|
||||
if (getaddrinfo (addr, NULL, &hints, &res) != 0)
|
||||
/* The local port is not important */
|
||||
n = getaddrinfo (addr, NULL, &hints, &res);
|
||||
if (n != 0) {
|
||||
log_message (LOG_INFO,
|
||||
"bind_socket: getaddrinfo failed for %s: ", addr, get_gai_error (n));
|
||||
return -1;
|
||||
}
|
||||
|
||||
ressave = res;
|
||||
|
||||
@ -96,7 +113,7 @@ int opensock (const char *host, int port, const char *bind_to)
|
||||
n = getaddrinfo (host, portstr, &hints, &res);
|
||||
if (n != 0) {
|
||||
log_message (LOG_ERR,
|
||||
"opensock: Could not retrieve info for %s", host);
|
||||
"opensock: Could not retrieve address info for %s:%d: %s", host, port, get_gai_error (n));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -117,16 +134,25 @@ int opensock (const char *host, int port, const char *bind_to)
|
||||
close (sockfd);
|
||||
continue; /* can't bind, so try again */
|
||||
}
|
||||
} else if (config.bind_address) {
|
||||
if (bind_socket (sockfd, config.bind_address,
|
||||
} else if (config->bind_address) {
|
||||
if (bind_socket (sockfd, config->bind_address,
|
||||
res->ai_family) < 0) {
|
||||
close (sockfd);
|
||||
continue; /* can't bind, so try again */
|
||||
}
|
||||
}
|
||||
|
||||
if (connect (sockfd, res->ai_addr, res->ai_addrlen) == 0)
|
||||
if (connect (sockfd, res->ai_addr, res->ai_addrlen) == 0) {
|
||||
union sockaddr_union *p = (void*) res->ai_addr, u;
|
||||
int af = res->ai_addr->sa_family;
|
||||
unsigned dport = ntohs(af == AF_INET ? p->v4.sin_port : p->v6.sin6_port);
|
||||
socklen_t slen = sizeof u;
|
||||
if (dport == config->port) {
|
||||
getsockname(sockfd, (void*)&u, &slen);
|
||||
loop_records_add(&u);
|
||||
}
|
||||
break; /* success */
|
||||
}
|
||||
|
||||
close (sockfd);
|
||||
} while ((res = res->ai_next) != NULL);
|
||||
@ -134,8 +160,9 @@ int opensock (const char *host, int port, const char *bind_to)
|
||||
freeaddrinfo (ressave);
|
||||
if (res == NULL) {
|
||||
log_message (LOG_ERR,
|
||||
"opensock: Could not establish a connection to %s",
|
||||
host);
|
||||
"opensock: Could not establish a connection to %s:%d",
|
||||
host,
|
||||
port);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -186,8 +213,7 @@ static int listen_on_one_socket(struct addrinfo *ad)
|
||||
ret = getnameinfo(ad->ai_addr, ad->ai_addrlen,
|
||||
numerichost, NI_MAXHOST, NULL, 0, flags);
|
||||
if (ret != 0) {
|
||||
log_message(LOG_ERR, "error calling getnameinfo: %s",
|
||||
gai_strerror(errno));
|
||||
log_message(LOG_ERR, "getnameinfo failed: %s", get_gai_error (ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -256,6 +282,7 @@ int listen_sock (const char *addr, uint16_t port, vector_t listen_fds)
|
||||
struct addrinfo hints, *result, *rp;
|
||||
char portstr[6];
|
||||
int ret = -1;
|
||||
int n;
|
||||
|
||||
assert (port > 0);
|
||||
assert (listen_fds != NULL);
|
||||
@ -270,10 +297,13 @@ int listen_sock (const char *addr, uint16_t port, vector_t listen_fds)
|
||||
|
||||
snprintf (portstr, sizeof (portstr), "%d", port);
|
||||
|
||||
if (getaddrinfo (addr, portstr, &hints, &result) != 0) {
|
||||
n = getaddrinfo (addr, portstr, &hints, &result);
|
||||
if (n != 0) {
|
||||
log_message (LOG_ERR,
|
||||
"Unable to getaddrinfo() because of %s",
|
||||
strerror (errno));
|
||||
"Unable to getaddrinfo() for %s:%d because of %s",
|
||||
addr,
|
||||
port,
|
||||
get_gai_error (n));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -334,27 +364,9 @@ int getsock_ip (int fd, char *ipaddr)
|
||||
/*
|
||||
* Return the peer's socket information.
|
||||
*/
|
||||
int getpeer_information (int fd, char *ipaddr, char *string_addr)
|
||||
void getpeer_information (union sockaddr_union* addr, char *ipaddr, size_t ipaddr_len)
|
||||
{
|
||||
struct sockaddr_storage sa;
|
||||
socklen_t salen = sizeof sa;
|
||||
|
||||
assert (fd >= 0);
|
||||
assert (ipaddr != NULL);
|
||||
assert (string_addr != NULL);
|
||||
|
||||
/* Set the strings to default values */
|
||||
ipaddr[0] = '\0';
|
||||
strlcpy (string_addr, "[unknown]", HOSTNAME_LENGTH);
|
||||
|
||||
/* Look up the IP address */
|
||||
if (getpeername (fd, (struct sockaddr *) &sa, &salen) != 0)
|
||||
return -1;
|
||||
|
||||
if (get_ip_string ((struct sockaddr *) &sa, ipaddr, IP_LENGTH) == NULL)
|
||||
return -1;
|
||||
|
||||
/* Get the full host name */
|
||||
return getnameinfo ((struct sockaddr *) &sa, salen,
|
||||
string_addr, HOSTNAME_LENGTH, NULL, 0, 0);
|
||||
int af = addr->v4.sin_family;
|
||||
void *ipdata = af == AF_INET ? (void*)&addr->v4.sin_addr : (void*)&addr->v6.sin6_addr;
|
||||
inet_ntop(af, ipdata, ipaddr, ipaddr_len);
|
||||
}
|
||||
|
@ -28,8 +28,14 @@
|
||||
|
||||
#define MAXLINE (1024 * 4)
|
||||
|
||||
#include "common.h"
|
||||
#include "vector.h"
|
||||
|
||||
union sockaddr_union {
|
||||
struct sockaddr_in v4;
|
||||
struct sockaddr_in6 v6;
|
||||
};
|
||||
|
||||
extern int opensock (const char *host, int port, const char *bind_to);
|
||||
extern int listen_sock (const char *addr, uint16_t port, vector_t listen_fds);
|
||||
|
||||
@ -37,6 +43,6 @@ extern int socket_nonblocking (int sock);
|
||||
extern int socket_blocking (int sock);
|
||||
|
||||
extern int getsock_ip (int fd, char *ipaddr);
|
||||
extern int getpeer_information (int fd, char *ipaddr, char *string_addr);
|
||||
extern void getpeer_information (union sockaddr_union *addr, char *ipaddr, size_t ipaddr_len);
|
||||
|
||||
#endif
|
||||
|
29
src/stats.c
29
src/stats.c
@ -33,6 +33,7 @@
|
||||
#include "stats.h"
|
||||
#include "utils.h"
|
||||
#include "conf.h"
|
||||
#include <pthread.h>
|
||||
|
||||
struct stat_s {
|
||||
unsigned long int num_reqs;
|
||||
@ -43,14 +44,16 @@ struct stat_s {
|
||||
};
|
||||
|
||||
static struct stat_s *stats;
|
||||
static pthread_mutex_t stats_update_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_mutex_t stats_file_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/*
|
||||
* Initialize the statistics information to zero.
|
||||
*/
|
||||
void init_stats (void)
|
||||
{
|
||||
stats = (struct stat_s *) malloc_shared_memory (sizeof (struct stat_s));
|
||||
if (stats == MAP_FAILED)
|
||||
stats = (struct stat_s *) safemalloc (sizeof (struct stat_s));
|
||||
if (!stats)
|
||||
return;
|
||||
|
||||
memset (stats, 0, sizeof (struct stat_s));
|
||||
@ -72,10 +75,15 @@ showstats (struct conn_s *connptr)
|
||||
snprintf (denied, sizeof (denied), "%lu", stats->num_denied);
|
||||
snprintf (refused, sizeof (refused), "%lu", stats->num_refused);
|
||||
|
||||
if (!config.statpage || (!(statfile = fopen (config.statpage, "r")))) {
|
||||
pthread_mutex_lock(&stats_file_lock);
|
||||
|
||||
if (!config->statpage || (!(statfile = fopen (config->statpage, "r")))) {
|
||||
message_buffer = (char *) safemalloc (MAXBUFFSIZE);
|
||||
if (!message_buffer)
|
||||
if (!message_buffer) {
|
||||
err_minus_one:
|
||||
pthread_mutex_unlock(&stats_file_lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf
|
||||
(message_buffer, MAXBUFFSIZE,
|
||||
@ -105,13 +113,13 @@ showstats (struct conn_s *connptr)
|
||||
if (send_http_message (connptr, 200, "OK",
|
||||
message_buffer) < 0) {
|
||||
safefree (message_buffer);
|
||||
return -1;
|
||||
goto err_minus_one;
|
||||
}
|
||||
|
||||
safefree (message_buffer);
|
||||
pthread_mutex_unlock(&stats_file_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
add_error_variable (connptr, "opens", opens);
|
||||
add_error_variable (connptr, "reqs", reqs);
|
||||
add_error_variable (connptr, "badconns", badconns);
|
||||
@ -121,6 +129,7 @@ showstats (struct conn_s *connptr)
|
||||
send_http_headers (connptr, 200, "Statistic requested");
|
||||
send_html_file (statfile, connptr);
|
||||
fclose (statfile);
|
||||
pthread_mutex_unlock(&stats_file_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -131,6 +140,9 @@ showstats (struct conn_s *connptr)
|
||||
*/
|
||||
int update_stats (status_t update_level)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
pthread_mutex_lock(&stats_update_lock);
|
||||
switch (update_level) {
|
||||
case STAT_BADCONN:
|
||||
++stats->num_badcons;
|
||||
@ -149,8 +161,9 @@ int update_stats (status_t update_level)
|
||||
++stats->num_denied;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
ret = -1;
|
||||
}
|
||||
pthread_mutex_unlock(&stats_update_lock);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
@ -65,10 +65,11 @@ do_transparent_proxy (struct conn_s *connptr, hashmap_t hashofheaders,
|
||||
length = hashmap_entry_by_key (hashofheaders, "host", (void **) &data);
|
||||
if (length <= 0) {
|
||||
struct sockaddr_in dest_addr;
|
||||
length = sizeof(dest_addr);
|
||||
|
||||
if (getsockname
|
||||
(connptr->client_fd, (struct sockaddr *) &dest_addr,
|
||||
&length) < 0) {
|
||||
&length) < 0 || length > sizeof(dest_addr)) {
|
||||
log_message (LOG_ERR,
|
||||
"process_request: cannot get destination IP for %d",
|
||||
connptr->client_fd);
|
||||
|
@ -84,10 +84,6 @@ Logfile "$TINYPROXY_LOG_DIR/tinyproxy.log"
|
||||
PidFile "$TINYPROXY_PID_FILE"
|
||||
LogLevel Info
|
||||
MaxClients 100
|
||||
MinSpareServers 5
|
||||
MaxSpareServers 20
|
||||
StartServers 10
|
||||
MaxRequestsPerChild 0
|
||||
Allow 127.0.0.0/8
|
||||
ViaProxyName "tinyproxy"
|
||||
#DisableViaHeader Yes
|
||||
|
Loading…
Reference in New Issue
Block a user