Reformat code to GNU coding style
This is a commit which simply ran all C source code files through GNU indent. No other modifications were made.
This commit is contained in:
parent
448c19077c
commit
a257703e59
447
src/acl.c
447
src/acl.c
@ -33,23 +33,27 @@
|
|||||||
/* Define how long an IPv6 address is in bytes (128 bits, 16 bytes) */
|
/* Define how long an IPv6 address is in bytes (128 bits, 16 bytes) */
|
||||||
#define IPV6_LEN 16
|
#define IPV6_LEN 16
|
||||||
|
|
||||||
enum acl_type { ACL_STRING, ACL_NUMERIC };
|
enum acl_type
|
||||||
|
{ ACL_STRING, ACL_NUMERIC };
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hold the information about a particular access control. We store
|
* Hold the information about a particular access control. We store
|
||||||
* whether it's an ALLOW or DENY entry, and also whether it's a string
|
* whether it's an ALLOW or DENY entry, and also whether it's a string
|
||||||
* entry (like a domain name) or an IP entry.
|
* entry (like a domain name) or an IP entry.
|
||||||
*/
|
*/
|
||||||
struct acl_s {
|
struct acl_s
|
||||||
acl_access_t access;
|
{
|
||||||
enum acl_type type;
|
acl_access_t access;
|
||||||
union {
|
enum acl_type type;
|
||||||
char *string;
|
union
|
||||||
struct {
|
{
|
||||||
unsigned char octet[IPV6_LEN];
|
char *string;
|
||||||
unsigned char mask[IPV6_LEN];
|
struct
|
||||||
} ip;
|
{
|
||||||
} address;
|
unsigned char octet[IPV6_LEN];
|
||||||
|
unsigned char mask[IPV6_LEN];
|
||||||
|
} ip;
|
||||||
|
} address;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -67,41 +71,45 @@ static vector_t access_list = NULL;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
inline static int
|
inline static int
|
||||||
fill_netmask_array(char *bitmask_string, unsigned char array[], unsigned int len)
|
fill_netmask_array (char *bitmask_string, unsigned char array[],
|
||||||
|
unsigned int len)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
long int mask;
|
long int mask;
|
||||||
char *endptr;
|
char *endptr;
|
||||||
|
|
||||||
errno = 0; /* to distinguish success/failure after call */
|
errno = 0; /* to distinguish success/failure after call */
|
||||||
mask = strtol(bitmask_string, &endptr, 10);
|
mask = strtol (bitmask_string, &endptr, 10);
|
||||||
|
|
||||||
/* check for various conversion errors */
|
/* check for various conversion errors */
|
||||||
if ((errno == ERANGE && (mask == LONG_MIN || mask == LONG_MAX))
|
if ((errno == ERANGE && (mask == LONG_MIN || mask == LONG_MAX))
|
||||||
|| (errno != 0 && mask == 0)
|
|| (errno != 0 && mask == 0) || (endptr == bitmask_string))
|
||||||
|| (endptr == bitmask_string))
|
return -1;
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* valid range for a bit mask */
|
/* valid range for a bit mask */
|
||||||
if (mask < 0 || mask > (8 * len))
|
if (mask < 0 || mask > (8 * len))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* we have a valid range to fill in the array */
|
/* we have a valid range to fill in the array */
|
||||||
for (i = 0; i != len; ++i) {
|
for (i = 0; i != len; ++i)
|
||||||
if (mask >= 8) {
|
{
|
||||||
array[i] = 0xff;
|
if (mask >= 8)
|
||||||
mask -= 8;
|
{
|
||||||
}
|
array[i] = 0xff;
|
||||||
else if (mask > 0) {
|
mask -= 8;
|
||||||
array[i] = (unsigned char)(0xff << (8 - mask));
|
}
|
||||||
mask = 0;
|
else if (mask > 0)
|
||||||
}
|
{
|
||||||
else {
|
array[i] = (unsigned char) (0xff << (8 - mask));
|
||||||
array[i] = 0;
|
mask = 0;
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
|
array[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -115,74 +123,82 @@ fill_netmask_array(char *bitmask_string, unsigned char array[], unsigned int len
|
|||||||
* 0 otherwise.
|
* 0 otherwise.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
insert_acl(char *location, acl_access_t access_type)
|
insert_acl (char *location, acl_access_t access_type)
|
||||||
{
|
{
|
||||||
struct acl_s acl;
|
struct acl_s acl;
|
||||||
int ret;
|
int ret;
|
||||||
char *p, ip_dst[IPV6_LEN];
|
char *p, ip_dst[IPV6_LEN];
|
||||||
|
|
||||||
assert(location != NULL);
|
assert (location != NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the access list has not been set up, create it.
|
* If the access list has not been set up, create it.
|
||||||
*/
|
*/
|
||||||
if (!access_list) {
|
if (!access_list)
|
||||||
access_list = vector_create();
|
{
|
||||||
if (!access_list) {
|
access_list = vector_create ();
|
||||||
log_message(LOG_ERR,
|
if (!access_list)
|
||||||
"Unable to allocate memory for access list");
|
{
|
||||||
return -1;
|
log_message (LOG_ERR, "Unable to allocate memory for access list");
|
||||||
}
|
return -1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start populating the access control structure.
|
* Start populating the access control structure.
|
||||||
*/
|
*/
|
||||||
memset(&acl, 0, sizeof(struct acl_s));
|
memset (&acl, 0, sizeof (struct acl_s));
|
||||||
acl.access = access_type;
|
acl.access = access_type;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check for a valid IP address (the simplest case) first.
|
* Check for a valid IP address (the simplest case) first.
|
||||||
*/
|
*/
|
||||||
if (full_inet_pton(location, ip_dst) > 0) {
|
if (full_inet_pton (location, ip_dst) > 0)
|
||||||
acl.type = ACL_NUMERIC;
|
{
|
||||||
memcpy(acl.address.ip.octet, ip_dst, IPV6_LEN);
|
acl.type = ACL_NUMERIC;
|
||||||
memset(acl.address.ip.mask, 0xff, IPV6_LEN);
|
memcpy (acl.address.ip.octet, ip_dst, IPV6_LEN);
|
||||||
} else {
|
memset (acl.address.ip.mask, 0xff, IPV6_LEN);
|
||||||
/*
|
}
|
||||||
* At this point we're either a hostname or an
|
else
|
||||||
* IP address with a slash.
|
{
|
||||||
*/
|
/*
|
||||||
p = strchr(location, '/');
|
* At this point we're either a hostname or an
|
||||||
if (p != NULL) {
|
* IP address with a slash.
|
||||||
/*
|
*/
|
||||||
* We have a slash, so it's intended to be an
|
p = strchr (location, '/');
|
||||||
* IP address with mask
|
if (p != NULL)
|
||||||
*/
|
{
|
||||||
*p = '\0';
|
/*
|
||||||
if (full_inet_pton(location, ip_dst) <= 0)
|
* We have a slash, so it's intended to be an
|
||||||
return -1;
|
* IP address with mask
|
||||||
|
*/
|
||||||
|
*p = '\0';
|
||||||
|
if (full_inet_pton (location, ip_dst) <= 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
acl.type = ACL_NUMERIC;
|
acl.type = ACL_NUMERIC;
|
||||||
memcpy(acl.address.ip.octet, ip_dst, IPV6_LEN);
|
memcpy (acl.address.ip.octet, ip_dst, IPV6_LEN);
|
||||||
|
|
||||||
if (fill_netmask_array(p + 1, &(acl.address.ip.mask[0]), IPV6_LEN) < 0)
|
if (fill_netmask_array (p + 1, &(acl.address.ip.mask[0]), IPV6_LEN)
|
||||||
return -1;
|
< 0)
|
||||||
} else {
|
return -1;
|
||||||
/* In all likelihood a string */
|
}
|
||||||
acl.type = ACL_STRING;
|
else
|
||||||
acl.address.string = safestrdup(location);
|
{
|
||||||
if (!acl.address.string)
|
/* In all likelihood a string */
|
||||||
return -1;
|
acl.type = ACL_STRING;
|
||||||
}
|
acl.address.string = safestrdup (location);
|
||||||
}
|
if (!acl.address.string)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add the entry and then clean up.
|
* Add the entry and then clean up.
|
||||||
*/
|
*/
|
||||||
ret = vector_append(access_list, &acl, sizeof(struct acl_s));
|
ret = vector_append (access_list, &acl, sizeof (struct acl_s));
|
||||||
safefree(acl.address.string);
|
safefree (acl.address.string);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -195,73 +211,79 @@ insert_acl(char *location, acl_access_t access_type)
|
|||||||
* -1 if no tests match, so skip
|
* -1 if no tests match, so skip
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
acl_string_processing(struct acl_s *acl,
|
acl_string_processing (struct acl_s *acl,
|
||||||
const char *ip_address, const char *string_address)
|
const char *ip_address, const char *string_address)
|
||||||
{
|
{
|
||||||
int match;
|
int match;
|
||||||
struct addrinfo hints, *res, *ressave;
|
struct addrinfo hints, *res, *ressave;
|
||||||
size_t test_length, match_length;
|
size_t test_length, match_length;
|
||||||
char ipbuf[512];
|
char ipbuf[512];
|
||||||
|
|
||||||
assert(acl && acl->type == ACL_STRING);
|
assert (acl && acl->type == ACL_STRING);
|
||||||
assert(ip_address && strlen(ip_address) > 0);
|
assert (ip_address && strlen (ip_address) > 0);
|
||||||
assert(string_address && strlen(string_address) > 0);
|
assert (string_address && strlen (string_address) > 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the first character of the ACL string is a period, we need to
|
* If the first character of the ACL string is a period, we need to
|
||||||
* do a string based test only; otherwise, we can do a reverse
|
* do a string based test only; otherwise, we can do a reverse
|
||||||
* lookup test as well.
|
* lookup test as well.
|
||||||
*/
|
*/
|
||||||
if (acl->address.string[0] != '.') {
|
if (acl->address.string[0] != '.')
|
||||||
memset(&hints, 0, sizeof(struct addrinfo));
|
{
|
||||||
hints.ai_family = AF_UNSPEC;
|
memset (&hints, 0, sizeof (struct addrinfo));
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
hints.ai_family = AF_UNSPEC;
|
||||||
if (getaddrinfo(acl->address.string, NULL, &hints, &res) != 0)
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
goto STRING_TEST;
|
if (getaddrinfo (acl->address.string, NULL, &hints, &res) != 0)
|
||||||
|
goto STRING_TEST;
|
||||||
|
|
||||||
ressave = res;
|
ressave = res;
|
||||||
|
|
||||||
match = FALSE;
|
match = FALSE;
|
||||||
do {
|
do
|
||||||
get_ip_string(res->ai_addr, ipbuf, sizeof(ipbuf));
|
{
|
||||||
if (strcmp(ip_address, ipbuf) == 0) {
|
get_ip_string (res->ai_addr, ipbuf, sizeof (ipbuf));
|
||||||
match = TRUE;
|
if (strcmp (ip_address, ipbuf) == 0)
|
||||||
break;
|
{
|
||||||
}
|
match = TRUE;
|
||||||
} while ((res = res->ai_next) != NULL);
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while ((res = res->ai_next) != NULL);
|
||||||
|
|
||||||
freeaddrinfo(ressave);
|
freeaddrinfo (ressave);
|
||||||
|
|
||||||
if (match) {
|
if (match)
|
||||||
if (acl->access == ACL_DENY)
|
{
|
||||||
return 0;
|
if (acl->access == ACL_DENY)
|
||||||
else
|
return 0;
|
||||||
return 1;
|
else
|
||||||
}
|
return 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
STRING_TEST:
|
STRING_TEST:
|
||||||
test_length = strlen(string_address);
|
test_length = strlen (string_address);
|
||||||
match_length = strlen(acl->address.string);
|
match_length = strlen (acl->address.string);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the string length is shorter than AC string, return a -1 so
|
* If the string length is shorter than AC string, return a -1 so
|
||||||
* that the "driver" will skip onto the next control in the list.
|
* that the "driver" will skip onto the next control in the list.
|
||||||
*/
|
*/
|
||||||
if (test_length < match_length)
|
if (test_length < match_length)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (strcasecmp
|
if (strcasecmp
|
||||||
(string_address + (test_length - match_length),
|
(string_address + (test_length - match_length),
|
||||||
acl->address.string) == 0) {
|
acl->address.string) == 0)
|
||||||
if (acl->access == ACL_DENY)
|
{
|
||||||
return 0;
|
if (acl->access == ACL_DENY)
|
||||||
else
|
return 0;
|
||||||
return 1;
|
else
|
||||||
}
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Indicate that no tests succeeded, so skip to next control. */
|
/* Indicate that no tests succeeded, so skip to next control. */
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -273,28 +295,29 @@ acl_string_processing(struct acl_s *acl,
|
|||||||
* -1 neither allowed nor denied.
|
* -1 neither allowed nor denied.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
check_numeric_acl(const struct acl_s *acl, const char *ip)
|
check_numeric_acl (const struct acl_s *acl, const char *ip)
|
||||||
{
|
{
|
||||||
uint8_t addr[IPV6_LEN], x, y;
|
uint8_t addr[IPV6_LEN], x, y;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
assert(acl && acl->type == ACL_NUMERIC);
|
assert (acl && acl->type == ACL_NUMERIC);
|
||||||
assert(ip && strlen(ip) > 0);
|
assert (ip && strlen (ip) > 0);
|
||||||
|
|
||||||
if (full_inet_pton(ip, &addr) <= 0)
|
if (full_inet_pton (ip, &addr) <= 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
for (i = 0; i != IPV6_LEN; ++i) {
|
for (i = 0; i != IPV6_LEN; ++i)
|
||||||
x = addr[i] & acl->address.ip.mask[i];
|
{
|
||||||
y = acl->address.ip.octet[i] & acl->address.ip.mask[i];
|
x = addr[i] & acl->address.ip.mask[i];
|
||||||
|
y = acl->address.ip.octet[i] & acl->address.ip.mask[i];
|
||||||
|
|
||||||
/* If x and y don't match, the IP addresses don't match */
|
/* If x and y don't match, the IP addresses don't match */
|
||||||
if (x != y)
|
if (x != y)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The addresses match, return the permission */
|
/* The addresses match, return the permission */
|
||||||
return (acl->access == ACL_ALLOW);
|
return (acl->access == ACL_ALLOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -305,50 +328,52 @@ check_numeric_acl(const struct acl_s *acl, const char *ip)
|
|||||||
* 0 if denied
|
* 0 if denied
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
check_acl(int fd, const char *ip, const char *host)
|
check_acl (int fd, const char *ip, const char *host)
|
||||||
{
|
{
|
||||||
struct acl_s *acl;
|
struct acl_s *acl;
|
||||||
int perm;
|
int perm;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
assert(fd >= 0);
|
assert (fd >= 0);
|
||||||
assert(ip != NULL);
|
assert (ip != NULL);
|
||||||
assert(host != NULL);
|
assert (host != NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If there is no access list allow everything.
|
* If there is no access list allow everything.
|
||||||
*/
|
*/
|
||||||
if (!access_list)
|
if (!access_list)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
for (i = 0; i != vector_length(access_list); ++i) {
|
for (i = 0; i != vector_length (access_list); ++i)
|
||||||
acl = vector_getentry(access_list, i, NULL);
|
{
|
||||||
switch (acl->type) {
|
acl = vector_getentry (access_list, i, NULL);
|
||||||
case ACL_STRING:
|
switch (acl->type)
|
||||||
perm = acl_string_processing(acl, ip, host);
|
{
|
||||||
break;
|
case ACL_STRING:
|
||||||
|
perm = acl_string_processing (acl, ip, host);
|
||||||
|
break;
|
||||||
|
|
||||||
case ACL_NUMERIC:
|
case ACL_NUMERIC:
|
||||||
if (ip[0] == '\0')
|
if (ip[0] == '\0')
|
||||||
continue;
|
continue;
|
||||||
perm = check_numeric_acl(acl, ip);
|
perm = check_numeric_acl (acl, ip);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check the return value too see if the IP address is
|
* Check the return value too see if the IP address is
|
||||||
* allowed or denied.
|
* allowed or denied.
|
||||||
*/
|
*/
|
||||||
if (perm == 0)
|
if (perm == 0)
|
||||||
break;
|
break;
|
||||||
else if (perm == 1)
|
else if (perm == 1)
|
||||||
return perm;
|
return perm;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Deny all connections by default.
|
* Deny all connections by default.
|
||||||
*/
|
*/
|
||||||
log_message(LOG_NOTICE, "Unauthorized connection from \"%s\" [%s].",
|
log_message (LOG_NOTICE, "Unauthorized connection from \"%s\" [%s].",
|
||||||
host, ip);
|
host, ip);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -21,10 +21,11 @@
|
|||||||
#ifndef TINYPROXY_ACL_H
|
#ifndef TINYPROXY_ACL_H
|
||||||
#define TINYPROXY_ACL_H
|
#define TINYPROXY_ACL_H
|
||||||
|
|
||||||
typedef enum { ACL_ALLOW, ACL_DENY } acl_access_t;
|
typedef enum
|
||||||
|
{ ACL_ALLOW, ACL_DENY } acl_access_t;
|
||||||
|
|
||||||
extern int insert_acl(char *location, acl_access_t access_type);
|
extern int insert_acl (char *location, acl_access_t access_type);
|
||||||
extern int check_acl(int fd, const char *ip_address,
|
extern int check_acl (int fd, const char *ip_address,
|
||||||
const char *string_address);
|
const char *string_address);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -30,9 +30,9 @@
|
|||||||
static hashmap_t anonymous_map = NULL;
|
static hashmap_t anonymous_map = NULL;
|
||||||
|
|
||||||
short int
|
short int
|
||||||
is_anonymous_enabled(void)
|
is_anonymous_enabled (void)
|
||||||
{
|
{
|
||||||
return (anonymous_map != NULL) ? 1 : 0;
|
return (anonymous_map != NULL) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -40,12 +40,12 @@ is_anonymous_enabled(void)
|
|||||||
* zero if the string was found, zero if it wasn't and negative upon error.
|
* zero if the string was found, zero if it wasn't and negative upon error.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
anonymous_search(char *s)
|
anonymous_search (char *s)
|
||||||
{
|
{
|
||||||
assert(s != NULL);
|
assert (s != NULL);
|
||||||
assert(anonymous_map != NULL);
|
assert (anonymous_map != NULL);
|
||||||
|
|
||||||
return hashmap_search(anonymous_map, s);
|
return hashmap_search (anonymous_map, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -55,23 +55,25 @@ anonymous_search(char *s)
|
|||||||
* successful.
|
* successful.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
anonymous_insert(char *s)
|
anonymous_insert (char *s)
|
||||||
{
|
{
|
||||||
char data = 1;
|
char data = 1;
|
||||||
|
|
||||||
assert(s != NULL);
|
assert (s != NULL);
|
||||||
|
|
||||||
if (!anonymous_map) {
|
if (!anonymous_map)
|
||||||
anonymous_map = hashmap_create(32);
|
{
|
||||||
if (!anonymous_map)
|
anonymous_map = hashmap_create (32);
|
||||||
return -1;
|
if (!anonymous_map)
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (hashmap_search(anonymous_map, s) > 0) {
|
if (hashmap_search (anonymous_map, s) > 0)
|
||||||
/* The key was already found, so return a positive number. */
|
{
|
||||||
return 0;
|
/* The key was already found, so return a positive number. */
|
||||||
}
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Insert the new key */
|
/* Insert the new key */
|
||||||
return hashmap_insert(anonymous_map, s, &data, sizeof(data));
|
return hashmap_insert (anonymous_map, s, &data, sizeof (data));
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,8 @@
|
|||||||
#ifndef _TINYPROXY_ANONYMOUS_H_
|
#ifndef _TINYPROXY_ANONYMOUS_H_
|
||||||
#define _TINYPROXY_ANONYMOUS_H_
|
#define _TINYPROXY_ANONYMOUS_H_
|
||||||
|
|
||||||
extern short int is_anonymous_enabled(void);
|
extern short int is_anonymous_enabled (void);
|
||||||
extern int anonymous_search(char *s);
|
extern int anonymous_search (char *s);
|
||||||
extern int anonymous_insert(char *s);
|
extern int anonymous_insert (char *s);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
357
src/buffer.c
357
src/buffer.c
@ -34,21 +34,23 @@
|
|||||||
#define BUFFER_HEAD(x) (x)->head
|
#define BUFFER_HEAD(x) (x)->head
|
||||||
#define BUFFER_TAIL(x) (x)->tail
|
#define BUFFER_TAIL(x) (x)->tail
|
||||||
|
|
||||||
struct bufline_s {
|
struct bufline_s
|
||||||
unsigned char *string; /* the actual string of data */
|
{
|
||||||
struct bufline_s *next; /* pointer to next in linked list */
|
unsigned char *string; /* the actual string of data */
|
||||||
size_t length; /* length of the string of data */
|
struct bufline_s *next; /* pointer to next in linked list */
|
||||||
size_t pos; /* start sending from this offset */
|
size_t length; /* length of the string of data */
|
||||||
|
size_t pos; /* start sending from this offset */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The buffer structure points to the beginning and end of the buffer list
|
* The buffer structure points to the beginning and end of the buffer list
|
||||||
* (and includes the total size)
|
* (and includes the total size)
|
||||||
*/
|
*/
|
||||||
struct buffer_s {
|
struct buffer_s
|
||||||
struct bufline_s *head; /* top of the buffer */
|
{
|
||||||
struct bufline_s *tail; /* bottom of the buffer */
|
struct bufline_s *head; /* top of the buffer */
|
||||||
size_t size; /* total size of the buffer */
|
struct bufline_s *tail; /* bottom of the buffer */
|
||||||
|
size_t size; /* total size of the buffer */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -57,155 +59,158 @@ struct buffer_s {
|
|||||||
* data buffer on the heap, delete it because you now have TWO copies.
|
* data buffer on the heap, delete it because you now have TWO copies.
|
||||||
*/
|
*/
|
||||||
static struct bufline_s *
|
static struct bufline_s *
|
||||||
makenewline(unsigned char *data, size_t length)
|
makenewline (unsigned char *data, size_t length)
|
||||||
{
|
{
|
||||||
struct bufline_s *newline;
|
struct bufline_s *newline;
|
||||||
|
|
||||||
assert(data != NULL);
|
assert (data != NULL);
|
||||||
assert(length > 0);
|
assert (length > 0);
|
||||||
|
|
||||||
if (!(newline = safemalloc(sizeof(struct bufline_s))))
|
if (!(newline = safemalloc (sizeof (struct bufline_s))))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (!(newline->string = safemalloc(length))) {
|
if (!(newline->string = safemalloc (length)))
|
||||||
safefree(newline);
|
{
|
||||||
return NULL;
|
safefree (newline);
|
||||||
}
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
memcpy(newline->string, data, length);
|
memcpy (newline->string, data, length);
|
||||||
|
|
||||||
newline->next = NULL;
|
newline->next = NULL;
|
||||||
newline->length = length;
|
newline->length = length;
|
||||||
|
|
||||||
/* Position our "read" pointer at the beginning of the data */
|
/* Position our "read" pointer at the beginning of the data */
|
||||||
newline->pos = 0;
|
newline->pos = 0;
|
||||||
|
|
||||||
return newline;
|
return newline;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Free the allocated buffer line
|
* Free the allocated buffer line
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
free_line(struct bufline_s *line)
|
free_line (struct bufline_s *line)
|
||||||
{
|
{
|
||||||
assert(line != NULL);
|
assert (line != NULL);
|
||||||
|
|
||||||
if (!line)
|
if (!line)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (line->string)
|
if (line->string)
|
||||||
safefree(line->string);
|
safefree (line->string);
|
||||||
|
|
||||||
safefree(line);
|
safefree (line);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a new buffer
|
* Create a new buffer
|
||||||
*/
|
*/
|
||||||
struct buffer_s *
|
struct buffer_s *
|
||||||
new_buffer(void)
|
new_buffer (void)
|
||||||
{
|
{
|
||||||
struct buffer_s *buffptr;
|
struct buffer_s *buffptr;
|
||||||
|
|
||||||
if (!(buffptr = safemalloc(sizeof(struct buffer_s))))
|
if (!(buffptr = safemalloc (sizeof (struct buffer_s))))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since the buffer is initially empty, set the HEAD and TAIL
|
* Since the buffer is initially empty, set the HEAD and TAIL
|
||||||
* pointers to NULL since they can't possibly point anywhere at the
|
* pointers to NULL since they can't possibly point anywhere at the
|
||||||
* moment.
|
* moment.
|
||||||
*/
|
*/
|
||||||
BUFFER_HEAD(buffptr) = BUFFER_TAIL(buffptr) = NULL;
|
BUFFER_HEAD (buffptr) = BUFFER_TAIL (buffptr) = NULL;
|
||||||
buffptr->size = 0;
|
buffptr->size = 0;
|
||||||
|
|
||||||
return buffptr;
|
return buffptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Delete all the lines in the buffer and the buffer itself
|
* Delete all the lines in the buffer and the buffer itself
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
delete_buffer(struct buffer_s *buffptr)
|
delete_buffer (struct buffer_s *buffptr)
|
||||||
{
|
{
|
||||||
struct bufline_s *next;
|
struct bufline_s *next;
|
||||||
|
|
||||||
assert(buffptr != NULL);
|
assert (buffptr != NULL);
|
||||||
|
|
||||||
while (BUFFER_HEAD(buffptr)) {
|
while (BUFFER_HEAD (buffptr))
|
||||||
next = BUFFER_HEAD(buffptr)->next;
|
{
|
||||||
free_line(BUFFER_HEAD(buffptr));
|
next = BUFFER_HEAD (buffptr)->next;
|
||||||
BUFFER_HEAD(buffptr) = next;
|
free_line (BUFFER_HEAD (buffptr));
|
||||||
}
|
BUFFER_HEAD (buffptr) = next;
|
||||||
|
}
|
||||||
|
|
||||||
safefree(buffptr);
|
safefree (buffptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return the current size of the buffer.
|
* Return the current size of the buffer.
|
||||||
*/
|
*/
|
||||||
size_t
|
size_t
|
||||||
buffer_size(struct buffer_s *buffptr)
|
buffer_size (struct buffer_s *buffptr)
|
||||||
{
|
{
|
||||||
return buffptr->size;
|
return buffptr->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Push a new line on to the end of the buffer.
|
* Push a new line on to the end of the buffer.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
add_to_buffer(struct buffer_s *buffptr, unsigned char *data, size_t length)
|
add_to_buffer (struct buffer_s *buffptr, unsigned char *data, size_t length)
|
||||||
{
|
{
|
||||||
struct bufline_s *newline;
|
struct bufline_s *newline;
|
||||||
|
|
||||||
assert(buffptr != NULL);
|
assert (buffptr != NULL);
|
||||||
assert(data != NULL);
|
assert (data != NULL);
|
||||||
assert(length > 0);
|
assert (length > 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sanity check here. A buffer with a non-NULL head pointer must
|
* Sanity check here. A buffer with a non-NULL head pointer must
|
||||||
* have a size greater than zero, and vice-versa.
|
* have a size greater than zero, and vice-versa.
|
||||||
*/
|
*/
|
||||||
if (BUFFER_HEAD(buffptr) == NULL)
|
if (BUFFER_HEAD (buffptr) == NULL)
|
||||||
assert(buffptr->size == 0);
|
assert (buffptr->size == 0);
|
||||||
else
|
else
|
||||||
assert(buffptr->size > 0);
|
assert (buffptr->size > 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make a new line so we can add it to the buffer.
|
* Make a new line so we can add it to the buffer.
|
||||||
*/
|
*/
|
||||||
if (!(newline = makenewline(data, length)))
|
if (!(newline = makenewline (data, length)))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (buffptr->size == 0)
|
if (buffptr->size == 0)
|
||||||
BUFFER_HEAD(buffptr) = BUFFER_TAIL(buffptr) = newline;
|
BUFFER_HEAD (buffptr) = BUFFER_TAIL (buffptr) = newline;
|
||||||
else {
|
else
|
||||||
BUFFER_TAIL(buffptr)->next = newline;
|
{
|
||||||
BUFFER_TAIL(buffptr) = newline;
|
BUFFER_TAIL (buffptr)->next = newline;
|
||||||
}
|
BUFFER_TAIL (buffptr) = newline;
|
||||||
|
}
|
||||||
|
|
||||||
buffptr->size += length;
|
buffptr->size += length;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remove the first line from the top of the buffer
|
* Remove the first line from the top of the buffer
|
||||||
*/
|
*/
|
||||||
static struct bufline_s *
|
static struct bufline_s *
|
||||||
remove_from_buffer(struct buffer_s *buffptr)
|
remove_from_buffer (struct buffer_s *buffptr)
|
||||||
{
|
{
|
||||||
struct bufline_s *line;
|
struct bufline_s *line;
|
||||||
|
|
||||||
assert(buffptr != NULL);
|
assert (buffptr != NULL);
|
||||||
assert(BUFFER_HEAD(buffptr) != NULL);
|
assert (BUFFER_HEAD (buffptr) != NULL);
|
||||||
|
|
||||||
line = BUFFER_HEAD(buffptr);
|
line = BUFFER_HEAD (buffptr);
|
||||||
BUFFER_HEAD(buffptr) = line->next;
|
BUFFER_HEAD (buffptr) = line->next;
|
||||||
|
|
||||||
buffptr->size -= line->length;
|
buffptr->size -= line->length;
|
||||||
|
|
||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -214,61 +219,69 @@ remove_from_buffer(struct buffer_s *buffptr)
|
|||||||
*/
|
*/
|
||||||
#define READ_BUFFER_SIZE (1024 * 2)
|
#define READ_BUFFER_SIZE (1024 * 2)
|
||||||
ssize_t
|
ssize_t
|
||||||
read_buffer(int fd, struct buffer_s * buffptr)
|
read_buffer (int fd, struct buffer_s * buffptr)
|
||||||
{
|
{
|
||||||
ssize_t bytesin;
|
ssize_t bytesin;
|
||||||
unsigned char *buffer;
|
unsigned char *buffer;
|
||||||
|
|
||||||
assert(fd >= 0);
|
assert (fd >= 0);
|
||||||
assert(buffptr != NULL);
|
assert (buffptr != NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Don't allow the buffer to grow larger than MAXBUFFSIZE
|
* Don't allow the buffer to grow larger than MAXBUFFSIZE
|
||||||
*/
|
*/
|
||||||
if (buffptr->size >= MAXBUFFSIZE)
|
if (buffptr->size >= MAXBUFFSIZE)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
buffer = safemalloc(READ_BUFFER_SIZE);
|
buffer = safemalloc (READ_BUFFER_SIZE);
|
||||||
if (!buffer) {
|
if (!buffer)
|
||||||
return -ENOMEM;
|
{
|
||||||
}
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
bytesin = read(fd, buffer, READ_BUFFER_SIZE);
|
bytesin = read (fd, buffer, READ_BUFFER_SIZE);
|
||||||
|
|
||||||
if (bytesin > 0) {
|
if (bytesin > 0)
|
||||||
if (add_to_buffer(buffptr, buffer, bytesin) < 0) {
|
{
|
||||||
log_message(LOG_ERR,
|
if (add_to_buffer (buffptr, buffer, bytesin) < 0)
|
||||||
"readbuff: add_to_buffer() error.");
|
{
|
||||||
bytesin = -1;
|
log_message (LOG_ERR, "readbuff: add_to_buffer() error.");
|
||||||
}
|
bytesin = -1;
|
||||||
} else {
|
}
|
||||||
if (bytesin == 0) {
|
}
|
||||||
/* connection was closed by client */
|
else
|
||||||
bytesin = -1;
|
{
|
||||||
} else {
|
if (bytesin == 0)
|
||||||
switch (errno) {
|
{
|
||||||
|
/* connection was closed by client */
|
||||||
|
bytesin = -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (errno)
|
||||||
|
{
|
||||||
#ifdef EWOULDBLOCK
|
#ifdef EWOULDBLOCK
|
||||||
case EWOULDBLOCK:
|
case EWOULDBLOCK:
|
||||||
#else
|
#else
|
||||||
# ifdef EAGAIN
|
# ifdef EAGAIN
|
||||||
case EAGAIN:
|
case EAGAIN:
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
case EINTR:
|
case EINTR:
|
||||||
bytesin = 0;
|
bytesin = 0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
log_message(LOG_ERR,
|
log_message (LOG_ERR,
|
||||||
"readbuff: recv() error \"%s\" on file descriptor %d",
|
"readbuff: recv() error \"%s\" on file descriptor %d",
|
||||||
strerror(errno), fd);
|
strerror (errno), fd);
|
||||||
bytesin = -1;
|
bytesin = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
safefree(buffer);
|
safefree (buffer);
|
||||||
return bytesin;
|
return bytesin;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -276,53 +289,57 @@ read_buffer(int fd, struct buffer_s * buffptr)
|
|||||||
* Takes a connection and returns the number of bytes written.
|
* Takes a connection and returns the number of bytes written.
|
||||||
*/
|
*/
|
||||||
ssize_t
|
ssize_t
|
||||||
write_buffer(int fd, struct buffer_s * buffptr)
|
write_buffer (int fd, struct buffer_s * buffptr)
|
||||||
{
|
{
|
||||||
ssize_t bytessent;
|
ssize_t bytessent;
|
||||||
struct bufline_s *line;
|
struct bufline_s *line;
|
||||||
|
|
||||||
assert(fd >= 0);
|
assert (fd >= 0);
|
||||||
assert(buffptr != NULL);
|
assert (buffptr != NULL);
|
||||||
|
|
||||||
if (buffptr->size == 0)
|
if (buffptr->size == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Sanity check. It would be bad to be using a NULL pointer! */
|
/* Sanity check. It would be bad to be using a NULL pointer! */
|
||||||
assert(BUFFER_HEAD(buffptr) != NULL);
|
assert (BUFFER_HEAD (buffptr) != NULL);
|
||||||
line = BUFFER_HEAD(buffptr);
|
line = BUFFER_HEAD (buffptr);
|
||||||
|
|
||||||
bytessent =
|
bytessent =
|
||||||
send(fd, line->string + line->pos, line->length - line->pos,
|
send (fd, line->string + line->pos, line->length - line->pos,
|
||||||
MSG_NOSIGNAL);
|
MSG_NOSIGNAL);
|
||||||
|
|
||||||
if (bytessent >= 0) {
|
if (bytessent >= 0)
|
||||||
/* bytes sent, adjust buffer */
|
{
|
||||||
line->pos += bytessent;
|
/* bytes sent, adjust buffer */
|
||||||
if (line->pos == line->length)
|
line->pos += bytessent;
|
||||||
free_line(remove_from_buffer(buffptr));
|
if (line->pos == line->length)
|
||||||
return bytessent;
|
free_line (remove_from_buffer (buffptr));
|
||||||
} else {
|
return bytessent;
|
||||||
switch (errno) {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (errno)
|
||||||
|
{
|
||||||
#ifdef EWOULDBLOCK
|
#ifdef EWOULDBLOCK
|
||||||
case EWOULDBLOCK:
|
case EWOULDBLOCK:
|
||||||
#else
|
#else
|
||||||
# ifdef EAGAIN
|
# ifdef EAGAIN
|
||||||
case EAGAIN:
|
case EAGAIN:
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
case EINTR:
|
case EINTR:
|
||||||
return 0;
|
return 0;
|
||||||
case ENOBUFS:
|
case ENOBUFS:
|
||||||
case ENOMEM:
|
case ENOMEM:
|
||||||
log_message(LOG_ERR,
|
log_message (LOG_ERR,
|
||||||
"writebuff: write() error [NOBUFS/NOMEM] \"%s\" on file descriptor %d",
|
"writebuff: write() error [NOBUFS/NOMEM] \"%s\" on file descriptor %d",
|
||||||
strerror(errno), fd);
|
strerror (errno), fd);
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
log_message(LOG_ERR,
|
log_message (LOG_ERR,
|
||||||
"writebuff: write() error \"%s\" on file descriptor %d",
|
"writebuff: write() error \"%s\" on file descriptor %d",
|
||||||
strerror(errno), fd);
|
strerror (errno), fd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
16
src/buffer.h
16
src/buffer.h
@ -24,17 +24,17 @@
|
|||||||
/* Forward declaration */
|
/* Forward declaration */
|
||||||
struct buffer_s;
|
struct buffer_s;
|
||||||
|
|
||||||
extern struct buffer_s *new_buffer(void);
|
extern struct buffer_s *new_buffer (void);
|
||||||
extern void delete_buffer(struct buffer_s *buffptr);
|
extern void delete_buffer (struct buffer_s *buffptr);
|
||||||
extern size_t buffer_size(struct buffer_s *buffptr);
|
extern size_t buffer_size (struct buffer_s *buffptr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a new line to the given buffer. The data IS copied into the structure.
|
* Add a new line to the given buffer. The data IS copied into the structure.
|
||||||
*/
|
*/
|
||||||
extern int add_to_buffer(struct buffer_s *buffptr, unsigned char *data,
|
extern int add_to_buffer (struct buffer_s *buffptr, unsigned char *data,
|
||||||
size_t length);
|
size_t length);
|
||||||
|
|
||||||
extern ssize_t read_buffer(int fd, struct buffer_s *buffptr);
|
extern ssize_t read_buffer (int fd, struct buffer_s *buffptr);
|
||||||
extern ssize_t write_buffer(int fd, struct buffer_s *buffptr);
|
extern ssize_t write_buffer (int fd, struct buffer_s *buffptr);
|
||||||
|
|
||||||
#endif /* __BUFFER_H_ */
|
#endif /* __BUFFER_H_ */
|
||||||
|
545
src/child.c
545
src/child.c
@ -37,11 +37,13 @@ static socklen_t addrlen;
|
|||||||
/*
|
/*
|
||||||
* Stores the internal data needed for each child (connection)
|
* Stores the internal data needed for each child (connection)
|
||||||
*/
|
*/
|
||||||
enum child_status_t { T_EMPTY, T_WAITING, T_CONNECTED };
|
enum child_status_t
|
||||||
struct child_s {
|
{ T_EMPTY, T_WAITING, T_CONNECTED };
|
||||||
pid_t tid;
|
struct child_s
|
||||||
unsigned int connects;
|
{
|
||||||
enum child_status_t status;
|
pid_t tid;
|
||||||
|
unsigned int connects;
|
||||||
|
enum child_status_t status;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -50,12 +52,13 @@ struct child_s {
|
|||||||
*/
|
*/
|
||||||
static struct child_s *child_ptr;
|
static struct child_s *child_ptr;
|
||||||
|
|
||||||
static struct child_config_s {
|
static struct child_config_s
|
||||||
int maxclients, maxrequestsperchild;
|
{
|
||||||
int maxspareservers, minspareservers, startservers;
|
int maxclients, maxrequestsperchild;
|
||||||
|
int maxspareservers, minspareservers, startservers;
|
||||||
} child_config;
|
} child_config;
|
||||||
|
|
||||||
static unsigned int *servers_waiting; /* servers waiting for a connection */
|
static unsigned int *servers_waiting; /* servers waiting for a connection */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Lock/Unlock the "servers_waiting" variable so that two children cannot
|
* Lock/Unlock the "servers_waiting" variable so that two children cannot
|
||||||
@ -74,47 +77,48 @@ static struct flock lock_it, unlock_it;
|
|||||||
static int lock_fd = -1;
|
static int lock_fd = -1;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_child_lock_init(void)
|
_child_lock_init (void)
|
||||||
{
|
{
|
||||||
char lock_file[] = "/tmp/tinyproxy.servers.lock.XXXXXX";
|
char lock_file[] = "/tmp/tinyproxy.servers.lock.XXXXXX";
|
||||||
|
|
||||||
/* Only allow u+rw bits. This may be required for some versions
|
/* Only allow u+rw bits. This may be required for some versions
|
||||||
* of glibc so that mkstemp() doesn't make us vulnerable.
|
* of glibc so that mkstemp() doesn't make us vulnerable.
|
||||||
*/
|
*/
|
||||||
umask(0177);
|
umask (0177);
|
||||||
|
|
||||||
lock_fd = mkstemp(lock_file);
|
lock_fd = mkstemp (lock_file);
|
||||||
unlink(lock_file);
|
unlink (lock_file);
|
||||||
|
|
||||||
lock_it.l_type = F_WRLCK;
|
lock_it.l_type = F_WRLCK;
|
||||||
lock_it.l_whence = SEEK_SET;
|
lock_it.l_whence = SEEK_SET;
|
||||||
lock_it.l_start = 0;
|
lock_it.l_start = 0;
|
||||||
lock_it.l_len = 0;
|
lock_it.l_len = 0;
|
||||||
|
|
||||||
unlock_it.l_type = F_UNLCK;
|
unlock_it.l_type = F_UNLCK;
|
||||||
unlock_it.l_whence = SEEK_SET;
|
unlock_it.l_whence = SEEK_SET;
|
||||||
unlock_it.l_start = 0;
|
unlock_it.l_start = 0;
|
||||||
unlock_it.l_len = 0;
|
unlock_it.l_len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_child_lock_wait(void)
|
_child_lock_wait (void)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
while ((rc = fcntl(lock_fd, F_SETLKW, &lock_it)) < 0) {
|
while ((rc = fcntl (lock_fd, F_SETLKW, &lock_it)) < 0)
|
||||||
if (errno == EINTR)
|
{
|
||||||
continue;
|
if (errno == EINTR)
|
||||||
else
|
continue;
|
||||||
return;
|
else
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_child_lock_release(void)
|
_child_lock_release (void)
|
||||||
{
|
{
|
||||||
if (fcntl(lock_fd, F_SETLKW, &unlock_it) < 0)
|
if (fcntl (lock_fd, F_SETLKW, &unlock_it) < 0)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* END OF LOCKING SECTION */
|
/* END OF LOCKING SECTION */
|
||||||
@ -138,126 +142,133 @@ _child_lock_release(void)
|
|||||||
* Set the configuration values for the various child related settings.
|
* Set the configuration values for the various child related settings.
|
||||||
*/
|
*/
|
||||||
short int
|
short int
|
||||||
child_configure(child_config_t type, int val)
|
child_configure (child_config_t type, int val)
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type)
|
||||||
case CHILD_MAXCLIENTS:
|
{
|
||||||
child_config.maxclients = val;
|
case CHILD_MAXCLIENTS:
|
||||||
break;
|
child_config.maxclients = val;
|
||||||
case CHILD_MAXSPARESERVERS:
|
break;
|
||||||
child_config.maxspareservers = val;
|
case CHILD_MAXSPARESERVERS:
|
||||||
break;
|
child_config.maxspareservers = val;
|
||||||
case CHILD_MINSPARESERVERS:
|
break;
|
||||||
child_config.minspareservers = val;
|
case CHILD_MINSPARESERVERS:
|
||||||
break;
|
child_config.minspareservers = val;
|
||||||
case CHILD_STARTSERVERS:
|
break;
|
||||||
child_config.startservers = val;
|
case CHILD_STARTSERVERS:
|
||||||
break;
|
child_config.startservers = val;
|
||||||
case CHILD_MAXREQUESTSPERCHILD:
|
break;
|
||||||
child_config.maxrequestsperchild = val;
|
case CHILD_MAXREQUESTSPERCHILD:
|
||||||
break;
|
child_config.maxrequestsperchild = val;
|
||||||
default:
|
break;
|
||||||
DEBUG2("Invalid type (%d)", type);
|
default:
|
||||||
return -1;
|
DEBUG2 ("Invalid type (%d)", type);
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is the main (per child) loop.
|
* This is the main (per child) loop.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
child_main(struct child_s *ptr)
|
child_main (struct child_s *ptr)
|
||||||
{
|
{
|
||||||
int connfd;
|
int connfd;
|
||||||
struct sockaddr *cliaddr;
|
struct sockaddr *cliaddr;
|
||||||
socklen_t clilen;
|
socklen_t clilen;
|
||||||
|
|
||||||
cliaddr = safemalloc(addrlen);
|
cliaddr = safemalloc (addrlen);
|
||||||
if (!cliaddr) {
|
if (!cliaddr)
|
||||||
log_message(LOG_CRIT,
|
{
|
||||||
"Could not allocate memory for child address.");
|
log_message (LOG_CRIT, "Could not allocate memory for child address.");
|
||||||
exit(0);
|
exit (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr->connects = 0;
|
ptr->connects = 0;
|
||||||
|
|
||||||
while (!config.quit) {
|
while (!config.quit)
|
||||||
ptr->status = T_WAITING;
|
{
|
||||||
|
ptr->status = T_WAITING;
|
||||||
|
|
||||||
clilen = addrlen;
|
clilen = addrlen;
|
||||||
|
|
||||||
connfd = accept(listenfd, cliaddr, &clilen);
|
connfd = accept (listenfd, cliaddr, &clilen);
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
/*
|
/*
|
||||||
* Enable the TINYPROXY_DEBUG environment variable if you
|
* Enable the TINYPROXY_DEBUG environment variable if you
|
||||||
* want to use the GDB debugger.
|
* want to use the GDB debugger.
|
||||||
*/
|
*/
|
||||||
if (getenv("TINYPROXY_DEBUG")) {
|
if (getenv ("TINYPROXY_DEBUG"))
|
||||||
/* Pause for 10 seconds to allow us to connect debugger */
|
{
|
||||||
fprintf(stderr,
|
/* Pause for 10 seconds to allow us to connect debugger */
|
||||||
"Process has accepted connection: %ld\n",
|
fprintf (stderr,
|
||||||
(long int)ptr->tid);
|
"Process has accepted connection: %ld\n",
|
||||||
sleep(10);
|
(long int) ptr->tid);
|
||||||
fprintf(stderr, "Continuing process: %ld\n",
|
sleep (10);
|
||||||
(long int)ptr->tid);
|
fprintf (stderr, "Continuing process: %ld\n", (long int) ptr->tid);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure no error occurred...
|
* Make sure no error occurred...
|
||||||
*/
|
*/
|
||||||
if (connfd < 0) {
|
if (connfd < 0)
|
||||||
log_message(LOG_ERR,
|
{
|
||||||
"Accept returned an error (%s) ... retrying.",
|
log_message (LOG_ERR,
|
||||||
strerror(errno));
|
"Accept returned an error (%s) ... retrying.",
|
||||||
continue;
|
strerror (errno));
|
||||||
}
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ptr->status = T_CONNECTED;
|
ptr->status = T_CONNECTED;
|
||||||
|
|
||||||
SERVER_DEC();
|
SERVER_DEC ();
|
||||||
|
|
||||||
handle_connection(connfd);
|
handle_connection (connfd);
|
||||||
ptr->connects++;
|
ptr->connects++;
|
||||||
|
|
||||||
if (child_config.maxrequestsperchild != 0) {
|
if (child_config.maxrequestsperchild != 0)
|
||||||
DEBUG2("%u connections so far...", ptr->connects);
|
{
|
||||||
|
DEBUG2 ("%u connections so far...", ptr->connects);
|
||||||
|
|
||||||
if (ptr->connects == child_config.maxrequestsperchild) {
|
if (ptr->connects == child_config.maxrequestsperchild)
|
||||||
log_message(LOG_NOTICE,
|
{
|
||||||
"Child has reached MaxRequestsPerChild (%u). Killing child.",
|
log_message (LOG_NOTICE,
|
||||||
ptr->connects);
|
"Child has reached MaxRequestsPerChild (%u). Killing child.",
|
||||||
break;
|
ptr->connects);
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SERVER_COUNT_LOCK();
|
SERVER_COUNT_LOCK ();
|
||||||
if (*servers_waiting > child_config.maxspareservers) {
|
if (*servers_waiting > child_config.maxspareservers)
|
||||||
/*
|
{
|
||||||
* There are too many spare children, kill ourself
|
/*
|
||||||
* off.
|
* There are too many spare children, kill ourself
|
||||||
*/
|
* off.
|
||||||
log_message(LOG_NOTICE,
|
*/
|
||||||
"Waiting servers (%d) exceeds MaxSpareServers (%d). Killing child.",
|
log_message (LOG_NOTICE,
|
||||||
*servers_waiting,
|
"Waiting servers (%d) exceeds MaxSpareServers (%d). Killing child.",
|
||||||
child_config.maxspareservers);
|
*servers_waiting, child_config.maxspareservers);
|
||||||
SERVER_COUNT_UNLOCK();
|
SERVER_COUNT_UNLOCK ();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
} else {
|
}
|
||||||
SERVER_COUNT_UNLOCK();
|
else
|
||||||
}
|
{
|
||||||
|
SERVER_COUNT_UNLOCK ();
|
||||||
|
}
|
||||||
|
|
||||||
SERVER_INC();
|
SERVER_INC ();
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr->status = T_EMPTY;
|
ptr->status = T_EMPTY;
|
||||||
|
|
||||||
safefree(cliaddr);
|
safefree (cliaddr);
|
||||||
exit(0);
|
exit (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -265,104 +276,113 @@ child_main(struct child_s *ptr)
|
|||||||
* child_main() function.
|
* child_main() function.
|
||||||
*/
|
*/
|
||||||
static pid_t
|
static pid_t
|
||||||
child_make(struct child_s *ptr)
|
child_make (struct child_s *ptr)
|
||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
if ((pid = fork()) > 0)
|
if ((pid = fork ()) > 0)
|
||||||
return pid; /* parent */
|
return pid; /* parent */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reset the SIGNALS so that the child can be reaped.
|
* Reset the SIGNALS so that the child can be reaped.
|
||||||
*/
|
*/
|
||||||
set_signal_handler(SIGCHLD, SIG_DFL);
|
set_signal_handler (SIGCHLD, SIG_DFL);
|
||||||
set_signal_handler(SIGTERM, SIG_DFL);
|
set_signal_handler (SIGTERM, SIG_DFL);
|
||||||
set_signal_handler(SIGHUP, SIG_DFL);
|
set_signal_handler (SIGHUP, SIG_DFL);
|
||||||
|
|
||||||
child_main(ptr); /* never returns */
|
child_main (ptr); /* never returns */
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a pool of children to handle incoming connections
|
* Create a pool of children to handle incoming connections
|
||||||
*/
|
*/
|
||||||
short int
|
short int
|
||||||
child_pool_create(void)
|
child_pool_create (void)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure the number of MaxClients is not zero, since this
|
* Make sure the number of MaxClients is not zero, since this
|
||||||
* variable determines the size of the array created for children
|
* variable determines the size of the array created for children
|
||||||
* later on.
|
* later on.
|
||||||
*/
|
*/
|
||||||
if (child_config.maxclients == 0) {
|
if (child_config.maxclients == 0)
|
||||||
log_message(LOG_ERR,
|
{
|
||||||
"child_pool_create: \"MaxClients\" must be greater than zero.");
|
log_message (LOG_ERR,
|
||||||
return -1;
|
"child_pool_create: \"MaxClients\" must be greater than zero.");
|
||||||
}
|
return -1;
|
||||||
if (child_config.startservers == 0) {
|
}
|
||||||
log_message(LOG_ERR,
|
if (child_config.startservers == 0)
|
||||||
"child_pool_create: \"StartServers\" must be greater than zero.");
|
{
|
||||||
return -1;
|
log_message (LOG_ERR,
|
||||||
}
|
"child_pool_create: \"StartServers\" must be greater than zero.");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
child_ptr = calloc_shared_memory(child_config.maxclients,
|
child_ptr = calloc_shared_memory (child_config.maxclients,
|
||||||
sizeof(struct child_s));
|
sizeof (struct child_s));
|
||||||
if (!child_ptr) {
|
if (!child_ptr)
|
||||||
log_message(LOG_ERR, "Could not allocate memory for children.");
|
{
|
||||||
return -1;
|
log_message (LOG_ERR, "Could not allocate memory for children.");
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
servers_waiting = malloc_shared_memory(sizeof(unsigned int));
|
servers_waiting = malloc_shared_memory (sizeof (unsigned int));
|
||||||
if (servers_waiting == MAP_FAILED) {
|
if (servers_waiting == MAP_FAILED)
|
||||||
log_message(LOG_ERR,
|
{
|
||||||
"Could not allocate memory for child counting.");
|
log_message (LOG_ERR, "Could not allocate memory for child counting.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
*servers_waiting = 0;
|
*servers_waiting = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a "locking" file for use around the servers_waiting
|
* Create a "locking" file for use around the servers_waiting
|
||||||
* variable.
|
* variable.
|
||||||
*/
|
*/
|
||||||
_child_lock_init();
|
_child_lock_init ();
|
||||||
|
|
||||||
if (child_config.startservers > child_config.maxclients) {
|
if (child_config.startservers > child_config.maxclients)
|
||||||
log_message(LOG_WARNING,
|
{
|
||||||
"Can not start more than \"MaxClients\" servers. Starting %u servers instead.",
|
log_message (LOG_WARNING,
|
||||||
child_config.maxclients);
|
"Can not start more than \"MaxClients\" servers. Starting %u servers instead.",
|
||||||
child_config.startservers = child_config.maxclients;
|
child_config.maxclients);
|
||||||
}
|
child_config.startservers = child_config.maxclients;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i != child_config.maxclients; i++) {
|
for (i = 0; i != child_config.maxclients; i++)
|
||||||
child_ptr[i].status = T_EMPTY;
|
{
|
||||||
child_ptr[i].connects = 0;
|
child_ptr[i].status = T_EMPTY;
|
||||||
}
|
child_ptr[i].connects = 0;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i != child_config.startservers; i++) {
|
for (i = 0; i != child_config.startservers; i++)
|
||||||
DEBUG2("Trying to create child %d of %d", i + 1,
|
{
|
||||||
child_config.startservers);
|
DEBUG2 ("Trying to create child %d of %d", i + 1,
|
||||||
child_ptr[i].status = T_WAITING;
|
child_config.startservers);
|
||||||
child_ptr[i].tid = child_make(&child_ptr[i]);
|
child_ptr[i].status = T_WAITING;
|
||||||
|
child_ptr[i].tid = child_make (&child_ptr[i]);
|
||||||
|
|
||||||
if (child_ptr[i].tid < 0) {
|
if (child_ptr[i].tid < 0)
|
||||||
log_message(LOG_WARNING,
|
{
|
||||||
"Could not create child number %d of %d",
|
log_message (LOG_WARNING,
|
||||||
i, child_config.startservers);
|
"Could not create child number %d of %d",
|
||||||
return -1;
|
i, child_config.startservers);
|
||||||
} else {
|
return -1;
|
||||||
log_message(LOG_INFO,
|
}
|
||||||
"Creating child number %d of %d ...",
|
else
|
||||||
i + 1, child_config.startservers);
|
{
|
||||||
|
log_message (LOG_INFO,
|
||||||
|
"Creating child number %d of %d ...",
|
||||||
|
i + 1, child_config.startservers);
|
||||||
|
|
||||||
SERVER_INC();
|
SERVER_INC ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log_message(LOG_INFO, "Finished creating all children.");
|
log_message (LOG_INFO, "Finished creating all children.");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -370,88 +390,95 @@ child_pool_create(void)
|
|||||||
* servers. It monitors this at least once a second.
|
* servers. It monitors this at least once a second.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
child_main_loop(void)
|
child_main_loop (void)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
while (1) {
|
while (1)
|
||||||
if (config.quit)
|
{
|
||||||
return;
|
if (config.quit)
|
||||||
|
return;
|
||||||
|
|
||||||
/* If there are not enough spare servers, create more */
|
/* If there are not enough spare servers, create more */
|
||||||
SERVER_COUNT_LOCK();
|
SERVER_COUNT_LOCK ();
|
||||||
if (*servers_waiting < child_config.minspareservers) {
|
if (*servers_waiting < child_config.minspareservers)
|
||||||
log_message(LOG_NOTICE,
|
{
|
||||||
"Waiting servers (%d) is less than MinSpareServers (%d). Creating new child.",
|
log_message (LOG_NOTICE,
|
||||||
*servers_waiting,
|
"Waiting servers (%d) is less than MinSpareServers (%d). Creating new child.",
|
||||||
child_config.minspareservers);
|
*servers_waiting, child_config.minspareservers);
|
||||||
|
|
||||||
SERVER_COUNT_UNLOCK();
|
SERVER_COUNT_UNLOCK ();
|
||||||
|
|
||||||
for (i = 0; i != child_config.maxclients; i++) {
|
for (i = 0; i != child_config.maxclients; i++)
|
||||||
if (child_ptr[i].status == T_EMPTY) {
|
{
|
||||||
child_ptr[i].status = T_WAITING;
|
if (child_ptr[i].status == T_EMPTY)
|
||||||
child_ptr[i].tid =
|
{
|
||||||
child_make(&child_ptr[i]);
|
child_ptr[i].status = T_WAITING;
|
||||||
if (child_ptr[i].tid < 0) {
|
child_ptr[i].tid = child_make (&child_ptr[i]);
|
||||||
log_message(LOG_NOTICE,
|
if (child_ptr[i].tid < 0)
|
||||||
"Could not create child");
|
{
|
||||||
|
log_message (LOG_NOTICE, "Could not create child");
|
||||||
|
|
||||||
child_ptr[i].status = T_EMPTY;
|
child_ptr[i].status = T_EMPTY;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
SERVER_INC();
|
SERVER_INC ();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
SERVER_COUNT_UNLOCK();
|
else
|
||||||
}
|
{
|
||||||
|
SERVER_COUNT_UNLOCK ();
|
||||||
|
}
|
||||||
|
|
||||||
sleep(5);
|
sleep (5);
|
||||||
|
|
||||||
/* Handle log rotation if it was requested */
|
/* Handle log rotation if it was requested */
|
||||||
if (received_sighup) {
|
if (received_sighup)
|
||||||
truncate_log_file();
|
{
|
||||||
|
truncate_log_file ();
|
||||||
|
|
||||||
#ifdef FILTER_ENABLE
|
#ifdef FILTER_ENABLE
|
||||||
if (config.filter) {
|
if (config.filter)
|
||||||
filter_destroy();
|
{
|
||||||
filter_init();
|
filter_destroy ();
|
||||||
}
|
filter_init ();
|
||||||
log_message(LOG_NOTICE, "Re-reading filter file.");
|
}
|
||||||
#endif /* FILTER_ENABLE */
|
log_message (LOG_NOTICE, "Re-reading filter file.");
|
||||||
|
#endif /* FILTER_ENABLE */
|
||||||
|
|
||||||
received_sighup = FALSE;
|
received_sighup = FALSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Go through all the non-empty children and cancel them.
|
* Go through all the non-empty children and cancel them.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
child_kill_children(void)
|
child_kill_children (void)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
for (i = 0; i != child_config.maxclients; i++) {
|
for (i = 0; i != child_config.maxclients; i++)
|
||||||
if (child_ptr[i].status != T_EMPTY)
|
{
|
||||||
kill(child_ptr[i].tid, SIGTERM);
|
if (child_ptr[i].status != T_EMPTY)
|
||||||
}
|
kill (child_ptr[i].tid, SIGTERM);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
child_listening_sock(uint16_t port)
|
child_listening_sock (uint16_t port)
|
||||||
{
|
{
|
||||||
listenfd = listen_sock(port, &addrlen);
|
listenfd = listen_sock (port, &addrlen);
|
||||||
return listenfd;
|
return listenfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
child_close_sock(void)
|
child_close_sock (void)
|
||||||
{
|
{
|
||||||
close(listenfd);
|
close (listenfd);
|
||||||
}
|
}
|
||||||
|
25
src/child.h
25
src/child.h
@ -21,20 +21,21 @@
|
|||||||
#ifndef TINYPROXY_CHILD_H
|
#ifndef TINYPROXY_CHILD_H
|
||||||
#define TINYPROXY_CHILD_H
|
#define TINYPROXY_CHILD_H
|
||||||
|
|
||||||
typedef enum {
|
typedef enum
|
||||||
CHILD_MAXCLIENTS,
|
{
|
||||||
CHILD_MAXSPARESERVERS,
|
CHILD_MAXCLIENTS,
|
||||||
CHILD_MINSPARESERVERS,
|
CHILD_MAXSPARESERVERS,
|
||||||
CHILD_STARTSERVERS,
|
CHILD_MINSPARESERVERS,
|
||||||
CHILD_MAXREQUESTSPERCHILD
|
CHILD_STARTSERVERS,
|
||||||
|
CHILD_MAXREQUESTSPERCHILD
|
||||||
} child_config_t;
|
} child_config_t;
|
||||||
|
|
||||||
extern short int child_pool_create(void);
|
extern short int child_pool_create (void);
|
||||||
extern int child_listening_sock(uint16_t port);
|
extern int child_listening_sock (uint16_t port);
|
||||||
extern void child_close_sock(void);
|
extern void child_close_sock (void);
|
||||||
extern void child_main_loop(void);
|
extern void child_main_loop (void);
|
||||||
extern void child_kill_children(void);
|
extern void child_kill_children (void);
|
||||||
|
|
||||||
extern short int child_configure(child_config_t type, int val);
|
extern short int child_configure (child_config_t type, int val);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
10
src/common.h
10
src/common.h
@ -173,13 +173,13 @@
|
|||||||
# define MSG_NOSIGNAL (0)
|
# define MSG_NOSIGNAL (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef SHUT_RD /* these three Posix.1g names are quite new */
|
#ifndef SHUT_RD /* these three Posix.1g names are quite new */
|
||||||
# define SHUT_RD 0 /* shutdown for reading */
|
# define SHUT_RD 0 /* shutdown for reading */
|
||||||
# define SHUT_WR 1 /* shutdown for writing */
|
# define SHUT_WR 1 /* shutdown for writing */
|
||||||
# define SHUT_RDWR 2 /* shutdown for reading and writing */
|
# define SHUT_RDWR 2 /* shutdown for reading and writing */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MAXLISTEN 1024 /* Max number of connections */
|
#define MAXLISTEN 1024 /* Max number of connections */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SunOS doesn't have INADDR_NONE defined.
|
* SunOS doesn't have INADDR_NONE defined.
|
||||||
|
792
src/conffile.c
792
src/conffile.c
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,7 @@
|
|||||||
#ifndef TINYPROXY_CONFFILE_H
|
#ifndef TINYPROXY_CONFFILE_H
|
||||||
#define TINYPROXY_CONFFILE_H
|
#define TINYPROXY_CONFFILE_H
|
||||||
|
|
||||||
extern int config_compile(void);
|
extern int config_compile (void);
|
||||||
extern int config_parse(struct config_s *conf, FILE * f);
|
extern int config_parse (struct config_s *conf, FILE * f);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
156
src/conns.c
156
src/conns.c
@ -31,118 +31,118 @@
|
|||||||
#include "stats.h"
|
#include "stats.h"
|
||||||
|
|
||||||
struct conn_s *
|
struct conn_s *
|
||||||
initialize_conn(int client_fd, const char *ipaddr, const char *string_addr,
|
initialize_conn (int client_fd, const char *ipaddr, const char *string_addr,
|
||||||
const char *sock_ipaddr)
|
const char *sock_ipaddr)
|
||||||
{
|
{
|
||||||
struct conn_s *connptr;
|
struct conn_s *connptr;
|
||||||
struct buffer_s *cbuffer, *sbuffer;
|
struct buffer_s *cbuffer, *sbuffer;
|
||||||
|
|
||||||
assert(client_fd >= 0);
|
assert (client_fd >= 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate the memory for all the internal components
|
* Allocate the memory for all the internal components
|
||||||
*/
|
*/
|
||||||
cbuffer = new_buffer();
|
cbuffer = new_buffer ();
|
||||||
sbuffer = new_buffer();
|
sbuffer = new_buffer ();
|
||||||
|
|
||||||
if (!cbuffer || !sbuffer)
|
if (!cbuffer || !sbuffer)
|
||||||
goto error_exit;
|
goto error_exit;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate the space for the conn_s structure itself.
|
* Allocate the space for the conn_s structure itself.
|
||||||
*/
|
*/
|
||||||
connptr = safemalloc(sizeof(struct conn_s));
|
connptr = safemalloc (sizeof (struct conn_s));
|
||||||
if (!connptr)
|
if (!connptr)
|
||||||
goto error_exit;
|
goto error_exit;
|
||||||
|
|
||||||
connptr->client_fd = client_fd;
|
connptr->client_fd = client_fd;
|
||||||
connptr->server_fd = -1;
|
connptr->server_fd = -1;
|
||||||
|
|
||||||
connptr->cbuffer = cbuffer;
|
connptr->cbuffer = cbuffer;
|
||||||
connptr->sbuffer = sbuffer;
|
connptr->sbuffer = sbuffer;
|
||||||
|
|
||||||
connptr->request_line = NULL;
|
connptr->request_line = NULL;
|
||||||
|
|
||||||
/* These store any error strings */
|
/* These store any error strings */
|
||||||
connptr->error_variables = NULL;
|
connptr->error_variables = NULL;
|
||||||
connptr->error_string = NULL;
|
connptr->error_string = NULL;
|
||||||
connptr->error_number = -1;
|
connptr->error_number = -1;
|
||||||
|
|
||||||
connptr->connect_method = FALSE;
|
connptr->connect_method = FALSE;
|
||||||
connptr->show_stats = FALSE;
|
connptr->show_stats = FALSE;
|
||||||
|
|
||||||
connptr->protocol.major = connptr->protocol.minor = 0;
|
connptr->protocol.major = connptr->protocol.minor = 0;
|
||||||
|
|
||||||
/* There is _no_ content length initially */
|
/* There is _no_ content length initially */
|
||||||
connptr->content_length.server = connptr->content_length.client = -1;
|
connptr->content_length.server = connptr->content_length.client = -1;
|
||||||
|
|
||||||
connptr->server_ip_addr = sock_ipaddr ? safestrdup(sock_ipaddr) : 0;
|
connptr->server_ip_addr = sock_ipaddr ? safestrdup (sock_ipaddr) : 0;
|
||||||
connptr->client_ip_addr = safestrdup(ipaddr);
|
connptr->client_ip_addr = safestrdup (ipaddr);
|
||||||
connptr->client_string_addr = safestrdup(string_addr);
|
connptr->client_string_addr = safestrdup (string_addr);
|
||||||
|
|
||||||
connptr->upstream_proxy = NULL;
|
connptr->upstream_proxy = NULL;
|
||||||
|
|
||||||
update_stats(STAT_OPEN);
|
update_stats (STAT_OPEN);
|
||||||
|
|
||||||
#ifdef REVERSE_SUPPORT
|
#ifdef REVERSE_SUPPORT
|
||||||
connptr->reversepath = NULL;
|
connptr->reversepath = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return connptr;
|
return connptr;
|
||||||
|
|
||||||
error_exit:
|
error_exit:
|
||||||
/*
|
/*
|
||||||
* If we got here, there was a problem allocating memory
|
* If we got here, there was a problem allocating memory
|
||||||
*/
|
*/
|
||||||
if (cbuffer)
|
if (cbuffer)
|
||||||
delete_buffer(cbuffer);
|
delete_buffer (cbuffer);
|
||||||
if (sbuffer)
|
if (sbuffer)
|
||||||
delete_buffer(sbuffer);
|
delete_buffer (sbuffer);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
destroy_conn(struct conn_s *connptr)
|
destroy_conn (struct conn_s *connptr)
|
||||||
{
|
{
|
||||||
assert(connptr != NULL);
|
assert (connptr != NULL);
|
||||||
|
|
||||||
if (connptr->client_fd != -1)
|
if (connptr->client_fd != -1)
|
||||||
if (close(connptr->client_fd) < 0)
|
if (close (connptr->client_fd) < 0)
|
||||||
log_message(LOG_INFO, "Client (%d) close message: %s",
|
log_message (LOG_INFO, "Client (%d) close message: %s",
|
||||||
connptr->client_fd, strerror(errno));
|
connptr->client_fd, strerror (errno));
|
||||||
if (connptr->server_fd != -1)
|
if (connptr->server_fd != -1)
|
||||||
if (close(connptr->server_fd) < 0)
|
if (close (connptr->server_fd) < 0)
|
||||||
log_message(LOG_INFO, "Server (%d) close message: %s",
|
log_message (LOG_INFO, "Server (%d) close message: %s",
|
||||||
connptr->server_fd, strerror(errno));
|
connptr->server_fd, strerror (errno));
|
||||||
|
|
||||||
if (connptr->cbuffer)
|
if (connptr->cbuffer)
|
||||||
delete_buffer(connptr->cbuffer);
|
delete_buffer (connptr->cbuffer);
|
||||||
if (connptr->sbuffer)
|
if (connptr->sbuffer)
|
||||||
delete_buffer(connptr->sbuffer);
|
delete_buffer (connptr->sbuffer);
|
||||||
|
|
||||||
if (connptr->request_line)
|
if (connptr->request_line)
|
||||||
safefree(connptr->request_line);
|
safefree (connptr->request_line);
|
||||||
|
|
||||||
if (connptr->error_variables)
|
if (connptr->error_variables)
|
||||||
hashmap_delete(connptr->error_variables);
|
hashmap_delete (connptr->error_variables);
|
||||||
|
|
||||||
if (connptr->error_string)
|
if (connptr->error_string)
|
||||||
safefree(connptr->error_string);
|
safefree (connptr->error_string);
|
||||||
|
|
||||||
if (connptr->server_ip_addr)
|
if (connptr->server_ip_addr)
|
||||||
safefree(connptr->server_ip_addr);
|
safefree (connptr->server_ip_addr);
|
||||||
if (connptr->client_ip_addr)
|
if (connptr->client_ip_addr)
|
||||||
safefree(connptr->client_ip_addr);
|
safefree (connptr->client_ip_addr);
|
||||||
if (connptr->client_string_addr)
|
if (connptr->client_string_addr)
|
||||||
safefree(connptr->client_string_addr);
|
safefree (connptr->client_string_addr);
|
||||||
|
|
||||||
#ifdef REVERSE_SUPPORT
|
#ifdef REVERSE_SUPPORT
|
||||||
if (connptr->reversepath)
|
if (connptr->reversepath)
|
||||||
safefree(connptr->reversepath);
|
safefree (connptr->reversepath);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
safefree(connptr);
|
safefree (connptr);
|
||||||
|
|
||||||
update_stats(STAT_CLOSE);
|
update_stats (STAT_CLOSE);
|
||||||
}
|
}
|
||||||
|
103
src/conns.h
103
src/conns.h
@ -27,73 +27,76 @@
|
|||||||
/*
|
/*
|
||||||
* Connection Definition
|
* Connection Definition
|
||||||
*/
|
*/
|
||||||
struct conn_s {
|
struct conn_s
|
||||||
int client_fd;
|
{
|
||||||
int server_fd;
|
int client_fd;
|
||||||
|
int server_fd;
|
||||||
|
|
||||||
struct buffer_s *cbuffer;
|
struct buffer_s *cbuffer;
|
||||||
struct buffer_s *sbuffer;
|
struct buffer_s *sbuffer;
|
||||||
|
|
||||||
/* The request line (first line) from the client */
|
/* The request line (first line) from the client */
|
||||||
char *request_line;
|
char *request_line;
|
||||||
|
|
||||||
/* Booleans */
|
/* Booleans */
|
||||||
unsigned int connect_method;
|
unsigned int connect_method;
|
||||||
unsigned int show_stats;
|
unsigned int show_stats;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This structure stores key -> value mappings for substitution
|
* This structure stores key -> value mappings for substitution
|
||||||
* in the error HTML files.
|
* in the error HTML files.
|
||||||
*/
|
*/
|
||||||
hashmap_t error_variables;
|
hashmap_t error_variables;
|
||||||
|
|
||||||
int error_number;
|
int error_number;
|
||||||
char *error_string;
|
char *error_string;
|
||||||
|
|
||||||
/* A Content-Length value from the remote server */
|
/* A Content-Length value from the remote server */
|
||||||
struct {
|
struct
|
||||||
long int server;
|
{
|
||||||
long int client;
|
long int server;
|
||||||
} content_length;
|
long int client;
|
||||||
|
} content_length;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Store the server's IP (for BindSame)
|
* Store the server's IP (for BindSame)
|
||||||
*/
|
*/
|
||||||
char *server_ip_addr;
|
char *server_ip_addr;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Store the client's IP and hostname information
|
* Store the client's IP and hostname information
|
||||||
*/
|
*/
|
||||||
char *client_ip_addr;
|
char *client_ip_addr;
|
||||||
char *client_string_addr;
|
char *client_string_addr;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Store the incoming request's HTTP protocol.
|
* Store the incoming request's HTTP protocol.
|
||||||
*/
|
*/
|
||||||
struct {
|
struct
|
||||||
unsigned int major;
|
{
|
||||||
unsigned int minor;
|
unsigned int major;
|
||||||
} protocol;
|
unsigned int minor;
|
||||||
|
} protocol;
|
||||||
|
|
||||||
#ifdef REVERSE_SUPPORT
|
#ifdef REVERSE_SUPPORT
|
||||||
/*
|
/*
|
||||||
* Place to store the current per-connection reverse proxy path
|
* Place to store the current per-connection reverse proxy path
|
||||||
*/
|
*/
|
||||||
char *reversepath;
|
char *reversepath;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pointer to upstream proxy.
|
* Pointer to upstream proxy.
|
||||||
*/
|
*/
|
||||||
struct upstream *upstream_proxy;
|
struct upstream *upstream_proxy;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Functions for the creation and destruction of a connection structure.
|
* Functions for the creation and destruction of a connection structure.
|
||||||
*/
|
*/
|
||||||
extern struct conn_s *initialize_conn(int client_fd, const char *ipaddr,
|
extern struct conn_s *initialize_conn (int client_fd, const char *ipaddr,
|
||||||
const char *string_addr,
|
const char *string_addr,
|
||||||
const char *sock_ipaddr);
|
const char *sock_ipaddr);
|
||||||
extern void destroy_conn(struct conn_s *connptr);
|
extern void destroy_conn (struct conn_s *connptr);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
61
src/daemon.c
61
src/daemon.c
@ -30,28 +30,28 @@
|
|||||||
* program a daemon process.
|
* program a daemon process.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
makedaemon(void)
|
makedaemon (void)
|
||||||
{
|
{
|
||||||
if (fork() != 0)
|
if (fork () != 0)
|
||||||
exit(0);
|
exit (0);
|
||||||
|
|
||||||
setsid();
|
setsid ();
|
||||||
set_signal_handler(SIGHUP, SIG_IGN);
|
set_signal_handler (SIGHUP, SIG_IGN);
|
||||||
|
|
||||||
if (fork() != 0)
|
if (fork () != 0)
|
||||||
exit(0);
|
exit (0);
|
||||||
|
|
||||||
chdir("/");
|
chdir ("/");
|
||||||
umask(0177);
|
umask (0177);
|
||||||
|
|
||||||
#if NDEBUG
|
#if NDEBUG
|
||||||
/*
|
/*
|
||||||
* When not in debugging mode, close the standard file
|
* When not in debugging mode, close the standard file
|
||||||
* descriptors.
|
* descriptors.
|
||||||
*/
|
*/
|
||||||
close(0);
|
close (0);
|
||||||
close(1);
|
close (1);
|
||||||
close(2);
|
close (2);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,25 +60,28 @@ makedaemon(void)
|
|||||||
* to handle signals sent to the process.
|
* to handle signals sent to the process.
|
||||||
*/
|
*/
|
||||||
signal_func *
|
signal_func *
|
||||||
set_signal_handler(int signo, signal_func *func)
|
set_signal_handler (int signo, signal_func * func)
|
||||||
{
|
{
|
||||||
struct sigaction act, oact;
|
struct sigaction act, oact;
|
||||||
|
|
||||||
act.sa_handler = func;
|
act.sa_handler = func;
|
||||||
sigemptyset(&act.sa_mask);
|
sigemptyset (&act.sa_mask);
|
||||||
act.sa_flags = 0;
|
act.sa_flags = 0;
|
||||||
if (signo == SIGALRM) {
|
if (signo == SIGALRM)
|
||||||
|
{
|
||||||
#ifdef SA_INTERRUPT
|
#ifdef SA_INTERRUPT
|
||||||
act.sa_flags |= SA_INTERRUPT; /* SunOS 4.x */
|
act.sa_flags |= SA_INTERRUPT; /* SunOS 4.x */
|
||||||
#endif
|
#endif
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
#ifdef SA_RESTART
|
#ifdef SA_RESTART
|
||||||
act.sa_flags |= SA_RESTART; /* SVR4, 4.4BSD */
|
act.sa_flags |= SA_RESTART; /* SVR4, 4.4BSD */
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sigaction(signo, &act, &oact) < 0)
|
if (sigaction (signo, &act, &oact) < 0)
|
||||||
return SIG_ERR;
|
return SIG_ERR;
|
||||||
|
|
||||||
return oact.sa_handler;
|
return oact.sa_handler;
|
||||||
}
|
}
|
||||||
|
@ -26,11 +26,11 @@ typedef void signal_func (int);
|
|||||||
/*
|
/*
|
||||||
* Pass a singal integer and a function to handle the signal.
|
* Pass a singal integer and a function to handle the signal.
|
||||||
*/
|
*/
|
||||||
extern signal_func *set_signal_handler(int signo, signal_func *func);
|
extern signal_func *set_signal_handler (int signo, signal_func * func);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make a program a daemon process
|
* Make a program a daemon process
|
||||||
*/
|
*/
|
||||||
extern void makedaemon(void);
|
extern void makedaemon (void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
268
src/filter.c
268
src/filter.c
@ -33,10 +33,11 @@
|
|||||||
|
|
||||||
static int err;
|
static int err;
|
||||||
|
|
||||||
struct filter_list {
|
struct filter_list
|
||||||
struct filter_list *next;
|
{
|
||||||
char *pat;
|
struct filter_list *next;
|
||||||
regex_t *cpat;
|
char *pat;
|
||||||
|
regex_t *cpat;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct filter_list *fl = NULL;
|
static struct filter_list *fl = NULL;
|
||||||
@ -47,171 +48,176 @@ static filter_policy_t default_policy = FILTER_DEFAULT_ALLOW;
|
|||||||
* Initializes a linked list of strings containing hosts/urls to be filtered
|
* Initializes a linked list of strings containing hosts/urls to be filtered
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
filter_init(void)
|
filter_init (void)
|
||||||
{
|
{
|
||||||
FILE *fd;
|
FILE *fd;
|
||||||
struct filter_list *p;
|
struct filter_list *p;
|
||||||
char buf[FILTER_BUFFER_LEN];
|
char buf[FILTER_BUFFER_LEN];
|
||||||
char *s;
|
char *s;
|
||||||
int cflags;
|
int cflags;
|
||||||
|
|
||||||
if (!fl && !already_init) {
|
if (!fl && !already_init)
|
||||||
fd = fopen(config.filter, "r");
|
{
|
||||||
if (fd) {
|
fd = fopen (config.filter, "r");
|
||||||
p = NULL;
|
if (fd)
|
||||||
|
{
|
||||||
|
p = NULL;
|
||||||
|
|
||||||
cflags = REG_NEWLINE | REG_NOSUB;
|
cflags = REG_NEWLINE | REG_NOSUB;
|
||||||
if (config.filter_extended)
|
if (config.filter_extended)
|
||||||
cflags |= REG_EXTENDED;
|
cflags |= REG_EXTENDED;
|
||||||
if (!config.filter_casesensitive)
|
if (!config.filter_casesensitive)
|
||||||
cflags |= REG_ICASE;
|
cflags |= REG_ICASE;
|
||||||
|
|
||||||
while (fgets(buf, FILTER_BUFFER_LEN, fd)) {
|
while (fgets (buf, FILTER_BUFFER_LEN, fd))
|
||||||
/*
|
{
|
||||||
* Remove any trailing white space and
|
/*
|
||||||
* comments.
|
* Remove any trailing white space and
|
||||||
*/
|
* comments.
|
||||||
s = buf;
|
*/
|
||||||
while (*s) {
|
s = buf;
|
||||||
if (isspace((unsigned char)*s))
|
while (*s)
|
||||||
break;
|
{
|
||||||
if (*s == '#') {
|
if (isspace ((unsigned char) *s))
|
||||||
/*
|
break;
|
||||||
* If the '#' char is preceeded by
|
if (*s == '#')
|
||||||
* an escape, it's not a comment
|
{
|
||||||
* string.
|
/*
|
||||||
*/
|
* If the '#' char is preceeded by
|
||||||
if (s == buf
|
* an escape, it's not a comment
|
||||||
|| *(s - 1) != '\\')
|
* string.
|
||||||
break;
|
*/
|
||||||
}
|
if (s == buf || *(s - 1) != '\\')
|
||||||
++s;
|
break;
|
||||||
}
|
}
|
||||||
*s = '\0';
|
++s;
|
||||||
|
}
|
||||||
|
*s = '\0';
|
||||||
|
|
||||||
/* skip leading whitespace */
|
/* skip leading whitespace */
|
||||||
s = buf;
|
s = buf;
|
||||||
while (*s && isspace((unsigned char)*s))
|
while (*s && isspace ((unsigned char) *s))
|
||||||
s++;
|
s++;
|
||||||
|
|
||||||
/* skip blank lines and comments */
|
/* skip blank lines and comments */
|
||||||
if (*s == '\0')
|
if (*s == '\0')
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!p) /* head of list */
|
if (!p) /* head of list */
|
||||||
fl = p =
|
fl = p = safecalloc (1, sizeof (struct filter_list));
|
||||||
safecalloc(1,
|
else
|
||||||
sizeof(struct
|
{ /* next entry */
|
||||||
filter_list));
|
p->next = safecalloc (1, sizeof (struct filter_list));
|
||||||
else { /* next entry */
|
p = p->next;
|
||||||
p->next =
|
}
|
||||||
safecalloc(1,
|
|
||||||
sizeof(struct
|
|
||||||
filter_list));
|
|
||||||
p = p->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
p->pat = safestrdup(s);
|
p->pat = safestrdup (s);
|
||||||
p->cpat = safemalloc(sizeof(regex_t));
|
p->cpat = safemalloc (sizeof (regex_t));
|
||||||
if ((err =
|
if ((err = regcomp (p->cpat, p->pat, cflags)) != 0)
|
||||||
regcomp(p->cpat, p->pat, cflags)) != 0) {
|
{
|
||||||
fprintf(stderr, "Bad regex in %s: %s\n",
|
fprintf (stderr, "Bad regex in %s: %s\n",
|
||||||
config.filter, p->pat);
|
config.filter, p->pat);
|
||||||
exit(EX_DATAERR);
|
exit (EX_DATAERR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ferror(fd)) {
|
if (ferror (fd))
|
||||||
perror("fgets");
|
{
|
||||||
exit(EX_DATAERR);
|
perror ("fgets");
|
||||||
}
|
exit (EX_DATAERR);
|
||||||
fclose(fd);
|
}
|
||||||
|
fclose (fd);
|
||||||
|
|
||||||
already_init = 1;
|
already_init = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* unlink the list */
|
/* unlink the list */
|
||||||
void
|
void
|
||||||
filter_destroy(void)
|
filter_destroy (void)
|
||||||
{
|
{
|
||||||
struct filter_list *p, *q;
|
struct filter_list *p, *q;
|
||||||
|
|
||||||
if (already_init) {
|
if (already_init)
|
||||||
for (p = q = fl; p; p = q) {
|
{
|
||||||
regfree(p->cpat);
|
for (p = q = fl; p; p = q)
|
||||||
safefree(p->cpat);
|
{
|
||||||
safefree(p->pat);
|
regfree (p->cpat);
|
||||||
q = p->next;
|
safefree (p->cpat);
|
||||||
safefree(p);
|
safefree (p->pat);
|
||||||
}
|
q = p->next;
|
||||||
fl = NULL;
|
safefree (p);
|
||||||
already_init = 0;
|
}
|
||||||
}
|
fl = NULL;
|
||||||
|
already_init = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return 0 to allow, non-zero to block */
|
/* Return 0 to allow, non-zero to block */
|
||||||
int
|
int
|
||||||
filter_domain(const char *host)
|
filter_domain (const char *host)
|
||||||
{
|
{
|
||||||
struct filter_list *p;
|
struct filter_list *p;
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
if (!fl || !already_init)
|
if (!fl || !already_init)
|
||||||
goto COMMON_EXIT;
|
goto COMMON_EXIT;
|
||||||
|
|
||||||
for (p = fl; p; p = p->next) {
|
for (p = fl; p; p = p->next)
|
||||||
result =
|
{
|
||||||
regexec(p->cpat, host, (size_t) 0, (regmatch_t *) 0, 0);
|
result = regexec (p->cpat, host, (size_t) 0, (regmatch_t *) 0, 0);
|
||||||
|
|
||||||
if (result == 0) {
|
if (result == 0)
|
||||||
if (default_policy == FILTER_DEFAULT_ALLOW)
|
{
|
||||||
return 1;
|
if (default_policy == FILTER_DEFAULT_ALLOW)
|
||||||
else
|
return 1;
|
||||||
return 0;
|
else
|
||||||
}
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
COMMON_EXIT:
|
COMMON_EXIT:
|
||||||
if (default_policy == FILTER_DEFAULT_ALLOW)
|
if (default_policy == FILTER_DEFAULT_ALLOW)
|
||||||
return 0;
|
return 0;
|
||||||
else
|
else
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* returns 0 to allow, non-zero to block */
|
/* returns 0 to allow, non-zero to block */
|
||||||
int
|
int
|
||||||
filter_url(const char *url)
|
filter_url (const char *url)
|
||||||
{
|
{
|
||||||
struct filter_list *p;
|
struct filter_list *p;
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
if (!fl || !already_init)
|
if (!fl || !already_init)
|
||||||
goto COMMON_EXIT;
|
goto COMMON_EXIT;
|
||||||
|
|
||||||
for (p = fl; p; p = p->next) {
|
for (p = fl; p; p = p->next)
|
||||||
result = regexec(p->cpat, url, (size_t) 0, (regmatch_t *) 0, 0);
|
{
|
||||||
|
result = regexec (p->cpat, url, (size_t) 0, (regmatch_t *) 0, 0);
|
||||||
|
|
||||||
if (result == 0) {
|
if (result == 0)
|
||||||
if (default_policy == FILTER_DEFAULT_ALLOW)
|
{
|
||||||
return 1;
|
if (default_policy == FILTER_DEFAULT_ALLOW)
|
||||||
else
|
return 1;
|
||||||
return 0;
|
else
|
||||||
}
|
return 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
COMMON_EXIT:
|
COMMON_EXIT:
|
||||||
if (default_policy == FILTER_DEFAULT_ALLOW)
|
if (default_policy == FILTER_DEFAULT_ALLOW)
|
||||||
return 0;
|
return 0;
|
||||||
else
|
else
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the default filtering policy
|
* Set the default filtering policy
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
filter_set_default_policy(filter_policy_t policy)
|
filter_set_default_policy (filter_policy_t policy)
|
||||||
{
|
{
|
||||||
default_policy = policy;
|
default_policy = policy;
|
||||||
}
|
}
|
||||||
|
17
src/filter.h
17
src/filter.h
@ -21,16 +21,17 @@
|
|||||||
#ifndef _TINYPROXY_FILTER_H_
|
#ifndef _TINYPROXY_FILTER_H_
|
||||||
#define _TINYPROXY_FILTER_H_
|
#define _TINYPROXY_FILTER_H_
|
||||||
|
|
||||||
typedef enum {
|
typedef enum
|
||||||
FILTER_DEFAULT_ALLOW,
|
{
|
||||||
FILTER_DEFAULT_DENY,
|
FILTER_DEFAULT_ALLOW,
|
||||||
|
FILTER_DEFAULT_DENY,
|
||||||
} filter_policy_t;
|
} filter_policy_t;
|
||||||
|
|
||||||
extern void filter_init(void);
|
extern void filter_init (void);
|
||||||
extern void filter_destroy(void);
|
extern void filter_destroy (void);
|
||||||
extern int filter_domain(const char *host);
|
extern int filter_domain (const char *host);
|
||||||
extern int filter_url(const char *url);
|
extern int filter_url (const char *url);
|
||||||
|
|
||||||
extern void filter_set_default_policy(filter_policy_t policy);
|
extern void filter_set_default_policy (filter_policy_t policy);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
532
src/hashmap.c
532
src/hashmap.c
@ -37,23 +37,26 @@
|
|||||||
* internal use. It stores the number of buckets the hashmap was created
|
* internal use. It stores the number of buckets the hashmap was created
|
||||||
* with.
|
* with.
|
||||||
*/
|
*/
|
||||||
struct hashentry_s {
|
struct hashentry_s
|
||||||
char *key;
|
{
|
||||||
void *data;
|
char *key;
|
||||||
size_t len;
|
void *data;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
struct hashentry_s *prev, *next;
|
struct hashentry_s *prev, *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct hashbucket_s {
|
struct hashbucket_s
|
||||||
struct hashentry_s *head, *tail;
|
{
|
||||||
|
struct hashentry_s *head, *tail;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct hashmap_s {
|
struct hashmap_s
|
||||||
unsigned int size;
|
{
|
||||||
hashmap_iter end_iterator;
|
unsigned int size;
|
||||||
|
hashmap_iter end_iterator;
|
||||||
|
|
||||||
struct hashbucket_s *buckets;
|
struct hashbucket_s *buckets;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -66,25 +69,26 @@ struct hashmap_s {
|
|||||||
* If any of the arguments are invalid a negative number is returned.
|
* If any of the arguments are invalid a negative number is returned.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
hashfunc(const char *key, unsigned int size)
|
hashfunc (const char *key, unsigned int size)
|
||||||
{
|
{
|
||||||
uint32_t hash;
|
uint32_t hash;
|
||||||
|
|
||||||
if (key == NULL)
|
if (key == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
return -ERANGE;
|
return -ERANGE;
|
||||||
|
|
||||||
for (hash = tolower(*key++); *key != '\0'; key++) {
|
for (hash = tolower (*key++); *key != '\0'; key++)
|
||||||
uint32_t bit = (hash & 1) ? (1 << (sizeof(uint32_t) - 1)) : 0;
|
{
|
||||||
|
uint32_t bit = (hash & 1) ? (1 << (sizeof (uint32_t) - 1)) : 0;
|
||||||
|
|
||||||
hash >>= 1;
|
hash >>= 1;
|
||||||
|
|
||||||
hash += tolower(*key) + bit;
|
hash += tolower (*key) + bit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Keep the hash within the table limits */
|
/* Keep the hash within the table limits */
|
||||||
return hash % size;
|
return hash % size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -95,28 +99,29 @@ hashfunc(const char *key, unsigned int size)
|
|||||||
* NULLs are also returned if memory could not be allocated for hashmap.
|
* NULLs are also returned if memory could not be allocated for hashmap.
|
||||||
*/
|
*/
|
||||||
hashmap_t
|
hashmap_t
|
||||||
hashmap_create(unsigned int nbuckets)
|
hashmap_create (unsigned int nbuckets)
|
||||||
{
|
{
|
||||||
struct hashmap_s *ptr;
|
struct hashmap_s *ptr;
|
||||||
|
|
||||||
if (nbuckets == 0)
|
if (nbuckets == 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
ptr = safecalloc(1, sizeof(struct hashmap_s));
|
ptr = safecalloc (1, sizeof (struct hashmap_s));
|
||||||
if (!ptr)
|
if (!ptr)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
ptr->size = nbuckets;
|
ptr->size = nbuckets;
|
||||||
ptr->buckets = safecalloc(nbuckets, sizeof(struct hashbucket_s));
|
ptr->buckets = safecalloc (nbuckets, sizeof (struct hashbucket_s));
|
||||||
if (!ptr->buckets) {
|
if (!ptr->buckets)
|
||||||
safefree(ptr);
|
{
|
||||||
return NULL;
|
safefree (ptr);
|
||||||
}
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* This points to "one" past the end of the hashmap. */
|
/* This points to "one" past the end of the hashmap. */
|
||||||
ptr->end_iterator = 0;
|
ptr->end_iterator = 0;
|
||||||
|
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -127,26 +132,27 @@ hashmap_create(unsigned int nbuckets)
|
|||||||
* negative number is returned if "entry" was NULL
|
* negative number is returned if "entry" was NULL
|
||||||
*/
|
*/
|
||||||
static inline int
|
static inline int
|
||||||
delete_hashbucket(struct hashbucket_s *bucket)
|
delete_hashbucket (struct hashbucket_s *bucket)
|
||||||
{
|
{
|
||||||
struct hashentry_s *nextptr;
|
struct hashentry_s *nextptr;
|
||||||
struct hashentry_s *ptr;
|
struct hashentry_s *ptr;
|
||||||
|
|
||||||
if (bucket == NULL || bucket->head == NULL)
|
if (bucket == NULL || bucket->head == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ptr = bucket->head;
|
ptr = bucket->head;
|
||||||
while (ptr) {
|
while (ptr)
|
||||||
nextptr = ptr->next;
|
{
|
||||||
|
nextptr = ptr->next;
|
||||||
|
|
||||||
safefree(ptr->key);
|
safefree (ptr->key);
|
||||||
safefree(ptr->data);
|
safefree (ptr->data);
|
||||||
safefree(ptr);
|
safefree (ptr);
|
||||||
|
|
||||||
ptr = nextptr;
|
ptr = nextptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -156,23 +162,25 @@ delete_hashbucket(struct hashbucket_s *bucket)
|
|||||||
* negative if a NULL "map" was supplied
|
* negative if a NULL "map" was supplied
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
hashmap_delete(hashmap_t map)
|
hashmap_delete (hashmap_t map)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
if (map == NULL)
|
if (map == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
for (i = 0; i != map->size; i++) {
|
for (i = 0; i != map->size; i++)
|
||||||
if (map->buckets[i].head != NULL) {
|
{
|
||||||
delete_hashbucket(&map->buckets[i]);
|
if (map->buckets[i].head != NULL)
|
||||||
}
|
{
|
||||||
}
|
delete_hashbucket (&map->buckets[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
safefree(map->buckets);
|
safefree (map->buckets);
|
||||||
safefree(map);
|
safefree (map);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -186,67 +194,69 @@ hashmap_delete(hashmap_t map)
|
|||||||
* negative number if there are errors
|
* negative number if there are errors
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
hashmap_insert(hashmap_t map, const char *key, const void *data, size_t len)
|
hashmap_insert (hashmap_t map, const char *key, const void *data, size_t len)
|
||||||
{
|
{
|
||||||
struct hashentry_s *ptr;
|
struct hashentry_s *ptr;
|
||||||
int hash;
|
int hash;
|
||||||
char *key_copy;
|
char *key_copy;
|
||||||
void *data_copy;
|
void *data_copy;
|
||||||
|
|
||||||
assert(map != NULL);
|
assert (map != NULL);
|
||||||
assert(key != NULL);
|
assert (key != NULL);
|
||||||
assert(data != NULL);
|
assert (data != NULL);
|
||||||
assert(len > 0);
|
assert (len > 0);
|
||||||
|
|
||||||
if (map == NULL || key == NULL)
|
if (map == NULL || key == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (!data || len < 1)
|
if (!data || len < 1)
|
||||||
return -ERANGE;
|
return -ERANGE;
|
||||||
|
|
||||||
hash = hashfunc(key, map->size);
|
hash = hashfunc (key, map->size);
|
||||||
if (hash < 0)
|
if (hash < 0)
|
||||||
return hash;
|
return hash;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* First make copies of the key and data in case there is a memory
|
* First make copies of the key and data in case there is a memory
|
||||||
* problem later.
|
* problem later.
|
||||||
*/
|
*/
|
||||||
key_copy = safestrdup(key);
|
key_copy = safestrdup (key);
|
||||||
if (!key_copy)
|
if (!key_copy)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
data_copy = safemalloc(len);
|
data_copy = safemalloc (len);
|
||||||
if (!data_copy) {
|
if (!data_copy)
|
||||||
safefree(key_copy);
|
{
|
||||||
return -ENOMEM;
|
safefree (key_copy);
|
||||||
}
|
return -ENOMEM;
|
||||||
memcpy(data_copy, data, len);
|
}
|
||||||
|
memcpy (data_copy, data, len);
|
||||||
|
|
||||||
ptr = safemalloc(sizeof(struct hashentry_s));
|
ptr = safemalloc (sizeof (struct hashentry_s));
|
||||||
if (!ptr) {
|
if (!ptr)
|
||||||
safefree(key_copy);
|
{
|
||||||
safefree(data_copy);
|
safefree (key_copy);
|
||||||
return -ENOMEM;
|
safefree (data_copy);
|
||||||
}
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
ptr->key = key_copy;
|
ptr->key = key_copy;
|
||||||
ptr->data = data_copy;
|
ptr->data = data_copy;
|
||||||
ptr->len = len;
|
ptr->len = len;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now add the entry to the end of the bucket chain.
|
* Now add the entry to the end of the bucket chain.
|
||||||
*/
|
*/
|
||||||
ptr->next = NULL;
|
ptr->next = NULL;
|
||||||
ptr->prev = map->buckets[hash].tail;
|
ptr->prev = map->buckets[hash].tail;
|
||||||
if (map->buckets[hash].tail)
|
if (map->buckets[hash].tail)
|
||||||
map->buckets[hash].tail->next = ptr;
|
map->buckets[hash].tail->next = ptr;
|
||||||
|
|
||||||
map->buckets[hash].tail = ptr;
|
map->buckets[hash].tail = ptr;
|
||||||
if (!map->buckets[hash].head)
|
if (!map->buckets[hash].head)
|
||||||
map->buckets[hash].head = ptr;
|
map->buckets[hash].head = ptr;
|
||||||
|
|
||||||
map->end_iterator++;
|
map->end_iterator++;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -255,17 +265,17 @@ hashmap_insert(hashmap_t map, const char *key, const void *data, size_t len)
|
|||||||
* Returns: an negative value upon error.
|
* Returns: an negative value upon error.
|
||||||
*/
|
*/
|
||||||
hashmap_iter
|
hashmap_iter
|
||||||
hashmap_first(hashmap_t map)
|
hashmap_first (hashmap_t map)
|
||||||
{
|
{
|
||||||
assert(map != NULL);
|
assert (map != NULL);
|
||||||
|
|
||||||
if (!map)
|
if (!map)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (map->end_iterator == 0)
|
if (map->end_iterator == 0)
|
||||||
return -1;
|
return -1;
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -275,18 +285,18 @@ hashmap_first(hashmap_t map)
|
|||||||
* 0 otherwise
|
* 0 otherwise
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
hashmap_is_end(hashmap_t map, hashmap_iter iter)
|
hashmap_is_end (hashmap_t map, hashmap_iter iter)
|
||||||
{
|
{
|
||||||
assert(map != NULL);
|
assert (map != NULL);
|
||||||
assert(iter >= 0);
|
assert (iter >= 0);
|
||||||
|
|
||||||
if (!map || iter < 0)
|
if (!map || iter < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (iter == map->end_iterator)
|
if (iter == map->end_iterator)
|
||||||
return 1;
|
return 1;
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -298,37 +308,40 @@ hashmap_is_end(hashmap_t map, hashmap_iter iter)
|
|||||||
* an "end-iterator" if the key wasn't found
|
* an "end-iterator" if the key wasn't found
|
||||||
*/
|
*/
|
||||||
hashmap_iter
|
hashmap_iter
|
||||||
hashmap_find(hashmap_t map, const char *key)
|
hashmap_find (hashmap_t map, const char *key)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
hashmap_iter iter = 0;
|
hashmap_iter iter = 0;
|
||||||
struct hashentry_s *ptr;
|
struct hashentry_s *ptr;
|
||||||
|
|
||||||
assert(map != NULL);
|
assert (map != NULL);
|
||||||
assert(key != NULL);
|
assert (key != NULL);
|
||||||
|
|
||||||
if (!map || !key)
|
if (!map || !key)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Loop through all the keys and look for the first occurrence
|
* Loop through all the keys and look for the first occurrence
|
||||||
* of a particular key.
|
* of a particular key.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i != map->size; i++) {
|
for (i = 0; i != map->size; i++)
|
||||||
ptr = map->buckets[i].head;
|
{
|
||||||
|
ptr = map->buckets[i].head;
|
||||||
|
|
||||||
while (ptr) {
|
while (ptr)
|
||||||
if (strcasecmp(ptr->key, key) == 0) {
|
{
|
||||||
/* Found it, so return the current count */
|
if (strcasecmp (ptr->key, key) == 0)
|
||||||
return iter;
|
{
|
||||||
}
|
/* Found it, so return the current count */
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
|
||||||
iter++;
|
iter++;
|
||||||
ptr = ptr->next;
|
ptr = ptr->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return iter;
|
return iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -338,37 +351,41 @@ hashmap_find(hashmap_t map, const char *key)
|
|||||||
* negative upon error
|
* negative upon error
|
||||||
*/
|
*/
|
||||||
ssize_t
|
ssize_t
|
||||||
hashmap_return_entry(hashmap_t map, hashmap_iter iter, char **key, void **data)
|
hashmap_return_entry (hashmap_t map, hashmap_iter iter, char **key,
|
||||||
|
void **data)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
struct hashentry_s *ptr;
|
struct hashentry_s *ptr;
|
||||||
hashmap_iter count = 0;
|
hashmap_iter count = 0;
|
||||||
|
|
||||||
assert(map != NULL);
|
assert (map != NULL);
|
||||||
assert(iter >= 0);
|
assert (iter >= 0);
|
||||||
assert(iter != map->end_iterator);
|
assert (iter != map->end_iterator);
|
||||||
assert(key != NULL);
|
assert (key != NULL);
|
||||||
assert(data != NULL);
|
assert (data != NULL);
|
||||||
|
|
||||||
if (!map || iter < 0 || !key || !data)
|
if (!map || iter < 0 || !key || !data)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
for (i = 0; i != map->size; i++) {
|
for (i = 0; i != map->size; i++)
|
||||||
ptr = map->buckets[i].head;
|
{
|
||||||
while (ptr) {
|
ptr = map->buckets[i].head;
|
||||||
if (count == iter) {
|
while (ptr)
|
||||||
/* This is the data so return it */
|
{
|
||||||
*key = ptr->key;
|
if (count == iter)
|
||||||
*data = ptr->data;
|
{
|
||||||
return ptr->len;
|
/* This is the data so return it */
|
||||||
}
|
*key = ptr->key;
|
||||||
|
*data = ptr->data;
|
||||||
|
return ptr->len;
|
||||||
|
}
|
||||||
|
|
||||||
ptr = ptr->next;
|
ptr = ptr->next;
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -379,31 +396,32 @@ hashmap_return_entry(hashmap_t map, hashmap_iter iter, char **key, void **data)
|
|||||||
* count found
|
* count found
|
||||||
*/
|
*/
|
||||||
ssize_t
|
ssize_t
|
||||||
hashmap_search(hashmap_t map, const char *key)
|
hashmap_search (hashmap_t map, const char *key)
|
||||||
{
|
{
|
||||||
int hash;
|
int hash;
|
||||||
struct hashentry_s *ptr;
|
struct hashentry_s *ptr;
|
||||||
ssize_t count = 0;
|
ssize_t count = 0;
|
||||||
|
|
||||||
if (map == NULL || key == NULL)
|
if (map == NULL || key == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
hash = hashfunc(key, map->size);
|
hash = hashfunc (key, map->size);
|
||||||
if (hash < 0)
|
if (hash < 0)
|
||||||
return hash;
|
return hash;
|
||||||
|
|
||||||
ptr = map->buckets[hash].head;
|
ptr = map->buckets[hash].head;
|
||||||
|
|
||||||
/* All right, there is an entry here, now see if it's the one we want */
|
/* All right, there is an entry here, now see if it's the one we want */
|
||||||
while (ptr) {
|
while (ptr)
|
||||||
if (strcasecmp(ptr->key, key) == 0)
|
{
|
||||||
++count;
|
if (strcasecmp (ptr->key, key) == 0)
|
||||||
|
++count;
|
||||||
|
|
||||||
/* This entry didn't contain the key; move to the next one */
|
/* This entry didn't contain the key; move to the next one */
|
||||||
ptr = ptr->next;
|
ptr = ptr->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -415,30 +433,32 @@ hashmap_search(hashmap_t map, const char *key)
|
|||||||
* length of data for the entry
|
* length of data for the entry
|
||||||
*/
|
*/
|
||||||
ssize_t
|
ssize_t
|
||||||
hashmap_entry_by_key(hashmap_t map, const char *key, void **data)
|
hashmap_entry_by_key (hashmap_t map, const char *key, void **data)
|
||||||
{
|
{
|
||||||
int hash;
|
int hash;
|
||||||
struct hashentry_s *ptr;
|
struct hashentry_s *ptr;
|
||||||
|
|
||||||
if (!map || !key || !data)
|
if (!map || !key || !data)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
hash = hashfunc(key, map->size);
|
hash = hashfunc (key, map->size);
|
||||||
if (hash < 0)
|
if (hash < 0)
|
||||||
return hash;
|
return hash;
|
||||||
|
|
||||||
ptr = map->buckets[hash].head;
|
ptr = map->buckets[hash].head;
|
||||||
|
|
||||||
while (ptr) {
|
while (ptr)
|
||||||
if (strcasecmp(ptr->key, key) == 0) {
|
{
|
||||||
*data = ptr->data;
|
if (strcasecmp (ptr->key, key) == 0)
|
||||||
return ptr->len;
|
{
|
||||||
}
|
*data = ptr->data;
|
||||||
|
return ptr->len;
|
||||||
|
}
|
||||||
|
|
||||||
ptr = ptr->next;
|
ptr = ptr->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -450,53 +470,55 @@ hashmap_entry_by_key(hashmap_t map, const char *key, void **data)
|
|||||||
* positive count of entries deleted
|
* positive count of entries deleted
|
||||||
*/
|
*/
|
||||||
ssize_t
|
ssize_t
|
||||||
hashmap_remove(hashmap_t map, const char *key)
|
hashmap_remove (hashmap_t map, const char *key)
|
||||||
{
|
{
|
||||||
int hash;
|
int hash;
|
||||||
struct hashentry_s *ptr, *next;
|
struct hashentry_s *ptr, *next;
|
||||||
short int deleted = 0;
|
short int deleted = 0;
|
||||||
|
|
||||||
if (map == NULL || key == NULL)
|
if (map == NULL || key == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
hash = hashfunc(key, map->size);
|
hash = hashfunc (key, map->size);
|
||||||
if (hash < 0)
|
if (hash < 0)
|
||||||
return hash;
|
return hash;
|
||||||
|
|
||||||
ptr = map->buckets[hash].head;
|
ptr = map->buckets[hash].head;
|
||||||
while (ptr) {
|
while (ptr)
|
||||||
if (strcasecmp(ptr->key, key) == 0) {
|
{
|
||||||
/*
|
if (strcasecmp (ptr->key, key) == 0)
|
||||||
* Found the data, now need to remove everything
|
{
|
||||||
* and update the hashmap.
|
/*
|
||||||
*/
|
* Found the data, now need to remove everything
|
||||||
next = ptr->next;
|
* and update the hashmap.
|
||||||
|
*/
|
||||||
|
next = ptr->next;
|
||||||
|
|
||||||
if (ptr->prev)
|
if (ptr->prev)
|
||||||
ptr->prev->next = ptr->next;
|
ptr->prev->next = ptr->next;
|
||||||
if (ptr->next)
|
if (ptr->next)
|
||||||
ptr->next->prev = ptr->prev;
|
ptr->next->prev = ptr->prev;
|
||||||
|
|
||||||
if (map->buckets[hash].head == ptr)
|
if (map->buckets[hash].head == ptr)
|
||||||
map->buckets[hash].head = ptr->next;
|
map->buckets[hash].head = ptr->next;
|
||||||
if (map->buckets[hash].tail == ptr)
|
if (map->buckets[hash].tail == ptr)
|
||||||
map->buckets[hash].tail = ptr->prev;
|
map->buckets[hash].tail = ptr->prev;
|
||||||
|
|
||||||
safefree(ptr->key);
|
safefree (ptr->key);
|
||||||
safefree(ptr->data);
|
safefree (ptr->data);
|
||||||
safefree(ptr);
|
safefree (ptr);
|
||||||
|
|
||||||
++deleted;
|
++deleted;
|
||||||
--map->end_iterator;
|
--map->end_iterator;
|
||||||
|
|
||||||
ptr = next;
|
ptr = next;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This entry didn't contain the key; move to the next one */
|
/* This entry didn't contain the key; move to the next one */
|
||||||
ptr = ptr->next;
|
ptr = ptr->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The key was not found, so return 0 */
|
/* The key was not found, so return 0 */
|
||||||
return deleted;
|
return deleted;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,8 @@
|
|||||||
|
|
||||||
/* Allow the use in C++ code. */
|
/* Allow the use in C++ code. */
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
extern "C" {
|
extern "C"
|
||||||
|
{
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -31,15 +32,15 @@ extern "C" {
|
|||||||
* hash map. Sure, it's a pointer, but the struct is hidden in the C file.
|
* hash map. Sure, it's a pointer, but the struct is hidden in the C file.
|
||||||
* So, just use the hashmap_t like it's a cookie. :)
|
* So, just use the hashmap_t like it's a cookie. :)
|
||||||
*/
|
*/
|
||||||
typedef struct hashmap_s *hashmap_t;
|
typedef struct hashmap_s *hashmap_t;
|
||||||
typedef int hashmap_iter;
|
typedef int hashmap_iter;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* hashmap_create() takes one argument, which is the number of buckets to
|
* hashmap_create() takes one argument, which is the number of buckets to
|
||||||
* use internally. hashmap_delete() is self explanatory.
|
* use internally. hashmap_delete() is self explanatory.
|
||||||
*/
|
*/
|
||||||
extern hashmap_t hashmap_create(unsigned int nbuckets);
|
extern hashmap_t hashmap_create (unsigned int nbuckets);
|
||||||
extern int hashmap_delete(hashmap_t map);
|
extern int hashmap_delete (hashmap_t map);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When the you insert a key/data pair into the hashmap it will the key
|
* When the you insert a key/data pair into the hashmap it will the key
|
||||||
@ -50,15 +51,15 @@ extern "C" {
|
|||||||
* Returns: negative on error
|
* Returns: negative on error
|
||||||
* 0 upon successful insert
|
* 0 upon successful insert
|
||||||
*/
|
*/
|
||||||
extern int hashmap_insert(hashmap_t map, const char *key,
|
extern int hashmap_insert (hashmap_t map, const char *key,
|
||||||
const void *data, size_t len);
|
const void *data, size_t len);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get an iterator to the first entry.
|
* Get an iterator to the first entry.
|
||||||
*
|
*
|
||||||
* Returns: an negative value upon error.
|
* Returns: an negative value upon error.
|
||||||
*/
|
*/
|
||||||
extern hashmap_iter hashmap_first(hashmap_t map);
|
extern hashmap_iter hashmap_first (hashmap_t map);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Checks to see if the iterator is pointing at the "end" of the entries.
|
* Checks to see if the iterator is pointing at the "end" of the entries.
|
||||||
@ -66,7 +67,7 @@ extern "C" {
|
|||||||
* Returns: 1 if it is the end
|
* Returns: 1 if it is the end
|
||||||
* 0 otherwise
|
* 0 otherwise
|
||||||
*/
|
*/
|
||||||
extern int hashmap_is_end(hashmap_t map, hashmap_iter iter);
|
extern int hashmap_is_end (hashmap_t map, hashmap_iter iter);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return a "pointer" to the first instance of the particular key. It can
|
* Return a "pointer" to the first instance of the particular key. It can
|
||||||
@ -76,7 +77,7 @@ extern "C" {
|
|||||||
* an "iterator" pointing at the first key
|
* an "iterator" pointing at the first key
|
||||||
* an "end-iterator" if the key wasn't found
|
* an "end-iterator" if the key wasn't found
|
||||||
*/
|
*/
|
||||||
extern hashmap_iter hashmap_find(hashmap_t map, const char *key);
|
extern hashmap_iter hashmap_find (hashmap_t map, const char *key);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Retrieve the key/data associated with a particular iterator.
|
* Retrieve the key/data associated with a particular iterator.
|
||||||
@ -86,8 +87,8 @@ extern "C" {
|
|||||||
* Returns: the length of the data block upon success
|
* Returns: the length of the data block upon success
|
||||||
* negative upon error
|
* negative upon error
|
||||||
*/
|
*/
|
||||||
extern ssize_t hashmap_return_entry(hashmap_t map, hashmap_iter iter,
|
extern ssize_t hashmap_return_entry (hashmap_t map, hashmap_iter iter,
|
||||||
char **key, void **data);
|
char **key, void **data);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the first entry (assuming there is more than one) for a particular
|
* Get the first entry (assuming there is more than one) for a particular
|
||||||
@ -97,8 +98,8 @@ extern "C" {
|
|||||||
* zero if no entry is found
|
* zero if no entry is found
|
||||||
* length of data for the entry
|
* length of data for the entry
|
||||||
*/
|
*/
|
||||||
extern ssize_t hashmap_entry_by_key(hashmap_t map, const char *key,
|
extern ssize_t hashmap_entry_by_key (hashmap_t map, const char *key,
|
||||||
void **data);
|
void **data);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Searches for _any_ occurrances of "key" within the hashmap and returns the
|
* Searches for _any_ occurrances of "key" within the hashmap and returns the
|
||||||
@ -108,7 +109,7 @@ extern "C" {
|
|||||||
* zero if no key is found
|
* zero if no key is found
|
||||||
* count found (positive value)
|
* count found (positive value)
|
||||||
*/
|
*/
|
||||||
extern ssize_t hashmap_search(hashmap_t map, const char *key);
|
extern ssize_t hashmap_search (hashmap_t map, const char *key);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Go through the hashmap and remove the particular key.
|
* Go through the hashmap and remove the particular key.
|
||||||
@ -118,9 +119,9 @@ extern "C" {
|
|||||||
* 0 if the key was not found
|
* 0 if the key was not found
|
||||||
* positive count of entries deleted
|
* positive count of entries deleted
|
||||||
*/
|
*/
|
||||||
extern ssize_t hashmap_remove(hashmap_t map, const char *key);
|
extern ssize_t hashmap_remove (hashmap_t map, const char *key);
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
}
|
}
|
||||||
#endif /* C++ */
|
#endif /* C++ */
|
||||||
#endif /* _HASHMAP_H */
|
#endif /* _HASHMAP_H */
|
||||||
|
135
src/heap.c
135
src/heap.c
@ -28,71 +28,72 @@
|
|||||||
#include "text.h"
|
#include "text.h"
|
||||||
|
|
||||||
void *
|
void *
|
||||||
debugging_calloc(size_t nmemb, size_t size, const char *file,
|
debugging_calloc (size_t nmemb, size_t size, const char *file,
|
||||||
unsigned long line)
|
unsigned long line)
|
||||||
{
|
{
|
||||||
void *ptr;
|
void *ptr;
|
||||||
|
|
||||||
assert(nmemb > 0);
|
assert (nmemb > 0);
|
||||||
assert(size > 0);
|
assert (size > 0);
|
||||||
|
|
||||||
ptr = calloc(nmemb, size);
|
ptr = calloc (nmemb, size);
|
||||||
fprintf(stderr, "{calloc: %p:%zu x %zu} %s:%lu\n", ptr, nmemb, size, file,
|
fprintf (stderr, "{calloc: %p:%zu x %zu} %s:%lu\n", ptr, nmemb, size, file,
|
||||||
line);
|
line);
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *
|
void *
|
||||||
debugging_malloc(size_t size, const char *file, unsigned long line)
|
debugging_malloc (size_t size, const char *file, unsigned long line)
|
||||||
{
|
{
|
||||||
void *ptr;
|
void *ptr;
|
||||||
|
|
||||||
assert(size > 0);
|
assert (size > 0);
|
||||||
|
|
||||||
ptr = malloc(size);
|
ptr = malloc (size);
|
||||||
fprintf(stderr, "{malloc: %p:%zu} %s:%lu\n", ptr, size, file, line);
|
fprintf (stderr, "{malloc: %p:%zu} %s:%lu\n", ptr, size, file, line);
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *
|
void *
|
||||||
debugging_realloc(void *ptr, size_t size, const char *file, unsigned long line)
|
debugging_realloc (void *ptr, size_t size, const char *file,
|
||||||
|
unsigned long line)
|
||||||
{
|
{
|
||||||
void *newptr;
|
void *newptr;
|
||||||
|
|
||||||
assert(size > 0);
|
assert (size > 0);
|
||||||
|
|
||||||
newptr = realloc(ptr, size);
|
newptr = realloc (ptr, size);
|
||||||
fprintf(stderr, "{realloc: %p -> %p:%zu} %s:%lu\n", ptr, newptr, size,
|
fprintf (stderr, "{realloc: %p -> %p:%zu} %s:%lu\n", ptr, newptr, size,
|
||||||
file, line);
|
file, line);
|
||||||
return newptr;
|
return newptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
debugging_free(void *ptr, const char *file, unsigned long line)
|
debugging_free (void *ptr, const char *file, unsigned long line)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "{free: %p} %s:%lu\n", ptr, file, line);
|
fprintf (stderr, "{free: %p} %s:%lu\n", ptr, file, line);
|
||||||
|
|
||||||
if (ptr != NULL)
|
if (ptr != NULL)
|
||||||
free(ptr);
|
free (ptr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
debugging_strdup(const char *s, const char *file, unsigned long line)
|
debugging_strdup (const char *s, const char *file, unsigned long line)
|
||||||
{
|
{
|
||||||
char *ptr;
|
char *ptr;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
assert(s != NULL);
|
assert (s != NULL);
|
||||||
|
|
||||||
len = strlen(s) + 1;
|
len = strlen (s) + 1;
|
||||||
ptr = malloc(len);
|
ptr = malloc (len);
|
||||||
if (!ptr)
|
if (!ptr)
|
||||||
return NULL;
|
return NULL;
|
||||||
memcpy(ptr, s, len);
|
memcpy (ptr, s, len);
|
||||||
|
|
||||||
fprintf(stderr, "{strdup: %p:%zu} %s:%lu\n", ptr, len, file, line);
|
fprintf (stderr, "{strdup: %p:%zu} %s:%lu\n", ptr, len, file, line);
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -104,32 +105,32 @@ debugging_strdup(const char *s, const char *file, unsigned long line)
|
|||||||
* solution.
|
* solution.
|
||||||
*/
|
*/
|
||||||
void *
|
void *
|
||||||
malloc_shared_memory(size_t size)
|
malloc_shared_memory (size_t size)
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
void *ptr;
|
void *ptr;
|
||||||
char buffer[32];
|
char buffer[32];
|
||||||
|
|
||||||
static char *shared_file = "/tmp/tinyproxy.shared.XXXXXX";
|
static char *shared_file = "/tmp/tinyproxy.shared.XXXXXX";
|
||||||
|
|
||||||
assert(size > 0);
|
assert (size > 0);
|
||||||
|
|
||||||
strlcpy(buffer, shared_file, sizeof(buffer));
|
strlcpy (buffer, shared_file, sizeof (buffer));
|
||||||
|
|
||||||
/* Only allow u+rw bits. This may be required for some versions
|
/* Only allow u+rw bits. This may be required for some versions
|
||||||
* of glibc so that mkstemp() doesn't make us vulnerable.
|
* of glibc so that mkstemp() doesn't make us vulnerable.
|
||||||
*/
|
*/
|
||||||
umask(0177);
|
umask (0177);
|
||||||
|
|
||||||
if ((fd = mkstemp(buffer)) == -1)
|
if ((fd = mkstemp (buffer)) == -1)
|
||||||
return MAP_FAILED;
|
return MAP_FAILED;
|
||||||
unlink(buffer);
|
unlink (buffer);
|
||||||
|
|
||||||
if (ftruncate(fd, size) == -1)
|
if (ftruncate (fd, size) == -1)
|
||||||
return MAP_FAILED;
|
return MAP_FAILED;
|
||||||
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
ptr = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
|
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -137,21 +138,21 @@ malloc_shared_memory(size_t size)
|
|||||||
* zero.
|
* zero.
|
||||||
*/
|
*/
|
||||||
void *
|
void *
|
||||||
calloc_shared_memory(size_t nmemb, size_t size)
|
calloc_shared_memory (size_t nmemb, size_t size)
|
||||||
{
|
{
|
||||||
void *ptr;
|
void *ptr;
|
||||||
long length;
|
long length;
|
||||||
|
|
||||||
assert(nmemb > 0);
|
assert (nmemb > 0);
|
||||||
assert(size > 0);
|
assert (size > 0);
|
||||||
|
|
||||||
length = nmemb * size;
|
length = nmemb * size;
|
||||||
|
|
||||||
ptr = malloc_shared_memory(length);
|
ptr = malloc_shared_memory (length);
|
||||||
if (ptr == MAP_FAILED)
|
if (ptr == MAP_FAILED)
|
||||||
return ptr;
|
return ptr;
|
||||||
|
|
||||||
memset(ptr, 0, length);
|
memset (ptr, 0, length);
|
||||||
|
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
22
src/heap.h
22
src/heap.h
@ -26,15 +26,15 @@
|
|||||||
*/
|
*/
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
|
||||||
extern void *debugging_calloc(size_t nmemb, size_t size, const char *file,
|
extern void *debugging_calloc (size_t nmemb, size_t size, const char *file,
|
||||||
unsigned long line);
|
unsigned long line);
|
||||||
extern void *debugging_malloc(size_t size, const char *file,
|
extern void *debugging_malloc (size_t size, const char *file,
|
||||||
unsigned long line);
|
unsigned long line);
|
||||||
extern void debugging_free(void *ptr, const char *file, unsigned long line);
|
extern void debugging_free (void *ptr, const char *file, unsigned long line);
|
||||||
extern void *debugging_realloc(void *ptr, size_t size, const char *file,
|
extern void *debugging_realloc (void *ptr, size_t size, const char *file,
|
||||||
unsigned long line);
|
unsigned long line);
|
||||||
extern char *debugging_strdup(const char *s, const char *file,
|
extern char *debugging_strdup (const char *s, const char *file,
|
||||||
unsigned long line);
|
unsigned long line);
|
||||||
|
|
||||||
# define safecalloc(x, y) debugging_calloc(x, y, __FILE__, __LINE__)
|
# define safecalloc(x, y) debugging_calloc(x, y, __FILE__, __LINE__)
|
||||||
# define safemalloc(x) debugging_malloc(x, __FILE__, __LINE__)
|
# define safemalloc(x) debugging_malloc(x, __FILE__, __LINE__)
|
||||||
@ -60,7 +60,7 @@ free(*__safefree_tmp); \
|
|||||||
/*
|
/*
|
||||||
* Allocate memory from the "shared" region of memory.
|
* Allocate memory from the "shared" region of memory.
|
||||||
*/
|
*/
|
||||||
extern void *malloc_shared_memory(size_t size);
|
extern void *malloc_shared_memory (size_t size);
|
||||||
extern void *calloc_shared_memory(size_t nmemb, size_t size);
|
extern void *calloc_shared_memory (size_t nmemb, size_t size);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
347
src/html-error.c
347
src/html-error.c
@ -33,77 +33,77 @@
|
|||||||
/*
|
/*
|
||||||
* Add an error number -> filename mapping to the errorpages list.
|
* Add an error number -> filename mapping to the errorpages list.
|
||||||
*/
|
*/
|
||||||
#define ERRORNUM_BUFSIZE 8 /* this is more than required */
|
#define ERRORNUM_BUFSIZE 8 /* this is more than required */
|
||||||
#define ERRPAGES_BUCKETCOUNT 16
|
#define ERRPAGES_BUCKETCOUNT 16
|
||||||
|
|
||||||
int
|
int
|
||||||
add_new_errorpage(char *filepath, unsigned int errornum)
|
add_new_errorpage (char *filepath, unsigned int errornum)
|
||||||
{
|
{
|
||||||
char errornbuf[ERRORNUM_BUFSIZE];
|
char errornbuf[ERRORNUM_BUFSIZE];
|
||||||
|
|
||||||
config.errorpages = hashmap_create(ERRPAGES_BUCKETCOUNT);
|
config.errorpages = hashmap_create (ERRPAGES_BUCKETCOUNT);
|
||||||
if (!config.errorpages)
|
if (!config.errorpages)
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|
||||||
snprintf(errornbuf, ERRORNUM_BUFSIZE, "%u", errornum);
|
snprintf (errornbuf, ERRORNUM_BUFSIZE, "%u", errornum);
|
||||||
|
|
||||||
if (hashmap_insert(config.errorpages, errornbuf,
|
if (hashmap_insert (config.errorpages, errornbuf,
|
||||||
filepath, strlen(filepath) + 1) < 0)
|
filepath, strlen (filepath) + 1) < 0)
|
||||||
return (-1);
|
return (-1);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the file appropriate for a given error.
|
* Get the file appropriate for a given error.
|
||||||
*/
|
*/
|
||||||
static char *
|
static char *
|
||||||
get_html_file(unsigned int errornum)
|
get_html_file (unsigned int errornum)
|
||||||
{
|
{
|
||||||
hashmap_iter result_iter;
|
hashmap_iter result_iter;
|
||||||
char errornbuf[ERRORNUM_BUFSIZE];
|
char errornbuf[ERRORNUM_BUFSIZE];
|
||||||
char *key;
|
char *key;
|
||||||
static char *val;
|
static char *val;
|
||||||
|
|
||||||
assert(errornum >= 100 && errornum < 1000);
|
assert (errornum >= 100 && errornum < 1000);
|
||||||
|
|
||||||
if (!config.errorpages)
|
if (!config.errorpages)
|
||||||
return (config.errorpage_undef);
|
return (config.errorpage_undef);
|
||||||
|
|
||||||
snprintf(errornbuf, ERRORNUM_BUFSIZE, "%u", errornum);
|
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))
|
if (hashmap_is_end (config.errorpages, result_iter))
|
||||||
return (config.errorpage_undef);
|
return (config.errorpage_undef);
|
||||||
|
|
||||||
if (hashmap_return_entry(config.errorpages, result_iter,
|
if (hashmap_return_entry (config.errorpages, result_iter,
|
||||||
&key, (void **)&val) < 0)
|
&key, (void **) &val) < 0)
|
||||||
return (config.errorpage_undef);
|
return (config.errorpage_undef);
|
||||||
|
|
||||||
return (val);
|
return (val);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Look up the value for a variable.
|
* Look up the value for a variable.
|
||||||
*/
|
*/
|
||||||
static char *
|
static char *
|
||||||
lookup_variable(struct conn_s *connptr, char *varname)
|
lookup_variable (struct conn_s *connptr, char *varname)
|
||||||
{
|
{
|
||||||
hashmap_iter result_iter;
|
hashmap_iter result_iter;
|
||||||
char *key;
|
char *key;
|
||||||
static char *data;
|
static char *data;
|
||||||
|
|
||||||
result_iter = hashmap_find(connptr->error_variables, varname);
|
result_iter = hashmap_find (connptr->error_variables, varname);
|
||||||
|
|
||||||
if (hashmap_is_end(connptr->error_variables, result_iter))
|
if (hashmap_is_end (connptr->error_variables, result_iter))
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
|
||||||
if (hashmap_return_entry(connptr->error_variables, result_iter,
|
if (hashmap_return_entry (connptr->error_variables, result_iter,
|
||||||
&key, (void **)&data) < 0)
|
&key, (void **) &data) < 0)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
|
|
||||||
return (data);
|
return (data);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define HTML_BUFSIZE 4096
|
#define HTML_BUFSIZE 4096
|
||||||
@ -112,116 +112,113 @@ lookup_variable(struct conn_s *connptr, char *varname)
|
|||||||
* Send an already-opened file to the client with variable substitution.
|
* Send an already-opened file to the client with variable substitution.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
send_html_file(FILE * infile, struct conn_s *connptr)
|
send_html_file (FILE * infile, struct conn_s *connptr)
|
||||||
{
|
{
|
||||||
char inbuf[HTML_BUFSIZE], *varstart = NULL, *p;
|
char inbuf[HTML_BUFSIZE], *varstart = NULL, *p;
|
||||||
char *varval;
|
char *varval;
|
||||||
int in_variable = 0, writeret;
|
int in_variable = 0, writeret;
|
||||||
|
|
||||||
while (fgets(inbuf, HTML_BUFSIZE, infile) != NULL) {
|
while (fgets (inbuf, HTML_BUFSIZE, infile) != NULL)
|
||||||
for (p = inbuf; *p; p++) {
|
{
|
||||||
switch (*p) {
|
for (p = inbuf; *p; p++)
|
||||||
case '}':
|
{
|
||||||
if (in_variable) {
|
switch (*p)
|
||||||
*p = '\0';
|
{
|
||||||
if (!
|
case '}':
|
||||||
(varval =
|
if (in_variable)
|
||||||
lookup_variable(connptr,
|
{
|
||||||
varstart)))
|
*p = '\0';
|
||||||
varval = "(unknown)";
|
if (!(varval = lookup_variable (connptr, varstart)))
|
||||||
writeret =
|
varval = "(unknown)";
|
||||||
write_message(connptr->client_fd,
|
writeret = write_message (connptr->client_fd, "%s", varval);
|
||||||
"%s", varval);
|
if (writeret)
|
||||||
if (writeret)
|
return (writeret);
|
||||||
return (writeret);
|
in_variable = 0;
|
||||||
in_variable = 0;
|
}
|
||||||
} else {
|
else
|
||||||
writeret =
|
{
|
||||||
write_message(connptr->client_fd,
|
writeret = write_message (connptr->client_fd, "%c", *p);
|
||||||
"%c", *p);
|
if (writeret)
|
||||||
if (writeret)
|
return (writeret);
|
||||||
return (writeret);
|
}
|
||||||
}
|
break;
|
||||||
break;
|
case '{':
|
||||||
case '{':
|
/* a {{ will print a single {. If we are NOT
|
||||||
/* a {{ will print a single {. If we are NOT
|
* already in a { variable, then proceed with
|
||||||
* already in a { variable, then proceed with
|
* setup. If we ARE already in a { variable,
|
||||||
* setup. If we ARE already in a { variable,
|
* this code will fallthrough to the code that
|
||||||
* this code will fallthrough to the code that
|
* just dumps a character to the client fd.
|
||||||
* just dumps a character to the client fd.
|
*/
|
||||||
*/
|
if (!in_variable)
|
||||||
if (!in_variable) {
|
{
|
||||||
varstart = p + 1;
|
varstart = p + 1;
|
||||||
in_variable++;
|
in_variable++;
|
||||||
} else
|
}
|
||||||
in_variable = 0;
|
else
|
||||||
default:
|
in_variable = 0;
|
||||||
if (!in_variable) {
|
default:
|
||||||
writeret =
|
if (!in_variable)
|
||||||
write_message(connptr->client_fd,
|
{
|
||||||
"%c", *p);
|
writeret = write_message (connptr->client_fd, "%c", *p);
|
||||||
if (writeret)
|
if (writeret)
|
||||||
return (writeret);
|
return (writeret);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in_variable = 0;
|
in_variable = 0;
|
||||||
}
|
}
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
send_http_headers(struct conn_s *connptr, int code, char *message)
|
send_http_headers (struct conn_s *connptr, int code, char *message)
|
||||||
{
|
{
|
||||||
char *headers =
|
char *headers =
|
||||||
"HTTP/1.0 %d %s\r\n"
|
"HTTP/1.0 %d %s\r\n"
|
||||||
"Server: %s/%s\r\n"
|
"Server: %s/%s\r\n"
|
||||||
"Content-Type: text/html\r\n" "Connection: close\r\n" "\r\n";
|
"Content-Type: text/html\r\n" "Connection: close\r\n" "\r\n";
|
||||||
|
|
||||||
return (write_message(connptr->client_fd, headers,
|
return (write_message (connptr->client_fd, headers,
|
||||||
code, message, PACKAGE, VERSION));
|
code, message, PACKAGE, VERSION));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Display an error to the client.
|
* Display an error to the client.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
send_http_error_message(struct conn_s *connptr)
|
send_http_error_message (struct conn_s *connptr)
|
||||||
{
|
{
|
||||||
char *error_file;
|
char *error_file;
|
||||||
FILE *infile;
|
FILE *infile;
|
||||||
int ret;
|
int ret;
|
||||||
char *fallback_error =
|
char *fallback_error =
|
||||||
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
|
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
|
||||||
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
|
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
|
||||||
"<html>\n"
|
"<html>\n"
|
||||||
"<head><title>%d %s</title></head>\n"
|
"<head><title>%d %s</title></head>\n"
|
||||||
"<body>\n"
|
"<body>\n"
|
||||||
"<h1>%s</h1>\n"
|
"<h1>%s</h1>\n"
|
||||||
"<p>%s</p>\n"
|
"<p>%s</p>\n"
|
||||||
"<hr />\n"
|
"<hr />\n"
|
||||||
"<p><em>Generated by %s version %s.</em></p>\n"
|
"<p><em>Generated by %s version %s.</em></p>\n" "</body>\n" "</html>\n";
|
||||||
"</body>\n"
|
|
||||||
"</html>\n";
|
|
||||||
|
|
||||||
send_http_headers(connptr, connptr->error_number,
|
send_http_headers (connptr, connptr->error_number, connptr->error_string);
|
||||||
connptr->error_string);
|
|
||||||
|
|
||||||
error_file = get_html_file(connptr->error_number);
|
error_file = get_html_file (connptr->error_number);
|
||||||
if (!(infile = fopen(error_file, "r"))) {
|
if (!(infile = fopen (error_file, "r")))
|
||||||
char *detail = lookup_variable(connptr, "detail");
|
{
|
||||||
return (write_message(connptr->client_fd, fallback_error,
|
char *detail = lookup_variable (connptr, "detail");
|
||||||
connptr->error_number,
|
return (write_message (connptr->client_fd, fallback_error,
|
||||||
connptr->error_string,
|
connptr->error_number,
|
||||||
connptr->error_string,
|
connptr->error_string,
|
||||||
detail,
|
connptr->error_string,
|
||||||
PACKAGE, VERSION));
|
detail, PACKAGE, VERSION));
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = send_html_file(infile, connptr);
|
ret = send_html_file (infile, connptr);
|
||||||
fclose(infile);
|
fclose (infile);
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -231,16 +228,14 @@ send_http_error_message(struct conn_s *connptr)
|
|||||||
#define ERRVAR_BUCKETCOUNT 16
|
#define ERRVAR_BUCKETCOUNT 16
|
||||||
|
|
||||||
int
|
int
|
||||||
add_error_variable(struct conn_s *connptr, char *key, char *val)
|
add_error_variable (struct conn_s *connptr, char *key, char *val)
|
||||||
{
|
{
|
||||||
if (!connptr->error_variables)
|
if (!connptr->error_variables)
|
||||||
if (!
|
if (!(connptr->error_variables = hashmap_create (ERRVAR_BUCKETCOUNT)))
|
||||||
(connptr->error_variables =
|
return (-1);
|
||||||
hashmap_create(ERRVAR_BUCKETCOUNT)))
|
|
||||||
return (-1);
|
|
||||||
|
|
||||||
return hashmap_insert(connptr->error_variables, key, val,
|
return hashmap_insert (connptr->error_variables, key, val,
|
||||||
strlen(val) + 1);
|
strlen (val) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ADD_VAR_RET(x, y) \
|
#define ADD_VAR_RET(x, y) \
|
||||||
@ -255,61 +250,63 @@ add_error_variable(struct conn_s *connptr, char *key, char *val)
|
|||||||
* Set some standard variables used by all HTML pages
|
* Set some standard variables used by all HTML pages
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
add_standard_vars(struct conn_s *connptr)
|
add_standard_vars (struct conn_s *connptr)
|
||||||
{
|
{
|
||||||
char errnobuf[16];
|
char errnobuf[16];
|
||||||
char timebuf[30];
|
char timebuf[30];
|
||||||
time_t global_time;
|
time_t global_time;
|
||||||
|
|
||||||
snprintf(errnobuf, sizeof errnobuf, "%d", connptr->error_number);
|
snprintf (errnobuf, sizeof errnobuf, "%d", connptr->error_number);
|
||||||
ADD_VAR_RET("errno", errnobuf);
|
ADD_VAR_RET ("errno", errnobuf);
|
||||||
|
|
||||||
ADD_VAR_RET("cause", connptr->error_string);
|
ADD_VAR_RET ("cause", connptr->error_string);
|
||||||
ADD_VAR_RET("request", connptr->request_line);
|
ADD_VAR_RET ("request", connptr->request_line);
|
||||||
ADD_VAR_RET("clientip", connptr->client_ip_addr);
|
ADD_VAR_RET ("clientip", connptr->client_ip_addr);
|
||||||
ADD_VAR_RET("clienthost", connptr->client_string_addr);
|
ADD_VAR_RET ("clienthost", connptr->client_string_addr);
|
||||||
|
|
||||||
/* The following value parts are all non-NULL and will
|
/* The following value parts are all non-NULL and will
|
||||||
* trigger warnings in ADD_VAR_RET(), so we use
|
* trigger warnings in ADD_VAR_RET(), so we use
|
||||||
* add_error_variable() directly.
|
* add_error_variable() directly.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
global_time = time(NULL);
|
global_time = time (NULL);
|
||||||
strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT",
|
strftime (timebuf, sizeof (timebuf), "%a, %d %b %Y %H:%M:%S GMT",
|
||||||
gmtime(&global_time));
|
gmtime (&global_time));
|
||||||
add_error_variable(connptr, "date", timebuf);
|
add_error_variable (connptr, "date", timebuf);
|
||||||
|
|
||||||
add_error_variable(connptr, "website", "http://tinyproxy.banu.com/");
|
add_error_variable (connptr, "website", "http://tinyproxy.banu.com/");
|
||||||
add_error_variable(connptr, "version", VERSION);
|
add_error_variable (connptr, "version", VERSION);
|
||||||
add_error_variable(connptr, "package", PACKAGE);
|
add_error_variable (connptr, "package", PACKAGE);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add the error information to the conn structure.
|
* Add the error information to the conn structure.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
indicate_http_error(struct conn_s *connptr, int number, char *message, ...)
|
indicate_http_error (struct conn_s *connptr, int number, char *message, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
char *key, *val;
|
char *key, *val;
|
||||||
|
|
||||||
va_start(ap, message);
|
va_start (ap, message);
|
||||||
|
|
||||||
while ((key = va_arg(ap, char *))) {
|
while ((key = va_arg (ap, char *)))
|
||||||
val = va_arg(ap, char *);
|
{
|
||||||
|
val = va_arg (ap, char *);
|
||||||
|
|
||||||
if (add_error_variable(connptr, key, val) == -1) {
|
if (add_error_variable (connptr, key, val) == -1)
|
||||||
va_end(ap);
|
{
|
||||||
return (-1);
|
va_end (ap);
|
||||||
}
|
return (-1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
connptr->error_number = number;
|
connptr->error_number = number;
|
||||||
connptr->error_string = safestrdup(message);
|
connptr->error_string = safestrdup (message);
|
||||||
|
|
||||||
va_end(ap);
|
va_end (ap);
|
||||||
|
|
||||||
return (add_standard_vars(connptr));
|
return (add_standard_vars (connptr));
|
||||||
}
|
}
|
||||||
|
@ -24,13 +24,14 @@
|
|||||||
/* Forward declaration */
|
/* Forward declaration */
|
||||||
struct conn_s;
|
struct conn_s;
|
||||||
|
|
||||||
extern int add_new_errorpage(char *filepath, unsigned int errornum);
|
extern int add_new_errorpage (char *filepath, unsigned int errornum);
|
||||||
extern int send_http_error_message(struct conn_s *connptr);
|
extern int send_http_error_message (struct conn_s *connptr);
|
||||||
extern int indicate_http_error(struct conn_s *connptr, int number,
|
extern int indicate_http_error (struct conn_s *connptr, int number,
|
||||||
char *message, ...);
|
char *message, ...);
|
||||||
extern int add_error_variable(struct conn_s *connptr, char *key, char *val);
|
extern int add_error_variable (struct conn_s *connptr, char *key, char *val);
|
||||||
extern int send_html_file(FILE * infile, struct conn_s *connptr);
|
extern int send_html_file (FILE * infile, struct conn_s *connptr);
|
||||||
extern int send_http_headers(struct conn_s *connptr, int code, char *message);
|
extern int send_http_headers (struct conn_s *connptr, int code,
|
||||||
extern int add_standard_vars(struct conn_s *connptr);
|
char *message);
|
||||||
|
extern int add_standard_vars (struct conn_s *connptr);
|
||||||
|
|
||||||
#endif /* !TINYPROXY_HTML_ERROR_H */
|
#endif /* !TINYPROXY_HTML_ERROR_H */
|
||||||
|
@ -30,29 +30,33 @@
|
|||||||
* Also, the caller MUST NOT free the memory while the structure is
|
* Also, the caller MUST NOT free the memory while the structure is
|
||||||
* still in use---bad things would happen.
|
* still in use---bad things would happen.
|
||||||
*/
|
*/
|
||||||
struct http_message_s {
|
struct http_message_s
|
||||||
/* Response string and code supplied on the HTTP status line */
|
{
|
||||||
struct {
|
/* Response string and code supplied on the HTTP status line */
|
||||||
const char *string;
|
struct
|
||||||
int code;
|
{
|
||||||
} response;
|
const char *string;
|
||||||
|
int code;
|
||||||
|
} response;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A group of headers to be sent with this message. Right now
|
* A group of headers to be sent with this message. Right now
|
||||||
* the strings are referenced through pointers in an array.
|
* the strings are referenced through pointers in an array.
|
||||||
* I might change this to a vector in the future.
|
* I might change this to a vector in the future.
|
||||||
*/
|
*/
|
||||||
struct {
|
struct
|
||||||
char **strings;
|
{
|
||||||
unsigned int total;
|
char **strings;
|
||||||
unsigned int used;
|
unsigned int total;
|
||||||
} headers;
|
unsigned int used;
|
||||||
|
} headers;
|
||||||
|
|
||||||
/* Body of the message (most likely an HTML message) */
|
/* Body of the message (most likely an HTML message) */
|
||||||
struct {
|
struct
|
||||||
const char *text;
|
{
|
||||||
size_t length;
|
const char *text;
|
||||||
} body;
|
size_t length;
|
||||||
|
} body;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -61,18 +65,18 @@ struct http_message_s {
|
|||||||
* number is returned. Useful for if() tests and assert() tests.
|
* number is returned. Useful for if() tests and assert() tests.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
is_http_message_valid(http_message_t msg)
|
is_http_message_valid (http_message_t msg)
|
||||||
{
|
{
|
||||||
if (msg == NULL)
|
if (msg == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
if (msg->headers.strings == NULL)
|
if (msg->headers.strings == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
if (msg->response.string == NULL)
|
if (msg->response.string == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
if (msg->response.code < 1 || msg->response.code > 999)
|
if (msg->response.code < 1 || msg->response.code > 999)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initially allocate space for 128 headers */
|
/* Initially allocate space for 128 headers */
|
||||||
@ -83,32 +87,34 @@ is_http_message_valid(http_message_t msg)
|
|||||||
* If memory could not be allocated, return a NULL.
|
* If memory could not be allocated, return a NULL.
|
||||||
*/
|
*/
|
||||||
http_message_t
|
http_message_t
|
||||||
http_message_create(int response_code, const char *response_string)
|
http_message_create (int response_code, const char *response_string)
|
||||||
{
|
{
|
||||||
http_message_t msg;
|
http_message_t msg;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
msg = safecalloc(1, sizeof(struct http_message_s));
|
msg = safecalloc (1, sizeof (struct http_message_s));
|
||||||
if (msg == NULL)
|
if (msg == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
msg->headers.strings = safecalloc(NUMBER_OF_HEADERS, sizeof(char *));
|
msg->headers.strings = safecalloc (NUMBER_OF_HEADERS, sizeof (char *));
|
||||||
if (msg->headers.strings == NULL) {
|
if (msg->headers.strings == NULL)
|
||||||
safefree(msg);
|
{
|
||||||
return NULL;
|
safefree (msg);
|
||||||
}
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
msg->headers.total = NUMBER_OF_HEADERS;
|
msg->headers.total = NUMBER_OF_HEADERS;
|
||||||
|
|
||||||
/* Store the HTTP response information in the structure */
|
/* Store the HTTP response information in the structure */
|
||||||
ret = http_message_set_response(msg, response_code, response_string);
|
ret = http_message_set_response (msg, response_code, response_string);
|
||||||
if (IS_HTTP_MSG_ERROR(ret)) {
|
if (IS_HTTP_MSG_ERROR (ret))
|
||||||
safefree(msg->headers.strings);
|
{
|
||||||
safefree(msg);
|
safefree (msg->headers.strings);
|
||||||
return NULL;
|
safefree (msg);
|
||||||
}
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -117,19 +123,19 @@ http_message_create(int response_code, const char *response_string)
|
|||||||
* is the responsibility of the caller.
|
* is the responsibility of the caller.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
http_message_destroy(http_message_t msg)
|
http_message_destroy (http_message_t msg)
|
||||||
{
|
{
|
||||||
assert(msg != NULL);
|
assert (msg != NULL);
|
||||||
assert(msg->headers.strings != NULL);
|
assert (msg->headers.strings != NULL);
|
||||||
|
|
||||||
/* Check for valid arguments */
|
/* Check for valid arguments */
|
||||||
if (msg == NULL)
|
if (msg == NULL)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
if (msg->headers.strings != NULL)
|
if (msg->headers.strings != NULL)
|
||||||
safefree(msg->headers.strings);
|
safefree (msg->headers.strings);
|
||||||
safefree(msg);
|
safefree (msg);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -137,135 +143,135 @@ http_message_destroy(http_message_t msg)
|
|||||||
* must be a NUL ('\0') terminated C string.
|
* must be a NUL ('\0') terminated C string.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
http_message_set_response(http_message_t msg,
|
http_message_set_response (http_message_t msg,
|
||||||
int response_code, const char *response_string)
|
int response_code, const char *response_string)
|
||||||
{
|
{
|
||||||
/* Check for valid arguments */
|
/* Check for valid arguments */
|
||||||
if (msg == NULL)
|
if (msg == NULL)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
if (response_code < 1 || response_code > 999)
|
if (response_code < 1 || response_code > 999)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (response_string == NULL)
|
if (response_string == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (strlen(response_string) == 0)
|
if (strlen (response_string) == 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
msg->response.code = response_code;
|
msg->response.code = response_code;
|
||||||
msg->response.string = response_string;
|
msg->response.string = response_string;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the HTTP message body.
|
* Set the HTTP message body.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
http_message_set_body(http_message_t msg, const char *body, size_t len)
|
http_message_set_body (http_message_t msg, const char *body, size_t len)
|
||||||
{
|
{
|
||||||
/* Check for valid arguments */
|
/* Check for valid arguments */
|
||||||
if (msg == NULL)
|
if (msg == NULL)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
if (body == NULL)
|
if (body == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
msg->body.text = body;
|
msg->body.text = body;
|
||||||
msg->body.length = len;
|
msg->body.length = len;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add headers to the structure.
|
* Add headers to the structure.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
http_message_add_headers(http_message_t msg, char **headers, int num_headers)
|
http_message_add_headers (http_message_t msg, char **headers, int num_headers)
|
||||||
{
|
{
|
||||||
char **new_headers;
|
char **new_headers;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* Check for valid arguments */
|
/* Check for valid arguments */
|
||||||
if (msg == NULL)
|
if (msg == NULL)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
if (headers == NULL)
|
if (headers == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (num_headers < 1)
|
if (num_headers < 1)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the number of headers to add is greater than the space
|
* If the number of headers to add is greater than the space
|
||||||
* available, reallocate the memory.
|
* available, reallocate the memory.
|
||||||
*/
|
*/
|
||||||
if (msg->headers.used + num_headers > msg->headers.total) {
|
if (msg->headers.used + num_headers > msg->headers.total)
|
||||||
new_headers = safecalloc(msg->headers.total * 2,
|
{
|
||||||
sizeof(char *));
|
new_headers = safecalloc (msg->headers.total * 2, sizeof (char *));
|
||||||
if (new_headers == NULL)
|
if (new_headers == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
/* Copy the array */
|
/* Copy the array */
|
||||||
for (i = 0; i != msg->headers.used; ++i)
|
for (i = 0; i != msg->headers.used; ++i)
|
||||||
new_headers[i] = msg->headers.strings[i];
|
new_headers[i] = msg->headers.strings[i];
|
||||||
|
|
||||||
/* Remove the old array and replace it with the new array */
|
/* Remove the old array and replace it with the new array */
|
||||||
safefree(msg->headers.strings);
|
safefree (msg->headers.strings);
|
||||||
msg->headers.strings = new_headers;
|
msg->headers.strings = new_headers;
|
||||||
msg->headers.total *= 2;
|
msg->headers.total *= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add the new headers to the structure
|
* Add the new headers to the structure
|
||||||
*/
|
*/
|
||||||
for (i = 0; i != num_headers; ++i)
|
for (i = 0; i != num_headers; ++i)
|
||||||
msg->headers.strings[i + msg->headers.used] = headers[i];
|
msg->headers.strings[i + msg->headers.used] = headers[i];
|
||||||
msg->headers.used += num_headers;
|
msg->headers.used += num_headers;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Send the completed HTTP message via the supplied file descriptor.
|
* Send the completed HTTP message via the supplied file descriptor.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
http_message_send(http_message_t msg, int fd)
|
http_message_send (http_message_t msg, int fd)
|
||||||
{
|
{
|
||||||
char timebuf[30];
|
char timebuf[30];
|
||||||
time_t global_time;
|
time_t global_time;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
assert(is_http_message_valid(msg));
|
assert (is_http_message_valid (msg));
|
||||||
|
|
||||||
/* Check for valid arguments */
|
/* Check for valid arguments */
|
||||||
if (msg == NULL)
|
if (msg == NULL)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
if (fd < 1)
|
if (fd < 1)
|
||||||
return -EBADF;
|
return -EBADF;
|
||||||
if (!is_http_message_valid(msg))
|
if (!is_http_message_valid (msg))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* Write the response line */
|
/* Write the response line */
|
||||||
write_message(fd, "HTTP/1.0 %d %s\r\n",
|
write_message (fd, "HTTP/1.0 %d %s\r\n",
|
||||||
msg->response.code, msg->response.string);
|
msg->response.code, msg->response.string);
|
||||||
|
|
||||||
/* Go through all the headers */
|
/* Go through all the headers */
|
||||||
for (i = 0; i != msg->headers.used; ++i)
|
for (i = 0; i != msg->headers.used; ++i)
|
||||||
write_message(fd, "%s\r\n", msg->headers.strings[i]);
|
write_message (fd, "%s\r\n", msg->headers.strings[i]);
|
||||||
|
|
||||||
/* Output the date */
|
/* Output the date */
|
||||||
global_time = time(NULL);
|
global_time = time (NULL);
|
||||||
strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT",
|
strftime (timebuf, sizeof (timebuf), "%a, %d %b %Y %H:%M:%S GMT",
|
||||||
gmtime(&global_time));
|
gmtime (&global_time));
|
||||||
write_message(fd, "Date: %s\r\n", timebuf);
|
write_message (fd, "Date: %s\r\n", timebuf);
|
||||||
|
|
||||||
/* Output the content-length */
|
/* Output the content-length */
|
||||||
write_message(fd, "Content-length: %u\r\n", msg->body.length);
|
write_message (fd, "Content-length: %u\r\n", msg->body.length);
|
||||||
|
|
||||||
/* Write the separator between the headers and body */
|
/* Write the separator between the headers and body */
|
||||||
safe_write(fd, "\r\n", 2);
|
safe_write (fd, "\r\n", 2);
|
||||||
|
|
||||||
/* If there's a body, send it! */
|
/* If there's a body, send it! */
|
||||||
if (msg->body.length > 0)
|
if (msg->body.length > 0)
|
||||||
safe_write(fd, msg->body.text, msg->body.length);
|
safe_write (fd, msg->body.text, msg->body.length);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -58,28 +58,28 @@ typedef struct http_message_s *http_message_t;
|
|||||||
#define IS_HTTP_MSG_ERROR(x) (x < 0)
|
#define IS_HTTP_MSG_ERROR(x) (x < 0)
|
||||||
|
|
||||||
/* Initialize the internal structure of the HTTP message */
|
/* Initialize the internal structure of the HTTP message */
|
||||||
extern http_message_t http_message_create(int response_code,
|
extern http_message_t http_message_create (int response_code,
|
||||||
const char *response_string);
|
const char *response_string);
|
||||||
|
|
||||||
/* Free up an _internal_ resources */
|
/* Free up an _internal_ resources */
|
||||||
extern int http_message_destroy(http_message_t msg);
|
extern int http_message_destroy (http_message_t msg);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Send an HTTP message via the supplied file descriptor. This function
|
* Send an HTTP message via the supplied file descriptor. This function
|
||||||
* will add the "Date" header before it's sent.
|
* will add the "Date" header before it's sent.
|
||||||
*/
|
*/
|
||||||
extern int http_message_send(http_message_t msg, int fd);
|
extern int http_message_send (http_message_t msg, int fd);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Change the internal state of the HTTP message. Either set the
|
* Change the internal state of the HTTP message. Either set the
|
||||||
* body of the message, update the response information, or
|
* body of the message, update the response information, or
|
||||||
* add a new set of headers.
|
* add a new set of headers.
|
||||||
*/
|
*/
|
||||||
extern int http_message_set_body(http_message_t msg,
|
extern int http_message_set_body (http_message_t msg,
|
||||||
const char *body, size_t len);
|
const char *body, size_t len);
|
||||||
extern int http_message_set_response(http_message_t msg,
|
extern int http_message_set_response (http_message_t msg,
|
||||||
int response_code,
|
int response_code,
|
||||||
const char *response_string);
|
const char *response_string);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the headers for this HTTP message. Each string must be NUL ('\0')
|
* Set the headers for this HTTP message. Each string must be NUL ('\0')
|
||||||
@ -87,7 +87,7 @@ extern int http_message_set_response(http_message_t msg,
|
|||||||
* line-feeds (LF) since they will be included when the http_message is
|
* line-feeds (LF) since they will be included when the http_message is
|
||||||
* sent.
|
* sent.
|
||||||
*/
|
*/
|
||||||
extern int http_message_add_headers(http_message_t msg,
|
extern int http_message_add_headers (http_message_t msg,
|
||||||
char **headers, int num_headers);
|
char **headers, int num_headers);
|
||||||
|
|
||||||
#endif /* _TINYPROXY_HTTP_MESSAGE_H_ */
|
#endif /* _TINYPROXY_HTTP_MESSAGE_H_ */
|
||||||
|
210
src/log.c
210
src/log.c
@ -29,15 +29,15 @@
|
|||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
|
||||||
static char *syslog_level[] = {
|
static char *syslog_level[] = {
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
"CRITICAL",
|
"CRITICAL",
|
||||||
"ERROR",
|
"ERROR",
|
||||||
"WARNING",
|
"WARNING",
|
||||||
"NOTICE",
|
"NOTICE",
|
||||||
"INFO",
|
"INFO",
|
||||||
"DEBUG",
|
"DEBUG",
|
||||||
"CONNECT"
|
"CONNECT"
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TIME_LENGTH 16
|
#define TIME_LENGTH 16
|
||||||
@ -65,165 +65,177 @@ static vector_t log_message_storage;
|
|||||||
* Open the log file and store the file descriptor in a global location.
|
* Open the log file and store the file descriptor in a global location.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
open_log_file(const char *log_file_name)
|
open_log_file (const char *log_file_name)
|
||||||
{
|
{
|
||||||
log_file_fd = create_file_safely(log_file_name, FALSE);
|
log_file_fd = create_file_safely (log_file_name, FALSE);
|
||||||
return log_file_fd;
|
return log_file_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Close the log file
|
* Close the log file
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
close_log_file(void)
|
close_log_file (void)
|
||||||
{
|
{
|
||||||
close(log_file_fd);
|
close (log_file_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Truncate log file to a zero length.
|
* Truncate log file to a zero length.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
truncate_log_file(void)
|
truncate_log_file (void)
|
||||||
{
|
{
|
||||||
lseek(log_file_fd, 0, SEEK_SET);
|
lseek (log_file_fd, 0, SEEK_SET);
|
||||||
ftruncate(log_file_fd, 0);
|
ftruncate (log_file_fd, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the log level for writing to the log file.
|
* Set the log level for writing to the log file.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
set_log_level(int level)
|
set_log_level (int level)
|
||||||
{
|
{
|
||||||
log_level = level;
|
log_level = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This routine logs messages to either the log file or the syslog function.
|
* This routine logs messages to either the log file or the syslog function.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
log_message(int level, char *fmt, ...)
|
log_message (int level, char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
time_t nowtime;
|
time_t nowtime;
|
||||||
|
|
||||||
char time_string[TIME_LENGTH];
|
char time_string[TIME_LENGTH];
|
||||||
char str[STRING_LENGTH];
|
char str[STRING_LENGTH];
|
||||||
|
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
/*
|
/*
|
||||||
* Figure out if we should write the message or not.
|
* Figure out if we should write the message or not.
|
||||||
*/
|
*/
|
||||||
if (log_level == LOG_CONN) {
|
if (log_level == LOG_CONN)
|
||||||
if (level == LOG_INFO)
|
{
|
||||||
return;
|
if (level == LOG_INFO)
|
||||||
} else if (log_level == LOG_INFO) {
|
return;
|
||||||
if (level > LOG_INFO && level != LOG_CONN)
|
}
|
||||||
return;
|
else if (log_level == LOG_INFO)
|
||||||
} else if (level > log_level)
|
{
|
||||||
return;
|
if (level > LOG_INFO && level != LOG_CONN)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (level > log_level)
|
||||||
|
return;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_SYSLOG_H
|
#ifdef HAVE_SYSLOG_H
|
||||||
if (config.syslog && level == LOG_CONN)
|
if (config.syslog && level == LOG_CONN)
|
||||||
level = LOG_INFO;
|
level = LOG_INFO;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
va_start(args, fmt);
|
va_start (args, fmt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the config file hasn't been processed, then we need to store
|
* If the config file hasn't been processed, then we need to store
|
||||||
* the messages for later processing.
|
* the messages for later processing.
|
||||||
*/
|
*/
|
||||||
if (!processed_config_file) {
|
if (!processed_config_file)
|
||||||
char *entry_buffer;
|
{
|
||||||
|
char *entry_buffer;
|
||||||
|
|
||||||
if (!log_message_storage) {
|
if (!log_message_storage)
|
||||||
log_message_storage = vector_create();
|
{
|
||||||
if (!log_message_storage)
|
log_message_storage = vector_create ();
|
||||||
goto out;
|
if (!log_message_storage)
|
||||||
}
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
vsnprintf(str, STRING_LENGTH, fmt, args);
|
vsnprintf (str, STRING_LENGTH, fmt, args);
|
||||||
|
|
||||||
entry_buffer = safemalloc(strlen(str) + 6);
|
entry_buffer = safemalloc (strlen (str) + 6);
|
||||||
if (!entry_buffer)
|
if (!entry_buffer)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
sprintf(entry_buffer, "%d %s", level, str);
|
sprintf (entry_buffer, "%d %s", level, str);
|
||||||
vector_append(log_message_storage, entry_buffer,
|
vector_append (log_message_storage, entry_buffer,
|
||||||
strlen(entry_buffer) + 1);
|
strlen (entry_buffer) + 1);
|
||||||
|
|
||||||
safefree(entry_buffer);
|
safefree (entry_buffer);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
#ifdef HAVE_SYSLOG_H
|
#ifdef HAVE_SYSLOG_H
|
||||||
if (config.syslog) {
|
if (config.syslog)
|
||||||
|
{
|
||||||
# ifdef HAVE_VSYSLOG_H
|
# ifdef HAVE_VSYSLOG_H
|
||||||
vsyslog(level, fmt, args);
|
vsyslog (level, fmt, args);
|
||||||
# else
|
# else
|
||||||
vsnprintf(str, STRING_LENGTH, fmt, args);
|
vsnprintf (str, STRING_LENGTH, fmt, args);
|
||||||
syslog(level, "%s", str);
|
syslog (level, "%s", str);
|
||||||
# endif
|
# endif
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
#endif
|
#endif
|
||||||
nowtime = time(NULL);
|
nowtime = time (NULL);
|
||||||
/* Format is month day hour:minute:second (24 time) */
|
/* Format is month day hour:minute:second (24 time) */
|
||||||
strftime(time_string, TIME_LENGTH, "%b %d %H:%M:%S",
|
strftime (time_string, TIME_LENGTH, "%b %d %H:%M:%S",
|
||||||
localtime(&nowtime));
|
localtime (&nowtime));
|
||||||
|
|
||||||
snprintf(str, STRING_LENGTH, "%-9s %s [%ld]: ",
|
snprintf (str, STRING_LENGTH, "%-9s %s [%ld]: ",
|
||||||
syslog_level[level], time_string, (long int)getpid());
|
syslog_level[level], time_string, (long int) getpid ());
|
||||||
|
|
||||||
assert(log_file_fd >= 0);
|
assert (log_file_fd >= 0);
|
||||||
|
|
||||||
write(log_file_fd, str, strlen(str));
|
write (log_file_fd, str, strlen (str));
|
||||||
vsnprintf(str, STRING_LENGTH, fmt, args);
|
vsnprintf (str, STRING_LENGTH, fmt, args);
|
||||||
write(log_file_fd, str, strlen(str));
|
write (log_file_fd, str, strlen (str));
|
||||||
write(log_file_fd, "\n", 1);
|
write (log_file_fd, "\n", 1);
|
||||||
fsync(log_file_fd);
|
fsync (log_file_fd);
|
||||||
|
|
||||||
#ifdef HAVE_SYSLOG_H
|
#ifdef HAVE_SYSLOG_H
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
out:
|
out:
|
||||||
va_end(args);
|
va_end (args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This needs to send any stored log messages.
|
* This needs to send any stored log messages.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
send_stored_logs(void)
|
send_stored_logs (void)
|
||||||
{
|
{
|
||||||
char *string;
|
char *string;
|
||||||
char *ptr;
|
char *ptr;
|
||||||
|
|
||||||
int level;
|
int level;
|
||||||
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
for (i = 0; i != vector_length(log_message_storage); ++i) {
|
for (i = 0; i != vector_length (log_message_storage); ++i)
|
||||||
string = vector_getentry(log_message_storage, i, NULL);
|
{
|
||||||
|
string = vector_getentry (log_message_storage, i, NULL);
|
||||||
|
|
||||||
ptr = strchr(string, ' ') + 1;
|
ptr = strchr (string, ' ') + 1;
|
||||||
level = atoi(string);
|
level = atoi (string);
|
||||||
|
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
if (log_level == LOG_CONN && level == LOG_INFO)
|
if (log_level == LOG_CONN && level == LOG_INFO)
|
||||||
continue;
|
continue;
|
||||||
else if (log_level == LOG_INFO) {
|
else if (log_level == LOG_INFO)
|
||||||
if (level > LOG_INFO && level != LOG_CONN)
|
{
|
||||||
continue;
|
if (level > LOG_INFO && level != LOG_CONN)
|
||||||
} else if (level > log_level)
|
continue;
|
||||||
continue;
|
}
|
||||||
|
else if (level > log_level)
|
||||||
|
continue;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
log_message(level, ptr);
|
log_message (level, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
vector_delete(log_message_storage);
|
vector_delete (log_message_storage);
|
||||||
log_message_storage = NULL;
|
log_message_storage = NULL;
|
||||||
}
|
}
|
||||||
|
14
src/log.h
14
src/log.h
@ -87,7 +87,7 @@
|
|||||||
# define LOG_DEBUG 7
|
# define LOG_DEBUG 7
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define LOG_CONN 8 /* extra to log connections without the INFO stuff */
|
#define LOG_CONN 8 /* extra to log connections without the INFO stuff */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Use this for debugging. The format is specific:
|
* Use this for debugging. The format is specific:
|
||||||
@ -102,12 +102,12 @@
|
|||||||
# define DEBUG2(x, y...) do { } while(0)
|
# define DEBUG2(x, y...) do { } while(0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern int open_log_file(const char *file);
|
extern int open_log_file (const char *file);
|
||||||
extern void close_log_file(void);
|
extern void close_log_file (void);
|
||||||
extern void truncate_log_file(void);
|
extern void truncate_log_file (void);
|
||||||
|
|
||||||
extern void log_message(int level, char *fmt, ...);
|
extern void log_message (int level, char *fmt, ...);
|
||||||
extern void set_log_level(int level);
|
extern void set_log_level (int level);
|
||||||
extern void send_stored_logs(void);
|
extern void send_stored_logs (void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
404
src/network.c
404
src/network.c
@ -33,35 +33,37 @@
|
|||||||
* again. Keep sending until the buffer has been sent.
|
* again. Keep sending until the buffer has been sent.
|
||||||
*/
|
*/
|
||||||
ssize_t
|
ssize_t
|
||||||
safe_write(int fd, const char *buffer, size_t count)
|
safe_write (int fd, const char *buffer, size_t count)
|
||||||
{
|
{
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
size_t bytestosend;
|
size_t bytestosend;
|
||||||
|
|
||||||
assert(fd >= 0);
|
assert (fd >= 0);
|
||||||
assert(buffer != NULL);
|
assert (buffer != NULL);
|
||||||
assert(count > 0);
|
assert (count > 0);
|
||||||
|
|
||||||
bytestosend = count;
|
bytestosend = count;
|
||||||
|
|
||||||
while (1) {
|
while (1)
|
||||||
len = send(fd, buffer, bytestosend, MSG_NOSIGNAL);
|
{
|
||||||
|
len = send (fd, buffer, bytestosend, MSG_NOSIGNAL);
|
||||||
|
|
||||||
if (len < 0) {
|
if (len < 0)
|
||||||
if (errno == EINTR)
|
{
|
||||||
continue;
|
if (errno == EINTR)
|
||||||
else
|
continue;
|
||||||
return -errno;
|
else
|
||||||
}
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
if (len == bytestosend)
|
if (len == bytestosend)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
buffer += len;
|
buffer += len;
|
||||||
bytestosend -= len;
|
bytestosend -= len;
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -69,15 +71,17 @@ safe_write(int fd, const char *buffer, size_t count)
|
|||||||
* again.
|
* again.
|
||||||
*/
|
*/
|
||||||
ssize_t
|
ssize_t
|
||||||
safe_read(int fd, char *buffer, size_t count)
|
safe_read (int fd, char *buffer, size_t count)
|
||||||
{
|
{
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
|
|
||||||
do {
|
do
|
||||||
len = read(fd, buffer, count);
|
{
|
||||||
} while (len < 0 && errno == EINTR);
|
len = read (fd, buffer, count);
|
||||||
|
}
|
||||||
|
while (len < 0 && errno == EINTR);
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -87,47 +91,51 @@ safe_read(int fd, char *buffer, size_t count)
|
|||||||
* (although I did fix a memory leak. :)
|
* (although I did fix a memory leak. :)
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
write_message(int fd, const char *fmt, ...)
|
write_message (int fd, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
ssize_t n;
|
ssize_t n;
|
||||||
size_t size = (1024 * 8); /* start with 8 KB and go from there */
|
size_t size = (1024 * 8); /* start with 8 KB and go from there */
|
||||||
char *buf, *tmpbuf;
|
char *buf, *tmpbuf;
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
if ((buf = safemalloc(size)) == NULL)
|
if ((buf = safemalloc (size)) == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
while (1) {
|
while (1)
|
||||||
va_start(ap, fmt);
|
{
|
||||||
n = vsnprintf(buf, size, fmt, ap);
|
va_start (ap, fmt);
|
||||||
va_end(ap);
|
n = vsnprintf (buf, size, fmt, ap);
|
||||||
|
va_end (ap);
|
||||||
|
|
||||||
/* If that worked, break out so we can send the buffer */
|
/* If that worked, break out so we can send the buffer */
|
||||||
if (n > -1 && n < size)
|
if (n > -1 && n < size)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Else, try again with more space */
|
/* Else, try again with more space */
|
||||||
if (n > -1)
|
if (n > -1)
|
||||||
/* precisely what is needed (glibc2.1) */
|
/* precisely what is needed (glibc2.1) */
|
||||||
size = n + 1;
|
size = n + 1;
|
||||||
else
|
else
|
||||||
/* twice the old size (glibc2.0) */
|
/* twice the old size (glibc2.0) */
|
||||||
size *= 2;
|
size *= 2;
|
||||||
|
|
||||||
if ((tmpbuf = saferealloc(buf, size)) == NULL) {
|
if ((tmpbuf = saferealloc (buf, size)) == NULL)
|
||||||
safefree(buf);
|
{
|
||||||
return -1;
|
safefree (buf);
|
||||||
} else
|
return -1;
|
||||||
buf = tmpbuf;
|
}
|
||||||
}
|
else
|
||||||
|
buf = tmpbuf;
|
||||||
|
}
|
||||||
|
|
||||||
if (safe_write(fd, buf, n) < 0) {
|
if (safe_write (fd, buf, n) < 0)
|
||||||
safefree(buf);
|
{
|
||||||
return -1;
|
safefree (buf);
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
safefree(buf);
|
safefree (buf);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -142,103 +150,113 @@ write_message(int fd, const char *fmt, ...)
|
|||||||
#define SEGMENT_LEN (512)
|
#define SEGMENT_LEN (512)
|
||||||
#define MAXIMUM_BUFFER_LENGTH (128 * 1024)
|
#define MAXIMUM_BUFFER_LENGTH (128 * 1024)
|
||||||
ssize_t
|
ssize_t
|
||||||
readline(int fd, char **whole_buffer)
|
readline (int fd, char **whole_buffer)
|
||||||
{
|
{
|
||||||
ssize_t whole_buffer_len;
|
ssize_t whole_buffer_len;
|
||||||
char buffer[SEGMENT_LEN];
|
char buffer[SEGMENT_LEN];
|
||||||
char *ptr;
|
char *ptr;
|
||||||
|
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
ssize_t diff;
|
ssize_t diff;
|
||||||
|
|
||||||
struct read_lines_s {
|
struct read_lines_s
|
||||||
char *data;
|
{
|
||||||
size_t len;
|
char *data;
|
||||||
struct read_lines_s *next;
|
size_t len;
|
||||||
};
|
struct read_lines_s *next;
|
||||||
struct read_lines_s *first_line, *line_ptr;
|
};
|
||||||
|
struct read_lines_s *first_line, *line_ptr;
|
||||||
|
|
||||||
first_line = safecalloc(sizeof(struct read_lines_s), 1);
|
first_line = safecalloc (sizeof (struct read_lines_s), 1);
|
||||||
if (!first_line)
|
if (!first_line)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
line_ptr = first_line;
|
line_ptr = first_line;
|
||||||
|
|
||||||
whole_buffer_len = 0;
|
whole_buffer_len = 0;
|
||||||
for (;;) {
|
for (;;)
|
||||||
ret = recv(fd, buffer, SEGMENT_LEN, MSG_PEEK);
|
{
|
||||||
if (ret <= 0)
|
ret = recv (fd, buffer, SEGMENT_LEN, MSG_PEEK);
|
||||||
goto CLEANUP;
|
if (ret <= 0)
|
||||||
|
goto CLEANUP;
|
||||||
|
|
||||||
ptr = memchr(buffer, '\n', ret);
|
ptr = memchr (buffer, '\n', ret);
|
||||||
if (ptr)
|
if (ptr)
|
||||||
diff = ptr - buffer + 1;
|
diff = ptr - buffer + 1;
|
||||||
else
|
else
|
||||||
diff = ret;
|
diff = ret;
|
||||||
|
|
||||||
whole_buffer_len += diff;
|
whole_buffer_len += diff;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Don't allow the buffer to grow without bound. If we
|
* Don't allow the buffer to grow without bound. If we
|
||||||
* get to more than MAXIMUM_BUFFER_LENGTH close.
|
* get to more than MAXIMUM_BUFFER_LENGTH close.
|
||||||
*/
|
*/
|
||||||
if (whole_buffer_len > MAXIMUM_BUFFER_LENGTH) {
|
if (whole_buffer_len > MAXIMUM_BUFFER_LENGTH)
|
||||||
ret = -ERANGE;
|
{
|
||||||
goto CLEANUP;
|
ret = -ERANGE;
|
||||||
}
|
goto CLEANUP;
|
||||||
|
}
|
||||||
|
|
||||||
line_ptr->data = safemalloc(diff);
|
line_ptr->data = safemalloc (diff);
|
||||||
if (!line_ptr->data) {
|
if (!line_ptr->data)
|
||||||
ret = -ENOMEM;
|
{
|
||||||
goto CLEANUP;
|
ret = -ENOMEM;
|
||||||
}
|
goto CLEANUP;
|
||||||
|
}
|
||||||
|
|
||||||
recv(fd, line_ptr->data, diff, 0);
|
recv (fd, line_ptr->data, diff, 0);
|
||||||
line_ptr->len = diff;
|
line_ptr->len = diff;
|
||||||
|
|
||||||
if (ptr) {
|
if (ptr)
|
||||||
line_ptr->next = NULL;
|
{
|
||||||
break;
|
line_ptr->next = NULL;
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
line_ptr->next = safecalloc(sizeof(struct read_lines_s), 1);
|
line_ptr->next = safecalloc (sizeof (struct read_lines_s), 1);
|
||||||
if (!line_ptr->next) {
|
if (!line_ptr->next)
|
||||||
ret = -ENOMEM;
|
{
|
||||||
goto CLEANUP;
|
ret = -ENOMEM;
|
||||||
}
|
goto CLEANUP;
|
||||||
line_ptr = line_ptr->next;
|
}
|
||||||
}
|
line_ptr = line_ptr->next;
|
||||||
|
}
|
||||||
|
|
||||||
*whole_buffer = safemalloc(whole_buffer_len + 1);
|
*whole_buffer = safemalloc (whole_buffer_len + 1);
|
||||||
if (!*whole_buffer) {
|
if (!*whole_buffer)
|
||||||
ret = -ENOMEM;
|
{
|
||||||
goto CLEANUP;
|
ret = -ENOMEM;
|
||||||
}
|
goto CLEANUP;
|
||||||
|
}
|
||||||
|
|
||||||
*(*whole_buffer + whole_buffer_len) = '\0';
|
*(*whole_buffer + whole_buffer_len) = '\0';
|
||||||
|
|
||||||
whole_buffer_len = 0;
|
whole_buffer_len = 0;
|
||||||
line_ptr = first_line;
|
line_ptr = first_line;
|
||||||
while (line_ptr) {
|
while (line_ptr)
|
||||||
memcpy(*whole_buffer + whole_buffer_len, line_ptr->data,
|
{
|
||||||
line_ptr->len);
|
memcpy (*whole_buffer + whole_buffer_len, line_ptr->data,
|
||||||
whole_buffer_len += line_ptr->len;
|
line_ptr->len);
|
||||||
|
whole_buffer_len += line_ptr->len;
|
||||||
|
|
||||||
line_ptr = line_ptr->next;
|
line_ptr = line_ptr->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = whole_buffer_len;
|
ret = whole_buffer_len;
|
||||||
|
|
||||||
CLEANUP:
|
CLEANUP:
|
||||||
do {
|
do
|
||||||
line_ptr = first_line->next;
|
{
|
||||||
if (first_line->data)
|
line_ptr = first_line->next;
|
||||||
safefree(first_line->data);
|
if (first_line->data)
|
||||||
safefree(first_line);
|
safefree (first_line->data);
|
||||||
first_line = line_ptr;
|
safefree (first_line);
|
||||||
} while (first_line);
|
first_line = line_ptr;
|
||||||
|
}
|
||||||
|
while (first_line);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -246,32 +264,35 @@ readline(int fd, char **whole_buffer)
|
|||||||
* hex string.
|
* hex string.
|
||||||
*/
|
*/
|
||||||
char *
|
char *
|
||||||
get_ip_string(struct sockaddr *sa, char *buf, size_t buflen)
|
get_ip_string (struct sockaddr *sa, char *buf, size_t buflen)
|
||||||
{
|
{
|
||||||
assert(sa != NULL);
|
assert (sa != NULL);
|
||||||
assert(buf != NULL);
|
assert (buf != NULL);
|
||||||
assert(buflen != 0);
|
assert (buflen != 0);
|
||||||
buf[0] = '\0'; /* start with an empty string */
|
buf[0] = '\0'; /* start with an empty string */
|
||||||
|
|
||||||
switch (sa->sa_family) {
|
switch (sa->sa_family)
|
||||||
case AF_INET:{
|
{
|
||||||
struct sockaddr_in *sa_in = (struct sockaddr_in *)sa;
|
case AF_INET:
|
||||||
|
{
|
||||||
|
struct sockaddr_in *sa_in = (struct sockaddr_in *) sa;
|
||||||
|
|
||||||
inet_ntop(AF_INET, &sa_in->sin_addr, buf, buflen);
|
inet_ntop (AF_INET, &sa_in->sin_addr, buf, buflen);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case AF_INET6:{
|
case AF_INET6:
|
||||||
struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)sa;
|
{
|
||||||
|
struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *) sa;
|
||||||
|
|
||||||
inet_ntop(AF_INET6, &sa_in6->sin6_addr, buf, buflen);
|
inet_ntop (AF_INET6, &sa_in6->sin6_addr, buf, buflen);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
/* no valid family */
|
/* no valid family */
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -282,41 +303,42 @@ get_ip_string(struct sockaddr *sa, char *buf, size_t buflen)
|
|||||||
* Returns the same as inet_pton().
|
* Returns the same as inet_pton().
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
full_inet_pton(const char *ip, void *dst)
|
full_inet_pton (const char *ip, void *dst)
|
||||||
{
|
{
|
||||||
char buf[24], tmp[24]; /* IPv4->IPv6 = ::FFFF:xxx.xxx.xxx.xxx\0 */
|
char buf[24], tmp[24]; /* IPv4->IPv6 = ::FFFF:xxx.xxx.xxx.xxx\0 */
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
assert(ip != NULL && strlen(ip) != 0);
|
assert (ip != NULL && strlen (ip) != 0);
|
||||||
assert(dst != NULL);
|
assert (dst != NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the string is an IPv4 numeric address. We use the
|
* Check if the string is an IPv4 numeric address. We use the
|
||||||
* older inet_aton() call since it handles more IPv4 numeric
|
* older inet_aton() call since it handles more IPv4 numeric
|
||||||
* address formats.
|
* address formats.
|
||||||
*/
|
*/
|
||||||
n = inet_aton(ip, (struct in_addr *)dst);
|
n = inet_aton (ip, (struct in_addr *) dst);
|
||||||
if (n == 0) {
|
if (n == 0)
|
||||||
/*
|
{
|
||||||
* Simple case: "ip" wasn't an IPv4 numeric address, so
|
/*
|
||||||
* try doing the conversion as an IPv6 address. This
|
* Simple case: "ip" wasn't an IPv4 numeric address, so
|
||||||
* will either succeed or fail, but we can't do any
|
* try doing the conversion as an IPv6 address. This
|
||||||
* more processing anyway.
|
* will either succeed or fail, but we can't do any
|
||||||
*/
|
* more processing anyway.
|
||||||
return inet_pton(AF_INET6, ip, dst);
|
*/
|
||||||
}
|
return inet_pton (AF_INET6, ip, dst);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "ip" was an IPv4 address, so we need to convert it to
|
* "ip" was an IPv4 address, so we need to convert it to
|
||||||
* an IPv4-mapped IPv6 address and do the conversion
|
* an IPv4-mapped IPv6 address and do the conversion
|
||||||
* again to get the IPv6 network structure.
|
* again to get the IPv6 network structure.
|
||||||
*
|
*
|
||||||
* We convert the IPv4 binary address back into the
|
* We convert the IPv4 binary address back into the
|
||||||
* standard dotted-decimal format using inet_ntop()
|
* standard dotted-decimal format using inet_ntop()
|
||||||
* so we can be sure that inet_pton will accept the
|
* so we can be sure that inet_pton will accept the
|
||||||
* full string.
|
* full string.
|
||||||
*/
|
*/
|
||||||
snprintf(buf, sizeof(buf), "::ffff:%s",
|
snprintf (buf, sizeof (buf), "::ffff:%s",
|
||||||
inet_ntop(AF_INET, dst, tmp, sizeof(tmp)));
|
inet_ntop (AF_INET, dst, tmp, sizeof (tmp)));
|
||||||
return inet_pton(AF_INET6, buf, dst);
|
return inet_pton (AF_INET6, buf, dst);
|
||||||
}
|
}
|
||||||
|
@ -21,13 +21,13 @@
|
|||||||
#ifndef TINYPROXY_NETWORK_H
|
#ifndef TINYPROXY_NETWORK_H
|
||||||
#define TINYPROXY_NETWORK_H
|
#define TINYPROXY_NETWORK_H
|
||||||
|
|
||||||
extern ssize_t safe_write(int fd, const char *buffer, size_t count);
|
extern ssize_t safe_write (int fd, const char *buffer, size_t count);
|
||||||
extern ssize_t safe_read(int fd, char *buffer, size_t count);
|
extern ssize_t safe_read (int fd, char *buffer, size_t count);
|
||||||
|
|
||||||
extern int write_message(int fd, const char *fmt, ...);
|
extern int write_message (int fd, const char *fmt, ...);
|
||||||
extern ssize_t readline(int fd, char **whole_buffer);
|
extern ssize_t readline (int fd, char **whole_buffer);
|
||||||
|
|
||||||
extern char *get_ip_string(struct sockaddr *sa, char *buf, size_t len);
|
extern char *get_ip_string (struct sockaddr *sa, char *buf, size_t len);
|
||||||
extern int full_inet_pton(const char *ip, void *dst);
|
extern int full_inet_pton (const char *ip, void *dst);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
2383
src/reqs.c
2383
src/reqs.c
File diff suppressed because it is too large
Load Diff
19
src/reqs.h
19
src/reqs.h
@ -34,18 +34,19 @@
|
|||||||
/*
|
/*
|
||||||
* This structure holds the information pulled from a URL request.
|
* This structure holds the information pulled from a URL request.
|
||||||
*/
|
*/
|
||||||
struct request_s {
|
struct request_s
|
||||||
char *method;
|
{
|
||||||
char *protocol;
|
char *method;
|
||||||
|
char *protocol;
|
||||||
|
|
||||||
char *host;
|
char *host;
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
|
|
||||||
char *path;
|
char *path;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void handle_connection(int fd);
|
extern void handle_connection (int fd);
|
||||||
extern void add_connect_port_allowed(int port);
|
extern void add_connect_port_allowed (int port);
|
||||||
extern void upstream_add(const char *host, int port, const char *domain);
|
extern void upstream_add (const char *host, int port, const char *domain);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -30,128 +30,132 @@
|
|||||||
* Add entry to the reversepath list
|
* Add entry to the reversepath list
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
reversepath_add(const char *path, const char *url)
|
reversepath_add (const char *path, const char *url)
|
||||||
{
|
{
|
||||||
struct reversepath *reverse;
|
struct reversepath *reverse;
|
||||||
|
|
||||||
if (url == NULL) {
|
if (url == NULL)
|
||||||
log_message(LOG_WARNING,
|
{
|
||||||
"Illegal reverse proxy rule: missing url");
|
log_message (LOG_WARNING, "Illegal reverse proxy rule: missing url");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strstr(url, "://")) {
|
if (!strstr (url, "://"))
|
||||||
log_message(LOG_WARNING,
|
{
|
||||||
"Skipping reverse proxy rule: '%s' is not a valid url",
|
log_message (LOG_WARNING,
|
||||||
url);
|
"Skipping reverse proxy rule: '%s' is not a valid url",
|
||||||
return;
|
url);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (path && *path != '/') {
|
if (path && *path != '/')
|
||||||
log_message(LOG_WARNING,
|
{
|
||||||
"Skipping reverse proxy rule: path '%s' doesn't start with a /",
|
log_message (LOG_WARNING,
|
||||||
path);
|
"Skipping reverse proxy rule: path '%s' doesn't start with a /",
|
||||||
return;
|
path);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(reverse = safemalloc(sizeof(struct reversepath)))) {
|
if (!(reverse = safemalloc (sizeof (struct reversepath))))
|
||||||
log_message(LOG_ERR,
|
{
|
||||||
"Unable to allocate memory in reversepath_add()");
|
log_message (LOG_ERR, "Unable to allocate memory in reversepath_add()");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!path)
|
if (!path)
|
||||||
reverse->path = safestrdup("/");
|
reverse->path = safestrdup ("/");
|
||||||
else
|
else
|
||||||
reverse->path = safestrdup(path);
|
reverse->path = safestrdup (path);
|
||||||
|
|
||||||
reverse->url = safestrdup(url);
|
reverse->url = safestrdup (url);
|
||||||
|
|
||||||
reverse->next = config.reversepath_list;
|
reverse->next = config.reversepath_list;
|
||||||
config.reversepath_list = reverse;
|
config.reversepath_list = reverse;
|
||||||
|
|
||||||
log_message(LOG_INFO,
|
log_message (LOG_INFO,
|
||||||
"Added reverse proxy rule: %s -> %s", reverse->path,
|
"Added reverse proxy rule: %s -> %s", reverse->path,
|
||||||
reverse->url);
|
reverse->url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if a request url is in the reversepath list
|
* Check if a request url is in the reversepath list
|
||||||
*/
|
*/
|
||||||
struct reversepath *
|
struct reversepath *
|
||||||
reversepath_get(char *url)
|
reversepath_get (char *url)
|
||||||
{
|
{
|
||||||
struct reversepath *reverse = config.reversepath_list;
|
struct reversepath *reverse = config.reversepath_list;
|
||||||
|
|
||||||
while (reverse) {
|
while (reverse)
|
||||||
if (strstr(url, reverse->path) == url)
|
{
|
||||||
return reverse;
|
if (strstr (url, reverse->path) == url)
|
||||||
|
return reverse;
|
||||||
|
|
||||||
reverse = reverse->next;
|
reverse = reverse->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Rewrite the URL for reverse proxying.
|
* Rewrite the URL for reverse proxying.
|
||||||
*/
|
*/
|
||||||
char *
|
char *
|
||||||
reverse_rewrite_url(struct conn_s *connptr, hashmap_t hashofheaders, char *url)
|
reverse_rewrite_url (struct conn_s *connptr, hashmap_t hashofheaders,
|
||||||
|
char *url)
|
||||||
{
|
{
|
||||||
char *rewrite_url = NULL;
|
char *rewrite_url = NULL;
|
||||||
char *cookie = NULL;
|
char *cookie = NULL;
|
||||||
char *cookieval;
|
char *cookieval;
|
||||||
struct reversepath *reverse;
|
struct reversepath *reverse;
|
||||||
|
|
||||||
/* Reverse requests always start with a slash */
|
/* Reverse requests always start with a slash */
|
||||||
if (*url == '/') {
|
if (*url == '/')
|
||||||
/* First try locating the reverse mapping by request url */
|
{
|
||||||
reverse = reversepath_get(url);
|
/* First try locating the reverse mapping by request url */
|
||||||
if (reverse) {
|
reverse = reversepath_get (url);
|
||||||
rewrite_url = safemalloc(strlen(url) +
|
if (reverse)
|
||||||
strlen(reverse->url) + 1);
|
{
|
||||||
strcpy(rewrite_url, reverse->url);
|
rewrite_url = safemalloc (strlen (url) + strlen (reverse->url) + 1);
|
||||||
strcat(rewrite_url, url + strlen(reverse->path));
|
strcpy (rewrite_url, reverse->url);
|
||||||
} else if (config.reversemagic
|
strcat (rewrite_url, url + strlen (reverse->path));
|
||||||
&& hashmap_entry_by_key(hashofheaders,
|
}
|
||||||
"cookie",
|
else if (config.reversemagic
|
||||||
(void **)&cookie) > 0) {
|
&& hashmap_entry_by_key (hashofheaders,
|
||||||
|
"cookie", (void **) &cookie) > 0)
|
||||||
|
{
|
||||||
|
|
||||||
/* No match - try the magical tracking cookie next */
|
/* No match - try the magical tracking cookie next */
|
||||||
if ((cookieval = strstr(cookie, REVERSE_COOKIE "="))
|
if ((cookieval = strstr (cookie, REVERSE_COOKIE "="))
|
||||||
&& (reverse =
|
&& (reverse =
|
||||||
reversepath_get(cookieval +
|
reversepath_get (cookieval + strlen (REVERSE_COOKIE) + 1)))
|
||||||
strlen(REVERSE_COOKIE) + 1))) {
|
{
|
||||||
|
|
||||||
rewrite_url = safemalloc(strlen(url) +
|
rewrite_url = safemalloc (strlen (url) +
|
||||||
strlen
|
strlen (reverse->url) + 1);
|
||||||
(reverse->url) + 1);
|
strcpy (rewrite_url, reverse->url);
|
||||||
strcpy(rewrite_url, reverse->url);
|
strcat (rewrite_url, url + 1);
|
||||||
strcat(rewrite_url, url + 1);
|
|
||||||
|
|
||||||
log_message(LOG_INFO,
|
log_message (LOG_INFO,
|
||||||
"Magical tracking cookie says: %s",
|
"Magical tracking cookie says: %s", reverse->path);
|
||||||
reverse->path);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Forward proxy support off and no reverse path match found */
|
/* Forward proxy support off and no reverse path match found */
|
||||||
if (config.reverseonly && !rewrite_url) {
|
if (config.reverseonly && !rewrite_url)
|
||||||
log_message(LOG_ERR, "Bad request");
|
{
|
||||||
indicate_http_error(connptr, 400, "Bad Request",
|
log_message (LOG_ERR, "Bad request");
|
||||||
"detail",
|
indicate_http_error (connptr, 400, "Bad Request",
|
||||||
"Request has an invalid URL", "url",
|
"detail",
|
||||||
url, NULL);
|
"Request has an invalid URL", "url", url, NULL);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_message(LOG_CONN, "Rewriting URL: %s -> %s", url, rewrite_url);
|
log_message (LOG_CONN, "Rewriting URL: %s -> %s", url, rewrite_url);
|
||||||
|
|
||||||
/* Store reverse path so that the magical tracking cookie can be set */
|
/* Store reverse path so that the magical tracking cookie can be set */
|
||||||
if (config.reversemagic)
|
if (config.reversemagic)
|
||||||
connptr->reversepath = safestrdup(reverse->path);
|
connptr->reversepath = safestrdup (reverse->path);
|
||||||
|
|
||||||
return rewrite_url;
|
return rewrite_url;
|
||||||
}
|
}
|
||||||
|
@ -23,17 +23,18 @@
|
|||||||
|
|
||||||
#include "conns.h"
|
#include "conns.h"
|
||||||
|
|
||||||
struct reversepath {
|
struct reversepath
|
||||||
struct reversepath *next;
|
{
|
||||||
char *path;
|
struct reversepath *next;
|
||||||
char *url;
|
char *path;
|
||||||
|
char *url;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define REVERSE_COOKIE "yummy_magical_cookie"
|
#define REVERSE_COOKIE "yummy_magical_cookie"
|
||||||
|
|
||||||
extern void reversepath_add(const char *path, const char *url);
|
extern void reversepath_add (const char *path, const char *url);
|
||||||
extern struct reversepath *reversepath_get(char *url);
|
extern struct reversepath *reversepath_get (char *url);
|
||||||
extern char *reverse_rewrite_url(struct conn_s *connptr,
|
extern char *reverse_rewrite_url (struct conn_s *connptr,
|
||||||
hashmap_t hashofheaders, char *url);
|
hashmap_t hashofheaders, char *url);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
284
src/sock.c
284
src/sock.c
@ -39,34 +39,36 @@
|
|||||||
* to indicate an error.
|
* to indicate an error.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
bind_socket(int sockfd, const char *addr)
|
bind_socket (int sockfd, const char *addr)
|
||||||
{
|
{
|
||||||
struct addrinfo hints, *res, *ressave;
|
struct addrinfo hints, *res, *ressave;
|
||||||
|
|
||||||
assert(sockfd >= 0);
|
assert (sockfd >= 0);
|
||||||
assert(addr != NULL && strlen(addr) != 0);
|
assert (addr != NULL && strlen (addr) != 0);
|
||||||
|
|
||||||
memset(&hints, 0, sizeof(struct addrinfo));
|
memset (&hints, 0, sizeof (struct addrinfo));
|
||||||
hints.ai_family = AF_UNSPEC;
|
hints.ai_family = AF_UNSPEC;
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
|
||||||
/* The local port it not important */
|
/* The local port it not important */
|
||||||
if (getaddrinfo(addr, NULL, &hints, &res) != 0)
|
if (getaddrinfo (addr, NULL, &hints, &res) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
ressave = res;
|
ressave = res;
|
||||||
|
|
||||||
/* Loop through the addresses and try to bind to each */
|
/* Loop through the addresses and try to bind to each */
|
||||||
do {
|
do
|
||||||
if (bind(sockfd, res->ai_addr, res->ai_addrlen) == 0)
|
{
|
||||||
break; /* success */
|
if (bind (sockfd, res->ai_addr, res->ai_addrlen) == 0)
|
||||||
} while ((res = res->ai_next) != NULL);
|
break; /* success */
|
||||||
|
}
|
||||||
|
while ((res = res->ai_next) != NULL);
|
||||||
|
|
||||||
freeaddrinfo(ressave);
|
freeaddrinfo (ressave);
|
||||||
if (res == NULL) /* was not able to bind to any address */
|
if (res == NULL) /* was not able to bind to any address */
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return sockfd;
|
return sockfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -75,91 +77,97 @@ bind_socket(int sockfd, const char *addr)
|
|||||||
* independent implementation (mostly for IPv4 and IPv6 addresses.)
|
* independent implementation (mostly for IPv4 and IPv6 addresses.)
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
opensock(const char *host, int port, const char *bind_to)
|
opensock (const char *host, int port, const char *bind_to)
|
||||||
{
|
{
|
||||||
int sockfd, n;
|
int sockfd, n;
|
||||||
struct addrinfo hints, *res, *ressave;
|
struct addrinfo hints, *res, *ressave;
|
||||||
char portstr[6];
|
char portstr[6];
|
||||||
|
|
||||||
assert(host != NULL);
|
assert (host != NULL);
|
||||||
assert(port > 0);
|
assert (port > 0);
|
||||||
|
|
||||||
memset(&hints, 0, sizeof(struct addrinfo));
|
memset (&hints, 0, sizeof (struct addrinfo));
|
||||||
hints.ai_family = AF_UNSPEC;
|
hints.ai_family = AF_UNSPEC;
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
|
||||||
snprintf(portstr, sizeof(portstr), "%d", port);
|
snprintf (portstr, sizeof (portstr), "%d", port);
|
||||||
|
|
||||||
n = getaddrinfo(host, portstr, &hints, &res);
|
n = getaddrinfo (host, portstr, &hints, &res);
|
||||||
if (n != 0) {
|
if (n != 0)
|
||||||
log_message(LOG_ERR, "opensock: Could not retrieve info for %s",
|
{
|
||||||
host);
|
log_message (LOG_ERR, "opensock: Could not retrieve info for %s", host);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ressave = res;
|
ressave = res;
|
||||||
do {
|
do
|
||||||
sockfd =
|
{
|
||||||
socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
sockfd = socket (res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||||
if (sockfd < 0)
|
if (sockfd < 0)
|
||||||
continue; /* ignore this one */
|
continue; /* ignore this one */
|
||||||
|
|
||||||
/* Bind to the specified address */
|
/* Bind to the specified address */
|
||||||
if (bind_to) {
|
if (bind_to)
|
||||||
if (bind_socket(sockfd, bind_to) < 0) {
|
{
|
||||||
close(sockfd);
|
if (bind_socket (sockfd, bind_to) < 0)
|
||||||
continue; /* can't bind, so try again */
|
{
|
||||||
}
|
close (sockfd);
|
||||||
} else if (config.bind_address) {
|
continue; /* can't bind, so try again */
|
||||||
if (bind_socket(sockfd, config.bind_address) < 0) {
|
}
|
||||||
close(sockfd);
|
}
|
||||||
continue; /* can't bind, so try again */
|
else if (config.bind_address)
|
||||||
}
|
{
|
||||||
}
|
if (bind_socket (sockfd, config.bind_address) < 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)
|
||||||
break; /* success */
|
break; /* success */
|
||||||
|
|
||||||
close(sockfd);
|
close (sockfd);
|
||||||
} while ((res = res->ai_next) != NULL);
|
}
|
||||||
|
while ((res = res->ai_next) != NULL);
|
||||||
|
|
||||||
freeaddrinfo(ressave);
|
freeaddrinfo (ressave);
|
||||||
if (res == NULL) {
|
if (res == NULL)
|
||||||
log_message(LOG_ERR,
|
{
|
||||||
"opensock: Could not establish a connection to %s",
|
log_message (LOG_ERR,
|
||||||
host);
|
"opensock: Could not establish a connection to %s", host);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sockfd;
|
return sockfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the socket to non blocking -rjkaes
|
* Set the socket to non blocking -rjkaes
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
socket_nonblocking(int sock)
|
socket_nonblocking (int sock)
|
||||||
{
|
{
|
||||||
int flags;
|
int flags;
|
||||||
|
|
||||||
assert(sock >= 0);
|
assert (sock >= 0);
|
||||||
|
|
||||||
flags = fcntl(sock, F_GETFL, 0);
|
flags = fcntl (sock, F_GETFL, 0);
|
||||||
return fcntl(sock, F_SETFL, flags | O_NONBLOCK);
|
return fcntl (sock, F_SETFL, flags | O_NONBLOCK);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the socket to blocking -rjkaes
|
* Set the socket to blocking -rjkaes
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
socket_blocking(int sock)
|
socket_blocking (int sock)
|
||||||
{
|
{
|
||||||
int flags;
|
int flags;
|
||||||
|
|
||||||
assert(sock >= 0);
|
assert (sock >= 0);
|
||||||
|
|
||||||
flags = fcntl(sock, F_GETFL, 0);
|
flags = fcntl (sock, F_GETFL, 0);
|
||||||
return fcntl(sock, F_SETFL, flags & ~O_NONBLOCK);
|
return fcntl (sock, F_SETFL, flags & ~O_NONBLOCK);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -169,95 +177,101 @@ socket_blocking(int sock)
|
|||||||
* - rjkaes
|
* - rjkaes
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
listen_sock(uint16_t port, socklen_t * addrlen)
|
listen_sock (uint16_t port, socklen_t * addrlen)
|
||||||
{
|
{
|
||||||
int listenfd;
|
int listenfd;
|
||||||
const int on = 1;
|
const int on = 1;
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
|
|
||||||
assert(port > 0);
|
assert (port > 0);
|
||||||
assert(addrlen != NULL);
|
assert (addrlen != NULL);
|
||||||
|
|
||||||
listenfd = socket(AF_INET, SOCK_STREAM, 0);
|
listenfd = socket (AF_INET, SOCK_STREAM, 0);
|
||||||
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
setsockopt (listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on));
|
||||||
|
|
||||||
memset(&addr, 0, sizeof(addr));
|
memset (&addr, 0, sizeof (addr));
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
addr.sin_port = htons(port);
|
addr.sin_port = htons (port);
|
||||||
|
|
||||||
if (config.ipAddr) {
|
if (config.ipAddr)
|
||||||
addr.sin_addr.s_addr = inet_addr(config.ipAddr);
|
{
|
||||||
} else {
|
addr.sin_addr.s_addr = inet_addr (config.ipAddr);
|
||||||
addr.sin_addr.s_addr = inet_addr("0.0.0.0");
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
|
addr.sin_addr.s_addr = inet_addr ("0.0.0.0");
|
||||||
|
}
|
||||||
|
|
||||||
if (bind(listenfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
if (bind (listenfd, (struct sockaddr *) &addr, sizeof (addr)) < 0)
|
||||||
log_message(LOG_ERR,
|
{
|
||||||
"Unable to bind listening socket because of %s",
|
log_message (LOG_ERR,
|
||||||
strerror(errno));
|
"Unable to bind listening socket because of %s",
|
||||||
return -1;
|
strerror (errno));
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (listen(listenfd, MAXLISTEN) < 0) {
|
if (listen (listenfd, MAXLISTEN) < 0)
|
||||||
log_message(LOG_ERR,
|
{
|
||||||
"Unable to start listening socket because of %s",
|
log_message (LOG_ERR,
|
||||||
strerror(errno));
|
"Unable to start listening socket because of %s",
|
||||||
return -1;
|
strerror (errno));
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
*addrlen = sizeof(addr);
|
*addrlen = sizeof (addr);
|
||||||
|
|
||||||
return listenfd;
|
return listenfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Takes a socket descriptor and returns the socket's IP address.
|
* Takes a socket descriptor and returns the socket's IP address.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
getsock_ip(int fd, char *ipaddr)
|
getsock_ip (int fd, char *ipaddr)
|
||||||
{
|
{
|
||||||
struct sockaddr_storage name;
|
struct sockaddr_storage name;
|
||||||
socklen_t namelen = sizeof(name);
|
socklen_t namelen = sizeof (name);
|
||||||
|
|
||||||
assert(fd >= 0);
|
assert (fd >= 0);
|
||||||
|
|
||||||
if (getsockname(fd, (struct sockaddr *)&name, &namelen) != 0) {
|
if (getsockname (fd, (struct sockaddr *) &name, &namelen) != 0)
|
||||||
log_message(LOG_ERR, "getsock_ip: getsockname() error: %s",
|
{
|
||||||
strerror(errno));
|
log_message (LOG_ERR, "getsock_ip: getsockname() error: %s",
|
||||||
return -1;
|
strerror (errno));
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (get_ip_string((struct sockaddr *)&name, ipaddr, IP_LENGTH) == NULL)
|
if (get_ip_string ((struct sockaddr *) &name, ipaddr, IP_LENGTH) == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return the peer's socket information.
|
* Return the peer's socket information.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
getpeer_information(int fd, char *ipaddr, char *string_addr)
|
getpeer_information (int fd, char *ipaddr, char *string_addr)
|
||||||
{
|
{
|
||||||
struct sockaddr_storage sa;
|
struct sockaddr_storage sa;
|
||||||
socklen_t salen = sizeof sa;
|
socklen_t salen = sizeof sa;
|
||||||
|
|
||||||
assert(fd >= 0);
|
assert (fd >= 0);
|
||||||
assert(ipaddr != NULL);
|
assert (ipaddr != NULL);
|
||||||
assert(string_addr != NULL);
|
assert (string_addr != NULL);
|
||||||
|
|
||||||
/* Set the strings to default values */
|
/* Set the strings to default values */
|
||||||
ipaddr[0] = '\0';
|
ipaddr[0] = '\0';
|
||||||
strlcpy(string_addr, "[unknown]", HOSTNAME_LENGTH);
|
strlcpy (string_addr, "[unknown]", HOSTNAME_LENGTH);
|
||||||
|
|
||||||
/* Look up the IP address */
|
/* Look up the IP address */
|
||||||
if (getpeername(fd, (struct sockaddr *) &sa, &salen) != 0)
|
if (getpeername (fd, (struct sockaddr *) &sa, &salen) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (get_ip_string((struct sockaddr *)&sa, ipaddr, IP_LENGTH) == NULL)
|
if (get_ip_string ((struct sockaddr *) &sa, ipaddr, IP_LENGTH) == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* Get the full host name */
|
/* Get the full host name */
|
||||||
return getnameinfo((struct sockaddr *)&sa, salen,
|
return getnameinfo ((struct sockaddr *) &sa, salen,
|
||||||
string_addr, HOSTNAME_LENGTH, NULL, 0, 0);
|
string_addr, HOSTNAME_LENGTH, NULL, 0, 0);
|
||||||
}
|
}
|
||||||
|
12
src/sock.h
12
src/sock.h
@ -28,13 +28,13 @@
|
|||||||
|
|
||||||
#define MAXLINE (1024 * 4)
|
#define MAXLINE (1024 * 4)
|
||||||
|
|
||||||
extern int opensock(const char *host, int port, const char *bind_to);
|
extern int opensock (const char *host, int port, const char *bind_to);
|
||||||
extern int listen_sock(uint16_t port, socklen_t * addrlen);
|
extern int listen_sock (uint16_t port, socklen_t * addrlen);
|
||||||
|
|
||||||
extern int socket_nonblocking(int sock);
|
extern int socket_nonblocking (int sock);
|
||||||
extern int socket_blocking(int sock);
|
extern int socket_blocking (int sock);
|
||||||
|
|
||||||
extern int getsock_ip(int fd, char *ipaddr);
|
extern int getsock_ip (int fd, char *ipaddr);
|
||||||
extern int getpeer_information(int fd, char *ipaddr, char *string_addr);
|
extern int getpeer_information (int fd, char *ipaddr, char *string_addr);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
177
src/stats.c
177
src/stats.c
@ -33,12 +33,13 @@
|
|||||||
#include "stats.h"
|
#include "stats.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
struct stat_s {
|
struct stat_s
|
||||||
unsigned long int num_reqs;
|
{
|
||||||
unsigned long int num_badcons;
|
unsigned long int num_reqs;
|
||||||
unsigned long int num_open;
|
unsigned long int num_badcons;
|
||||||
unsigned long int num_refused;
|
unsigned long int num_open;
|
||||||
unsigned long int num_denied;
|
unsigned long int num_refused;
|
||||||
|
unsigned long int num_denied;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct stat_s *stats;
|
static struct stat_s *stats;
|
||||||
@ -47,83 +48,82 @@ static struct stat_s *stats;
|
|||||||
* Initialize the statistics information to zero.
|
* Initialize the statistics information to zero.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
init_stats(void)
|
init_stats (void)
|
||||||
{
|
{
|
||||||
stats = malloc_shared_memory(sizeof(struct stat_s));
|
stats = malloc_shared_memory (sizeof (struct stat_s));
|
||||||
if (stats == MAP_FAILED)
|
if (stats == MAP_FAILED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
memset(stats, 0, sizeof(struct stat_s));
|
memset (stats, 0, sizeof (struct stat_s));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Display the statics of the tinyproxy server.
|
* Display the statics of the tinyproxy server.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
showstats(struct conn_s *connptr)
|
showstats (struct conn_s *connptr)
|
||||||
{
|
{
|
||||||
static char *msg =
|
static char *msg =
|
||||||
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
|
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
|
||||||
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
|
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
|
||||||
"<html>\n"
|
"<html>\n"
|
||||||
"<head><title>%s version %s run-time statistics</title></head>\n"
|
"<head><title>%s version %s run-time statistics</title></head>\n"
|
||||||
"<body>\n"
|
"<body>\n"
|
||||||
"<h1>%s version %s run-time statistics</h1>\n"
|
"<h1>%s version %s run-time statistics</h1>\n"
|
||||||
"<p>\n"
|
"<p>\n"
|
||||||
"Number of open connections: %lu<br />\n"
|
"Number of open connections: %lu<br />\n"
|
||||||
"Number of requests: %lu<br />\n"
|
"Number of requests: %lu<br />\n"
|
||||||
"Number of bad connections: %lu<br />\n"
|
"Number of bad connections: %lu<br />\n"
|
||||||
"Number of denied connections: %lu<br />\n"
|
"Number of denied connections: %lu<br />\n"
|
||||||
"Number of refused connections due to high load: %lu\n"
|
"Number of refused connections due to high load: %lu\n"
|
||||||
"</p>\n"
|
"</p>\n"
|
||||||
"<hr />\n"
|
"<hr />\n"
|
||||||
"<p><em>Generated by %s version %s.</em></p>\n"
|
"<p><em>Generated by %s version %s.</em></p>\n" "</body>\n" "</html>\n";
|
||||||
"</body>\n"
|
|
||||||
"</html>\n";
|
|
||||||
|
|
||||||
char *message_buffer;
|
char *message_buffer;
|
||||||
char opens[16], reqs[16], badconns[16], denied[16], refused[16];
|
char opens[16], reqs[16], badconns[16], denied[16], refused[16];
|
||||||
FILE *statfile;
|
FILE *statfile;
|
||||||
|
|
||||||
snprintf(opens, sizeof(opens), "%lu", stats->num_open);
|
snprintf (opens, sizeof (opens), "%lu", stats->num_open);
|
||||||
snprintf(reqs, sizeof(reqs), "%lu", stats->num_reqs);
|
snprintf (reqs, sizeof (reqs), "%lu", stats->num_reqs);
|
||||||
snprintf(badconns, sizeof(badconns), "%lu", stats->num_badcons);
|
snprintf (badconns, sizeof (badconns), "%lu", stats->num_badcons);
|
||||||
snprintf(denied, sizeof(denied), "%lu", stats->num_denied);
|
snprintf (denied, sizeof (denied), "%lu", stats->num_denied);
|
||||||
snprintf(refused, sizeof(refused), "%lu", stats->num_refused);
|
snprintf (refused, sizeof (refused), "%lu", stats->num_refused);
|
||||||
|
|
||||||
if (!config.statpage || (!(statfile = fopen(config.statpage, "r")))) {
|
if (!config.statpage || (!(statfile = fopen (config.statpage, "r"))))
|
||||||
message_buffer = safemalloc(MAXBUFFSIZE);
|
{
|
||||||
if (!message_buffer)
|
message_buffer = safemalloc (MAXBUFFSIZE);
|
||||||
return -1;
|
if (!message_buffer)
|
||||||
|
return -1;
|
||||||
|
|
||||||
snprintf(message_buffer, MAXBUFFSIZE, msg,
|
snprintf (message_buffer, MAXBUFFSIZE, msg,
|
||||||
PACKAGE, VERSION, PACKAGE, VERSION,
|
PACKAGE, VERSION, PACKAGE, VERSION,
|
||||||
stats->num_open,
|
stats->num_open,
|
||||||
stats->num_reqs,
|
stats->num_reqs,
|
||||||
stats->num_badcons, stats->num_denied,
|
stats->num_badcons, stats->num_denied,
|
||||||
stats->num_refused,
|
stats->num_refused, PACKAGE, VERSION);
|
||||||
PACKAGE, VERSION);
|
|
||||||
|
|
||||||
if (send_http_message(connptr, 200, "OK", message_buffer) < 0) {
|
if (send_http_message (connptr, 200, "OK", message_buffer) < 0)
|
||||||
safefree(message_buffer);
|
{
|
||||||
return -1;
|
safefree (message_buffer);
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
safefree(message_buffer);
|
safefree (message_buffer);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
add_error_variable(connptr, "opens", opens);
|
add_error_variable (connptr, "opens", opens);
|
||||||
add_error_variable(connptr, "reqs", reqs);
|
add_error_variable (connptr, "reqs", reqs);
|
||||||
add_error_variable(connptr, "badconns", badconns);
|
add_error_variable (connptr, "badconns", badconns);
|
||||||
add_error_variable(connptr, "deniedconns", denied);
|
add_error_variable (connptr, "deniedconns", denied);
|
||||||
add_error_variable(connptr, "refusedconns", refused);
|
add_error_variable (connptr, "refusedconns", refused);
|
||||||
add_standard_vars(connptr);
|
add_standard_vars (connptr);
|
||||||
send_http_headers(connptr, 200, "Statistic requested");
|
send_http_headers (connptr, 200, "Statistic requested");
|
||||||
send_html_file(statfile, connptr);
|
send_html_file (statfile, connptr);
|
||||||
fclose(statfile);
|
fclose (statfile);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -131,28 +131,29 @@ showstats(struct conn_s *connptr)
|
|||||||
* stats.h
|
* stats.h
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
update_stats(status_t update_level)
|
update_stats (status_t update_level)
|
||||||
{
|
{
|
||||||
switch (update_level) {
|
switch (update_level)
|
||||||
case STAT_BADCONN:
|
{
|
||||||
++stats->num_badcons;
|
case STAT_BADCONN:
|
||||||
break;
|
++stats->num_badcons;
|
||||||
case STAT_OPEN:
|
break;
|
||||||
++stats->num_open;
|
case STAT_OPEN:
|
||||||
++stats->num_reqs;
|
++stats->num_open;
|
||||||
break;
|
++stats->num_reqs;
|
||||||
case STAT_CLOSE:
|
break;
|
||||||
--stats->num_open;
|
case STAT_CLOSE:
|
||||||
break;
|
--stats->num_open;
|
||||||
case STAT_REFUSE:
|
break;
|
||||||
++stats->num_refused;
|
case STAT_REFUSE:
|
||||||
break;
|
++stats->num_refused;
|
||||||
case STAT_DENIED:
|
break;
|
||||||
++stats->num_denied;
|
case STAT_DENIED:
|
||||||
break;
|
++stats->num_denied;
|
||||||
default:
|
break;
|
||||||
return -1;
|
default:
|
||||||
}
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
19
src/stats.h
19
src/stats.h
@ -26,19 +26,20 @@
|
|||||||
/*
|
/*
|
||||||
* Various logable statistics
|
* Various logable statistics
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum
|
||||||
STAT_BADCONN, /* bad connection, for unknown reason */
|
{
|
||||||
STAT_OPEN, /* connection opened */
|
STAT_BADCONN, /* bad connection, for unknown reason */
|
||||||
STAT_CLOSE, /* connection closed */
|
STAT_OPEN, /* connection opened */
|
||||||
STAT_REFUSE, /* connection refused (to outside world) */
|
STAT_CLOSE, /* connection closed */
|
||||||
STAT_DENIED /* connection denied to tinyproxy itself */
|
STAT_REFUSE, /* connection refused (to outside world) */
|
||||||
|
STAT_DENIED /* connection denied to tinyproxy itself */
|
||||||
} status_t;
|
} status_t;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Public API to the statistics for tinyproxy
|
* Public API to the statistics for tinyproxy
|
||||||
*/
|
*/
|
||||||
extern void init_stats(void);
|
extern void init_stats (void);
|
||||||
extern int showstats(struct conn_s *connptr);
|
extern int showstats (struct conn_s *connptr);
|
||||||
extern int update_stats(status_t update_level);
|
extern int update_stats (status_t update_level);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
78
src/text.c
78
src/text.c
@ -33,18 +33,18 @@
|
|||||||
* destination buffer.
|
* destination buffer.
|
||||||
*/
|
*/
|
||||||
size_t
|
size_t
|
||||||
strlcpy(char *dst, const char *src, size_t size)
|
strlcpy (char *dst, const char *src, size_t size)
|
||||||
{
|
{
|
||||||
size_t len = strlen(src);
|
size_t len = strlen (src);
|
||||||
size_t ret = len;
|
size_t ret = len;
|
||||||
|
|
||||||
if (len >= size)
|
if (len >= size)
|
||||||
len = size - 1;
|
len = size - 1;
|
||||||
|
|
||||||
memcpy(dst, src, len);
|
memcpy (dst, src, len);
|
||||||
dst[len] = '\0';
|
dst[len] = '\0';
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -56,20 +56,21 @@ strlcpy(char *dst, const char *src, size_t size)
|
|||||||
* length.
|
* length.
|
||||||
*/
|
*/
|
||||||
size_t
|
size_t
|
||||||
strlcat(char *dst, const char *src, size_t size)
|
strlcat (char *dst, const char *src, size_t size)
|
||||||
{
|
{
|
||||||
size_t len1 = strlen(dst);
|
size_t len1 = strlen (dst);
|
||||||
size_t len2 = strlen(src);
|
size_t len2 = strlen (src);
|
||||||
size_t ret = len1 + len2;
|
size_t ret = len1 + len2;
|
||||||
|
|
||||||
if (len1 + len2 >= size)
|
if (len1 + len2 >= size)
|
||||||
len2 = size - len1 - 1;
|
len2 = size - len1 - 1;
|
||||||
if (len2 > 0) {
|
if (len2 > 0)
|
||||||
memcpy(dst + len1, src, len2);
|
{
|
||||||
dst[len1 + len2] = '\0';
|
memcpy (dst + len1, src, len2);
|
||||||
}
|
dst[len1 + len2] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -83,30 +84,31 @@ strlcat(char *dst, const char *src, size_t size)
|
|||||||
* negative return value indicates an error.
|
* negative return value indicates an error.
|
||||||
*/
|
*/
|
||||||
ssize_t
|
ssize_t
|
||||||
chomp(char *buffer, size_t length)
|
chomp (char *buffer, size_t length)
|
||||||
{
|
{
|
||||||
size_t chars;
|
size_t chars;
|
||||||
|
|
||||||
assert(buffer != NULL);
|
assert (buffer != NULL);
|
||||||
assert(length > 0);
|
assert (length > 0);
|
||||||
|
|
||||||
/* Make sure the arguments are valid */
|
/* Make sure the arguments are valid */
|
||||||
if (buffer == NULL)
|
if (buffer == NULL)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
if (length < 1)
|
if (length < 1)
|
||||||
return -ERANGE;
|
return -ERANGE;
|
||||||
|
|
||||||
chars = 0;
|
chars = 0;
|
||||||
|
|
||||||
--length;
|
--length;
|
||||||
while (buffer[length] == '\r' || buffer[length] == '\n') {
|
while (buffer[length] == '\r' || buffer[length] == '\n')
|
||||||
buffer[length] = '\0';
|
{
|
||||||
chars++;
|
buffer[length] = '\0';
|
||||||
|
chars++;
|
||||||
|
|
||||||
/* Stop once we get to zero to prevent wrap-around */
|
/* Stop once we get to zero to prevent wrap-around */
|
||||||
if (length-- == 0)
|
if (length-- == 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return chars;
|
return chars;
|
||||||
}
|
}
|
||||||
|
10
src/text.h
10
src/text.h
@ -22,13 +22,13 @@
|
|||||||
#define TINYPROXY_TEXT_H
|
#define TINYPROXY_TEXT_H
|
||||||
|
|
||||||
#ifndef HAVE_STRLCAT
|
#ifndef HAVE_STRLCAT
|
||||||
extern size_t strlcat(char *dst, const char *src, size_t size);
|
extern size_t strlcat (char *dst, const char *src, size_t size);
|
||||||
#endif /* HAVE_STRLCAT */
|
#endif /* HAVE_STRLCAT */
|
||||||
|
|
||||||
#ifndef HAVE_STRLCPY
|
#ifndef HAVE_STRLCPY
|
||||||
extern size_t strlcpy(char *dst, const char *src, size_t size);
|
extern size_t strlcpy (char *dst, const char *src, size_t size);
|
||||||
#endif /* HAVE_STRLCPY */
|
#endif /* HAVE_STRLCPY */
|
||||||
|
|
||||||
extern ssize_t chomp(char *buffer, size_t length);
|
extern ssize_t chomp (char *buffer, size_t length);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
620
src/tinyproxy.c
620
src/tinyproxy.c
@ -41,60 +41,61 @@
|
|||||||
#include "stats.h"
|
#include "stats.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
RETSIGTYPE takesig(int sig);
|
RETSIGTYPE takesig (int sig);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Global Structures
|
* Global Structures
|
||||||
*/
|
*/
|
||||||
struct config_s config;
|
struct config_s config;
|
||||||
float load = 0.00;
|
float load = 0.00;
|
||||||
unsigned int received_sighup = FALSE; /* boolean */
|
unsigned int received_sighup = FALSE; /* boolean */
|
||||||
unsigned int processed_config_file = FALSE; /* boolean */
|
unsigned int processed_config_file = FALSE; /* boolean */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle a signal
|
* Handle a signal
|
||||||
*/
|
*/
|
||||||
RETSIGTYPE
|
RETSIGTYPE
|
||||||
takesig(int sig)
|
takesig (int sig)
|
||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
switch (sig) {
|
switch (sig)
|
||||||
case SIGHUP:
|
{
|
||||||
received_sighup = TRUE;
|
case SIGHUP:
|
||||||
break;
|
received_sighup = TRUE;
|
||||||
|
break;
|
||||||
|
|
||||||
case SIGTERM:
|
case SIGTERM:
|
||||||
config.quit = TRUE;
|
config.quit = TRUE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SIGCHLD:
|
case SIGCHLD:
|
||||||
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) ;
|
while ((pid = waitpid (-1, &status, WNOHANG)) > 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Display the version information for the user.
|
* Display the version information for the user.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
display_version(void)
|
display_version (void)
|
||||||
{
|
{
|
||||||
printf("%s %s (%s)\n", PACKAGE, VERSION, TARGET_SYSTEM);
|
printf ("%s %s (%s)\n", PACKAGE, VERSION, TARGET_SYSTEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Display the copyright and license for this program.
|
* Display the copyright and license for this program.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
display_license(void)
|
display_license (void)
|
||||||
{
|
{
|
||||||
display_version();
|
display_version ();
|
||||||
|
|
||||||
printf("\
|
printf ("\
|
||||||
Copyright 1998 Steven Young (sdyoung@well.com)\n\
|
Copyright 1998 Steven Young (sdyoung@well.com)\n\
|
||||||
Copyright 1998-2002 Robert James Kaes (rjkaes@users.sourceforge.net)\n\
|
Copyright 1998-2002 Robert James Kaes (rjkaes@users.sourceforge.net)\n\
|
||||||
Copyright 1999 George Talusan (gstalusan@uwaterloo.ca)\n\
|
Copyright 1999 George Talusan (gstalusan@uwaterloo.ca)\n\
|
||||||
@ -119,10 +120,10 @@ display_license(void)
|
|||||||
* Display usage to the user.
|
* Display usage to the user.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
display_usage(void)
|
display_usage (void)
|
||||||
{
|
{
|
||||||
printf("Usage: %s [options]\n", PACKAGE);
|
printf ("Usage: %s [options]\n", PACKAGE);
|
||||||
printf("\
|
printf ("\
|
||||||
Options:\n\
|
Options:\n\
|
||||||
-d Operate in DEBUG mode.\n\
|
-d Operate in DEBUG mode.\n\
|
||||||
-c FILE Use an alternate configuration file.\n\
|
-c FILE Use an alternate configuration file.\n\
|
||||||
@ -130,319 +131,344 @@ Options:\n\
|
|||||||
-l Display the license.\n\
|
-l Display the license.\n\
|
||||||
-v Display the version number.\n");
|
-v Display the version number.\n");
|
||||||
|
|
||||||
/* Display the modes compiled into tinyproxy */
|
/* Display the modes compiled into tinyproxy */
|
||||||
printf("\nFeatures compiled in:\n");
|
printf ("\nFeatures compiled in:\n");
|
||||||
#ifdef XTINYPROXY_ENABLE
|
#ifdef XTINYPROXY_ENABLE
|
||||||
printf(" XTinyproxy header\n");
|
printf (" XTinyproxy header\n");
|
||||||
#endif /* XTINYPROXY */
|
#endif /* XTINYPROXY */
|
||||||
#ifdef FILTER_ENABLE
|
#ifdef FILTER_ENABLE
|
||||||
printf(" Filtering\n");
|
printf (" Filtering\n");
|
||||||
#endif /* FILTER_ENABLE */
|
#endif /* FILTER_ENABLE */
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
printf(" Debugging code\n");
|
printf (" Debugging code\n");
|
||||||
#endif /* NDEBUG */
|
#endif /* NDEBUG */
|
||||||
#ifdef TRANSPARENT_PROXY
|
#ifdef TRANSPARENT_PROXY
|
||||||
printf(" Transparent proxy support\n");
|
printf (" Transparent proxy support\n");
|
||||||
#endif /* TRANSPARENT_PROXY */
|
#endif /* TRANSPARENT_PROXY */
|
||||||
#ifdef REVERSE_SUPPORT
|
#ifdef REVERSE_SUPPORT
|
||||||
printf(" Reverse proxy support\n");
|
printf (" Reverse proxy support\n");
|
||||||
#endif /* REVERSE_SUPPORT */
|
#endif /* REVERSE_SUPPORT */
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
get_id (char *str)
|
get_id (char *str)
|
||||||
{
|
{
|
||||||
char *tstr;
|
char *tstr;
|
||||||
|
|
||||||
if (str == NULL)
|
if (str == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
tstr = str;
|
tstr = str;
|
||||||
while (*tstr != 0) {
|
while (*tstr != 0)
|
||||||
if (!isdigit(*tstr))
|
{
|
||||||
return -1;
|
if (!isdigit (*tstr))
|
||||||
tstr++;
|
return -1;
|
||||||
}
|
tstr++;
|
||||||
|
}
|
||||||
|
|
||||||
return atoi(str);
|
return atoi (str);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char **argv)
|
main (int argc, char **argv)
|
||||||
{
|
{
|
||||||
int optch;
|
int optch;
|
||||||
unsigned int godaemon = TRUE; /* boolean */
|
unsigned int godaemon = TRUE; /* boolean */
|
||||||
struct passwd *thisuser = NULL;
|
struct passwd *thisuser = NULL;
|
||||||
struct group *thisgroup = NULL;
|
struct group *thisgroup = NULL;
|
||||||
FILE *config_file;
|
FILE *config_file;
|
||||||
|
|
||||||
/* Only allow u+rw bits. This may be required for some versions
|
/* Only allow u+rw bits. This may be required for some versions
|
||||||
* of glibc so that mkstemp() doesn't make us vulnerable.
|
* of glibc so that mkstemp() doesn't make us vulnerable.
|
||||||
*/
|
*/
|
||||||
umask(0177);
|
umask (0177);
|
||||||
|
|
||||||
/* Default configuration file location */
|
/* Default configuration file location */
|
||||||
config.config_file = DEFAULT_CONF_FILE;
|
config.config_file = DEFAULT_CONF_FILE;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Process the various options
|
* Process the various options
|
||||||
*/
|
*/
|
||||||
while ((optch = getopt(argc, argv, "c:vldh")) != EOF) {
|
while ((optch = getopt (argc, argv, "c:vldh")) != EOF)
|
||||||
switch (optch) {
|
{
|
||||||
case 'v':
|
switch (optch)
|
||||||
display_version();
|
{
|
||||||
exit(EX_OK);
|
case 'v':
|
||||||
case 'l':
|
display_version ();
|
||||||
display_license();
|
exit (EX_OK);
|
||||||
exit(EX_OK);
|
case 'l':
|
||||||
case 'd':
|
display_license ();
|
||||||
godaemon = FALSE;
|
exit (EX_OK);
|
||||||
break;
|
case 'd':
|
||||||
case 'c':
|
godaemon = FALSE;
|
||||||
config.config_file = safestrdup(optarg);
|
break;
|
||||||
if (!config.config_file) {
|
case 'c':
|
||||||
fprintf(stderr,
|
config.config_file = safestrdup (optarg);
|
||||||
"%s: Could not allocate memory.\n",
|
if (!config.config_file)
|
||||||
argv[0]);
|
{
|
||||||
exit(EX_SOFTWARE);
|
fprintf (stderr, "%s: Could not allocate memory.\n", argv[0]);
|
||||||
}
|
exit (EX_SOFTWARE);
|
||||||
break;
|
}
|
||||||
case 'h':
|
break;
|
||||||
default:
|
case 'h':
|
||||||
display_usage();
|
default:
|
||||||
exit(EX_OK);
|
display_usage ();
|
||||||
}
|
exit (EX_OK);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log_message(LOG_INFO, "Initializing " PACKAGE " ...");
|
log_message (LOG_INFO, "Initializing " PACKAGE " ...");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure the HTML error pages array is NULL to begin with.
|
* Make sure the HTML error pages array is NULL to begin with.
|
||||||
* (FIXME: Should have a better API for all this)
|
* (FIXME: Should have a better API for all this)
|
||||||
*/
|
*/
|
||||||
config.errorpages = NULL;
|
config.errorpages = NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read in the settings from the config file.
|
* Read in the settings from the config file.
|
||||||
*/
|
*/
|
||||||
config_file = fopen(config.config_file, "r");
|
config_file = fopen (config.config_file, "r");
|
||||||
if (!config_file) {
|
if (!config_file)
|
||||||
fprintf(stderr,
|
{
|
||||||
"%s: Could not open configuration file \"%s\".\n",
|
fprintf (stderr,
|
||||||
argv[0], config.config_file);
|
"%s: Could not open configuration file \"%s\".\n",
|
||||||
exit(EX_SOFTWARE);
|
argv[0], config.config_file);
|
||||||
}
|
exit (EX_SOFTWARE);
|
||||||
if (config_compile() || config_parse(&config, config_file)) {
|
}
|
||||||
fprintf(stderr,
|
if (config_compile () || config_parse (&config, config_file))
|
||||||
"Unable to parse configuration file. Not starting.\n");
|
{
|
||||||
exit(EX_SOFTWARE);
|
fprintf (stderr,
|
||||||
}
|
"Unable to parse configuration file. Not starting.\n");
|
||||||
fclose(config_file);
|
exit (EX_SOFTWARE);
|
||||||
|
}
|
||||||
|
fclose (config_file);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write to a user supplied log file if it's defined. This
|
* Write to a user supplied log file if it's defined. This
|
||||||
* will override using the syslog even if syslog is defined.
|
* will override using the syslog even if syslog is defined.
|
||||||
*/
|
*/
|
||||||
if (config.logf_name) {
|
if (config.logf_name)
|
||||||
if (open_log_file(config.logf_name) < 0) {
|
{
|
||||||
fprintf(stderr,
|
if (open_log_file (config.logf_name) < 0)
|
||||||
"%s: Could not create log file.\n", argv[0]);
|
{
|
||||||
exit(EX_SOFTWARE);
|
fprintf (stderr, "%s: Could not create log file.\n", argv[0]);
|
||||||
}
|
exit (EX_SOFTWARE);
|
||||||
config.syslog = FALSE; /* disable syslog */
|
}
|
||||||
} else if (config.syslog) {
|
config.syslog = FALSE; /* disable syslog */
|
||||||
if (godaemon == TRUE)
|
}
|
||||||
openlog("tinyproxy", LOG_PID, LOG_DAEMON);
|
else if (config.syslog)
|
||||||
else
|
{
|
||||||
openlog("tinyproxy", LOG_PID, LOG_USER);
|
if (godaemon == TRUE)
|
||||||
} else {
|
openlog ("tinyproxy", LOG_PID, LOG_DAEMON);
|
||||||
fprintf(stderr,
|
else
|
||||||
"%s: Either define a logfile or enable syslog logging\n",
|
openlog ("tinyproxy", LOG_PID, LOG_USER);
|
||||||
argv[0]);
|
}
|
||||||
exit(EX_SOFTWARE);
|
else
|
||||||
}
|
{
|
||||||
|
fprintf (stderr,
|
||||||
|
"%s: Either define a logfile or enable syslog logging\n",
|
||||||
|
argv[0]);
|
||||||
|
exit (EX_SOFTWARE);
|
||||||
|
}
|
||||||
|
|
||||||
processed_config_file = TRUE;
|
processed_config_file = TRUE;
|
||||||
send_stored_logs();
|
send_stored_logs ();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the default values if they were not set in the config file.
|
* Set the default values if they were not set in the config file.
|
||||||
*/
|
*/
|
||||||
if (config.port == 0) {
|
if (config.port == 0)
|
||||||
fprintf(stderr,
|
{
|
||||||
"%s: You MUST set a Port in the configuration file.\n",
|
fprintf (stderr,
|
||||||
argv[0]);
|
"%s: You MUST set a Port in the configuration file.\n",
|
||||||
exit(EX_SOFTWARE);
|
argv[0]);
|
||||||
}
|
exit (EX_SOFTWARE);
|
||||||
if (!config.stathost) {
|
}
|
||||||
log_message(LOG_INFO, "Setting stathost to \"%s\".",
|
if (!config.stathost)
|
||||||
DEFAULT_STATHOST);
|
{
|
||||||
config.stathost = DEFAULT_STATHOST;
|
log_message (LOG_INFO, "Setting stathost to \"%s\".", DEFAULT_STATHOST);
|
||||||
}
|
config.stathost = DEFAULT_STATHOST;
|
||||||
if (!config.user) {
|
}
|
||||||
log_message(LOG_WARNING,
|
if (!config.user)
|
||||||
"You SHOULD set a UserName in the configuration file. Using current user instead.");
|
{
|
||||||
}
|
log_message (LOG_WARNING,
|
||||||
if (config.idletimeout == 0) {
|
"You SHOULD set a UserName in the configuration file. Using current user instead.");
|
||||||
log_message(LOG_WARNING,
|
}
|
||||||
"Invalid idle time setting. Only values greater than zero allowed; therefore setting idle timeout to %u seconds.",
|
if (config.idletimeout == 0)
|
||||||
MAX_IDLE_TIME);
|
{
|
||||||
config.idletimeout = MAX_IDLE_TIME;
|
log_message (LOG_WARNING,
|
||||||
}
|
"Invalid idle time setting. Only values greater than zero allowed; therefore setting idle timeout to %u seconds.",
|
||||||
|
MAX_IDLE_TIME);
|
||||||
|
config.idletimeout = MAX_IDLE_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
init_stats();
|
init_stats ();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If ANONYMOUS is turned on, make sure that Content-Length is
|
* If ANONYMOUS is turned on, make sure that Content-Length is
|
||||||
* in the list of allowed headers, since it is required in a
|
* 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
|
* HTTP/1.0 request. Also add the Content-Type header since it goes
|
||||||
* hand in hand with Content-Length.
|
* hand in hand with Content-Length.
|
||||||
* - rjkaes
|
* - rjkaes
|
||||||
*/
|
*/
|
||||||
if (is_anonymous_enabled()) {
|
if (is_anonymous_enabled ())
|
||||||
anonymous_insert("Content-Length");
|
{
|
||||||
anonymous_insert("Content-Type");
|
anonymous_insert ("Content-Length");
|
||||||
}
|
anonymous_insert ("Content-Type");
|
||||||
|
}
|
||||||
|
|
||||||
if (godaemon == TRUE)
|
if (godaemon == TRUE)
|
||||||
makedaemon();
|
makedaemon ();
|
||||||
|
|
||||||
if (config.pidpath) {
|
if (config.pidpath)
|
||||||
if (pidfile_create(config.pidpath) < 0) {
|
{
|
||||||
fprintf(stderr, "%s: Could not create PID file.\n",
|
if (pidfile_create (config.pidpath) < 0)
|
||||||
argv[0]);
|
{
|
||||||
exit(EX_OSERR);
|
fprintf (stderr, "%s: Could not create PID file.\n", argv[0]);
|
||||||
}
|
exit (EX_OSERR);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (set_signal_handler(SIGPIPE, SIG_IGN) == SIG_ERR) {
|
if (set_signal_handler (SIGPIPE, SIG_IGN) == SIG_ERR)
|
||||||
fprintf(stderr, "%s: Could not set the \"SIGPIPE\" signal.\n",
|
{
|
||||||
argv[0]);
|
fprintf (stderr, "%s: Could not set the \"SIGPIPE\" signal.\n",
|
||||||
exit(EX_OSERR);
|
argv[0]);
|
||||||
}
|
exit (EX_OSERR);
|
||||||
|
}
|
||||||
#ifdef FILTER_ENABLE
|
#ifdef FILTER_ENABLE
|
||||||
if (config.filter)
|
if (config.filter)
|
||||||
filter_init();
|
filter_init ();
|
||||||
#endif /* FILTER_ENABLE */
|
#endif /* FILTER_ENABLE */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start listening on the selected port.
|
* Start listening on the selected port.
|
||||||
*/
|
*/
|
||||||
if (child_listening_sock(config.port) < 0) {
|
if (child_listening_sock (config.port) < 0)
|
||||||
fprintf(stderr, "%s: Could not create listening socket.\n",
|
{
|
||||||
argv[0]);
|
fprintf (stderr, "%s: Could not create listening socket.\n", argv[0]);
|
||||||
exit(EX_OSERR);
|
exit (EX_OSERR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Switch to a different user.
|
* Switch to a different user.
|
||||||
*/
|
*/
|
||||||
if (geteuid() == 0) {
|
if (geteuid () == 0)
|
||||||
if (config.group && strlen(config.group) > 0) {
|
{
|
||||||
int gid = get_id(config.group);
|
if (config.group && strlen (config.group) > 0)
|
||||||
if (gid < 0) {
|
{
|
||||||
thisgroup = getgrnam(config.group);
|
int gid = get_id (config.group);
|
||||||
if (!thisgroup) {
|
if (gid < 0)
|
||||||
fprintf(stderr,
|
{
|
||||||
"%s: Unable to find "
|
thisgroup = getgrnam (config.group);
|
||||||
"group \"%s\".\n",
|
if (!thisgroup)
|
||||||
argv[0], config.group);
|
{
|
||||||
exit(EX_NOUSER);
|
fprintf (stderr,
|
||||||
}
|
"%s: Unable to find "
|
||||||
gid = thisgroup->gr_gid;
|
"group \"%s\".\n", argv[0], config.group);
|
||||||
}
|
exit (EX_NOUSER);
|
||||||
if (setgid(gid) < 0) {
|
}
|
||||||
fprintf(stderr,
|
gid = thisgroup->gr_gid;
|
||||||
"%s: Unable to change to "
|
}
|
||||||
"group \"%s\".\n",
|
if (setgid (gid) < 0)
|
||||||
argv[0], config.group);
|
{
|
||||||
exit(EX_CANTCREAT);
|
fprintf (stderr,
|
||||||
}
|
"%s: Unable to change to "
|
||||||
log_message(LOG_INFO, "Now running as group \"%s\".",
|
"group \"%s\".\n", argv[0], config.group);
|
||||||
config.group);
|
exit (EX_CANTCREAT);
|
||||||
}
|
}
|
||||||
if (config.user && strlen(config.user) > 0) {
|
log_message (LOG_INFO, "Now running as group \"%s\".",
|
||||||
int uid = get_id(config.user);
|
config.group);
|
||||||
if (uid < 0) {
|
}
|
||||||
thisuser = getpwnam(config.user);
|
if (config.user && strlen (config.user) > 0)
|
||||||
if (!thisuser) {
|
{
|
||||||
fprintf(stderr,
|
int uid = get_id (config.user);
|
||||||
"%s: Unable to find "
|
if (uid < 0)
|
||||||
"user \"%s\".",
|
{
|
||||||
argv[0], config.user);
|
thisuser = getpwnam (config.user);
|
||||||
exit(EX_NOUSER);
|
if (!thisuser)
|
||||||
}
|
{
|
||||||
uid = thisuser->pw_uid;
|
fprintf (stderr,
|
||||||
}
|
"%s: Unable to find "
|
||||||
if (setuid(uid) < 0) {
|
"user \"%s\".", argv[0], config.user);
|
||||||
fprintf(stderr,
|
exit (EX_NOUSER);
|
||||||
"%s: Unable to change to user \"%s\".",
|
}
|
||||||
argv[0], config.user);
|
uid = thisuser->pw_uid;
|
||||||
exit(EX_CANTCREAT);
|
}
|
||||||
}
|
if (setuid (uid) < 0)
|
||||||
log_message(LOG_INFO, "Now running as user \"%s\".",
|
{
|
||||||
config.user);
|
fprintf (stderr,
|
||||||
}
|
"%s: Unable to change to user \"%s\".",
|
||||||
} else {
|
argv[0], config.user);
|
||||||
log_message(LOG_WARNING,
|
exit (EX_CANTCREAT);
|
||||||
"Not running as root, so not changing UID/GID.");
|
}
|
||||||
}
|
log_message (LOG_INFO, "Now running as user \"%s\".", config.user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log_message (LOG_WARNING,
|
||||||
|
"Not running as root, so not changing UID/GID.");
|
||||||
|
}
|
||||||
|
|
||||||
if (child_pool_create() < 0) {
|
if (child_pool_create () < 0)
|
||||||
fprintf(stderr, "%s: Could not create the pool of children.",
|
{
|
||||||
argv[0]);
|
fprintf (stderr, "%s: Could not create the pool of children.", argv[0]);
|
||||||
exit(EX_SOFTWARE);
|
exit (EX_SOFTWARE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These signals are only for the parent process.
|
* These signals are only for the parent process.
|
||||||
*/
|
*/
|
||||||
log_message(LOG_INFO, "Setting the various signals.");
|
log_message (LOG_INFO, "Setting the various signals.");
|
||||||
if (set_signal_handler(SIGCHLD, takesig) == SIG_ERR) {
|
if (set_signal_handler (SIGCHLD, takesig) == SIG_ERR)
|
||||||
fprintf(stderr, "%s: Could not set the \"SIGCHLD\" signal.\n",
|
{
|
||||||
argv[0]);
|
fprintf (stderr, "%s: Could not set the \"SIGCHLD\" signal.\n",
|
||||||
exit(EX_OSERR);
|
argv[0]);
|
||||||
}
|
exit (EX_OSERR);
|
||||||
if (set_signal_handler(SIGTERM, takesig) == SIG_ERR) {
|
}
|
||||||
fprintf(stderr, "%s: Could not set the \"SIGTERM\" signal.\n",
|
if (set_signal_handler (SIGTERM, takesig) == SIG_ERR)
|
||||||
argv[0]);
|
{
|
||||||
exit(EX_OSERR);
|
fprintf (stderr, "%s: Could not set the \"SIGTERM\" signal.\n",
|
||||||
}
|
argv[0]);
|
||||||
if (set_signal_handler(SIGHUP, takesig) == SIG_ERR) {
|
exit (EX_OSERR);
|
||||||
fprintf(stderr, "%s: Could not set the \"SIGHUP\" signal.\n",
|
}
|
||||||
argv[0]);
|
if (set_signal_handler (SIGHUP, takesig) == SIG_ERR)
|
||||||
exit(EX_OSERR);
|
{
|
||||||
}
|
fprintf (stderr, "%s: Could not set the \"SIGHUP\" signal.\n", argv[0]);
|
||||||
|
exit (EX_OSERR);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start the main loop.
|
* Start the main loop.
|
||||||
*/
|
*/
|
||||||
log_message(LOG_INFO, "Starting main loop. Accepting connections.");
|
log_message (LOG_INFO, "Starting main loop. Accepting connections.");
|
||||||
|
|
||||||
child_main_loop();
|
child_main_loop ();
|
||||||
|
|
||||||
log_message(LOG_INFO, "Shutting down.");
|
log_message (LOG_INFO, "Shutting down.");
|
||||||
|
|
||||||
child_kill_children();
|
child_kill_children ();
|
||||||
child_close_sock();
|
child_close_sock ();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remove the PID file.
|
* Remove the PID file.
|
||||||
*/
|
*/
|
||||||
if (unlink(config.pidpath) < 0) {
|
if (unlink (config.pidpath) < 0)
|
||||||
log_message(LOG_WARNING,
|
{
|
||||||
"Could not remove PID file \"%s\": %s.",
|
log_message (LOG_WARNING,
|
||||||
config.pidpath, strerror(errno));
|
"Could not remove PID file \"%s\": %s.",
|
||||||
}
|
config.pidpath, strerror (errno));
|
||||||
|
}
|
||||||
#ifdef FILTER_ENABLE
|
#ifdef FILTER_ENABLE
|
||||||
if (config.filter)
|
if (config.filter)
|
||||||
filter_destroy();
|
filter_destroy ();
|
||||||
#endif /* FILTER_ENABLE */
|
#endif /* FILTER_ENABLE */
|
||||||
|
|
||||||
if (config.syslog)
|
if (config.syslog)
|
||||||
closelog();
|
closelog ();
|
||||||
else
|
else
|
||||||
close_log_file();
|
close_log_file ();
|
||||||
|
|
||||||
exit(EX_OK);
|
exit (EX_OK);
|
||||||
}
|
}
|
||||||
|
108
src/tinyproxy.h
108
src/tinyproxy.h
@ -26,82 +26,84 @@
|
|||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
|
|
||||||
/* Global variables for the main controls of the program */
|
/* Global variables for the main controls of the program */
|
||||||
#define MAXBUFFSIZE ((size_t)(1024 * 96)) /* Max size of buffer */
|
#define MAXBUFFSIZE ((size_t)(1024 * 96)) /* Max size of buffer */
|
||||||
#define MAX_IDLE_TIME (60 * 10) /* 10 minutes of no activity */
|
#define MAX_IDLE_TIME (60 * 10) /* 10 minutes of no activity */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Even if upstream support is not compiled into tinyproxy, this
|
* Even if upstream support is not compiled into tinyproxy, this
|
||||||
* structure still needs to be defined.
|
* structure still needs to be defined.
|
||||||
*/
|
*/
|
||||||
struct upstream {
|
struct upstream
|
||||||
struct upstream *next;
|
{
|
||||||
char *domain; /* optional */
|
struct upstream *next;
|
||||||
char *host;
|
char *domain; /* optional */
|
||||||
int port;
|
char *host;
|
||||||
in_addr_t ip, mask;
|
int port;
|
||||||
|
in_addr_t ip, mask;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Hold all the configuration time information.
|
* Hold all the configuration time information.
|
||||||
*/
|
*/
|
||||||
struct config_s {
|
struct config_s
|
||||||
char *logf_name;
|
{
|
||||||
char *config_file;
|
char *logf_name;
|
||||||
unsigned int syslog; /* boolean */
|
char *config_file;
|
||||||
int port;
|
unsigned int syslog; /* boolean */
|
||||||
char *stathost;
|
int port;
|
||||||
unsigned int quit; /* boolean */
|
char *stathost;
|
||||||
char *user;
|
unsigned int quit; /* boolean */
|
||||||
char *group;
|
char *user;
|
||||||
char *ipAddr;
|
char *group;
|
||||||
|
char *ipAddr;
|
||||||
#ifdef FILTER_ENABLE
|
#ifdef FILTER_ENABLE
|
||||||
char *filter;
|
char *filter;
|
||||||
unsigned int filter_url; /* boolean */
|
unsigned int filter_url; /* boolean */
|
||||||
unsigned int filter_extended; /* boolean */
|
unsigned int filter_extended; /* boolean */
|
||||||
unsigned int filter_casesensitive; /* boolean */
|
unsigned int filter_casesensitive; /* boolean */
|
||||||
#endif /* FILTER_ENABLE */
|
#endif /* FILTER_ENABLE */
|
||||||
#ifdef XTINYPROXY_ENABLE
|
#ifdef XTINYPROXY_ENABLE
|
||||||
char *my_domain;
|
char *my_domain;
|
||||||
#endif
|
#endif
|
||||||
#ifdef REVERSE_SUPPORT
|
#ifdef REVERSE_SUPPORT
|
||||||
struct reversepath *reversepath_list;
|
struct reversepath *reversepath_list;
|
||||||
unsigned int reverseonly; /* boolean */
|
unsigned int reverseonly; /* boolean */
|
||||||
unsigned int reversemagic; /* boolean */
|
unsigned int reversemagic; /* boolean */
|
||||||
char *reversebaseurl;
|
char *reversebaseurl;
|
||||||
#endif
|
#endif
|
||||||
#ifdef UPSTREAM_SUPPORT
|
#ifdef UPSTREAM_SUPPORT
|
||||||
struct upstream *upstream_list;
|
struct upstream *upstream_list;
|
||||||
#endif /* UPSTREAM_SUPPORT */
|
#endif /* UPSTREAM_SUPPORT */
|
||||||
char *pidpath;
|
char *pidpath;
|
||||||
unsigned int idletimeout;
|
unsigned int idletimeout;
|
||||||
char *bind_address;
|
char *bind_address;
|
||||||
unsigned int bindsame;
|
unsigned int bindsame;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The configured name to use in the HTTP "Via" header field.
|
* The configured name to use in the HTTP "Via" header field.
|
||||||
*/
|
*/
|
||||||
char *via_proxy_name;
|
char *via_proxy_name;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Error page support. Map error numbers to file paths.
|
* Error page support. Map error numbers to file paths.
|
||||||
*/
|
*/
|
||||||
hashmap_t errorpages;
|
hashmap_t errorpages;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Error page to be displayed if appropriate page cannot be located
|
* Error page to be displayed if appropriate page cannot be located
|
||||||
* in the errorpages structure.
|
* in the errorpages structure.
|
||||||
*/
|
*/
|
||||||
char *errorpage_undef;
|
char *errorpage_undef;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The HTML statistics page.
|
* The HTML statistics page.
|
||||||
*/
|
*/
|
||||||
char *statpage;
|
char *statpage;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Global Structures used in the program */
|
/* Global Structures used in the program */
|
||||||
extern struct config_s config;
|
extern struct config_s config;
|
||||||
extern unsigned int received_sighup; /* boolean */
|
extern unsigned int received_sighup; /* boolean */
|
||||||
extern unsigned int processed_config_file; /* boolean */
|
extern unsigned int processed_config_file; /* boolean */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -36,86 +36,86 @@
|
|||||||
* Build a URL from parts.
|
* Build a URL from parts.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
build_url(char **url, const char *host, int port, const char *path)
|
build_url (char **url, const char *host, int port, const char *path)
|
||||||
{
|
{
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
assert(url != NULL);
|
assert (url != NULL);
|
||||||
assert(host != NULL);
|
assert (host != NULL);
|
||||||
assert(port > 0 && port < 32768);
|
assert (port > 0 && port < 32768);
|
||||||
assert(path != NULL);
|
assert (path != NULL);
|
||||||
|
|
||||||
len = strlen(host) + strlen(path) + 14;
|
len = strlen (host) + strlen (path) + 14;
|
||||||
*url = safemalloc(len);
|
*url = safemalloc (len);
|
||||||
if (*url == NULL)
|
if (*url == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return snprintf(*url, len, "http://%s:%d%s", host, port, path);
|
return snprintf (*url, len, "http://%s:%d%s", host, port, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
do_transparent_proxy(struct conn_s *connptr, hashmap_t hashofheaders,
|
do_transparent_proxy (struct conn_s *connptr, hashmap_t hashofheaders,
|
||||||
struct request_s *request, struct config_s *conf, char *url)
|
struct request_s *request, struct config_s *conf,
|
||||||
|
char *url)
|
||||||
{
|
{
|
||||||
socklen_t length;
|
socklen_t length;
|
||||||
char *data;
|
char *data;
|
||||||
|
|
||||||
length =
|
length = hashmap_entry_by_key (hashofheaders, "host", (void **) &data);
|
||||||
hashmap_entry_by_key(hashofheaders, "host", (void **)&data);
|
if (length <= 0)
|
||||||
if (length <= 0) {
|
{
|
||||||
struct sockaddr_in dest_addr;
|
struct sockaddr_in dest_addr;
|
||||||
|
|
||||||
if (getsockname
|
if (getsockname
|
||||||
(connptr->client_fd, (struct sockaddr *)&dest_addr,
|
(connptr->client_fd, (struct sockaddr *) &dest_addr, &length) < 0)
|
||||||
&length) < 0) {
|
{
|
||||||
log_message(LOG_ERR,
|
log_message (LOG_ERR,
|
||||||
"process_request: cannot get destination IP for %d",
|
"process_request: cannot get destination IP for %d",
|
||||||
connptr->client_fd);
|
connptr->client_fd);
|
||||||
indicate_http_error(connptr, 400, "Bad Request",
|
indicate_http_error (connptr, 400, "Bad Request",
|
||||||
"detail",
|
"detail",
|
||||||
"Unknown destination",
|
"Unknown destination", "url", url, NULL);
|
||||||
"url", url, NULL);
|
return 0;
|
||||||
return 0;
|
}
|
||||||
}
|
request->host = safemalloc (17);
|
||||||
request->host = safemalloc(17);
|
strcpy (request->host, inet_ntoa (dest_addr.sin_addr));
|
||||||
strcpy(request->host, inet_ntoa(dest_addr.sin_addr));
|
request->port = ntohs (dest_addr.sin_port);
|
||||||
request->port = ntohs(dest_addr.sin_port);
|
request->path = safemalloc (strlen (url) + 1);
|
||||||
request->path = safemalloc(strlen(url) + 1);
|
strcpy (request->path, url);
|
||||||
strcpy(request->path, url);
|
safefree (url);
|
||||||
safefree(url);
|
build_url (&url, request->host, request->port, request->path);
|
||||||
build_url(&url, request->host, request->port,
|
log_message (LOG_INFO,
|
||||||
request->path);
|
"process_request: trans IP %s %s for %d",
|
||||||
log_message(LOG_INFO,
|
request->method, url, connptr->client_fd);
|
||||||
"process_request: trans IP %s %s for %d",
|
}
|
||||||
request->method, url, connptr->client_fd);
|
else
|
||||||
} else {
|
{
|
||||||
request->host = safemalloc(length + 1);
|
request->host = safemalloc (length + 1);
|
||||||
if (sscanf
|
if (sscanf (data, "%[^:]:%hu", request->host, &request->port) != 2)
|
||||||
(data, "%[^:]:%hu", request->host,
|
{
|
||||||
&request->port) != 2) {
|
strcpy (request->host, data);
|
||||||
strcpy(request->host, data);
|
request->port = HTTP_PORT;
|
||||||
request->port = HTTP_PORT;
|
}
|
||||||
}
|
request->path = safemalloc (strlen (url) + 1);
|
||||||
request->path = safemalloc(strlen(url) + 1);
|
strcpy (request->path, url);
|
||||||
strcpy(request->path, url);
|
safefree (url);
|
||||||
safefree(url);
|
build_url (&url, request->host, request->port, request->path);
|
||||||
build_url(&url, request->host, request->port,
|
log_message (LOG_INFO,
|
||||||
request->path);
|
"process_request: trans Host %s %s for %d",
|
||||||
log_message(LOG_INFO,
|
request->method, url, connptr->client_fd);
|
||||||
"process_request: trans Host %s %s for %d",
|
}
|
||||||
request->method, url, connptr->client_fd);
|
if (conf->ipAddr && strcmp (request->host, conf->ipAddr) == 0)
|
||||||
}
|
{
|
||||||
if (conf->ipAddr && strcmp(request->host, conf->ipAddr) == 0) {
|
log_message (LOG_ERR,
|
||||||
log_message(LOG_ERR,
|
"process_request: destination IP is localhost %d",
|
||||||
"process_request: destination IP is localhost %d",
|
connptr->client_fd);
|
||||||
connptr->client_fd);
|
indicate_http_error (connptr, 400, "Bad Request",
|
||||||
indicate_http_error(connptr, 400, "Bad Request",
|
"detail",
|
||||||
"detail",
|
"You tried to connect to the machine the proxy is running on",
|
||||||
"You tried to connect to the machine the proxy is running on",
|
"url", url, NULL);
|
||||||
"url", url, NULL);
|
return 0;
|
||||||
return 0;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -29,9 +29,10 @@
|
|||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
#include "reqs.h"
|
#include "reqs.h"
|
||||||
|
|
||||||
extern int do_transparent_proxy(struct conn_s *connptr,
|
extern int do_transparent_proxy (struct conn_s *connptr,
|
||||||
hashmap_t hashofheaders, struct request_s *request,
|
hashmap_t hashofheaders,
|
||||||
struct config_s *config, char *url);
|
struct request_s *request,
|
||||||
|
struct config_s *config, char *url);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
284
src/utils.c
284
src/utils.c
@ -34,175 +34,183 @@
|
|||||||
* Build the data for a complete HTTP & HTML message for the client.
|
* Build the data for a complete HTTP & HTML message for the client.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
send_http_message(struct conn_s *connptr, int http_code,
|
send_http_message (struct conn_s *connptr, int http_code,
|
||||||
const char *error_title, const char *message)
|
const char *error_title, const char *message)
|
||||||
{
|
{
|
||||||
static char *headers[] = {
|
static char *headers[] = {
|
||||||
"Server: " PACKAGE "/" VERSION,
|
"Server: " PACKAGE "/" VERSION,
|
||||||
"Content-type: text/html",
|
"Content-type: text/html",
|
||||||
"Connection: close"
|
"Connection: close"
|
||||||
};
|
};
|
||||||
|
|
||||||
http_message_t msg;
|
http_message_t msg;
|
||||||
|
|
||||||
msg = http_message_create(http_code, error_title);
|
msg = http_message_create (http_code, error_title);
|
||||||
if (msg == NULL)
|
if (msg == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
http_message_add_headers(msg, headers, 3);
|
http_message_add_headers (msg, headers, 3);
|
||||||
http_message_set_body(msg, message, strlen(message));
|
http_message_set_body (msg, message, strlen (message));
|
||||||
http_message_send(msg, connptr->client_fd);
|
http_message_send (msg, connptr->client_fd);
|
||||||
http_message_destroy(msg);
|
http_message_destroy (msg);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Safely creates filename and returns the low-level file descriptor.
|
* Safely creates filename and returns the low-level file descriptor.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
create_file_safely(const char *filename, unsigned int truncate_file)
|
create_file_safely (const char *filename, unsigned int truncate_file)
|
||||||
{
|
{
|
||||||
struct stat lstatinfo;
|
struct stat lstatinfo;
|
||||||
int fildes;
|
int fildes;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* lstat() the file. If it doesn't exist, create it with O_EXCL.
|
* lstat() the file. If it doesn't exist, create it with O_EXCL.
|
||||||
* If it does exist, open it for writing and perform the fstat()
|
* If it does exist, open it for writing and perform the fstat()
|
||||||
* check.
|
* check.
|
||||||
*/
|
*/
|
||||||
if (lstat(filename, &lstatinfo) < 0) {
|
if (lstat (filename, &lstatinfo) < 0)
|
||||||
/*
|
{
|
||||||
* If lstat() failed for any reason other than "file not
|
/*
|
||||||
* existing", exit.
|
* If lstat() failed for any reason other than "file not
|
||||||
*/
|
* existing", exit.
|
||||||
if (errno != ENOENT) {
|
*/
|
||||||
fprintf(stderr,
|
if (errno != ENOENT)
|
||||||
"%s: Error checking file %s: %s\n",
|
{
|
||||||
PACKAGE, filename, strerror(errno));
|
fprintf (stderr,
|
||||||
return -EACCES;
|
"%s: Error checking file %s: %s\n",
|
||||||
}
|
PACKAGE, filename, strerror (errno));
|
||||||
|
return -EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The file doesn't exist, so create it with O_EXCL to make
|
* The file doesn't exist, so create it with O_EXCL to make
|
||||||
* sure an attacker can't slip in a file between the lstat()
|
* sure an attacker can't slip in a file between the lstat()
|
||||||
* and open()
|
* and open()
|
||||||
*/
|
*/
|
||||||
if ((fildes =
|
if ((fildes = open (filename, O_RDWR | O_CREAT | O_EXCL, 0600)) < 0)
|
||||||
open(filename, O_RDWR | O_CREAT | O_EXCL, 0600)) < 0) {
|
{
|
||||||
fprintf(stderr,
|
fprintf (stderr,
|
||||||
"%s: Could not create file %s: %s\n",
|
"%s: Could not create file %s: %s\n",
|
||||||
PACKAGE, filename, strerror(errno));
|
PACKAGE, filename, strerror (errno));
|
||||||
return fildes;
|
return fildes;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
struct stat fstatinfo;
|
else
|
||||||
int flags;
|
{
|
||||||
|
struct stat fstatinfo;
|
||||||
|
int flags;
|
||||||
|
|
||||||
flags = O_RDWR;
|
flags = O_RDWR;
|
||||||
if (!truncate_file)
|
if (!truncate_file)
|
||||||
flags |= O_APPEND;
|
flags |= O_APPEND;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open an existing file.
|
* Open an existing file.
|
||||||
*/
|
*/
|
||||||
if ((fildes = open(filename, flags)) < 0) {
|
if ((fildes = open (filename, flags)) < 0)
|
||||||
fprintf(stderr,
|
{
|
||||||
"%s: Could not open file %s: %s\n",
|
fprintf (stderr,
|
||||||
PACKAGE, filename, strerror(errno));
|
"%s: Could not open file %s: %s\n",
|
||||||
return fildes;
|
PACKAGE, filename, strerror (errno));
|
||||||
}
|
return fildes;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* fstat() the opened file and check that the file mode bits,
|
* fstat() the opened file and check that the file mode bits,
|
||||||
* inode, and device match.
|
* inode, and device match.
|
||||||
*/
|
*/
|
||||||
if (fstat(fildes, &fstatinfo) < 0
|
if (fstat (fildes, &fstatinfo) < 0
|
||||||
|| lstatinfo.st_mode != fstatinfo.st_mode
|
|| lstatinfo.st_mode != fstatinfo.st_mode
|
||||||
|| lstatinfo.st_ino != fstatinfo.st_ino
|
|| lstatinfo.st_ino != fstatinfo.st_ino
|
||||||
|| lstatinfo.st_dev != fstatinfo.st_dev) {
|
|| lstatinfo.st_dev != fstatinfo.st_dev)
|
||||||
fprintf(stderr,
|
{
|
||||||
"%s: The file %s has been changed before it could be opened\n",
|
fprintf (stderr,
|
||||||
PACKAGE, filename);
|
"%s: The file %s has been changed before it could be opened\n",
|
||||||
close(fildes);
|
PACKAGE, filename);
|
||||||
return -EIO;
|
close (fildes);
|
||||||
}
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the above check was passed, we know that the lstat()
|
* If the above check was passed, we know that the lstat()
|
||||||
* and fstat() were done on the same file. Now we check that
|
* and fstat() were done on the same file. Now we check that
|
||||||
* there's only one link, and that it's a normal file (this
|
* there's only one link, and that it's a normal file (this
|
||||||
* isn't strictly necessary because the fstat() vs lstat()
|
* isn't strictly necessary because the fstat() vs lstat()
|
||||||
* st_mode check would also find this)
|
* st_mode check would also find this)
|
||||||
*/
|
*/
|
||||||
if (fstatinfo.st_nlink > 1 || !S_ISREG(lstatinfo.st_mode)) {
|
if (fstatinfo.st_nlink > 1 || !S_ISREG (lstatinfo.st_mode))
|
||||||
fprintf(stderr,
|
{
|
||||||
"%s: The file %s has too many links, or is not a regular file: %s\n",
|
fprintf (stderr,
|
||||||
PACKAGE, filename, strerror(errno));
|
"%s: The file %s has too many links, or is not a regular file: %s\n",
|
||||||
close(fildes);
|
PACKAGE, filename, strerror (errno));
|
||||||
return -EMLINK;
|
close (fildes);
|
||||||
}
|
return -EMLINK;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Just return the file descriptor if we _don't_ want the file
|
* Just return the file descriptor if we _don't_ want the file
|
||||||
* truncated.
|
* truncated.
|
||||||
*/
|
*/
|
||||||
if (!truncate_file)
|
if (!truncate_file)
|
||||||
return fildes;
|
return fildes;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* On systems which don't support ftruncate() the best we can
|
* On systems which don't support ftruncate() the best we can
|
||||||
* do is to close the file and reopen it in create mode, which
|
* do is to close the file and reopen it in create mode, which
|
||||||
* unfortunately leads to a race condition, however "systems
|
* unfortunately leads to a race condition, however "systems
|
||||||
* which don't support ftruncate()" is pretty much SCO only,
|
* which don't support ftruncate()" is pretty much SCO only,
|
||||||
* and if you're using that you deserve what you get.
|
* and if you're using that you deserve what you get.
|
||||||
* ("Little sympathy has been extended")
|
* ("Little sympathy has been extended")
|
||||||
*/
|
*/
|
||||||
#ifdef HAVE_FTRUNCATE
|
#ifdef HAVE_FTRUNCATE
|
||||||
ftruncate(fildes, 0);
|
ftruncate (fildes, 0);
|
||||||
#else
|
#else
|
||||||
close(fildes);
|
close (fildes);
|
||||||
if ((fildes =
|
if ((fildes = open (filename, O_RDWR | O_CREAT | O_TRUNC, 0600)) < 0)
|
||||||
open(filename, O_RDWR | O_CREAT | O_TRUNC, 0600)) < 0) {
|
{
|
||||||
fprintf(stderr,
|
fprintf (stderr,
|
||||||
"%s: Could not open file %s: %s.",
|
"%s: Could not open file %s: %s.",
|
||||||
PACKAGE, filename, strerror(errno));
|
PACKAGE, filename, strerror (errno));
|
||||||
return fildes;
|
return fildes;
|
||||||
}
|
}
|
||||||
#endif /* HAVE_FTRUNCATE */
|
#endif /* HAVE_FTRUNCATE */
|
||||||
}
|
}
|
||||||
|
|
||||||
return fildes;
|
return fildes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write the PID of the program to the specified file.
|
* Write the PID of the program to the specified file.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
pidfile_create(const char *filename)
|
pidfile_create (const char *filename)
|
||||||
{
|
{
|
||||||
int fildes;
|
int fildes;
|
||||||
FILE *fd;
|
FILE *fd;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a new file
|
* Create a new file
|
||||||
*/
|
*/
|
||||||
if ((fildes = create_file_safely(filename, TRUE)) < 0)
|
if ((fildes = create_file_safely (filename, TRUE)) < 0)
|
||||||
return fildes;
|
return fildes;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open a stdio file over the low-level one.
|
* Open a stdio file over the low-level one.
|
||||||
*/
|
*/
|
||||||
if ((fd = fdopen(fildes, "w")) == NULL) {
|
if ((fd = fdopen (fildes, "w")) == NULL)
|
||||||
fprintf(stderr,
|
{
|
||||||
"%s: Could not write PID file %s: %s.",
|
fprintf (stderr,
|
||||||
PACKAGE, filename, strerror(errno));
|
"%s: Could not write PID file %s: %s.",
|
||||||
close(fildes);
|
PACKAGE, filename, strerror (errno));
|
||||||
unlink(filename);
|
close (fildes);
|
||||||
return -EIO;
|
unlink (filename);
|
||||||
}
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
fprintf(fd, "%ld\n", (long)getpid());
|
fprintf (fd, "%ld\n", (long) getpid ());
|
||||||
fclose(fd);
|
fclose (fd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -27,10 +27,11 @@
|
|||||||
*/
|
*/
|
||||||
struct conn_s;
|
struct conn_s;
|
||||||
|
|
||||||
extern int send_http_message(struct conn_s *connptr, int http_code,
|
extern int send_http_message (struct conn_s *connptr, int http_code,
|
||||||
const char *error_title, const char *message);
|
const char *error_title, const char *message);
|
||||||
|
|
||||||
extern int pidfile_create(const char *path);
|
extern int pidfile_create (const char *path);
|
||||||
extern int create_file_safely(const char *filename, unsigned int truncate_file);
|
extern int create_file_safely (const char *filename,
|
||||||
|
unsigned int truncate_file);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
170
src/vector.c
170
src/vector.c
@ -33,17 +33,19 @@
|
|||||||
* vector_s stores a pointer to the first vector (vector[0]) and a
|
* vector_s stores a pointer to the first vector (vector[0]) and a
|
||||||
* count of the number of entries (or how long the vector is.)
|
* count of the number of entries (or how long the vector is.)
|
||||||
*/
|
*/
|
||||||
struct vectorentry_s {
|
struct vectorentry_s
|
||||||
void *data;
|
{
|
||||||
size_t len;
|
void *data;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
struct vectorentry_s *next;
|
struct vectorentry_s *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct vector_s {
|
struct vector_s
|
||||||
size_t num_entries;
|
{
|
||||||
struct vectorentry_s *head;
|
size_t num_entries;
|
||||||
struct vectorentry_s *tail;
|
struct vectorentry_s *head;
|
||||||
|
struct vectorentry_s *tail;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -54,18 +56,18 @@ struct vector_s {
|
|||||||
* vector.
|
* vector.
|
||||||
*/
|
*/
|
||||||
vector_t
|
vector_t
|
||||||
vector_create(void)
|
vector_create (void)
|
||||||
{
|
{
|
||||||
vector_t vector;
|
vector_t vector;
|
||||||
|
|
||||||
vector = safemalloc(sizeof(struct vector_s));
|
vector = safemalloc (sizeof (struct vector_s));
|
||||||
if (!vector)
|
if (!vector)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
vector->num_entries = 0;
|
vector->num_entries = 0;
|
||||||
vector->head = vector->tail = NULL;
|
vector->head = vector->tail = NULL;
|
||||||
|
|
||||||
return vector;
|
return vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -75,25 +77,26 @@ vector_create(void)
|
|||||||
* negative if a NULL vector is supplied
|
* negative if a NULL vector is supplied
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
vector_delete(vector_t vector)
|
vector_delete (vector_t vector)
|
||||||
{
|
{
|
||||||
struct vectorentry_s *ptr, *next;
|
struct vectorentry_s *ptr, *next;
|
||||||
|
|
||||||
if (!vector)
|
if (!vector)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ptr = vector->head;
|
ptr = vector->head;
|
||||||
while (ptr) {
|
while (ptr)
|
||||||
next = ptr->next;
|
{
|
||||||
safefree(ptr->data);
|
next = ptr->next;
|
||||||
safefree(ptr);
|
safefree (ptr->data);
|
||||||
|
safefree (ptr);
|
||||||
|
|
||||||
ptr = next;
|
ptr = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
safefree(vector);
|
safefree (vector);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -110,44 +113,48 @@ vector_delete(vector_t vector)
|
|||||||
#define INSERT_APPEND 1
|
#define INSERT_APPEND 1
|
||||||
|
|
||||||
static int
|
static int
|
||||||
vector_insert(vector_t vector, void *data, ssize_t len, int pos)
|
vector_insert (vector_t vector, void *data, ssize_t len, int pos)
|
||||||
{
|
{
|
||||||
struct vectorentry_s *entry;
|
struct vectorentry_s *entry;
|
||||||
|
|
||||||
if (!vector || !data || len <= 0 ||
|
if (!vector || !data || len <= 0 ||
|
||||||
(pos != INSERT_PREPEND && pos != INSERT_APPEND))
|
(pos != INSERT_PREPEND && pos != INSERT_APPEND))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
entry = safemalloc(sizeof(struct vectorentry_s));
|
entry = safemalloc (sizeof (struct vectorentry_s));
|
||||||
if (!entry)
|
if (!entry)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
entry->data = safemalloc(len);
|
entry->data = safemalloc (len);
|
||||||
if (!entry->data) {
|
if (!entry->data)
|
||||||
safefree(entry);
|
{
|
||||||
return -ENOMEM;
|
safefree (entry);
|
||||||
}
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
memcpy(entry->data, data, len);
|
memcpy (entry->data, data, len);
|
||||||
entry->len = len;
|
entry->len = len;
|
||||||
entry->next = NULL;
|
entry->next = NULL;
|
||||||
|
|
||||||
/* If there is no head or tail, create them */
|
/* If there is no head or tail, create them */
|
||||||
if (!vector->head && !vector->tail)
|
if (!vector->head && !vector->tail)
|
||||||
vector->head = vector->tail = entry;
|
vector->head = vector->tail = entry;
|
||||||
else if (pos == 0) {
|
else if (pos == 0)
|
||||||
/* prepend the entry */
|
{
|
||||||
entry->next = vector->head;
|
/* prepend the entry */
|
||||||
vector->head = entry;
|
entry->next = vector->head;
|
||||||
} else {
|
vector->head = entry;
|
||||||
/* append the entry */
|
}
|
||||||
vector->tail->next = entry;
|
else
|
||||||
vector->tail = entry;
|
{
|
||||||
}
|
/* append the entry */
|
||||||
|
vector->tail->next = entry;
|
||||||
|
vector->tail = entry;
|
||||||
|
}
|
||||||
|
|
||||||
vector->num_entries++;
|
vector->num_entries++;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -156,15 +163,15 @@ vector_insert(vector_t vector, void *data, ssize_t len, int pos)
|
|||||||
* arguments.
|
* arguments.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
vector_append(vector_t vector, void *data, ssize_t len)
|
vector_append (vector_t vector, void *data, ssize_t len)
|
||||||
{
|
{
|
||||||
return vector_insert(vector, data, len, INSERT_APPEND);
|
return vector_insert (vector, data, len, INSERT_APPEND);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
vector_prepend(vector_t vector, void *data, ssize_t len)
|
vector_prepend (vector_t vector, void *data, ssize_t len)
|
||||||
{
|
{
|
||||||
return vector_insert(vector, data, len, INSERT_PREPEND);
|
return vector_insert (vector, data, len, INSERT_PREPEND);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -175,26 +182,27 @@ vector_prepend(vector_t vector, void *data, ssize_t len)
|
|||||||
* length of data if position is valid
|
* length of data if position is valid
|
||||||
*/
|
*/
|
||||||
void *
|
void *
|
||||||
vector_getentry(vector_t vector, size_t pos, size_t * size)
|
vector_getentry (vector_t vector, size_t pos, size_t * size)
|
||||||
{
|
{
|
||||||
struct vectorentry_s *ptr;
|
struct vectorentry_s *ptr;
|
||||||
size_t loc;
|
size_t loc;
|
||||||
|
|
||||||
if (!vector || pos >= vector->num_entries)
|
if (!vector || pos >= vector->num_entries)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
loc = 0;
|
loc = 0;
|
||||||
ptr = vector->head;
|
ptr = vector->head;
|
||||||
|
|
||||||
while (loc != pos) {
|
while (loc != pos)
|
||||||
ptr = ptr->next;
|
{
|
||||||
loc++;
|
ptr = ptr->next;
|
||||||
}
|
loc++;
|
||||||
|
}
|
||||||
|
|
||||||
if (size)
|
if (size)
|
||||||
*size = ptr->len;
|
*size = ptr->len;
|
||||||
|
|
||||||
return ptr->data;
|
return ptr->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -204,10 +212,10 @@ vector_getentry(vector_t vector, size_t pos, size_t * size)
|
|||||||
* positive length of vector otherwise
|
* positive length of vector otherwise
|
||||||
*/
|
*/
|
||||||
ssize_t
|
ssize_t
|
||||||
vector_length(vector_t vector)
|
vector_length (vector_t vector)
|
||||||
{
|
{
|
||||||
if (!vector)
|
if (!vector)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
return vector->num_entries;
|
return vector->num_entries;
|
||||||
}
|
}
|
||||||
|
22
src/vector.h
22
src/vector.h
@ -23,7 +23,8 @@
|
|||||||
|
|
||||||
/* Allow the use in C++ code. */
|
/* Allow the use in C++ code. */
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
extern "C" {
|
extern "C"
|
||||||
|
{
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -31,14 +32,14 @@ extern "C" {
|
|||||||
* vector. Sure, it's a pointer, but the struct is hidden in the C file.
|
* vector. Sure, it's a pointer, but the struct is hidden in the C file.
|
||||||
* So, just use the vector_t like it's a cookie. :)
|
* So, just use the vector_t like it's a cookie. :)
|
||||||
*/
|
*/
|
||||||
typedef struct vector_s *vector_t;
|
typedef struct vector_s *vector_t;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* vector_create() takes no arguments.
|
* vector_create() takes no arguments.
|
||||||
* vector_delete() is self explanatory.
|
* vector_delete() is self explanatory.
|
||||||
*/
|
*/
|
||||||
extern vector_t vector_create(void);
|
extern vector_t vector_create (void);
|
||||||
extern int vector_delete(vector_t vector);
|
extern int vector_delete (vector_t vector);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When you insert a piece of data into the vector, the data will be
|
* When you insert a piece of data into the vector, the data will be
|
||||||
@ -48,8 +49,8 @@ extern "C" {
|
|||||||
* Returns: negative on error
|
* Returns: negative on error
|
||||||
* 0 upon successful insert.
|
* 0 upon successful insert.
|
||||||
*/
|
*/
|
||||||
extern int vector_append(vector_t vector, void *data, ssize_t len);
|
extern int vector_append (vector_t vector, void *data, ssize_t len);
|
||||||
extern int vector_prepend(vector_t vector, void *data, ssize_t len);
|
extern int vector_prepend (vector_t vector, void *data, ssize_t len);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A pointer to the data at position "pos" (zero based) is returned and the
|
* A pointer to the data at position "pos" (zero based) is returned and the
|
||||||
@ -67,8 +68,7 @@ extern "C" {
|
|||||||
* Returns: NULL on error
|
* Returns: NULL on error
|
||||||
* valid pointer to data
|
* valid pointer to data
|
||||||
*/
|
*/
|
||||||
extern void *vector_getentry(vector_t vector, size_t pos,
|
extern void *vector_getentry (vector_t vector, size_t pos, size_t * size);
|
||||||
size_t * size);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the number of enteries (or the length) of the vector.
|
* Returns the number of enteries (or the length) of the vector.
|
||||||
@ -76,9 +76,9 @@ extern "C" {
|
|||||||
* Returns: negative if vector is not valid
|
* Returns: negative if vector is not valid
|
||||||
* positive length of vector otherwise
|
* positive length of vector otherwise
|
||||||
*/
|
*/
|
||||||
extern ssize_t vector_length(vector_t vector);
|
extern ssize_t vector_length (vector_t vector);
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
}
|
}
|
||||||
#endif /* C++ */
|
#endif /* C++ */
|
||||||
#endif /* _VECTOR_H */
|
#endif /* _VECTOR_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user