Merge branch 'master' into header-patch

This commit is contained in:
rofl0r 2020-09-07 01:12:23 +01:00 committed by GitHub
commit d0b17765bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 1840 additions and 1477 deletions

View File

@ -4,15 +4,15 @@ sudo: true
before_install: before_install:
- sudo apt-get update -qq - sudo apt-get update -qq
- sudo apt-get install --assume-yes asciidoc valgrind - sudo apt-get install --assume-yes valgrind
script: script:
- ./autogen.sh - ./autogen.sh
- ./configure - ./configure
- make - make
- make test - make test
- make clean
- ./configure --enable-debug --enable-transparent --enable-reverse - ./configure --enable-debug --enable-transparent --enable-reverse
- make - make
- make test - make test
- make valgrind-test - make valgrind-test
- make distcheck

View File

@ -1 +1 @@
1.10.0 1.11.0

View File

@ -9,6 +9,11 @@ AC_INIT([Tinyproxy], [tinyproxy_version],
[https://tinyproxy.github.io/], [https://tinyproxy.github.io/],
[tinyproxy]) [tinyproxy])
tpv=tinyproxy_version
if test "x$tpv" = "x" ; then
AC_MSG_ERROR([got empty result from version script!])
fi
AC_CANONICAL_TARGET AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([dist-bzip2 dist-xz]) AM_INIT_AUTOMAKE([dist-bzip2 dist-xz])
AC_CONFIG_HEADERS(config.h) AC_CONFIG_HEADERS(config.h)
@ -96,6 +101,15 @@ if test x"$transparent_enabled" = x"yes"; then
AC_DEFINE(TRANSPARENT_PROXY) AC_DEFINE(TRANSPARENT_PROXY)
fi fi
dnl Let user decide whether he wants support for manpages
dnl Which require either pod2man or a tarball release
AH_TEMPLATE([MANPAGE_SUPPORT],
[Build manpages with pod2man if they are missing from the distribution.])
TP_ARG_ENABLE(manpage_support,
[Enable support for building manpages (default is YES)],
yes)
AM_CONDITIONAL(HAVE_MANPAGE_INTEREST, test x"$manpage_support_enabled" = x"yes")
# This is required to build test programs below # This is required to build test programs below
AC_PROG_CC AC_PROG_CC
@ -131,10 +145,7 @@ AC_CHECK_HEADERS([sys/ioctl.h alloca.h memory.h malloc.h sysexits.h \
dnl Checks for libary functions dnl Checks for libary functions
AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK
AC_FUNC_MALLOC
AC_FUNC_REALLOC
AC_CHECK_FUNCS([inet_ntoa strdup])
AC_CHECK_FUNCS([strlcpy strlcat setgroups]) AC_CHECK_FUNCS([strlcpy strlcat setgroups])
dnl Enable extra warnings dnl Enable extra warnings
@ -144,9 +155,15 @@ if test -n "${MAINTAINER_MODE_FALSE}"; then
DESIRED_FLAGS="-Werror $DESIRED_FLAGS" DESIRED_FLAGS="-Werror $DESIRED_FLAGS"
fi fi
all_desired_work=false
AS_COMPILER_FLAG([$DESIRED_FLAGS], [all_desired_work=true])
if $all_desired_work ; then
CFLAGS="$CFLAGS $DESIRED_FLAGS"
else
for flag in $DESIRED_FLAGS; do for flag in $DESIRED_FLAGS; do
AS_COMPILER_FLAG([$flag], [CFLAGS="$CFLAGS $flag"]) AS_COMPILER_FLAG([$flag], [CFLAGS="$CFLAGS $flag"])
done done
fi
dnl Disable debugging if it's not specified dnl Disable debugging if it's not specified
if test x"$debug_enabled" != x"yes" ; then if test x"$debug_enabled" != x"yes" ; then
@ -173,28 +190,19 @@ AC_SUBST(CPPFLAGS)
AC_SUBST(LIBS) AC_SUBST(LIBS)
AC_SUBST(ADDITIONAL_OBJECTS) AC_SUBST(ADDITIONAL_OBJECTS)
# Check for xml tools if test x"$manpage_support_enabled" = x"yes"; then
AC_PATH_PROG(XSLTPROC, xsltproc, no) AC_PATH_PROG(POD2MAN, pod2man, no)
AM_CONDITIONAL(HAVE_XSLTPROC, test "x$XSLTPROC" != "xno")
# Check for asciidoc if test "x$POD2MAN" = "xno" && \
AC_PATH_PROG(A2X, a2x, no) ! test -e docs/man5/tinyproxy.conf.5 -a -e docs/man8/tinyproxy.8 ; then
AM_CONDITIONAL(HAVE_A2X, test "x$A2X" != "xno") AC_MSG_ERROR([
manpage generation requested, but neither pod2man
nor pre-generated manpages found.
Use --disable-manpage-support if you want to compile anyway.])
fi
fi #manpage_support_enabled
# checking xmllint AM_CONDITIONAL(HAVE_POD2MAN, test "x$POD2MAN" != "x" -a "x$POD2MAN" != "xno")
AC_PATH_PROG(XMLLINT, xmllint, no)
if test "x$XMLLINT" != "xno"; then
AS_ECHO_N("testing xmllint... ")
echo "TEST" > conftest.txt
if $A2X -f docbook conftest.txt 2>/dev/null; then
AS_ECHO("ok")
else
AS_ECHO("failed")
XMLLINT="no"
fi
rm -f conftest.txt conftest.xml
fi
AM_CONDITIONAL(HAVE_XMLLINT, test "x$XMLLINT" != "xno")
AC_CONFIG_FILES([ AC_CONFIG_FILES([
Makefile Makefile
@ -216,12 +224,12 @@ scripts/Makefile
AC_OUTPUT AC_OUTPUT
# the manpages are shipped in the release tarball and we don't want them to # the manpages are shipped in the release tarball and we don't want them to
# get regenerated if a2x is not available. the intermediate files from # get regenerated if pod2man is not available. the intermediate files from
# AC_CONFIG_FILES are created with config.status, which is created at configure # AC_CONFIG_FILES are created with config.status, which is created at configure
# runtime, so we need to touch them after config.status terminated to prevent # runtime, so we need to touch them after config.status terminated to prevent
# make from rebuild them. # make from rebuild them.
if test "x$A2X" = "xno"; then if test "x$POD2MAN" = "xno" ; then
touch docs/man5/tinyproxy.conf.txt touch docs/man5/tinyproxy.conf.txt
touch docs/man8/tinyproxy.txt touch docs/man8/tinyproxy.txt
if test -e docs/man5/tinyproxy.conf.5 ; then if test -e docs/man5/tinyproxy.conf.5 ; then

View File

@ -4,6 +4,5 @@ SUBDIRS = \
EXTRA_DIST = \ EXTRA_DIST = \
http-error-codes.txt \ http-error-codes.txt \
http-rfcs.txt \ http-rfcs.txt
filter-howto.txt

View File

@ -1,52 +0,0 @@
Using tinyproxy with Your Home/Small Business Network
Written: Patrick L. McGillan
Edited: Robert James Kaes (2002-06-04)
-----------------------------------------------------
Being as this will be the most common usage and there were no clear
basic instructions for this scenario, I thought I would write up what
I did for my home system.
First the layout of the network. A cable modem is connected through a
Linksys Router to a small hub. The computers hanging off the hub and
have a clear shot to the Internet.
So, the connection from the Internet to the hub looks like this:
Internet->Cable TV Line->Cable Modem->Linksys Router->Hub/Switch
Restricting Internet web access on some of those computers (connected
to the hub) is what using tinyproxy is all about. Using the web
interface to the Linksys router, turn off all Internet access to those
computers that shouldn't have direct access to the Internet. This is
done by clicking on the advanced tab and entering the IP number in the
filter range. Now those computers have to go through a proxy, for
their access, as they have no direct access.
On one of the Linux computers which still has Internet access (I use
an old 486) load up tinyproxy. Now have the users configure their
Internet Explorer/Netscape Navigator programs to use the proxy on the
tinyproxy computer box, along with the port number declared in the
tinyproxy configuration file. By default, there is no blocking of web
sites with this program, so I created a file, called "filter", to
start blocking some sites.
Example "filter" file entries:
bannerads.zwire.com
ad.doubleclick.net
ads.fortunecity.com
This filter file usually goes into the same folder, as your
configuration file. Be sure and uncomment the 'Filter' line in your
configuration file and make sure it points at your newly created
filter file.
------------------------------------------------------------------------
Copyright (c) 2002 Patrick L. McGillan <pmcgillan@dwx.com>
This document is released under the same copyright license as
tinyproxy. You should have found a COPYING file in the top level
directory of this distribution which contains the current license.

View File

@ -1,25 +1,25 @@
if HAVE_MANPAGE_INTEREST
MAN5_FILES = \ MAN5_FILES = \
tinyproxy.conf.txt tinyproxy.conf.txt
if HAVE_XMLLINT
A2X_ARGS = -d manpage -f manpage
else
A2X_ARGS = -d manpage -f manpage -L
endif endif
M_SECTION=5
M_NAME=TINYPROXY.CONF
man_MANS = \ man_MANS = \
$(MAN5_FILES:.txt=.5) $(MAN5_FILES:.txt=.5)
.txt.5: .txt.5:
if HAVE_A2X if HAVE_POD2MAN
$(AM_V_GEN) $(A2X) $(A2X_ARGS) $< $(AM_V_GEN) $(POD2MAN) --center="Tinyproxy manual" \
--section=$(M_SECTION) --name=$(M_NAME) --release="Version @VERSION@" \
$< > $@
else else
@echo "*** a2x (asciidoc) is required to regenerate $(@) ***"; exit 1; @echo "*** pod2man is required to regenerate $(@) ***"; exit 1;
endif endif
CLEANFILES = \ MAINTAINERCLEANFILES = \
$(MAN5_FILES:.txt=.5) \ $(MAN5_FILES:.txt=.5)
$(MAN5_FILES:.txt=.xml)
EXTRA_DIST = \ EXTRA_DIST = \
$(MAN5_FILES:.txt=.5) $(MAN5_FILES:.txt=.5)

View File

@ -1,24 +1,20 @@
TINYPROXY.CONF(5) =pod
=================
:man source: Version @VERSION@
:man manual: Tinyproxy manual
NAME =encoding utf8
----
=head1 NAME
tinyproxy.conf - Tinyproxy HTTP proxy daemon configuration file tinyproxy.conf - Tinyproxy HTTP proxy daemon configuration file
SYNOPSIS =head1 SYNOPSIS
--------
*tinyproxy.conf* B<tinyproxy.conf>
DESCRIPTION =head1 DESCRIPTION
-----------
`tinyproxy(8)` reads its configuration file, typically stored in L<tinyproxy(8)> reads its configuration file, typically stored in
`/etc/tinyproxy/tinyproxy.conf` (or passed to Tinyproxy with -c on the `/etc/tinyproxy/tinyproxy.conf` (or passed to Tinyproxy with -c on the
command line). This manpage describes the syntax and contents of the command line). This manpage describes the syntax and contents of the
configuration file. configuration file.
@ -31,59 +27,61 @@ contain spaces.
The possible keywords and their descriptions are as follows: The possible keywords and their descriptions are as follows:
*User*:: =over 4
=item B<User>
The user which the Tinyproxy process should run as, after the The user which the Tinyproxy process should run as, after the
initial port-binding has been done as the `root` user. Either the initial port-binding has been done as the `root` user. Either the
user name or the UID may be specified. user name or the UID may be specified.
*Group*:: =item B<Group>
The group which the Tinyproxy process should run as, after the The group which the Tinyproxy process should run as, after the
initial port-binding has been done as the `root` user. Either the initial port-binding has been done as the `root` user. Either the
group name or the GID may be specified. group name or the GID may be specified.
*Port*:: =item B<Port>
The port which the Tinyproxy service will listen on. If the port is The port which the Tinyproxy service will listen on. If the port is
less than 1024, you will need to start the Tinyproxy process as the less than 1024, you will need to start the Tinyproxy process as the
`root` user. `root` user.
*Listen*:: =item B<Listen>
By default, Tinyproxy listens for connections on all available By default, Tinyproxy listens for connections on all available
interfaces (i.e. it listens on the wildcard address `0.0.0.0`). interfaces (i.e. it listens on the wildcard address `0.0.0.0`).
With this configuration parameter, Tinyproxy can be told to listen With this configuration parameter, Tinyproxy can be told to listen
only on one specific address. only on one specific address.
*Bind*:: =item B<Bind>
This allows you to specify which address Tinyproxy will bind This allows you to specify which address Tinyproxy will bind
to for outgoing connections to web servers or upstream proxies. to for outgoing connections to web servers or upstream proxies.
*BindSame*:: =item B<BindSame>
If this boolean parameter is set to `yes`, then Tinyproxy will If this boolean parameter is set to `yes`, then Tinyproxy will
bind the outgoing connection to the IP address of the incoming bind the outgoing connection to the IP address of the incoming
connection that triggered the outgoing request. connection that triggered the outgoing request.
*Timeout*:: =item B<Timeout>
The maximum number of seconds of inactivity a connection is The maximum number of seconds of inactivity a connection is
allowed to have before it is closed by Tinyproxy. allowed to have before it is closed by Tinyproxy.
*ErrorFile*:: =item B<ErrorFile>
This parameter controls which HTML file Tinyproxy returns when a This parameter controls which HTML file Tinyproxy returns when a
given HTTP error occurs. It takes two arguments, the error number given HTTP error occurs. It takes two arguments, the error number
and the location of the HTML error file. and the location of the HTML error file.
*DefaultErrorFile*:: =item B<DefaultErrorFile>
This parameter controls the HTML template file returned when an This parameter controls the HTML template file returned when an
error occurs for which no specific error file has been set. error occurs for which no specific error file has been set.
*StatHost*:: =item B<StatHost>
This configures the host name or IP address that is treated This configures the host name or IP address that is treated
as the `stat host`: Whenever a request for this host is received, as the `stat host`: Whenever a request for this host is received,
@ -92,58 +90,67 @@ The possible keywords and their descriptions are as follows:
page can be configured with the `StatFile` configuration option. page can be configured with the `StatFile` configuration option.
The default value of `StatHost` is `@TINYPROXY_STATHOST@`. The default value of `StatHost` is `@TINYPROXY_STATHOST@`.
*StatFile*:: =item B<StatFile>
This configures the HTML file that Tinyproxy sends when This configures the HTML file that Tinyproxy sends when
a request for the stathost is received. If this parameter is a request for the stathost is received. If this parameter is
not set, Tinyproxy returns a hard-coded basic statistics page. not set, Tinyproxy returns a hard-coded basic statistics page.
See the STATHOST section in the `tinyproxy(8)` manual page See the STATHOST section in the L<tinyproxy(8)> manual page
for details. for details.
+
Note that the StatFile and the error files configured with ErrorFile Note that the StatFile and the error files configured with ErrorFile
and DefaultErrorFile are template files that can contain a few and DefaultErrorFile are template files that can contain a few
template variables that Tinyproxy expands prior to delivery. template variables that Tinyproxy expands prior to delivery.
Examples are "\{cause}" for an abbreviated error description and Examples are "{cause}" for an abbreviated error description and
"\{detail}" for a detailed error message. The `tinyproxy(8)` "{detail}" for a detailed error message. The L<tinyproxy(8)>
manual page contains a description of all template variables. manual page contains a description of all template variables.
*LogFile*:: =item B<LogFile>
This controls the location of the file to which Tinyproxy This controls the location of the file to which Tinyproxy
writes its debug output. Alternatively, Tinyproxy can log writes its debug output. Alternatively, Tinyproxy can log
to syslog -- see the Syslog option. to syslog -- see the Syslog option.
*Syslog*:: =item B<Syslog>
When set to `On`, this option tells Tinyproxy to write its When set to `On`, this option tells Tinyproxy to write its
debug messages to syslog instead of to a log file configured debug messages to syslog instead of to a log file configured
with `LogFile`. These two options are mutually exclusive. with `LogFile`. These two options are mutually exclusive.
*LogLevel*:: =item B<LogLevel>
Sets the log level. Messages from the set level and above are Sets the log level. Messages from the set level and above are
logged. For example, if the LogLevel was set to Warning, then all logged. For example, if the LogLevel was set to Warning, then all
log messages from Warning to Critical would be output, but Notice log messages from Warning to Critical would be output, but Notice
and below would be suppressed. Allowed values are: and below would be suppressed. Allowed values are:
* Critical (least verbose) =over 4
* Error
* Warning
* Notice
* Connect (log connections without Info's noise)
* Info (most verbose)
*PidFile*:: =item * Critical (least verbose)
=item * Error
=item * Warning
=item * Notice
=item * Connect (log connections without Info's noise)
=item * Info (most verbose)
=back
=item B<PidFile>
This option controls the location of the file where the main This option controls the location of the file where the main
Tinyproxy process stores its process ID for signaling purposes. Tinyproxy process stores its process ID for signaling purposes.
*XTinyproxy*:: =item B<XTinyproxy>
Setting this option to `Yes` tells Tinyproxy to add a header Setting this option to `Yes` tells Tinyproxy to add a header
`X-Tinyproxy` containing the client's IP address to the request. `X-Tinyproxy` containing the client's IP address to the request.
*Upstream*:: =item B<Upstream>
This option allows you to set up a set of rules for deciding This option allows you to set up a set of rules for deciding
whether an upstream proxy server is to be used, based on the whether an upstream proxy server is to be used, based on the
@ -152,63 +159,55 @@ The possible keywords and their descriptions are as follows:
LAST matching rule wins. The following forms for specifying upstream LAST matching rule wins. The following forms for specifying upstream
rules exist: rules exist:
* 'upstream type host:port' turns proxy upstream support on generally. =over 4
* 'upstream type user:pass@host:port' does the same, but uses the =item * I<upstream type host:port> turns proxy upstream support on generally.
supplied credentials for authentication.
* 'upstream type host:port "site_spec"' turns on the upstream proxy =item * I<upstream type user:pass@host:port>
for the sites matching `site_spec`. does the same, but uses the supplied credentials for authentication.
=item * I<upstream type host:port "site_spec">
turns on the upstream proxy for the sites matching `site_spec`.
`type` can be one of `http`, `socks4`, `socks5`, `none`. `type` can be one of `http`, `socks4`, `socks5`, `none`.
* 'upstream none "site_spec"' turns off upstream support for sites =item * I<upstream none "site_spec">
matching `site_spec`. turns off upstream support for sites matching `site_spec`, that means the
connection is done directly.
=back
The site can be specified in various forms as a hostname, domain The site can be specified in various forms as a hostname, domain
name or as an IP range: name or as an IP range:
* 'name' matches host exactly =over 4
* '.name' matches any host in domain "name"
* '.' matches any host with no domain (in 'empty' domain)
* 'IP/bits' matches network/mask
* 'IP/mask' matches network/mask
*MaxClients*:: =item * I<name> matches host exactly
Tinyproxy creates one child process for each connected client. =item * I<.name> matches any host in domain "name"
=item * I<.> matches any host with no domain (in 'empty' domain)
=item * I<IP/bits> matches network/mask
=item * I<IP/mask> matches network/mask
=back
Note that the upstream directive can also be used to null-route
a specific target domain/host, e.g.:
`upstream http 0.0.0.0:0 ".adserver.com"`
=item B<MaxClients>
Tinyproxy creates one thread for each connected client.
This options specifies the absolute highest number processes that This options specifies the absolute highest number processes that
will be created. With other words, only MaxClients clients can be will be created. With other words, only MaxClients clients can be
connected to Tinyproxy simultaneously. connected to Tinyproxy simultaneously.
*MinSpareServers*:: =item B<Allow>
*MaxSpareServers*::
Tinyproxy always keeps a certain number of idle child processes =item B<Deny>
so that it can handle new incoming client requests quickly.
`MinSpareServer` and `MaxSpareServers` control the lower and upper
limits for the number of spare processes. I.e. when the number of
spare servers drops below `MinSpareServers` then Tinyproxy will
start forking new spare processes in the background and when the
number of spare processes exceeds `MaxSpareServers` then Tinyproxy
will kill off extra processes.
*StartServers*::
The number of servers to start initially. This should usually be
set to a value between MinSpareServers and MaxSpareServers.
*MaxRequestsPerChild*::
This limits the number of connections that a child process
will handle before it is killed. The default value is `0`
which disables this feature. This option is meant as an
emergency measure in the case of problems with memory leakage.
In that case, setting `MaxRequestsPerChild` to a value of e.g.
1000, or 10000 can be useful.
*Allow*::
*Deny*::
The `Allow` and `Deny` options provide a means to customize The `Allow` and `Deny` options provide a means to customize
which clients are allowed to access Tinyproxy. `Allow` and `Deny` which clients are allowed to access Tinyproxy. `Allow` and `Deny`
@ -222,19 +221,28 @@ The possible keywords and their descriptions are as follows:
end of the client host name, i.e, this can be a full host name end of the client host name, i.e, this can be a full host name
like `host.example.com` or a domain name like `.example.com` or like `host.example.com` or a domain name like `.example.com` or
even a top level domain name like `.com`. even a top level domain name like `.com`.
Note that by adding a rule using a host or domain name, a costly name
lookup has to be done for every new connection, which could slow down
the service considerably.
*AddHeader*:: =item B<BasicAuth>
Configure HTTP "Basic Authentication" username and password
for accessing the proxy. If there are any entries specified,
access is only granted for authenticated users.
BasicAuth user password
=item B<AddHeader>
Configure one or more HTTP request headers to be added to outgoing Configure one or more HTTP request headers to be added to outgoing
HTTP requests that Tinyproxy makes. Note that this option will not HTTP requests that Tinyproxy makes. Note that this option will not
work for HTTPS traffic, as Tinyproxy has no control over what work for HTTPS traffic, as Tinyproxy has no control over what
headers are exchanged. headers are exchanged.
+
----
AddHeader "X-My-Header" "Powered by Tinyproxy"
----
*ViaProxyName*:: AddHeader "X-My-Header" "Powered by Tinyproxy"
=item B<ViaProxyName>
RFC 2616 requires proxies to add a `Via` header to the HTTP RFC 2616 requires proxies to add a `Via` header to the HTTP
requests, but using the real host name can be a security requests, but using the real host name can be a security
@ -242,7 +250,7 @@ AddHeader "X-My-Header" "Powered by Tinyproxy"
string value will be used as the host name in the Via header. string value will be used as the host name in the Via header.
Otherwise, the server's host name will be used. Otherwise, the server's host name will be used.
*DisableViaHeader*:: =item B<DisableViaHeader>
When this is set to yes, Tinyproxy does NOT add the `Via` header When this is set to yes, Tinyproxy does NOT add the `Via` header
to the requests. This virtually puts Tinyproxy into stealth mode. to the requests. This virtually puts Tinyproxy into stealth mode.
@ -250,90 +258,107 @@ AddHeader "X-My-Header" "Powered by Tinyproxy"
enabling this option, you break compliance. enabling this option, you break compliance.
Don't disable the `Via` header unless you know what you are doing... Don't disable the `Via` header unless you know what you are doing...
*Filter*:: =item B<Filter>
Tinyproxy supports filtering of web sites based on URLs or Tinyproxy supports filtering of web sites based on URLs or
domains. This option specifies the location of the file domains. This option specifies the location of the file
containing the filter rules, one rule per line. containing the filter rules, one rule per line.
*FilterURLs*:: Rules are specified as POSIX basic regular expressions (BRE), unless
FilterExtended is activated.
Comment lines start with a `#` character.
Example filter file contents:
# filter exactly cnn.com
^cnn\.com$
# filter all subdomains of cnn.com, but not cnn.com itself
.*\.cnn.com$
# filter any domain that has cnn.com in it, like xcnn.comfy.org
cnn\.com
# filter any domain that ends in cnn.com
cnn\.com$
# filter any domain that starts with adserver
^adserver
=item B<FilterURLs>
If this boolean option is set to `Yes` or `On`, filtering is If this boolean option is set to `Yes` or `On`, filtering is
performed for URLs rather than for domains. The default is to performed for URLs rather than for domains. The default is to
filter based on domains. filter based on domains.
*FilterExtended*:: =item B<FilterExtended>
If this boolean option is set to `Yes`, then extended POSIX If this boolean option is set to `Yes`, then extended POSIX
regular expressions are used for matching the filter rules. regular expressions are used for matching the filter rules.
The default is to use basic POSIX regular expressions. The default is to use basic POSIX regular expressions.
*FilterCaseSensitive*:: =item B<FilterCaseSensitive>
If this boolean option is set to `Yes`, then the filter rules If this boolean option is set to `Yes`, then the filter rules
are matched in a case sensitive manner. The default is to are matched in a case sensitive manner. The default is to
match case-insensitively. match case-insensitively.
*FilterDefaultDeny*:: =item B<FilterDefaultDeny>
The default filtering policy is to allow everything that is The default filtering policy is to allow everything that is
not matched by a filtering rule. Setting `FilterDefaultDeny` not matched by a filtering rule. Setting `FilterDefaultDeny`
to `Yes` changes the policy do deny everything but the domains to `Yes` changes the policy do deny everything but the domains
or URLs matched by the filtering rules. or URLs matched by the filtering rules.
*Anonymous*:: =item B<Anonymous>
If an `Anonymous` keyword is present, then anonymous proxying If an `Anonymous` keyword is present, then anonymous proxying
is enabled. The headers listed with `Anonymous` are allowed is enabled. The headers listed with `Anonymous` are allowed
through, while all others are denied. If no Anonymous keyword through, while all others are denied. If no Anonymous keyword
is present, then all headers are allowed through. You must is present, then all headers are allowed through. You must
include quotes around the headers. include quotes around the headers.
+
Most sites require cookies to be enabled for them to work correctly, so Most sites require cookies to be enabled for them to work correctly, so
you will need to allow cookies through if you access those sites. you will need to allow cookies through if you access those sites.
+
Example: Example:
+
----
Anonymous "Host" Anonymous "Host"
Anonymous "Authorization" Anonymous "Authorization"
Anonymous "Cookie" Anonymous "Cookie"
----
*ConnectPort*:: =item B<ConnectPort>
This option can be used to specify the ports allowed for the This option can be used to specify the ports allowed for the
CONNECT method. If no `ConnectPort` line is found, then all CONNECT method. If no `ConnectPort` line is found, then all
ports are allowed. To disable CONNECT altogether, include a ports are allowed. To disable CONNECT altogether, include a
single ConnectPort line with a value of `0`. single ConnectPort line with a value of `0`.
*ReversePath*:: =item B<ReversePath>
Configure one or more ReversePath directives to enable reverse proxy Configure one or more ReversePath directives to enable reverse proxy
support. With reverse proxying it's possible to make a number of support. With reverse proxying it's possible to make a number of
sites appear as if they were part of a single site. sites appear as if they were part of a single site.
+
If you uncomment the following two directives and run Tinyproxy If you uncomment the following two directives and run Tinyproxy
on your own computer at port 8888, you can access example.com, on your own computer at port 8888, you can access example.com,
using http://localhost:8888/example/. using http://localhost:8888/example/.
+
----
ReversePath "/example/" "http://www.example.com/"
----
*ReverseOnly*:: ReversePath "/example/" "http://www.example.com/"
=item B<ReverseOnly>
When using Tinyproxy as a reverse proxy, it is STRONGLY When using Tinyproxy as a reverse proxy, it is STRONGLY
recommended that the normal proxy is turned off by setting recommended that the normal proxy is turned off by setting
this boolean option to `Yes`. this boolean option to `Yes`.
*ReverseMagic*:: =item B<ReverseMagic>
Setting this option to `Yes`, makes Tinyproxy use a cookie to Setting this option to `Yes`, makes Tinyproxy use a cookie to
track reverse proxy mappings. If you need to reverse proxy track reverse proxy mappings. If you need to reverse proxy
sites which have absolute links you must use this option. sites which have absolute links you must use this option.
*ReverseBaseURL*:: =item B<ReverseBaseURL>
The URL that is used to access this reverse proxy. The URL is The URL that is used to access this reverse proxy. The URL is
used to rewrite HTTP redirects so that they won't escape the used to rewrite HTTP redirects so that they won't escape the
@ -342,30 +367,29 @@ ReversePath "/example/" "http://www.example.com/"
types into his/her browser). If this option is not set then types into his/her browser). If this option is not set then
no rewriting of redirects occurs. no rewriting of redirects occurs.
=back
BUGS =head1 BUGS
----
To report bugs in Tinyproxy, please visit To report bugs in Tinyproxy, please visit
<https://tinyproxy.github.io/[https://tinyproxy.github.io/]>. L<https://tinyproxy.github.io/>.
SEE ALSO =head1 SEE ALSO
--------
tinyproxy(8) L<tinyproxy(8)>
AUTHOR =head1 AUTHOR
------
This manpage was written by the Tinyproxy project team. This manpage was written by the Tinyproxy project team.
COPYRIGHT =head1 COPYRIGHT
---------
Copyright (c) 1998-2018 the Tinyproxy authors. Copyright (c) 1998-2020 the Tinyproxy authors.
This program is distributed under the terms of the GNU General Public This program is distributed under the terms of the GNU General Public
License version 2 or above. See the COPYING file for additional License version 2 or above. See the COPYING file for additional
information. information.

View File

@ -1,25 +1,25 @@
if HAVE_MANPAGE_INTEREST
MAN8_FILES = \ MAN8_FILES = \
tinyproxy.txt tinyproxy.txt
if HAVE_XMLLINT
A2X_ARGS = -d manpage -f manpage
else
A2X_ARGS = -d manpage -f manpage -L
endif endif
M_SECTION=8
M_NAME=TINYPROXY
man_MANS = \ man_MANS = \
$(MAN8_FILES:.txt=.8) $(MAN8_FILES:.txt=.8)
.txt.8: .txt.8:
if HAVE_A2X if HAVE_POD2MAN
$(AM_V_GEN) $(A2X) $(A2X_ARGS) $< $(AM_V_GEN) $(POD2MAN) --center="Tinyproxy manual" \
--section=$(M_SECTION) --name=$(M_NAME) --release="Version @VERSION@" \
$< > $@
else else
@echo "*** a2x (asciidoc) is required to regenerate $(@) ***"; exit 1; @echo "*** pod2man is required to regenerate $(@) ***"; exit 1;
endif endif
CLEANFILES = \ MAINTAINERCLEANFILES = \
$(MAN8_FILES:.txt=.8) \ $(MAN8_FILES:.txt=.8)
$(MAN8_FILES:.txt=.xml)
EXTRA_DIST = \ EXTRA_DIST = \
$(MAN8_FILES:.txt=.8) $(MAN8_FILES:.txt=.8)

View File

@ -1,24 +1,20 @@
TINYPROXY(8) =pod
============
:man source: Version @VERSION@
:man manual: Tinyproxy manual
NAME =encoding utf8
----
=head1 NAME
tinyproxy - A light-weight HTTP proxy daemon tinyproxy - A light-weight HTTP proxy daemon
SYNOPSIS =head1 SYNOPSIS
--------
*tinyproxy* [-vdch] B<tinyproxy> [-vdch]
DESCRIPTION =head1 DESCRIPTION
-----------
*tinyproxy* is a light-weight HTTP proxy daemon designed to consume a B<tinyproxy> is a light-weight HTTP proxy daemon designed to consume a
minimum amount of system resources. It listens on a given TCP port and minimum amount of system resources. It listens on a given TCP port and
handles HTTP proxy requests. Designed from the ground up to be fast and handles HTTP proxy requests. Designed from the ground up to be fast and
yet small, it is an ideal solution for use cases such as embedded yet small, it is an ideal solution for use cases such as embedded
@ -26,93 +22,127 @@ deployments where a full featured HTTP proxy is required, but the system
resources for a larger proxy are unavailable. resources for a larger proxy are unavailable.
OPTIONS =head1 OPTIONS
-------
*tinyproxy* accepts the following options: B<tinyproxy> accepts the following options:
=over 4
=item B<-c <config-file>>
*-c <config-file>*::
Use an alternate configuration file. Use an alternate configuration file.
*-d*:: =item B<-d>
Don't daemonize and stay in the foreground. Useful for debugging purposes. Don't daemonize and stay in the foreground. Useful for debugging purposes.
*-h*:: =item B<-h>
Display a short help screen of command line arguments and exit. Display a short help screen of command line arguments and exit.
*-v*:: =item B<-v>
Display version information and exit. Display version information and exit.
=back
SIGNALS =head1 SIGNALS
-------
In addition to command-line options, there are also several signals that In addition to command-line options, there are also several signals that
can be sent to *tinyproxy* while it is running to generate debugging can be sent to B<tinyproxy> while it is running to generate debugging
information and to force certain events. information and to force certain events.
*SIGHUP*:: =over 4
=item B<SIGHUP>
Force Tinyproxy to do a garbage collection on the current Force Tinyproxy to do a garbage collection on the current
connections linked list. This is usually done automatically after a connections linked list. This is usually done automatically after a
certain number of connections have been handled. certain number of connections have been handled.
(Daemon mode only)
=item B<SIGUSR1>
TEMPLATE FILES Force reload of config file and filter list.
-------------- This is handy to update the configuration if Tinyproxy is running
in foreground without dropping active connections.
=back
=head1 TEMPLATE FILES
There are two occasions when Tinyproxy delivers HTML pages to There are two occasions when Tinyproxy delivers HTML pages to
the client on it's own right: the client on it's own right:
. When an error occurred, a corresponding error page is returned. =over 4
. When a request for the stathost is made, a page summarizing the
=item * When an error occurred, a corresponding error page is returned.
=item * When a request for the stathost is made, a page summarizing the
connection statistics is returned. (See STATHOST below.) connection statistics is returned. (See STATHOST below.)
=back
The layout of both error pages and the statistics page can be The layout of both error pages and the statistics page can be
controlled via configurable HTML template files that are plain controlled via configurable HTML template files that are plain
HTML files that additionally understand a few template HTML files that additionally understand a few template
variables. variables.
TEMPLATE VARIABLES =head1 TEMPLATE VARIABLES
------------------
There are several standard HTML variables that are available in every There are several standard HTML variables that are available in every
template file: template file:
*request*:: =over 4
=item B<request>
The full HTTP request line. The full HTTP request line.
*cause*:: =item B<cause>
The abbreviated cause of the error condition. The abbreviated cause of the error condition.
*clientip*:: =item B<clientip>
The IP address of the client making the request. The IP address of the client making the request.
*clienthost*:: =item B<clienthost>
The hostname of the client making the request. The hostname of the client making the request.
*version*:: =item B<version>
The version of Tinyproxy. The version of Tinyproxy.
*package*:: =item B<package>
The package name. Presently, resolves to 'tinyproxy'. The package name. Presently, resolves to 'tinyproxy'.
*date*:: =item B<date>
The current date/time in HTTP format. The current date/time in HTTP format.
=back
In addition, almost all templates support: In addition, almost all templates support:
*detail*:: =over 4
=item B<detail>
A detailed, plain English explanation of the error and possible A detailed, plain English explanation of the error and possible
causes. causes.
=back
When Tinyproxy finds a variable name enclosed in braces, e.g. When Tinyproxy finds a variable name enclosed in braces, e.g.
"\{request}", then this is replaced by the value of the corresponding "{request}", then this is replaced by the value of the corresponding
variable before delivery of the page. variable before delivery of the page.
STATHOST =head1 STATHOST
--------
Tinyproxy returns a HTML page with connection statistics when it Tinyproxy returns a HTML page with connection statistics when it
receives a HTTP request for a certain host -- the stathost. The receives a HTTP request for a certain host -- the stathost. The
@ -124,33 +154,29 @@ The stat file template can be changed at runtime through the
configuration variable `StatFile`. configuration variable `StatFile`.
FILES =head1 FILES
-----
`/etc/tinyproxy/tinyproxy.conf`, `/var/run/tinyproxy/tinyproxy.pid`, `/var/log/tinyproxy/tinyproxy.log` `/etc/tinyproxy/tinyproxy.conf`, `/var/run/tinyproxy/tinyproxy.pid`, `/var/log/tinyproxy/tinyproxy.log`
BUGS =head1 BUGS
----
To report bugs in Tinyproxy, please visit To report bugs in Tinyproxy, please visit
<https://tinyproxy.github.io/[https://tinyproxy.github.io/]>. L<https://tinyproxy.github.io/>.
SEE ALSO =head1 SEE ALSO
--------
tinyproxy.conf(5) L<tinyproxy.conf(5)>
AUTHOR =head1 AUTHOR
------
This manpage was written by the Tinyproxy project team. This manpage was written by the Tinyproxy project team.
COPYRIGHT =head1 COPYRIGHT
---------
Copyright (c) 1998-2018 the Tinyproxy authors. Copyright (c) 1998-2020 the Tinyproxy authors.
This program is distributed under the terms of the GNU General Public This program is distributed under the terms of the GNU General Public
License version 2 or above. See the COPYING file for additional License version 2 or above. See the COPYING file for additional

15
docs/web/Makefile Normal file
View File

@ -0,0 +1,15 @@
# test webpage with `python -m SimpleHTTPServer`
all: index.html
tp.html.conf: ../man5/tinyproxy.conf.txt
pod2html --noindex < $^ | awk -f podhtml-filter.awk > $@
index.html: tp.html.head tp.html.conf tp.html.foot
cat $^ > $@
clean:
rm tp.html.conf index.html *.tmp
.PHONY: all clean

View File

@ -0,0 +1,5 @@
BEGIN {i=0}
/<\/{0,1}h1/ {if(!i)i=1; gsub("h1", "h4", $0);}
#/<\/body>/ {i=0;}
/BUGS/ {i=-1}
{if(i==1) print;}

View File

@ -0,0 +1,425 @@
/*******************************************************************************
Slate Theme for GitHub Pages
by Jason Costello, @jsncostello
*******************************************************************************/
@import url(github-light.css);
/*******************************************************************************
MeyerWeb Reset
*******************************************************************************/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
ol, ul {
list-style: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
/*******************************************************************************
Theme Styles
*******************************************************************************/
body {
box-sizing: border-box;
color:#373737;
background: #212121;
font-size: 16px;
font-family: 'Myriad Pro', Calibri, Helvetica, Arial, sans-serif;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4, h5, h6 {
margin: 10px 0;
font-weight: 700;
color:#222222;
font-family: 'Lucida Grande', 'Calibri', Helvetica, Arial, sans-serif;
letter-spacing: -1px;
}
h1 {
font-size: 36px;
font-weight: 700;
}
h2 {
padding-bottom: 10px;
font-size: 32px;
background: url('../images/bg_hr.png') repeat-x bottom;
}
h3 {
font-size: 24px;
}
h4 {
font-size: 21px;
}
h5 {
font-size: 18px;
}
h6 {
font-size: 16px;
}
p {
margin: 10px 0 15px 0;
}
footer p {
color: #f2f2f2;
}
a {
text-decoration: none;
color: #007edf;
text-shadow: none;
transition: color 0.5s ease;
transition: text-shadow 0.5s ease;
-webkit-transition: color 0.5s ease;
-webkit-transition: text-shadow 0.5s ease;
-moz-transition: color 0.5s ease;
-moz-transition: text-shadow 0.5s ease;
-o-transition: color 0.5s ease;
-o-transition: text-shadow 0.5s ease;
-ms-transition: color 0.5s ease;
-ms-transition: text-shadow 0.5s ease;
}
a:hover, a:focus {text-decoration: underline;}
footer a {
color: #F2F2F2;
text-decoration: underline;
}
em {
font-style: italic;
}
strong {
font-weight: bold;
}
img {
position: relative;
margin: 0 auto;
max-width: 739px;
padding: 5px;
margin: 10px 0 10px 0;
border: 1px solid #ebebeb;
box-shadow: 0 0 5px #ebebeb;
-webkit-box-shadow: 0 0 5px #ebebeb;
-moz-box-shadow: 0 0 5px #ebebeb;
-o-box-shadow: 0 0 5px #ebebeb;
-ms-box-shadow: 0 0 5px #ebebeb;
}
p img {
display: inline;
margin: 0;
padding: 0;
vertical-align: middle;
text-align: center;
border: none;
}
pre, code {
width: 100%;
color: #222;
background-color: #fff;
font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace;
font-size: 14px;
border-radius: 2px;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
}
pre {
width: 100%;
padding: 10px;
box-shadow: 0 0 10px rgba(0,0,0,.1);
overflow: auto;
}
code {
padding: 3px;
margin: 0 3px;
box-shadow: 0 0 10px rgba(0,0,0,.1);
}
pre code {
display: block;
box-shadow: none;
}
blockquote {
color: #666;
margin-bottom: 20px;
padding: 0 0 0 20px;
border-left: 3px solid #bbb;
}
ul, ol, dl {
margin-bottom: 15px
}
ul {
list-style-position: inside;
list-style: disc;
padding-left: 20px;
}
ol {
list-style-position: inside;
list-style: decimal;
padding-left: 20px;
}
dl dt {
font-weight: bold;
}
dl dd {
padding-left: 20px;
/* font-style: italic; */
}
dl p {
padding-left: 20px;
/* font-style: italic; */
}
hr {
height: 1px;
margin-bottom: 5px;
border: none;
background: url('../images/bg_hr.png') repeat-x center;
}
table {
border: 1px solid #373737;
margin-bottom: 20px;
text-align: left;
}
th {
font-family: 'Lucida Grande', 'Helvetica Neue', Helvetica, Arial, sans-serif;
padding: 10px;
background: #373737;
color: #fff;
}
td {
padding: 10px;
border: 1px solid #373737;
}
form {
background: #f2f2f2;
padding: 20px;
}
/*******************************************************************************
Full-Width Styles
*******************************************************************************/
.outer {
width: 100%;
}
.inner {
position: relative;
max-width: 640px;
padding: 20px 10px;
margin: 0 auto;
}
#forkme_banner {
display: block;
position: absolute;
top:0;
right: 10px;
z-index: 10;
padding: 10px 50px 10px 10px;
color: #fff;
background: url('../images/blacktocat.png') #0090ff no-repeat 95% 50%;
font-weight: 700;
box-shadow: 0 0 10px rgba(0,0,0,.5);
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
#header_wrap {
background: #212121;
background: -moz-linear-gradient(top, #373737, #212121);
background: -webkit-linear-gradient(top, #373737, #212121);
background: -ms-linear-gradient(top, #373737, #212121);
background: -o-linear-gradient(top, #373737, #212121);
background: linear-gradient(top, #373737, #212121);
}
#header_wrap .inner {
padding: 50px 10px 30px 10px;
}
#project_title {
margin: 0;
color: #fff;
font-size: 42px;
font-weight: 700;
text-shadow: #111 0px 0px 10px;
}
#project_tagline {
color: #fff;
font-size: 24px;
font-weight: 300;
background: none;
text-shadow: #111 0px 0px 10px;
}
#downloads {
position: absolute;
width: 210px;
z-index: 10;
bottom: -40px;
right: 0;
height: 70px;
background: url('../images/icon_download.png') no-repeat 0% 90%;
}
.zip_download_link {
display: block;
float: right;
width: 90px;
height:70px;
text-indent: -5000px;
overflow: hidden;
background: url(../images/sprite_download.png) no-repeat bottom left;
}
.tar_download_link {
display: block;
float: right;
width: 90px;
height:70px;
text-indent: -5000px;
overflow: hidden;
background: url(../images/sprite_download.png) no-repeat bottom right;
margin-left: 10px;
}
.zip_download_link:hover {
background: url(../images/sprite_download.png) no-repeat top left;
}
.tar_download_link:hover {
background: url(../images/sprite_download.png) no-repeat top right;
}
#main_content_wrap {
background: #f2f2f2;
border-top: 1px solid #111;
border-bottom: 1px solid #111;
}
#main_content {
padding-top: 40px;
}
#footer_wrap {
background: #212121;
}
/*******************************************************************************
Small Device Styles
*******************************************************************************/
@media screen and (max-width: 480px) {
body {
font-size:14px;
}
#downloads {
display: none;
}
.inner {
min-width: 320px;
max-width: 480px;
}
#project_title {
font-size: 32px;
}
h1 {
font-size: 28px;
}
h2 {
font-size: 24px;
}
h3 {
font-size: 21px;
}
h4 {
font-size: 18px;
}
h5 {
font-size: 14px;
}
h6 {
font-size: 12px;
}
code, pre {
min-width: 320px;
max-width: 480px;
font-size: 11px;
}
}

21
docs/web/tp.html.foot Normal file
View File

@ -0,0 +1,21 @@
<h2>
<a id="support" class="anchor" href="#support" aria-hidden="true"><span class="octicon octicon-link"></span></a>Support</h2>
<ul>
<li>Feel free to report a new bug or suggest features via github issues.</li>
<li>Tinyproxy developers hang out in #tinyproxy on irc.freenode.net.</li>
</ul>
</section>
</div>
<!-- FOOTER -->
<div id="footer_wrap" class="outer">
<footer class="inner">
<p>Published with <a href="https://pages.github.com">GitHub Pages</a></p>
</footer>
</div>
</body>
</html>

82
docs/web/tp.html.head Normal file
View File

@ -0,0 +1,82 @@
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<meta name="description" content="Tinyproxy : lightweight http(s) proxy daemon">
<link rel="stylesheet" type="text/css" media="screen" href="stylesheets/stylesheet.css">
<title>Tinyproxy</title>
</head>
<body>
<!-- HEADER -->
<div id="header_wrap" class="outer">
<header class="inner">
<a id="forkme_banner" href="https://github.com/tinyproxy">View on GitHub</a>
<h1 id="project_title">Tinyproxy</h1>
<h2 id="project_tagline">lightweight http(s) proxy daemon</h2>
</header>
</div>
<!-- MAIN CONTENT -->
<div id="main_content_wrap" class="outer">
<section id="main_content" class="inner">
<h1>
<a id="tinyproxy" class="anchor" href="#tinyproxy" aria-hidden="true"><span class="octicon octicon-link"></span></a>Tinyproxy</h1>
<p>Tinyproxy is a light-weight HTTP/HTTPS proxy daemon for POSIX operating systems. Designed from the ground up to be fast and yet small, it is an ideal solution for use cases such as embedded deployments where a full featured HTTP proxy is required, but the system resources for a larger proxy are unavailable.</p>
<p>Tinyproxy is distributed using the GNU GPL license (version 2 or above).</p>
<h2>
<a id="features" class="anchor" href="#features" aria-hidden="true"><span class="octicon octicon-link"></span></a>Features</h2>
<p>Tinyproxy has a <strong>small footprint</strong> and requires very little in the way of system resources. The memory footprint tends to be around 2 MB with glibc, and the CPU load increases linearly with the number of simultaneous connections (depending on the speed of the connection). Thus, Tinyproxy can be run on an older machine, or on a network appliance such as a Linux-based broadband router, without any noticeable impact on performance.</p>
<p>Tinyproxy requires only a <strong>minimal POSIX environment</strong> to build and operate. It can use additional libraries to add functionality though.</p>
<p>Tinyproxy allows forwarding of <strong>HTTPS connections</strong> without modifying traffic in any way through the <code>CONNECT</code> method (see the <code>ConnectPort</code> directive).</p>
<p>Tinyproxy supports being configured as a <strong>transparent proxy</strong>, so that a proxy can be used without requiring any client-side configuration. You can also use it as a <strong>reverse proxy</strong> front-end to your websites.</p>
<p>Using the <code>AddHeader</code> directive, you can <strong>add/insert HTTP headers</strong> to outgoing traffic.</p>
<p>If you're looking to build a custom web proxy, Tinyproxy is <strong>easy to modify</strong> to your custom needs. The source is straightforward, adhering to the KISS principle. As such, it can be used as a foundation for anything you may need a web proxy to do.</p>
<p>Tinyproxy has <strong>privacy features</strong> which can let you configure which HTTP headers should be allowed through, and which should be blocked. This allows you to restrict both what data comes to your web browser from the HTTP server (e.g., cookies), and to restrict what data is allowed through from your web browser to the HTTP server (e.g., version information).</p>
<p>Using the <strong>remote monitoring</strong> facility, you can access proxy statistics from afar, letting you know exactly how busy the proxy is.</p>
<p>You can configure Tinyproxy to <strong>control access</strong> by only allowing requests from a certain subnet, or from a certain interface, thus ensuring that random, unauthorized people will not be using your proxy.</p>
<p>With a bit of configuration (specifically, making Tinyproxy created files owned by a non-root user and running it on a port greater than 1024), Tinyproxy can be made to run without any special privileges, thus minimizing the chance of system compromise. Furthermore, it was designed with an eye towards preventing buffer overflows. The simplicity of the code ensures it remains easy to spot such bugs.</p>
<h2>
<a id="downloads" class="anchor" href="#downloads" aria-hidden="true"><span class="octicon octicon-link"></span></a>Downloads</h2>
<ul>
<li>On Red Hat Enterprise Linux, or its derivatives such as CentOS, install Tinyproxy from the EPEL repository by running yum install tinyproxy.</li>
<li>On Fedora, install Tinyproxy by running yum install tinyproxy.</li>
<li>On Debian and derived distributions, run apt-get install tinyproxy to install Tinyproxy.</li>
<li>For openSUSE run: zypper in tinyproxy</li>
<li>Arch users can install the Tinyproxy package from the community repository. Run pacman -S tinyproxy to install it.</li>
<li>FreeBSD, OpenBSD or NetBSD users can use the pkg_add utility to install the tinyproxy package.</li>
<li>Mac OS X users can check MacPorts to see if the Tinyproxy port there is recent enough.</li>
</ul>
<p>If you feel that the Tinyproxy binary package in your operating system is not recent, please contact the package maintainer for that particular operating system. If this fails, you can always compile the latest stable version from source code.</p>
<p>We distribute Tinyproxy in source code form, and it has to be compiled in order to be used on your system. Please see the INSTALL file in the source code tree for build instructions. The current stable version of Tinyproxy is available on the <a href="https://github.com/tinyproxy/tinyproxy/releases">releases page</a>. The Tinyproxy NEWS file contains the release notes. You can verify the tarball using its PGP signature. You can also browse the older releases of Tinyproxy.</p>
<p>We use Git as the version control system for the Tinyproxy source code repository. To get a copy of the Tinyproxy repository, use the command:</p>
<p>git clone <a href="https://github.com/tinyproxy/tinyproxy.git">https://github.com/tinyproxy/tinyproxy.git</a></p>
<h2>
<a id="documentation" class="anchor" href="#documentation" aria-hidden="true"><span class="octicon octicon-link"></span></a>Documentation</h2>

View File

@ -189,30 +189,6 @@ LogLevel Info
# #
MaxClients 100 MaxClients 100
#
# MinSpareServers/MaxSpareServers: These settings set the upper and
# lower limit for the number of spare servers which should be available.
#
# If the number of spare servers falls below MinSpareServers then new
# server processes will be spawned. If the number of servers exceeds
# MaxSpareServers then the extras will be killed off.
#
MinSpareServers 5
MaxSpareServers 20
#
# StartServers: The number of servers to start initially.
#
StartServers 10
#
# MaxRequestsPerChild: The number of connections a thread will handle
# before it is killed. In practise this should be set to 0, which
# disables thread reaping. If you do notice problems with memory
# leakage, then set this to something like 10000.
#
MaxRequestsPerChild 0
# #
# Allow: Customization of authorization controls. If there are any # Allow: Customization of authorization controls. If there are any
# access control keywords then the default action is to DENY. Otherwise, # access control keywords then the default action is to DENY. Otherwise,
@ -222,6 +198,7 @@ MaxRequestsPerChild 0
# tested against the controls based on order. # tested against the controls based on order.
# #
Allow 127.0.0.1 Allow 127.0.0.1
Allow ::1
# BasicAuth: HTTP "Basic Authentication" for accessing the proxy. # BasicAuth: HTTP "Basic Authentication" for accessing the proxy.
# If there are any entries specified, access is only granted for authenticated # If there are any entries specified, access is only granted for authenticated

View File

@ -5,8 +5,12 @@ GIT_DIR="${SCRIPT_DIR}/../.git"
if test -d "${GIT_DIR}" ; then if test -d "${GIT_DIR}" ; then
if type git >/dev/null 2>&1 ; then if type git >/dev/null 2>&1 ; then
git describe --match '[0-9]*.[0-9]*.[0-9]*' 2>/dev/null \ gitstr=$(git describe --match '[0-9]*.[0-9]*.[0-9]*' 2>/dev/null)
| sed -e 's/-/-git-/' if test "x$?" != x0 ; then
sed 's/$/-git/' < VERSION
else
printf "%s\n" "$gitstr" | sed -e 's/-/-git-/'
fi
else else
sed 's/$/-git/' < VERSION sed 's/$/-git/' < VERSION
fi fi

View File

@ -48,10 +48,12 @@ tinyproxy_SOURCES = \
upstream.c upstream.h \ upstream.c upstream.h \
basicauth.c basicauth.h \ basicauth.c basicauth.h \
base64.c base64.h \ base64.c base64.h \
sblist.c sblist.h \
loop.c loop.h \
connect-ports.c connect-ports.h connect-ports.c connect-ports.h
EXTRA_tinyproxy_SOURCES = filter.c filter.h \ EXTRA_tinyproxy_SOURCES = filter.c filter.h \
reverse-proxy.c reverse-proxy.h \ reverse-proxy.c reverse-proxy.h \
transparent-proxy.c transparent-proxy.h transparent-proxy.c transparent-proxy.h
tinyproxy_DEPENDENCIES = @ADDITIONAL_OBJECTS@ tinyproxy_DEPENDENCIES = @ADDITIONAL_OBJECTS@
tinyproxy_LDADD = @ADDITIONAL_OBJECTS@ tinyproxy_LDADD = @ADDITIONAL_OBJECTS@ -lpthread

View File

@ -221,8 +221,8 @@ insert_acl (char *location, acl_access_t access_type, vector_t *access_list)
* -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 *ip_address, const char *string_address) union sockaddr_union *addr, char *string_addr)
{ {
int match; int match;
struct addrinfo hints, *res, *ressave; struct addrinfo hints, *res, *ressave;
@ -231,7 +231,6 @@ acl_string_processing (struct acl_s *acl,
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);
/* /*
* 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
@ -267,7 +266,15 @@ acl_string_processing (struct acl_s *acl,
} }
STRING_TEST: STRING_TEST:
test_length = strlen (string_address); if(string_addr[0] == 0) {
/* only do costly hostname resolution when it is absolutely needed,
and only once */
if(getnameinfo ((void *) addr, sizeof (*addr),
string_addr, HOSTNAME_LENGTH, NULL, 0, 0) != 0)
return -1;
}
test_length = strlen (string_addr);
match_length = strlen (acl->address.string); match_length = strlen (acl->address.string);
/* /*
@ -278,7 +285,7 @@ STRING_TEST:
return -1; return -1;
if (strcasecmp if (strcasecmp
(string_address + (test_length - match_length), (string_addr + (test_length - match_length),
acl->address.string) == 0) { acl->address.string) == 0) {
if (acl->access == ACL_DENY) if (acl->access == ACL_DENY)
return 0; return 0;
@ -329,14 +336,17 @@ static int check_numeric_acl (const struct acl_s *acl, const char *ip)
* 1 if allowed * 1 if allowed
* 0 if denied * 0 if denied
*/ */
int check_acl (const char *ip, const char *host, vector_t access_list) int check_acl (const char *ip, union sockaddr_union *addr, vector_t access_list)
{ {
struct acl_s *acl; struct acl_s *acl;
int perm = 0; int perm = 0;
size_t i; size_t i;
char string_addr[HOSTNAME_LENGTH];
assert (ip != NULL); assert (ip != NULL);
assert (host != NULL); assert (addr != NULL);
string_addr[0] = 0;
/* /*
* If there is no access list allow everything. * If there is no access list allow everything.
@ -348,7 +358,7 @@ int check_acl (const char *ip, const char *host, vector_t access_list)
acl = (struct acl_s *) vector_getentry (access_list, i, NULL); acl = (struct acl_s *) vector_getentry (access_list, i, NULL);
switch (acl->type) { switch (acl->type) {
case ACL_STRING: case ACL_STRING:
perm = acl_string_processing (acl, ip, host); perm = acl_string_processing (acl, ip, addr, string_addr);
break; break;
case ACL_NUMERIC: case ACL_NUMERIC:
@ -371,8 +381,8 @@ int check_acl (const char *ip, const char *host, vector_t access_list)
/* /*
* Deny all connections by default. * Deny all connections by default.
*/ */
log_message (LOG_NOTICE, "Unauthorized connection from \"%s\" [%s].", log_message (LOG_NOTICE, "Unauthorized connection from \"%s\".",
host, ip); ip);
return 0; return 0;
} }

View File

@ -22,12 +22,13 @@
#define TINYPROXY_ACL_H #define TINYPROXY_ACL_H
#include "vector.h" #include "vector.h"
#include "sock.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,
vector_t *access_list); vector_t *access_list);
extern int check_acl (const char *ip_address, const char *string_address, extern int check_acl (const char *ip_address, union sockaddr_union *addr,
vector_t access_list); vector_t access_list);
extern void flush_access_list (vector_t access_list); extern void flush_access_list (vector_t access_list);

View File

@ -28,21 +28,21 @@
#include "log.h" #include "log.h"
#include "conf.h" #include "conf.h"
short int is_anonymous_enabled (void) short int is_anonymous_enabled (struct config_s *conf)
{ {
return (config.anonymous_map != NULL) ? 1 : 0; return (conf->anonymous_map != NULL) ? 1 : 0;
} }
/* /*
* Search for the header. This function returns a positive value greater than * Search for the header. This function returns a positive value greater than
* zero if the string was found, zero if it wasn't and negative upon error. * zero if the string was found, zero if it wasn't and negative upon error.
*/ */
int anonymous_search (const char *s) int anonymous_search (struct config_s *conf, const char *s)
{ {
assert (s != NULL); assert (s != NULL);
assert (config.anonymous_map != NULL); assert (conf->anonymous_map != NULL);
return hashmap_search (config.anonymous_map, s); return hashmap_search (conf->anonymous_map, s);
} }
/* /*
@ -51,23 +51,23 @@ int anonymous_search (const char *s)
* Return -1 if there is an error, otherwise a 0 is returned if the insert was * Return -1 if there is an error, otherwise a 0 is returned if the insert was
* successful. * successful.
*/ */
int anonymous_insert (const char *s) int anonymous_insert (struct config_s *conf, const char *s)
{ {
char data = 1; char data = 1;
assert (s != NULL); assert (s != NULL);
if (!config.anonymous_map) { if (!conf->anonymous_map) {
config.anonymous_map = hashmap_create (32); conf->anonymous_map = hashmap_create (32);
if (!config.anonymous_map) if (!conf->anonymous_map)
return -1; return -1;
} }
if (hashmap_search (config.anonymous_map, s) > 0) { if (hashmap_search (conf->anonymous_map, s) > 0) {
/* The key was already found, so return a positive number. */ /* The key was already found, so return a positive number. */
return 0; return 0;
} }
/* Insert the new key */ /* Insert the new key */
return hashmap_insert (config.anonymous_map, s, &data, sizeof (data)); return hashmap_insert (conf->anonymous_map, s, &data, sizeof (data));
} }

View File

@ -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 (struct config_s *conf);
extern int anonymous_search (const char *s); extern int anonymous_search (struct config_s *conf, const char *s);
extern int anonymous_insert (const char *s); extern int anonymous_insert (struct config_s *conf, const char *s);
#endif #endif

View File

@ -31,181 +31,99 @@
#include "sock.h" #include "sock.h"
#include "utils.h" #include "utils.h"
#include "conf.h" #include "conf.h"
#include "sblist.h"
#include "loop.h"
#include <pthread.h>
static vector_t listen_fds; static vector_t listen_fds;
/* struct client {
* Stores the internal data needed for each child (connection) union sockaddr_union addr;
*/ int fd;
enum child_status_t { T_EMPTY, T_WAITING, T_CONNECTED };
struct child_s {
pid_t tid;
unsigned int connects;
enum child_status_t status;
}; };
/* struct child {
* A pointer to an array of children. A certain number of children are pthread_t thread;
* created when the program is started. struct client client;
*/ volatile int done;
static struct child_s *child_ptr; };
static struct child_config_s { static void* child_thread(void* data)
unsigned int maxclients, maxrequestsperchild;
unsigned int maxspareservers, minspareservers, startservers;
} child_config;
static unsigned int *servers_waiting; /* servers waiting for a connection */
/*
* Lock/Unlock the "servers_waiting" variable so that two children cannot
* modify it at the same time.
*/
#define SERVER_COUNT_LOCK() _child_lock_wait()
#define SERVER_COUNT_UNLOCK() _child_lock_release()
/* START OF LOCKING SECTION */
/*
* These variables are required for the locking mechanism. Also included
* are the "private" functions for locking/unlocking.
*/
static struct flock lock_it, unlock_it;
static int lock_fd = -1;
static void _child_lock_init (void)
{ {
char lock_file[] = "/tmp/tinyproxy.servers.lock.XXXXXX"; struct child *c = data;
handle_connection (c->client.fd, &c->client.addr);
/* Only allow u+rw bits. This may be required for some versions c->done = 1;
* of glibc so that mkstemp() doesn't make us vulnerable. return NULL;
*/
umask (0177);
lock_fd = mkstemp (lock_file);
unlink (lock_file);
lock_it.l_type = F_WRLCK;
lock_it.l_whence = SEEK_SET;
lock_it.l_start = 0;
lock_it.l_len = 0;
unlock_it.l_type = F_UNLCK;
unlock_it.l_whence = SEEK_SET;
unlock_it.l_start = 0;
unlock_it.l_len = 0;
} }
static void _child_lock_wait (void) static sblist *childs;
static void collect_threads(void)
{ {
int rc; size_t i;
for (i = 0; i < sblist_getsize(childs); ) {
while ((rc = fcntl (lock_fd, F_SETLKW, &lock_it)) < 0) { struct child *c = *((struct child**)sblist_get(childs, i));
if (errno == EINTR) if (c->done) {
continue; pthread_join(c->thread, 0);
else sblist_delete(childs, i);
return; safefree(c);
} } else i++;
}
static void _child_lock_release (void)
{
if (fcntl (lock_fd, F_SETLKW, &unlock_it) < 0)
return;
}
/* END OF LOCKING SECTION */
#define SERVER_INC() do { \
SERVER_COUNT_LOCK(); \
++(*servers_waiting); \
DEBUG2("INC: servers_waiting: %d", *servers_waiting); \
SERVER_COUNT_UNLOCK(); \
} while (0)
#define SERVER_DEC() do { \
SERVER_COUNT_LOCK(); \
assert(*servers_waiting > 0); \
--(*servers_waiting); \
DEBUG2("DEC: servers_waiting: %d", *servers_waiting); \
SERVER_COUNT_UNLOCK(); \
} while (0)
/*
* Set the configuration values for the various child related settings.
*/
short int child_configure (child_config_t type, unsigned int val)
{
switch (type) {
case CHILD_MAXCLIENTS:
child_config.maxclients = val;
break;
case CHILD_MAXSPARESERVERS:
child_config.maxspareservers = val;
break;
case CHILD_MINSPARESERVERS:
child_config.minspareservers = val;
break;
case CHILD_STARTSERVERS:
child_config.startservers = val;
break;
case CHILD_MAXREQUESTSPERCHILD:
child_config.maxrequestsperchild = val;
break;
default:
DEBUG2 ("Invalid type (%d)", type);
return -1;
}
return 0;
}
/**
* child signal handler for sighup
*/
static void child_sighup_handler (int sig)
{
if (sig == SIGHUP) {
/*
* Ignore the return value of reload_config for now.
* This should actually be handled somehow...
*/
reload_config ();
#ifdef FILTER_ENABLE
filter_reload ();
#endif /* FILTER_ENABLE */
} }
} }
/* /*
* This is the main (per child) loop. * This is the main loop accepting new connections.
*/ */
static void child_main (struct child_s *ptr) void child_main_loop (void)
{ {
int connfd; int connfd;
struct sockaddr *cliaddr; union sockaddr_union cliaddr_storage;
socklen_t clilen; struct sockaddr *cliaddr = (void*) &cliaddr_storage;
socklen_t clilen = sizeof(cliaddr_storage);
fd_set rfds; fd_set rfds;
int maxfd = 0;
ssize_t i; ssize_t i;
int ret; int ret, listenfd, maxfd, was_full = 0;
pthread_attr_t *attrp, attr;
struct child *child;
cliaddr = (struct sockaddr *) childs = sblist_new(sizeof (struct child*), config->maxclients);
safemalloc (sizeof(struct sockaddr_storage));
if (!cliaddr) {
log_message (LOG_CRIT,
"Could not allocate memory for child address.");
exit (0);
}
ptr->connects = 0; loop_records_init();
srand(time(NULL));
/* /*
* We have to wait for connections on multiple fds, * We have to wait for connections on multiple fds,
* so use select. * so use select.
*/ */
while (!config->quit) {
collect_threads();
if (sblist_getsize(childs) >= config->maxclients) {
if (!was_full)
log_message (LOG_WARNING,
"Maximum number of connections reached. "
"Refusing new connections.");
was_full = 1;
usleep(16);
continue;
}
was_full = 0;
listenfd = -1;
maxfd = 0;
/* Handle log rotation if it was requested */
if (received_sighup) {
reload_config (1);
#ifdef FILTER_ENABLE
filter_reload ();
#endif /* FILTER_ENABLE */
received_sighup = FALSE;
}
FD_ZERO(&rfds); FD_ZERO(&rfds);
@ -217,20 +135,13 @@ static void child_main (struct child_s *ptr)
log_message(LOG_ERR, "Failed to set the listening " log_message(LOG_ERR, "Failed to set the listening "
"socket %d to non-blocking: %s", "socket %d to non-blocking: %s",
fd, strerror(errno)); fd, strerror(errno));
exit(1); continue;
} }
FD_SET(*fd, &rfds); FD_SET(*fd, &rfds);
maxfd = max(maxfd, *fd); maxfd = max(maxfd, *fd);
} }
while (!config.quit) {
int listenfd = -1;
ptr->status = T_WAITING;
clilen = sizeof(struct sockaddr_storage);
ret = select(maxfd + 1, &rfds, NULL, NULL, NULL); ret = select(maxfd + 1, &rfds, NULL, NULL, NULL);
if (ret == -1) { if (ret == -1) {
if (errno == EINTR) { if (errno == EINTR) {
@ -238,7 +149,7 @@ static void child_main (struct child_s *ptr)
} }
log_message (LOG_ERR, "error calling select: %s", log_message (LOG_ERR, "error calling select: %s",
strerror(errno)); strerror(errno));
exit(1); continue;
} else if (ret == 0) { } else if (ret == 0) {
log_message (LOG_WARNING, "Strange: select returned 0 " log_message (LOG_WARNING, "Strange: select returned 0 "
"but we did not specify a timeout..."); "but we did not specify a timeout...");
@ -269,7 +180,7 @@ static void child_main (struct child_s *ptr)
log_message(LOG_ERR, "Failed to set listening " log_message(LOG_ERR, "Failed to set listening "
"socket %d to blocking for accept: %s", "socket %d to blocking for accept: %s",
listenfd, strerror(errno)); listenfd, strerror(errno));
exit(1); continue;
} }
/* /*
@ -279,21 +190,6 @@ static void child_main (struct child_s *ptr)
connfd = accept (listenfd, cliaddr, &clilen); connfd = accept (listenfd, cliaddr, &clilen);
#ifndef NDEBUG
/*
* Enable the TINYPROXY_DEBUG environment variable if you
* want to use the GDB debugger.
*/
if (getenv ("TINYPROXY_DEBUG")) {
/* Pause for 10 seconds to allow us to connect debugger */
fprintf (stderr,
"Process has accepted connection: %ld\n",
(long int) ptr->tid);
sleep (10);
fprintf (stderr, "Continuing process: %ld\n",
(long int) ptr->tid);
}
#endif
/* /*
* Make sure no error occurred... * Make sure no error occurred...
@ -305,223 +201,36 @@ static void child_main (struct child_s *ptr)
continue; continue;
} }
ptr->status = T_CONNECTED; child = safemalloc(sizeof(struct child));
if (!child) {
SERVER_DEC (); oom:
close(connfd);
handle_connection (connfd); log_message (LOG_CRIT,
ptr->connects++; "Could not allocate memory for child.");
usleep(16); /* prevent 100% CPU usage in OOM situation */
if (child_config.maxrequestsperchild != 0) { continue;
DEBUG2 ("%u connections so far...", ptr->connects);
if (ptr->connects == child_config.maxrequestsperchild) {
log_message (LOG_NOTICE,
"Child has reached MaxRequestsPerChild (%u). "
"Killing child.", ptr->connects);
break;
}
} }
SERVER_COUNT_LOCK (); child->done = 0;
if (*servers_waiting > child_config.maxspareservers) {
/*
* There are too many spare children, kill ourself
* off.
*/
log_message (LOG_NOTICE,
"Waiting servers (%d) exceeds MaxSpareServers (%d). "
"Killing child.",
*servers_waiting,
child_config.maxspareservers);
SERVER_COUNT_UNLOCK ();
break; if (!sblist_add(childs, &child)) {
} else { free(child);
SERVER_COUNT_UNLOCK (); goto oom;
} }
SERVER_INC (); child->client.fd = connfd;
memcpy(&child->client.addr, &cliaddr_storage, sizeof(cliaddr_storage));
attrp = 0;
if (pthread_attr_init(&attr) == 0) {
attrp = &attr;
pthread_attr_setstacksize(attrp, 256*1024);
} }
ptr->status = T_EMPTY; if (pthread_create(&child->thread, attrp, child_thread, child) != 0) {
sblist_delete(childs, sblist_getsize(childs) -1);
safefree (cliaddr); free(child);
exit (0); goto oom;
}
/*
* Fork a child "child" (or in our case a process) and then start up the
* child_main() function.
*/
static pid_t child_make (struct child_s *ptr)
{
pid_t pid;
if ((pid = fork ()) > 0)
return pid; /* parent */
/*
* Reset the SIGNALS so that the child can be reaped.
*/
set_signal_handler (SIGCHLD, SIG_DFL);
set_signal_handler (SIGTERM, SIG_DFL);
set_signal_handler (SIGHUP, child_sighup_handler);
child_main (ptr); /* never returns */
return -1;
}
/*
* Create a pool of children to handle incoming connections
*/
short int child_pool_create (void)
{
unsigned int i;
/*
* Make sure the number of MaxClients is not zero, since this
* variable determines the size of the array created for children
* later on.
*/
if (child_config.maxclients == 0) {
log_message (LOG_ERR,
"child_pool_create: \"MaxClients\" must be "
"greater than zero.");
return -1;
}
if (child_config.startservers == 0) {
log_message (LOG_ERR,
"child_pool_create: \"StartServers\" must be "
"greater than zero.");
return -1;
}
child_ptr =
(struct child_s *) calloc_shared_memory (child_config.maxclients,
sizeof (struct child_s));
if (!child_ptr) {
log_message (LOG_ERR,
"Could not allocate memory for children.");
return -1;
}
servers_waiting =
(unsigned int *) malloc_shared_memory (sizeof (unsigned int));
if (servers_waiting == MAP_FAILED) {
log_message (LOG_ERR,
"Could not allocate memory for child counting.");
return -1;
}
*servers_waiting = 0;
/*
* Create a "locking" file for use around the servers_waiting
* variable.
*/
_child_lock_init ();
if (child_config.startservers > child_config.maxclients) {
log_message (LOG_WARNING,
"Can not start more than \"MaxClients\" servers. "
"Starting %u servers instead.",
child_config.maxclients);
child_config.startservers = child_config.maxclients;
}
for (i = 0; i != child_config.maxclients; i++) {
child_ptr[i].status = T_EMPTY;
child_ptr[i].connects = 0;
}
for (i = 0; i != child_config.startservers; i++) {
DEBUG2 ("Trying to create child %d of %d", i + 1,
child_config.startservers);
child_ptr[i].status = T_WAITING;
child_ptr[i].tid = child_make (&child_ptr[i]);
if (child_ptr[i].tid < 0) {
log_message (LOG_WARNING,
"Could not create child number %d of %d",
i, child_config.startservers);
return -1;
} else {
log_message (LOG_INFO,
"Creating child number %d of %d ...",
i + 1, child_config.startservers);
SERVER_INC ();
}
}
log_message (LOG_INFO, "Finished creating all children.");
return 0;
}
/*
* Keep the proper number of servers running. This is the birth of the
* servers. It monitors this at least once a second.
*/
void child_main_loop (void)
{
unsigned int i;
while (1) {
if (config.quit)
return;
/* If there are not enough spare servers, create more */
SERVER_COUNT_LOCK ();
if (*servers_waiting < child_config.minspareservers) {
log_message (LOG_NOTICE,
"Waiting servers (%d) is less than MinSpareServers (%d). "
"Creating new child.",
*servers_waiting,
child_config.minspareservers);
SERVER_COUNT_UNLOCK ();
for (i = 0; i != child_config.maxclients; i++) {
if (child_ptr[i].status == T_EMPTY) {
child_ptr[i].status = T_WAITING;
child_ptr[i].tid =
child_make (&child_ptr[i]);
if (child_ptr[i].tid < 0) {
log_message (LOG_NOTICE,
"Could not create child");
child_ptr[i].status = T_EMPTY;
break;
}
SERVER_INC ();
break;
}
}
} else {
SERVER_COUNT_UNLOCK ();
}
sleep (5);
/* Handle log rotation if it was requested */
if (received_sighup) {
/*
* Ignore the return value of reload_config for now.
* This should actually be handled somehow...
*/
reload_config ();
#ifdef FILTER_ENABLE
filter_reload ();
#endif /* FILTER_ENABLE */
/* propagate filter reload to all children */
child_kill_children (SIGHUP);
received_sighup = FALSE;
} }
} }
} }
@ -531,13 +240,26 @@ void child_main_loop (void)
*/ */
void child_kill_children (int sig) void child_kill_children (int sig)
{ {
unsigned int i; size_t i;
for (i = 0; i != child_config.maxclients; i++) { if (sig != SIGTERM) return;
if (child_ptr[i].status != T_EMPTY)
kill (child_ptr[i].tid, sig); for (i = 0; i < sblist_getsize(childs); i++) {
struct child *c = *((struct child**)sblist_get(childs, i));
if (!c->done) {
/* interrupt blocking operations.
this should cause the threads to shutdown orderly. */
close(c->client.fd);
} }
} }
usleep(16);
collect_threads();
if (sblist_getsize(childs) != 0)
log_message (LOG_CRIT,
"child_kill_children: %zu threads still alive!",
sblist_getsize(childs)
);
}
/** /**

View File

@ -27,7 +27,6 @@
#include "acl.h" #include "acl.h"
#include "anonymous.h" #include "anonymous.h"
#include "child.h"
#include "filter.h" #include "filter.h"
#include "heap.h" #include "heap.h"
#include "html-error.h" #include "html-error.h"
@ -52,6 +51,8 @@
#define BOOL "(yes|on|no|off)" #define BOOL "(yes|on|no|off)"
#define INT "((0x)?[[:digit:]]+)" #define INT "((0x)?[[:digit:]]+)"
#define ALNUM "([-a-z0-9._]+)" #define ALNUM "([-a-z0-9._]+)"
#define USERNAME "([^:]*)"
#define PASSWORD "([^@]*)"
#define IP "((([0-9]{1,3})\\.){3}[0-9]{1,3})" #define IP "((([0-9]{1,3})\\.){3}[0-9]{1,3})"
#define IPMASK "(" IP "(/[[:digit:]]+)?)" #define IPMASK "(" IP "(/[[:digit:]]+)?)"
#define IPV6 "(" \ #define IPV6 "(" \
@ -90,7 +91,8 @@
* All configuration handling functions are REQUIRED to be defined * All configuration handling functions are REQUIRED to be defined
* with the same function template as below. * with the same function template as below.
*/ */
typedef int (*CONFFILE_HANDLER) (struct config_s *, const char *, regmatch_t[]); typedef int (*CONFFILE_HANDLER) (struct config_s *, const char *,
unsigned long, regmatch_t[]);
/* /*
* Define the pattern used by any directive handling function. The * Define the pattern used by any directive handling function. The
@ -105,7 +107,7 @@ typedef int (*CONFFILE_HANDLER) (struct config_s *, const char *, regmatch_t[]);
*/ */
#define HANDLE_FUNC(func) \ #define HANDLE_FUNC(func) \
int func(struct config_s* conf, const char* line, \ int func(struct config_s* conf, const char* line, \
regmatch_t match[]) unsigned long lineno, regmatch_t match[])
/* /*
* List all the handling functions. These are defined later, but they need * List all the handling functions. These are defined later, but they need
@ -138,9 +140,7 @@ static HANDLE_FUNC (handle_listen);
static HANDLE_FUNC (handle_logfile); static HANDLE_FUNC (handle_logfile);
static HANDLE_FUNC (handle_loglevel); static HANDLE_FUNC (handle_loglevel);
static HANDLE_FUNC (handle_maxclients); static HANDLE_FUNC (handle_maxclients);
static HANDLE_FUNC (handle_maxrequestsperchild); static HANDLE_FUNC (handle_obsolete);
static HANDLE_FUNC (handle_maxspareservers);
static HANDLE_FUNC (handle_minspareservers);
static HANDLE_FUNC (handle_pidfile); static HANDLE_FUNC (handle_pidfile);
static HANDLE_FUNC (handle_port); static HANDLE_FUNC (handle_port);
#ifdef REVERSE_SUPPORT #ifdef REVERSE_SUPPORT
@ -149,7 +149,6 @@ static HANDLE_FUNC (handle_reversemagic);
static HANDLE_FUNC (handle_reverseonly); static HANDLE_FUNC (handle_reverseonly);
static HANDLE_FUNC (handle_reversepath); static HANDLE_FUNC (handle_reversepath);
#endif #endif
static HANDLE_FUNC (handle_startservers);
static HANDLE_FUNC (handle_statfile); static HANDLE_FUNC (handle_statfile);
static HANDLE_FUNC (handle_stathost); static HANDLE_FUNC (handle_stathost);
static HANDLE_FUNC (handle_syslog); static HANDLE_FUNC (handle_syslog);
@ -215,10 +214,10 @@ struct {
/* integer arguments */ /* integer arguments */
STDCONF ("port", INT, handle_port), STDCONF ("port", INT, handle_port),
STDCONF ("maxclients", INT, handle_maxclients), STDCONF ("maxclients", INT, handle_maxclients),
STDCONF ("maxspareservers", INT, handle_maxspareservers), STDCONF ("maxspareservers", INT, handle_obsolete),
STDCONF ("minspareservers", INT, handle_minspareservers), STDCONF ("minspareservers", INT, handle_obsolete),
STDCONF ("startservers", INT, handle_startservers), STDCONF ("startservers", INT, handle_obsolete),
STDCONF ("maxrequestsperchild", INT, handle_maxrequestsperchild), STDCONF ("maxrequestsperchild", INT, handle_obsolete),
STDCONF ("timeout", INT, handle_timeout), STDCONF ("timeout", INT, handle_timeout),
STDCONF ("connectport", INT, handle_connectport), STDCONF ("connectport", INT, handle_connectport),
/* alphanumeric arguments */ /* alphanumeric arguments */
@ -257,7 +256,7 @@ struct {
}, },
{ {
BEGIN "(upstream)" WS "(http|socks4|socks5)" WS BEGIN "(upstream)" WS "(http|socks4|socks5)" WS
"(" ALNUM /*username*/ ":" ALNUM /*password*/ "@" ")?" "(" USERNAME /*username*/ ":" PASSWORD /*password*/ "@" ")?"
"(" IP "|" ALNUM ")" "(" IP "|" ALNUM ")"
":" INT "(" WS STR ")?" ":" INT "(" WS STR ")?"
END, handle_upstream, NULL END, handle_upstream, NULL
@ -288,7 +287,6 @@ free_added_headers (vector_t add_headers)
static void free_config (struct config_s *conf) static void free_config (struct config_s *conf)
{ {
safefree (conf->config_file);
safefree (conf->logf_name); safefree (conf->logf_name);
safefree (conf->stathost); safefree (conf->stathost);
safefree (conf->user); safefree (conf->user);
@ -376,7 +374,8 @@ config_free_regex (void)
* Returns 0 if a match was found and successfully processed; otherwise, * Returns 0 if a match was found and successfully processed; otherwise,
* a negative number is returned. * a negative number is returned.
*/ */
static int check_match (struct config_s *conf, const char *line) static int check_match (struct config_s *conf, const char *line,
unsigned long lineno)
{ {
regmatch_t match[RE_MAX_MATCHES]; regmatch_t match[RE_MAX_MATCHES];
unsigned int i; unsigned int i;
@ -387,7 +386,7 @@ static int check_match (struct config_s *conf, const char *line)
assert (directives[i].cre); assert (directives[i].cre);
if (!regexec if (!regexec
(directives[i].cre, line, RE_MAX_MATCHES, match, 0)) (directives[i].cre, line, RE_MAX_MATCHES, match, 0))
return (*directives[i].handler) (conf, line, match); return (*directives[i].handler) (conf, line, lineno, match);
} }
return -1; return -1;
@ -398,11 +397,11 @@ static int check_match (struct config_s *conf, const char *line)
*/ */
static int config_parse (struct config_s *conf, FILE * f) static int config_parse (struct config_s *conf, FILE * f)
{ {
char buffer[1024]; /* 1KB lines should be plenty */ char buffer[LINE_MAX];
unsigned long lineno = 1; unsigned long lineno = 1;
while (fgets (buffer, sizeof (buffer), f)) { while (fgets (buffer, sizeof (buffer), f)) {
if (check_match (conf, buffer)) { if (check_match (conf, buffer, lineno)) {
printf ("Syntax error on line %ld\n", lineno); printf ("Syntax error on line %ld\n", lineno);
return 1; return 1;
} }
@ -442,113 +441,26 @@ done:
return ret; return ret;
} }
static void initialize_with_defaults (struct config_s *conf, static void initialize_config_defaults (struct config_s *conf)
struct config_s *defaults)
{ {
if (defaults->logf_name) { memset (conf, 0, sizeof(*conf));
conf->logf_name = safestrdup (defaults->logf_name);
}
if (defaults->config_file) { /*
conf->config_file = safestrdup (defaults->config_file); * Make sure the HTML error pages array is NULL to begin with.
} * (FIXME: Should have a better API for all this)
*/
conf->syslog = defaults->syslog; conf->errorpages = NULL;
conf->port = defaults->port; conf->stathost = safestrdup (TINYPROXY_STATHOST);
conf->idletimeout = MAX_IDLE_TIME;
if (defaults->stathost) { conf->logf_name = NULL;
conf->stathost = safestrdup (defaults->stathost); conf->pidpath = NULL;
} conf->maxclients = 100;
conf->godaemon = defaults->godaemon;
conf->quit = defaults->quit;
if (defaults->user) {
conf->user = safestrdup (defaults->user);
}
if (defaults->group) {
conf->group = safestrdup (defaults->group);
}
if (defaults->listen_addrs) {
ssize_t i;
conf->listen_addrs = vector_create();
for (i=0; i < vector_length(defaults->listen_addrs); i++) {
char *addr;
size_t size;
addr = (char *)vector_getentry(defaults->listen_addrs,
i, &size);
vector_append(conf->listen_addrs, addr, size);
}
}
#ifdef FILTER_ENABLE
if (defaults->filter) {
conf->filter = safestrdup (defaults->filter);
}
conf->filter_url = defaults->filter_url;
conf->filter_extended = defaults->filter_extended;
conf->filter_casesensitive = defaults->filter_casesensitive;
#endif /* FILTER_ENABLE */
#ifdef XTINYPROXY_ENABLE
conf->add_xtinyproxy = defaults->add_xtinyproxy;
#endif
#ifdef REVERSE_SUPPORT
/* struct reversepath *reversepath_list; */
conf->reverseonly = defaults->reverseonly;
conf->reversemagic = defaults->reversemagic;
if (defaults->reversebaseurl) {
conf->reversebaseurl = safestrdup (defaults->reversebaseurl);
}
#endif
#ifdef UPSTREAM_SUPPORT
/* struct upstream *upstream_list; */
#endif /* UPSTREAM_SUPPORT */
if (defaults->pidpath) {
conf->pidpath = safestrdup (defaults->pidpath);
}
conf->idletimeout = defaults->idletimeout;
if (defaults->bind_address) {
conf->bind_address = safestrdup (defaults->bind_address);
}
conf->bindsame = defaults->bindsame;
if (defaults->via_proxy_name) {
conf->via_proxy_name = safestrdup (defaults->via_proxy_name);
}
conf->disable_viaheader = defaults->disable_viaheader;
if (defaults->errorpage_undef) {
conf->errorpage_undef = safestrdup (defaults->errorpage_undef);
}
if (defaults->statpage) {
conf->statpage = safestrdup (defaults->statpage);
}
/* vector_t access_list; */
/* vector_t connect_ports; */
/* hashmap_t anonymous_map; */
} }
/** /**
* Load the configuration. * Load the configuration.
*/ */
int reload_config_file (const char *config_fname, struct config_s *conf, int reload_config_file (const char *config_fname, struct config_s *conf)
struct config_s *defaults)
{ {
int ret; int ret;
@ -556,7 +468,7 @@ int reload_config_file (const char *config_fname, struct config_s *conf,
free_config (conf); free_config (conf);
initialize_with_defaults (conf, defaults); initialize_config_defaults (conf);
ret = load_config_file (config_fname, conf); ret = load_config_file (config_fname, conf);
if (ret != 0) { if (ret != 0) {
@ -712,7 +624,7 @@ static HANDLE_FUNC (handle_anonymous)
if (!arg) if (!arg)
return -1; return -1;
anonymous_insert (arg); anonymous_insert (conf, arg);
safefree (arg); safefree (arg);
return 0; return 0;
} }
@ -803,34 +715,14 @@ static HANDLE_FUNC (handle_port)
static HANDLE_FUNC (handle_maxclients) static HANDLE_FUNC (handle_maxclients)
{ {
child_configure (CHILD_MAXCLIENTS, get_long_arg (line, &match[2])); set_int_arg (&conf->maxclients, line, &match[2]);
return 0; return 0;
} }
static HANDLE_FUNC (handle_maxspareservers) static HANDLE_FUNC (handle_obsolete)
{ {
child_configure (CHILD_MAXSPARESERVERS, fprintf (stderr, "WARNING: obsolete config item on line %lu\n",
get_long_arg (line, &match[2])); lineno);
return 0;
}
static HANDLE_FUNC (handle_minspareservers)
{
child_configure (CHILD_MINSPARESERVERS,
get_long_arg (line, &match[2]));
return 0;
}
static HANDLE_FUNC (handle_startservers)
{
child_configure (CHILD_STARTSERVERS, get_long_arg (line, &match[2]));
return 0;
}
static HANDLE_FUNC (handle_maxrequestsperchild)
{
child_configure (CHILD_MAXREQUESTSPERCHILD,
get_long_arg (line, &match[2]));
return 0; return 0;
} }

View File

@ -39,12 +39,11 @@ typedef struct {
struct config_s { struct config_s {
vector_t basicauth_list; vector_t basicauth_list;
char *logf_name; char *logf_name;
char *config_file;
unsigned int syslog; /* boolean */ unsigned int syslog; /* boolean */
unsigned int port; unsigned int port;
char *stathost; char *stathost;
unsigned int godaemon; /* boolean */
unsigned int quit; /* boolean */ unsigned int quit; /* boolean */
unsigned int maxclients;
char *user; char *user;
char *group; char *group;
vector_t listen_addrs; vector_t listen_addrs;
@ -113,8 +112,7 @@ struct config_s {
vector_t add_headers; vector_t add_headers;
}; };
extern int reload_config_file (const char *config_fname, struct config_s *conf, extern int reload_config_file (const char *config_fname, struct config_s *conf);
struct config_s *defaults);
int config_compile_regex (void); int config_compile_regex (void);

View File

@ -31,7 +31,6 @@
#include "stats.h" #include "stats.h"
struct conn_s *initialize_conn (int client_fd, const char *ipaddr, struct conn_s *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;
@ -79,7 +78,6 @@ struct conn_s *initialize_conn (int client_fd, const char *ipaddr,
connptr->server_ip_addr = (sock_ipaddr ? connptr->server_ip_addr = (sock_ipaddr ?
safestrdup (sock_ipaddr) : NULL); safestrdup (sock_ipaddr) : NULL);
connptr->client_ip_addr = safestrdup (ipaddr); connptr->client_ip_addr = safestrdup (ipaddr);
connptr->client_string_addr = safestrdup (string_addr);
connptr->upstream_proxy = NULL; connptr->upstream_proxy = NULL;
@ -134,8 +132,6 @@ void destroy_conn (struct conn_s *connptr)
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)
safefree (connptr->client_string_addr);
#ifdef REVERSE_SUPPORT #ifdef REVERSE_SUPPORT
if (connptr->reversepath) if (connptr->reversepath)

View File

@ -62,10 +62,9 @@ struct conn_s {
char *server_ip_addr; char *server_ip_addr;
/* /*
* Store the client's IP and hostname information * Store the client's IP information
*/ */
char *client_ip_addr; char *client_ip_addr;
char *client_string_addr;
/* /*
* Store the incoming request's HTTP protocol. * Store the incoming request's HTTP protocol.
@ -92,7 +91,6 @@ struct conn_s {
* 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 *sock_ipaddr); const char *sock_ipaddr);
extern void destroy_conn (struct conn_s *connptr); extern void destroy_conn (struct conn_s *connptr);

View File

@ -29,18 +29,17 @@
#include "log.h" #include "log.h"
#include "reqs.h" #include "reqs.h"
#include "conf.h" #include "conf.h"
#include "sblist.h"
#define FILTER_BUFFER_LEN (512) #define FILTER_BUFFER_LEN (512)
static int err; static int err;
struct filter_list { struct filter_list {
struct filter_list *next; regex_t cpatb;
char *pat;
regex_t *cpat;
}; };
static struct filter_list *fl = NULL; static sblist *fl = NULL;
static int already_init = 0; static int already_init = 0;
static filter_policy_t default_policy = FILTER_DEFAULT_ALLOW; static filter_policy_t default_policy = FILTER_DEFAULT_ALLOW;
@ -50,34 +49,38 @@ static filter_policy_t default_policy = FILTER_DEFAULT_ALLOW;
void filter_init (void) void filter_init (void)
{ {
FILE *fd; FILE *fd;
struct filter_list *p; struct filter_list fe;
char buf[FILTER_BUFFER_LEN]; char buf[FILTER_BUFFER_LEN];
char *s; char *s, *start;
int cflags; int cflags, lineno = 0;
if (fl || already_init) { if (fl || already_init) {
return; return;
} }
fd = fopen (config.filter, "r"); fd = fopen (config->filter, "r");
if (!fd) { if (!fd) {
return; return;
} }
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)) {
++lineno;
/* skip leading whitespace */
s = buf;
while (*s && isspace ((unsigned char) *s))
s++;
start = s;
/* /*
* Remove any trailing white space and * Remove any trailing white space and
* comments. * comments.
*/ */
s = buf;
while (*s) { while (*s) {
if (isspace ((unsigned char) *s)) if (isspace ((unsigned char) *s))
break; break;
@ -93,34 +96,28 @@ void filter_init (void)
++s; ++s;
} }
*s = '\0'; *s = '\0';
s = start;
/* skip leading whitespace */
s = buf;
while (*s && isspace ((unsigned char) *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 (!fl) fl = sblist_new(sizeof(struct filter_list),
fl = p = 4096/sizeof(struct filter_list));
(struct filter_list *)
safecalloc (1, sizeof (struct filter_list));
else { /* next entry */
p->next =
(struct filter_list *)
safecalloc (1, sizeof (struct filter_list));
p = p->next;
}
p->pat = safestrdup (s); err = regcomp (&fe.cpatb, s, cflags);
p->cpat = (regex_t *) safemalloc (sizeof (regex_t));
err = regcomp (p->cpat, p->pat, cflags);
if (err != 0) { if (err != 0) {
if (err == REG_ESPACE) goto oom;
fprintf (stderr, fprintf (stderr,
"Bad regex in %s: %s\n", "Bad regex in %s: line %d - %s\n",
config.filter, p->pat); config->filter, lineno, s);
exit (EX_DATAERR);
}
if (!sblist_add(fl, &fe)) {
oom:;
fprintf (stderr,
"out of memory parsing filter file %s: line %d\n",
config->filter, lineno);
exit (EX_DATAERR); exit (EX_DATAERR);
} }
} }
@ -136,15 +133,16 @@ void filter_init (void)
/* unlink the list */ /* unlink the list */
void filter_destroy (void) void filter_destroy (void)
{ {
struct filter_list *p, *q; struct filter_list *p;
size_t i;
if (already_init) { if (already_init) {
for (p = q = fl; p; p = q) { if (fl) {
regfree (p->cpat); for (i = 0; i < sblist_getsize(fl); ++i) {
safefree (p->cpat); p = sblist_get(fl, i);
safefree (p->pat); regfree (&p->cpatb);
q = p->next; }
safefree (p); sblist_free(fl);
} }
fl = NULL; fl = NULL;
already_init = 0; already_init = 0;
@ -156,7 +154,7 @@ void filter_destroy (void)
*/ */
void filter_reload (void) void filter_reload (void)
{ {
if (config.filter) { if (config->filter) {
log_message (LOG_NOTICE, "Re-reading filter file."); log_message (LOG_NOTICE, "Re-reading filter file.");
filter_destroy (); filter_destroy ();
filter_init (); filter_init ();
@ -164,45 +162,19 @@ void filter_reload (void)
} }
/* Return 0 to allow, non-zero to block */ /* Return 0 to allow, non-zero to block */
int filter_domain (const char *host) int filter_run (const char *str)
{ {
struct filter_list *p; struct filter_list *p;
size_t i;
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 (i = 0; i < sblist_getsize(fl); ++i) {
p = sblist_get(fl, i);
result = result =
regexec (p->cpat, host, (size_t) 0, (regmatch_t *) 0, 0); regexec (&p->cpatb, str, (size_t) 0, (regmatch_t *) 0, 0);
if (result == 0) {
if (default_policy == FILTER_DEFAULT_ALLOW)
return 1;
else
return 0;
}
}
COMMON_EXIT:
if (default_policy == FILTER_DEFAULT_ALLOW)
return 0;
else
return 1;
}
/* returns 0 to allow, non-zero to block */
int filter_url (const char *url)
{
struct filter_list *p;
int result;
if (!fl || !already_init)
goto COMMON_EXIT;
for (p = fl; p; p = p->next) {
result =
regexec (p->cpat, url, (size_t) 0, (regmatch_t *) 0, 0);
if (result == 0) { if (result == 0) {
if (default_policy == FILTER_DEFAULT_ALLOW) if (default_policy == FILTER_DEFAULT_ALLOW)

View File

@ -29,8 +29,7 @@ typedef enum {
extern void filter_init (void); extern void filter_init (void);
extern void filter_destroy (void); extern void filter_destroy (void);
extern void filter_reload (void); extern void filter_reload (void);
extern int filter_domain (const char *host); extern int filter_run (const char *str);
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);

View File

@ -97,61 +97,3 @@ char *debugging_strdup (const char *s, const char *file, unsigned long line)
#endif /* !NDEBUG */ #endif /* !NDEBUG */
/*
* Allocate a block of memory in the "shared" memory region.
*
* FIXME: This uses the most basic (and slowest) means of creating a
* shared memory location. It requires the use of a temporary file. We might
* want to look into something like MM (Shared Memory Library) for a better
* solution.
*/
void *malloc_shared_memory (size_t size)
{
int fd;
void *ptr;
char buffer[32];
static const char *shared_file = "/tmp/tinyproxy.shared.XXXXXX";
assert (size > 0);
strlcpy (buffer, shared_file, sizeof (buffer));
/* Only allow u+rw bits. This may be required for some versions
* of glibc so that mkstemp() doesn't make us vulnerable.
*/
umask (0177);
if ((fd = mkstemp (buffer)) == -1)
return MAP_FAILED;
unlink (buffer);
if (ftruncate (fd, size) == -1)
return MAP_FAILED;
ptr = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
return ptr;
}
/*
* Allocate a block of memory from the "shared" region an initialize it to
* zero.
*/
void *calloc_shared_memory (size_t nmemb, size_t size)
{
void *ptr;
long length;
assert (nmemb > 0);
assert (size > 0);
length = nmemb * size;
ptr = malloc_shared_memory (length);
if (ptr == MAP_FAILED)
return ptr;
memset (ptr, 0, length);
return ptr;
}

View File

@ -52,10 +52,4 @@ extern char *debugging_strdup (const char *s, const char *file,
#endif #endif
/*
* Allocate memory from the "shared" region of memory.
*/
extern void *malloc_shared_memory (size_t size);
extern void *calloc_shared_memory (size_t nmemb, size_t size);
#endif #endif

View File

@ -41,13 +41,13 @@ int 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);
@ -66,19 +66,19 @@ static char *get_html_file (unsigned int errornum)
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);
} }
@ -107,7 +107,7 @@ send_html_file (FILE *infile, struct conn_s *connptr)
varval = (const char *) varval = (const char *)
lookup_variable (connptr->error_variables, lookup_variable (connptr->error_variables,
varstart); varstart);
if (!varval) if (!varval || !varval[0])
varval = "(unknown)"; varval = "(unknown)";
r = write_message (connptr->client_fd, r = write_message (connptr->client_fd,
"%s", varval); "%s", varval);
@ -164,13 +164,17 @@ int send_http_headers (struct conn_s *connptr, int code, const char *message)
"%s" "%s"
"Connection: close\r\n" "\r\n"; "Connection: close\r\n" "\r\n";
const char auth_str[] = const char p_auth_str[] =
"Proxy-Authenticate: Basic realm=\"" "Proxy-Authenticate: Basic realm=\""
PACKAGE_NAME "\"\r\n"; PACKAGE_NAME "\"\r\n";
const char w_auth_str[] =
"WWW-Authenticate: Basic realm=\""
PACKAGE_NAME "\"\r\n";
/* according to rfc7235, the 407 error must be accompanied by /* according to rfc7235, the 407 error must be accompanied by
a Proxy-Authenticate header field. */ a Proxy-Authenticate header field. */
const char *add = code == 407 ? auth_str : ""; const char *add = code == 407 ? p_auth_str : (code == 401 ? w_auth_str : "");
return (write_message (connptr->client_fd, headers, return (write_message (connptr->client_fd, headers,
code, message, PACKAGE, VERSION, code, message, PACKAGE, VERSION,
@ -258,7 +262,6 @@ int add_standard_vars (struct conn_s *connptr)
ADD_VAR_RET ("cause", connptr->error_string); ADD_VAR_RET ("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);
/* 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

View File

@ -29,6 +29,7 @@
#include "utils.h" #include "utils.h"
#include "vector.h" #include "vector.h"
#include "conf.h" #include "conf.h"
#include <pthread.h>
static const char *syslog_level[] = { static const char *syslog_level[] = {
NULL, NULL,
@ -45,6 +46,8 @@ static const char *syslog_level[] = {
#define TIME_LENGTH 16 #define TIME_LENGTH 16
#define STRING_LENGTH 800 #define STRING_LENGTH 800
static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
/* /*
* Global file descriptor for the log file * Global file descriptor for the log file
*/ */
@ -71,10 +74,7 @@ static unsigned int logging_initialized = FALSE; /* boolean */
int open_log_file (const char *log_file_name) int open_log_file (const char *log_file_name)
{ {
if (log_file_name == NULL) { if (log_file_name == NULL) {
if(config.godaemon == FALSE)
log_file_fd = fileno(stdout); log_file_fd = fileno(stdout);
else
log_file_fd = -1;
} else { } else {
log_file_fd = create_file_safely (log_file_name, FALSE); log_file_fd = create_file_safely (log_file_name, FALSE);
} }
@ -129,7 +129,7 @@ void log_message (int level, const char *fmt, ...)
return; return;
#endif #endif
if (config.syslog && level == LOG_CONN) if (config && config->syslog && level == LOG_CONN)
level = LOG_INFO; level = LOG_INFO;
va_start (args, fmt); va_start (args, fmt);
@ -161,16 +161,18 @@ void log_message (int level, const char *fmt, ...)
goto out; goto out;
} }
if(!config.syslog && log_file_fd == -1) if(!config->syslog && log_file_fd == -1)
goto out; goto out;
if (config.syslog) { if (config->syslog) {
pthread_mutex_lock(&log_mutex);
#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
pthread_mutex_unlock(&log_mutex);
} else { } else {
char *p; char *p;
@ -196,18 +198,24 @@ void log_message (int level, const char *fmt, ...)
assert (log_file_fd >= 0); assert (log_file_fd >= 0);
pthread_mutex_lock(&log_mutex);
ret = write (log_file_fd, str, strlen (str)); ret = write (log_file_fd, str, strlen (str));
pthread_mutex_unlock(&log_mutex);
if (ret == -1) { if (ret == -1) {
config.syslog = TRUE; config->syslog = TRUE;
log_message(LOG_CRIT, "ERROR: Could not write to log " log_message(LOG_CRIT, "ERROR: Could not write to log "
"file %s: %s.", "file %s: %s.",
config.logf_name, strerror(errno)); config->logf_name, strerror(errno));
log_message(LOG_CRIT, log_message(LOG_CRIT,
"Falling back to syslog logging"); "Falling back to syslog logging");
} }
pthread_mutex_lock(&log_mutex);
fsync (log_file_fd); fsync (log_file_fd);
pthread_mutex_unlock(&log_mutex);
} }
out: out:
@ -264,26 +272,23 @@ static void send_stored_logs (void)
*/ */
int setup_logging (void) int setup_logging (void)
{ {
if (!config.syslog) { if (!config->syslog) {
if (open_log_file (config.logf_name) < 0) { if (open_log_file (config->logf_name) < 0) {
/* /*
* If opening the log file fails, we try * If opening the log file fails, we try
* to fall back to syslog logging... * to fall back to syslog logging...
*/ */
config.syslog = TRUE; config->syslog = TRUE;
log_message (LOG_CRIT, "ERROR: Could not create log " log_message (LOG_CRIT, "ERROR: Could not create log "
"file %s: %s.", "file %s: %s.",
config.logf_name, strerror (errno)); config->logf_name, strerror (errno));
log_message (LOG_CRIT, log_message (LOG_CRIT,
"Falling back to syslog logging."); "Falling back to syslog logging.");
} }
} }
if (config.syslog) { if (config->syslog) {
if (config.godaemon == TRUE)
openlog ("tinyproxy", LOG_PID, LOG_DAEMON);
else
openlog ("tinyproxy", LOG_PID, LOG_USER); openlog ("tinyproxy", LOG_PID, LOG_USER);
} }
@ -302,7 +307,7 @@ void shutdown_logging (void)
return; return;
} }
if (config.syslog) { if (config->syslog) {
closelog (); closelog ();
} else { } else {
close_log_file (); close_log_file ();

76
src/loop.c Normal file
View File

@ -0,0 +1,76 @@
#include <pthread.h>
#include <time.h>
#include "loop.h"
#include "conf.h"
#include "main.h"
#include "sblist.h"
#include "sock.h"
struct loop_record {
union sockaddr_union addr;
time_t tstamp;
};
static sblist *loop_records;
static pthread_mutex_t loop_records_lock = PTHREAD_MUTEX_INITIALIZER;
void loop_records_init(void) {
loop_records = sblist_new(sizeof (struct loop_record), 32);
}
#if 0
static void su_to_str(union sockaddr_union *addr, char *buf) {
int af = addr->v4.sin_family;
unsigned port = ntohs(af == AF_INET ? addr->v4.sin_port : addr->v6.sin6_port);
char portb[32];
sprintf(portb, ":%u", port);
getpeer_information (addr, buf, 256);
strcat(buf, portb);
}
#endif
void loop_records_add(union sockaddr_union *addr) {
time_t now =time(0);
struct loop_record rec;
pthread_mutex_lock(&loop_records_lock);
rec.tstamp = now;
rec.addr = *addr;
sblist_add(loop_records, &rec);
pthread_mutex_unlock(&loop_records_lock);
}
#define TIMEOUT_SECS 15
int connection_loops (union sockaddr_union *addr) {
int ret = 0, af, our_af = addr->v4.sin_family;
void *ipdata, *our_ipdata = our_af == AF_INET ? (void*)&addr->v4.sin_addr.s_addr : (void*)&addr->v6.sin6_addr.s6_addr;
size_t i, cmp_len = our_af == AF_INET ? sizeof(addr->v4.sin_addr.s_addr) : sizeof(addr->v6.sin6_addr.s6_addr);
unsigned port, our_port = ntohs(our_af == AF_INET ? addr->v4.sin_port : addr->v6.sin6_port);
time_t now = time(0);
pthread_mutex_lock(&loop_records_lock);
for (i = 0; i < sblist_getsize(loop_records); ) {
struct loop_record *rec = sblist_get(loop_records, i);
if (rec->tstamp + TIMEOUT_SECS < now) {
sblist_delete(loop_records, i);
continue;
}
if (!ret) {
af = rec->addr.v4.sin_family;
if (af != our_af) goto next;
port = ntohs(af == AF_INET ? rec->addr.v4.sin_port : rec->addr.v6.sin6_port);
if (port != our_port) goto next;
ipdata = af == AF_INET ? (void*)&rec->addr.v4.sin_addr.s_addr : (void*)&rec->addr.v6.sin6_addr.s6_addr;
if (!memcmp(ipdata, our_ipdata, cmp_len)) {
ret = 1;
}
}
next:
i++;
}
pthread_mutex_unlock(&loop_records_lock);
return ret;
}

11
src/loop.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef LOOP_H
#define LOOP_H
#include "sock.h"
void loop_records_init(void);
void loop_records_add(union sockaddr_union *addr);
int connection_loops (union sockaddr_union *addr);
#endif

View File

@ -47,10 +47,18 @@
/* /*
* Global Structures * Global Structures
*/ */
struct config_s config; struct config_s *config;
struct config_s config_defaults; static struct config_s configs[2];
static const char* config_file;
unsigned int received_sighup = FALSE; /* boolean */ unsigned int received_sighup = FALSE; /* boolean */
static struct config_s*
get_next_config(void)
{
if (config == &configs[0]) return &configs[1];
return &configs[0];
}
/* /*
* Handle a signal * Handle a signal
*/ */
@ -61,12 +69,14 @@ takesig (int sig)
int status; int status;
switch (sig) { switch (sig) {
case SIGUSR1:
case SIGHUP: case SIGHUP:
received_sighup = TRUE; received_sighup = TRUE;
break; break;
case SIGINT:
case SIGTERM: case SIGTERM:
config.quit = TRUE; config->quit = TRUE;
break; break;
case SIGCHLD: case SIGCHLD:
@ -161,52 +171,6 @@ get_id (char *str)
return atoi (str); return atoi (str);
} }
/**
* process_cmdline:
* @argc: argc as passed to main()
* @argv: argv as passed to main()
*
* This function parses command line arguments.
**/
static void
process_cmdline (int argc, char **argv, struct config_s *conf)
{
int opt;
while ((opt = getopt (argc, argv, "c:vdh")) != EOF) {
switch (opt) {
case 'v':
display_version ();
exit (EX_OK);
case 'd':
conf->godaemon = FALSE;
break;
case 'c':
if (conf->config_file != NULL) {
safefree (conf->config_file);
}
conf->config_file = safestrdup (optarg);
if (!conf->config_file) {
fprintf (stderr,
"%s: Could not allocate memory.\n",
argv[0]);
exit (EX_SOFTWARE);
}
break;
case 'h':
display_usage ();
exit (EX_OK);
default:
display_usage ();
exit (EX_USAGE);
}
}
}
/** /**
* change_user: * change_user:
* @program: The name of the program. Pass argv[0] here. * @program: The name of the program. Pass argv[0] here.
@ -218,16 +182,16 @@ process_cmdline (int argc, char **argv, struct config_s *conf)
static void static void
change_user (const char *program) change_user (const char *program)
{ {
if (config.group && strlen (config.group) > 0) { if (config->group && strlen (config->group) > 0) {
int gid = get_id (config.group); int gid = get_id (config->group);
if (gid < 0) { if (gid < 0) {
struct group *thisgroup = getgrnam (config.group); struct group *thisgroup = getgrnam (config->group);
if (!thisgroup) { if (!thisgroup) {
fprintf (stderr, fprintf (stderr,
"%s: Unable to find group \"%s\".\n", "%s: Unable to find group \"%s\".\n",
program, config.group); program, config->group);
exit (EX_NOUSER); exit (EX_NOUSER);
} }
@ -237,7 +201,7 @@ change_user (const char *program)
if (setgid (gid) < 0) { if (setgid (gid) < 0) {
fprintf (stderr, fprintf (stderr,
"%s: Unable to change to group \"%s\".\n", "%s: Unable to change to group \"%s\".\n",
program, config.group); program, config->group);
exit (EX_NOPERM); exit (EX_NOPERM);
} }
@ -252,19 +216,19 @@ change_user (const char *program)
#endif #endif
log_message (LOG_INFO, "Now running as group \"%s\".", log_message (LOG_INFO, "Now running as group \"%s\".",
config.group); config->group);
} }
if (config.user && strlen (config.user) > 0) { if (config->user && strlen (config->user) > 0) {
int uid = get_id (config.user); int uid = get_id (config->user);
if (uid < 0) { if (uid < 0) {
struct passwd *thisuser = getpwnam (config.user); struct passwd *thisuser = getpwnam (config->user);
if (!thisuser) { if (!thisuser) {
fprintf (stderr, fprintf (stderr,
"%s: Unable to find user \"%s\".\n", "%s: Unable to find user \"%s\".\n",
program, config.user); program, config->user);
exit (EX_NOUSER); exit (EX_NOUSER);
} }
@ -274,61 +238,56 @@ change_user (const char *program)
if (setuid (uid) < 0) { if (setuid (uid) < 0) {
fprintf (stderr, fprintf (stderr,
"%s: Unable to change to user \"%s\".\n", "%s: Unable to change to user \"%s\".\n",
program, config.user); program, config->user);
exit (EX_NOPERM); exit (EX_NOPERM);
} }
log_message (LOG_INFO, "Now running as user \"%s\".", log_message (LOG_INFO, "Now running as user \"%s\".",
config.user); config->user);
} }
} }
static void initialize_config_defaults (struct config_s *conf)
{
memset (conf, 0, sizeof(*conf));
conf->config_file = safestrdup (SYSCONFDIR "/tinyproxy.conf");
if (!conf->config_file) {
fprintf (stderr, PACKAGE ": Could not allocate memory.\n");
exit (EX_SOFTWARE);
}
conf->godaemon = TRUE;
/*
* Make sure the HTML error pages array is NULL to begin with.
* (FIXME: Should have a better API for all this)
*/
conf->errorpages = NULL;
conf->stathost = safestrdup (TINYPROXY_STATHOST);
conf->idletimeout = MAX_IDLE_TIME;
conf->logf_name = NULL;
conf->pidpath = NULL;
}
/** /**
* convenience wrapper around reload_config_file * convenience wrapper around reload_config_file
* that also re-initializes logging. * that also re-initializes logging.
*/ */
int reload_config (void) int reload_config (int reload_logging)
{ {
int ret; int ret;
struct config_s *c_next = get_next_config();
shutdown_logging (); if (reload_logging) shutdown_logging ();
ret = reload_config_file (config_file, c_next);
ret = reload_config_file (config_defaults.config_file, &config,
&config_defaults);
if (ret != 0) { if (ret != 0) {
goto done; goto done;
} }
ret = setup_logging (); config = c_next;
if (reload_logging) ret = setup_logging ();
done: done:
return ret; return ret;
} }
static void setup_sig(int sig, signal_func *sigh,
const char* signame, const char* argv0) {
if (set_signal_handler (sig, sigh) == SIG_ERR) {
fprintf (stderr, "%s: Could not set the \"%s\" signal.\n",
argv0, signame);
exit (EX_OSERR);
}
}
int int
main (int argc, char **argv) main (int argc, char **argv)
{ {
int opt, daemonized = TRUE;
srand(time(NULL)); /* for hashmap seeds */
/* Only allow u+rw bits. This may be required for some versions /* 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.
*/ */
@ -340,12 +299,33 @@ main (int argc, char **argv)
exit (EX_SOFTWARE); exit (EX_SOFTWARE);
} }
initialize_config_defaults (&config_defaults); config_file = SYSCONFDIR "/tinyproxy.conf";
process_cmdline (argc, argv, &config_defaults);
if (reload_config_file (config_defaults.config_file, while ((opt = getopt (argc, argv, "c:vdh")) != EOF) {
&config, switch (opt) {
&config_defaults)) { case 'v':
display_version ();
exit (EX_OK);
case 'd':
daemonized = FALSE;
break;
case 'c':
config_file = optarg;
break;
case 'h':
display_usage ();
exit (EX_OK);
default:
display_usage ();
exit (EX_USAGE);
}
}
if (reload_config(0)) {
exit (EX_SOFTWARE); exit (EX_SOFTWARE);
} }
@ -355,40 +335,36 @@ main (int argc, char **argv)
* 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 * HTTP/1.0 request. Also add the Content-Type header since it
* goes hand in hand with Content-Length. */ * goes hand in hand with Content-Length. */
if (is_anonymous_enabled ()) { if (is_anonymous_enabled (config)) {
anonymous_insert ("Content-Length"); anonymous_insert (config, "Content-Length");
anonymous_insert ("Content-Type"); anonymous_insert (config, "Content-Type");
} }
if (config.godaemon == TRUE) { if (daemonized == TRUE) {
if (!config.syslog && config.logf_name == NULL) if (!config->syslog && config->logf_name == NULL)
fprintf(stderr, "WARNING: logging deactivated " fprintf(stderr, "WARNING: logging deactivated "
"(can't log to stdout when daemonized)\n"); "(can't log to stdout when daemonized)\n");
makedaemon (); makedaemon ();
} }
if (set_signal_handler (SIGPIPE, SIG_IGN) == SIG_ERR) { setup_sig(SIGPIPE, SIG_IGN, "SIGPIPE", argv[0]);
fprintf (stderr, "%s: Could not set the \"SIGPIPE\" signal.\n",
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_sockets(config.listen_addrs, config.port) < 0) { if (child_listening_sockets(config->listen_addrs, config->port) < 0) {
fprintf (stderr, "%s: Could not create listening sockets.\n", fprintf (stderr, "%s: Could not create listening sockets.\n",
argv[0]); argv[0]);
exit (EX_OSERR); exit (EX_OSERR);
} }
/* Create pid file before we drop privileges */ /* Create pid file before we drop privileges */
if (config.pidpath) { if (config->pidpath) {
if (pidfile_create (config.pidpath) < 0) { if (pidfile_create (config->pidpath) < 0) {
fprintf (stderr, "%s: Could not create PID file.\n", fprintf (stderr, "%s: Could not create PID file.\n",
argv[0]); argv[0]);
exit (EX_OSERR); exit (EX_OSERR);
@ -407,33 +383,13 @@ main (int argc, char **argv)
exit (EX_SOFTWARE); exit (EX_SOFTWARE);
} }
if (child_pool_create () < 0) {
fprintf (stderr,
"%s: Could not create the pool of children.\n",
argv[0]);
exit (EX_SOFTWARE);
}
/* These signals are only for the parent process. */ /* 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) { setup_sig (SIGCHLD, takesig, "SIGCHLD", argv[0]);
fprintf (stderr, "%s: Could not set the \"SIGCHLD\" signal.\n", setup_sig (SIGTERM, takesig, "SIGTERM", argv[0]);
argv[0]); if (daemonized) setup_sig (SIGHUP, takesig, "SIGHUP", argv[0]);
exit (EX_OSERR); setup_sig (SIGUSR1, takesig, "SIGUSR1", argv[0]);
}
if (set_signal_handler (SIGTERM, takesig) == SIG_ERR) {
fprintf (stderr, "%s: Could not set the \"SIGTERM\" signal.\n",
argv[0]);
exit (EX_OSERR);
}
if (set_signal_handler (SIGHUP, takesig) == SIG_ERR) {
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.");
@ -446,14 +402,14 @@ main (int argc, char **argv)
child_close_sock (); child_close_sock ();
/* Remove the PID file */ /* Remove the PID file */
if (config.pidpath != NULL && unlink (config.pidpath) < 0) { if (config->pidpath != NULL && unlink (config->pidpath) < 0) {
log_message (LOG_WARNING, log_message (LOG_WARNING,
"Could not remove PID file \"%s\": %s.", "Could not remove PID file \"%s\": %s.",
config.pidpath, strerror (errno)); 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 */

View File

@ -29,9 +29,9 @@
#define MAX_IDLE_TIME (60 * 10) /* 10 minutes of no activity */ #define MAX_IDLE_TIME (60 * 10) /* 10 minutes of no activity */
/* 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 int reload_config (void); extern int reload_config (int reload_logging);
#endif /* __MAIN_H__ */ #endif /* __MAIN_H__ */

View File

@ -49,6 +49,7 @@
#include "connect-ports.h" #include "connect-ports.h"
#include "conf.h" #include "conf.h"
#include "basicauth.h" #include "basicauth.h"
#include "loop.h"
/* /*
* Maximum length of a HTTP line * Maximum length of a HTTP line
@ -60,8 +61,8 @@
* enabled. * enabled.
*/ */
#ifdef UPSTREAM_SUPPORT #ifdef UPSTREAM_SUPPORT
# define UPSTREAM_CONFIGURED() (config.upstream_list != NULL) # define UPSTREAM_CONFIGURED() (config->upstream_list != NULL)
# define UPSTREAM_HOST(host) upstream_get(host, config.upstream_list) # define UPSTREAM_HOST(host) upstream_get(host, config->upstream_list)
# define UPSTREAM_IS_HTTP(conn) (conn->upstream_proxy != NULL && conn->upstream_proxy->type == PT_HTTP) # define UPSTREAM_IS_HTTP(conn) (conn->upstream_proxy != NULL && conn->upstream_proxy->type == PT_HTTP)
#else #else
# define UPSTREAM_CONFIGURED() (0) # define UPSTREAM_CONFIGURED() (0)
@ -372,7 +373,7 @@ BAD_REQUEST_ERROR:
} }
#ifdef REVERSE_SUPPORT #ifdef REVERSE_SUPPORT
if (config.reversepath_list != NULL) { if (config->reversepath_list != NULL) {
/* /*
* Rewrite the URL based on the reverse path. After calling * Rewrite the URL based on the reverse path. After calling
* reverse_rewrite_url "url" can be freed since we either * reverse_rewrite_url "url" can be freed since we either
@ -386,7 +387,7 @@ BAD_REQUEST_ERROR:
if (reverse_url != NULL) { if (reverse_url != NULL) {
safefree (url); safefree (url);
url = reverse_url; url = reverse_url;
} else if (config.reverseonly) { } else if (config->reverseonly) {
log_message (LOG_ERR, log_message (LOG_ERR,
"Bad request, no mapping for '%s' found", "Bad request, no mapping for '%s' found",
url); url);
@ -419,7 +420,7 @@ BAD_REQUEST_ERROR:
/* Verify that the port in the CONNECT method is allowed */ /* Verify that the port in the CONNECT method is allowed */
if (!check_allowed_connect_ports (request->port, if (!check_allowed_connect_ports (request->port,
config.connect_ports)) config->connect_ports))
{ {
indicate_http_error (connptr, 403, "Access violation", indicate_http_error (connptr, 403, "Access violation",
"detail", "detail",
@ -436,7 +437,7 @@ BAD_REQUEST_ERROR:
} else { } else {
#ifdef TRANSPARENT_PROXY #ifdef TRANSPARENT_PROXY
if (!do_transparent_proxy if (!do_transparent_proxy
(connptr, hashofheaders, request, &config, &url)) { (connptr, hashofheaders, request, config, &url)) {
goto fail; goto fail;
} }
#else #else
@ -454,16 +455,16 @@ BAD_REQUEST_ERROR:
/* /*
* Filter restricted domains/urls * Filter restricted domains/urls
*/ */
if (config.filter) { if (config->filter) {
if (config.filter_url) if (config->filter_url)
ret = filter_url (url); ret = filter_run (url);
else else
ret = filter_domain (request->host); ret = filter_run (request->host);
if (ret) { if (ret) {
update_stats (STAT_DENIED); update_stats (STAT_DENIED);
if (config.filter_url) if (config->filter_url)
log_message (LOG_NOTICE, log_message (LOG_NOTICE,
"Proxying refused on filtered url \"%s\"", "Proxying refused on filtered url \"%s\"",
url); url);
@ -485,7 +486,7 @@ BAD_REQUEST_ERROR:
/* /*
* Check to see if they're requesting the stat host * Check to see if they're requesting the stat host
*/ */
if (config.stathost && strcmp (config.stathost, request->host) == 0) { if (config->stathost && strcmp (config->stathost, request->host) == 0) {
log_message (LOG_NOTICE, "Request for the stathost."); log_message (LOG_NOTICE, "Request for the stathost.");
connptr->show_stats = TRUE; connptr->show_stats = TRUE;
goto fail; goto fail;
@ -803,13 +804,13 @@ write_via_header (int fd, hashmap_t hashofheaders,
char *data; char *data;
int ret; int ret;
if (config.disable_viaheader) { if (config->disable_viaheader) {
ret = 0; ret = 0;
goto done; goto done;
} }
if (config.via_proxy_name) { if (config->via_proxy_name) {
strlcpy (hostname, config.via_proxy_name, sizeof (hostname)); strlcpy (hostname, config->via_proxy_name, sizeof (hostname));
} else if (gethostname (hostname, sizeof (hostname)) < 0) { } else if (gethostname (hostname, sizeof (hostname)) < 0) {
strlcpy (hostname, "unknown", 512); strlcpy (hostname, "unknown", 512);
} }
@ -918,8 +919,8 @@ process_client_headers (struct conn_s *connptr, hashmap_t hashofheaders)
hashmap_return_entry (hashofheaders, hashmap_return_entry (hashofheaders,
iter, &data, (void **) &header); iter, &data, (void **) &header);
if (!is_anonymous_enabled () if (!is_anonymous_enabled (config)
|| anonymous_search (data) > 0) { || anonymous_search (config, data) > 0) {
ret = ret =
write_message (connptr->server_fd, write_message (connptr->server_fd,
"%s: %s\r\n", data, header); "%s: %s\r\n", data, header);
@ -937,7 +938,7 @@ process_client_headers (struct conn_s *connptr, hashmap_t hashofheaders)
} }
} }
#if defined(XTINYPROXY_ENABLE) #if defined(XTINYPROXY_ENABLE)
if (config.add_xtinyproxy) if (config->add_xtinyproxy)
add_xtinyproxy_header (connptr); add_xtinyproxy_header (connptr);
#endif #endif
@ -980,7 +981,7 @@ static int process_server_headers (struct conn_s *connptr)
int ret; int ret;
#ifdef REVERSE_SUPPORT #ifdef REVERSE_SUPPORT
struct reversepath *reverse = config.reversepath_list; struct reversepath *reverse = config->reversepath_list;
#endif #endif
/* Get the response line from the remote server. */ /* Get the response line from the remote server. */
@ -1072,7 +1073,7 @@ retry:
#ifdef REVERSE_SUPPORT #ifdef REVERSE_SUPPORT
/* Write tracking cookie for the magical reverse proxy path hack */ /* Write tracking cookie for the magical reverse proxy path hack */
if (config.reversemagic && connptr->reversepath) { if (config->reversemagic && connptr->reversepath) {
ret = write_message (connptr->client_fd, ret = write_message (connptr->client_fd,
"Set-Cookie: " REVERSE_COOKIE "Set-Cookie: " REVERSE_COOKIE
"=%s; path=/\r\n", connptr->reversepath); "=%s; path=/\r\n", connptr->reversepath);
@ -1081,7 +1082,7 @@ retry:
} }
/* Rewrite the HTTP redirect if needed */ /* Rewrite the HTTP redirect if needed */
if (config.reversebaseurl && if (config->reversebaseurl &&
hashmap_entry_by_key (hashofheaders, "location", hashmap_entry_by_key (hashofheaders, "location",
(void **) &header) > 0) { (void **) &header) > 0) {
@ -1099,14 +1100,14 @@ retry:
ret = ret =
write_message (connptr->client_fd, write_message (connptr->client_fd,
"Location: %s%s%s\r\n", "Location: %s%s%s\r\n",
config.reversebaseurl, config->reversebaseurl,
(reverse->path + 1), (header + len)); (reverse->path + 1), (header + len));
if (ret < 0) if (ret < 0)
goto ERROR_EXIT; goto ERROR_EXIT;
log_message (LOG_INFO, log_message (LOG_INFO,
"Rewriting HTTP redirect: %s -> %s%s%s", "Rewriting HTTP redirect: %s -> %s%s%s",
header, config.reversebaseurl, header, config->reversebaseurl,
(reverse->path + 1), (header + len)); (reverse->path + 1), (header + len));
hashmap_remove (hashofheaders, "location"); hashmap_remove (hashofheaders, "location");
} }
@ -1180,7 +1181,7 @@ static void relay_connection (struct conn_s *connptr)
FD_ZERO (&wset); FD_ZERO (&wset);
tv.tv_sec = tv.tv_sec =
config.idletimeout - difftime (time (NULL), last_access); config->idletimeout - difftime (time (NULL), last_access);
tv.tv_usec = 0; tv.tv_usec = 0;
if (buffer_size (connptr->sbuffer) > 0) if (buffer_size (connptr->sbuffer) > 0)
@ -1196,10 +1197,10 @@ static void relay_connection (struct conn_s *connptr)
if (ret == 0) { if (ret == 0) {
tdiff = difftime (time (NULL), last_access); tdiff = difftime (time (NULL), last_access);
if (tdiff > config.idletimeout) { if (tdiff > config->idletimeout) {
log_message (LOG_INFO, log_message (LOG_INFO,
"Idle Timeout (after select) as %g > %u.", "Idle Timeout (after select) as %g > %u.",
tdiff, config.idletimeout); tdiff, config->idletimeout);
return; return;
} else { } else {
continue; continue;
@ -1533,35 +1534,56 @@ get_request_entity(struct conn_s *connptr)
* tinyproxy code, which was confusing, redundant. Hail progress. * tinyproxy code, which was confusing, redundant. Hail progress.
* - rjkaes * - rjkaes
*/ */
void handle_connection (int fd) void handle_connection (int fd, union sockaddr_union* addr)
{ {
ssize_t i; ssize_t i;
struct conn_s *connptr; struct conn_s *connptr;
struct request_s *request = NULL; struct request_s *request = NULL;
struct timeval tv;
hashmap_t hashofheaders = NULL; hashmap_t hashofheaders = NULL;
char sock_ipaddr[IP_LENGTH]; char sock_ipaddr[IP_LENGTH];
char peer_ipaddr[IP_LENGTH]; char peer_ipaddr[IP_LENGTH];
char peer_string[HOSTNAME_LENGTH];
getpeer_information (fd, peer_ipaddr, peer_string); getpeer_information (addr, peer_ipaddr, sizeof(peer_ipaddr));
if (config.bindsame) if (config->bindsame)
getsock_ip (fd, sock_ipaddr); getsock_ip (fd, sock_ipaddr);
log_message (LOG_CONN, config.bindsame ? log_message (LOG_CONN, config->bindsame ?
"Connect (file descriptor %d): %s [%s] at [%s]" : "Connect (file descriptor %d): %s at [%s]" :
"Connect (file descriptor %d): %s [%s]", "Connect (file descriptor %d): %s",
fd, peer_string, peer_ipaddr, sock_ipaddr); fd, peer_ipaddr, sock_ipaddr);
connptr = initialize_conn (fd, peer_ipaddr, peer_string, connptr = initialize_conn (fd, peer_ipaddr,
config.bindsame ? sock_ipaddr : NULL); config->bindsame ? sock_ipaddr : NULL);
if (!connptr) { if (!connptr) {
close (fd); close (fd);
return; return;
} }
if (check_acl (peer_ipaddr, peer_string, config.access_list) <= 0) { tv.tv_usec = 0;
tv.tv_sec = config->idletimeout;
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (void*) &tv, sizeof(tv));
tv.tv_usec = 0;
tv.tv_sec = config->idletimeout;
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (void*) &tv, sizeof(tv));
if (connection_loops (addr)) {
log_message (LOG_CONN,
"Prevented endless loop (file descriptor %d): %s",
fd, peer_ipaddr);
indicate_http_error(connptr, 400, "Bad Request",
"detail",
"You tried to connect to the "
"machine the proxy is running on",
NULL);
goto fail;
}
if (check_acl (peer_ipaddr, addr, config->access_list) <= 0) {
update_stats (STAT_DENIED); update_stats (STAT_DENIED);
indicate_http_error (connptr, 403, "Access denied", indicate_http_error (connptr, 403, "Access denied",
"detail", "detail",
@ -1608,14 +1630,25 @@ void handle_connection (int fd)
goto fail; goto fail;
} }
if (config.basicauth_list != NULL) { if (config->basicauth_list != NULL) {
ssize_t len; ssize_t len;
char *authstring; char *authstring;
int failure = 1; int failure = 1, stathost_connect = 0;
len = hashmap_entry_by_key (hashofheaders, "proxy-authorization", len = hashmap_entry_by_key (hashofheaders, "proxy-authorization",
(void **) &authstring); (void **) &authstring);
if (len == 0 && config->stathost) {
len = hashmap_entry_by_key (hashofheaders, "host",
(void **) &authstring);
if (len && !strncmp(authstring, config->stathost, strlen(config->stathost))) {
len = hashmap_entry_by_key (hashofheaders, "authorization",
(void **) &authstring);
stathost_connect = 1;
} else len = 0;
}
if (len == 0) { if (len == 0) {
if (stathost_connect) goto e401;
update_stats (STAT_DENIED); update_stats (STAT_DENIED);
indicate_http_error (connptr, 407, "Proxy Authentication Required", indicate_http_error (connptr, 407, "Proxy Authentication Required",
"detail", "detail",
@ -1626,9 +1659,10 @@ void handle_connection (int fd)
if ( /* currently only "basic" auth supported */ if ( /* currently only "basic" auth supported */
(strncmp(authstring, "Basic ", 6) == 0 || (strncmp(authstring, "Basic ", 6) == 0 ||
strncmp(authstring, "basic ", 6) == 0) && strncmp(authstring, "basic ", 6) == 0) &&
basicauth_check (config.basicauth_list, authstring + 6) == 1) basicauth_check (config->basicauth_list, authstring + 6) == 1)
failure = 0; failure = 0;
if(failure) { if(failure) {
e401:
update_stats (STAT_DENIED); update_stats (STAT_DENIED);
indicate_http_error (connptr, 401, "Unauthorized", indicate_http_error (connptr, 401, "Unauthorized",
"detail", "detail",
@ -1644,9 +1678,9 @@ void handle_connection (int fd)
* Add any user-specified headers (AddHeader directive) to the * Add any user-specified headers (AddHeader directive) to the
* outgoing HTTP request. * outgoing HTTP request.
*/ */
for (i = 0; i < vector_length (config.add_headers); i++) { for (i = 0; i < vector_length (config->add_headers); i++) {
http_header_t *header = (http_header_t *) http_header_t *header = (http_header_t *)
vector_getentry (config.add_headers, i, NULL); vector_getentry (config->add_headers, i, NULL);
hashmap_insert (hashofheaders, hashmap_insert (hashofheaders,
header->name, header->name,

View File

@ -23,6 +23,7 @@
#define _TINYPROXY_REQS_H_ #define _TINYPROXY_REQS_H_
#include "common.h" #include "common.h"
#include "sock.h"
/* /*
* Port constants for HTTP (80) and SSL (443) * Port constants for HTTP (80) and SSL (443)
@ -43,6 +44,6 @@ struct request_s {
char *path; char *path;
}; };
extern void handle_connection (int fd); extern void handle_connection (int fd, union sockaddr_union* addr);
#endif #endif

View File

@ -122,14 +122,14 @@ char *reverse_rewrite_url (struct conn_s *connptr, hashmap_t hashofheaders,
/* 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 */ /* First try locating the reverse mapping by request url */
reverse = reversepath_get (url, config.reversepath_list); reverse = reversepath_get (url, config->reversepath_list);
if (reverse) { if (reverse) {
rewrite_url = (char *) rewrite_url = (char *)
safemalloc (strlen (url) + strlen (reverse->url) + safemalloc (strlen (url) + strlen (reverse->url) +
1); 1);
strcpy (rewrite_url, reverse->url); strcpy (rewrite_url, reverse->url);
strcat (rewrite_url, url + strlen (reverse->path)); strcat (rewrite_url, url + strlen (reverse->path));
} else if (config.reversemagic } else if (config->reversemagic
&& hashmap_entry_by_key (hashofheaders, && hashmap_entry_by_key (hashofheaders,
"cookie", "cookie",
(void **) &cookie) > 0) { (void **) &cookie) > 0) {
@ -139,7 +139,7 @@ char *reverse_rewrite_url (struct conn_s *connptr, hashmap_t hashofheaders,
&& (reverse = && (reverse =
reversepath_get (cookieval + reversepath_get (cookieval +
strlen (REVERSE_COOKIE) + 1, strlen (REVERSE_COOKIE) + 1,
config.reversepath_list))) config->reversepath_list)))
{ {
rewrite_url = (char *) safemalloc rewrite_url = (char *) safemalloc
@ -163,7 +163,7 @@ char *reverse_rewrite_url (struct conn_s *connptr, hashmap_t hashofheaders,
log_message (LOG_CONN, "Rewriting URL: %s -> %s", url, rewrite_url); 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 && reverse) if (config->reversemagic && reverse)
connptr->reversepath = safestrdup (reverse->path); connptr->reversepath = safestrdup (reverse->path);
return rewrite_url; return rewrite_url;

80
src/sblist.c Normal file
View File

@ -0,0 +1,80 @@
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200809L
#include "sblist.h"
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#define MY_PAGE_SIZE 4096
sblist* sblist_new(size_t itemsize, size_t blockitems) {
sblist* ret = (sblist*) malloc(sizeof(sblist));
sblist_init(ret, itemsize, blockitems);
return ret;
}
static void sblist_clear(sblist* l) {
l->items = NULL;
l->capa = 0;
l->count = 0;
}
void sblist_init(sblist* l, size_t itemsize, size_t blockitems) {
if(l) {
l->blockitems = blockitems ? blockitems : MY_PAGE_SIZE / itemsize;
l->itemsize = itemsize;
sblist_clear(l);
}
}
void sblist_free_items(sblist* l) {
if(l) {
if(l->items) free(l->items);
sblist_clear(l);
}
}
void sblist_free(sblist* l) {
if(l) {
sblist_free_items(l);
free(l);
}
}
char* sblist_item_from_index(sblist* l, size_t idx) {
return l->items + (idx * l->itemsize);
}
void* sblist_get(sblist* l, size_t item) {
if(item < l->count) return (void*) sblist_item_from_index(l, item);
return NULL;
}
int sblist_set(sblist* l, void* item, size_t pos) {
if(pos >= l->count) return 0;
memcpy(sblist_item_from_index(l, pos), item, l->itemsize);
return 1;
}
int sblist_grow_if_needed(sblist* l) {
char* temp;
if(l->count == l->capa) {
temp = realloc(l->items, (l->capa + l->blockitems) * l->itemsize);
if(!temp) return 0;
l->capa += l->blockitems;
l->items = temp;
}
return 1;
}
int sblist_add(sblist* l, void* item) {
if(!sblist_grow_if_needed(l)) return 0;
l->count++;
return sblist_set(l, item, l->count - 1);
}
void sblist_delete(sblist* l, size_t item) {
if (l->count && item < l->count) {
memmove(sblist_item_from_index(l, item), sblist_item_from_index(l, item + 1), (sblist_getsize(l) - (item + 1)) * l->itemsize);
l->count--;
}
}

92
src/sblist.h Normal file
View File

@ -0,0 +1,92 @@
#ifndef SBLIST_H
#define SBLIST_H
/* this file is part of libulz, as of commit 8ab361a27743aaf025323ee43b8b8876dc054fdd
modified for direct inclusion in tinyproxy, and for this purpose released under
the license of tinyproxy. */
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
/*
* simple buffer list.
*
* this thing here is basically a generic dynamic array
* will realloc after every blockitems inserts
* can store items of any size.
*
* so think of it as a by-value list, as opposed to a typical by-ref list.
* you typically use it by having some struct on the stack, and pass a pointer
* to sblist_add, which will copy the contents into its internal memory.
*
*/
typedef struct {
size_t itemsize;
size_t blockitems;
size_t count;
size_t capa;
char* items;
} sblist;
#define sblist_getsize(X) ((X)->count)
#define sblist_get_count(X) ((X)->count)
#define sblist_empty(X) ((X)->count == 0)
/* for dynamic style */
sblist* sblist_new(size_t itemsize, size_t blockitems);
void sblist_free(sblist* l);
/*for static style*/
void sblist_init(sblist* l, size_t itemsize, size_t blockitems);
void sblist_free_items(sblist* l);
/* accessors */
void* sblist_get(sblist* l, size_t item);
/* returns 1 on success, 0 on OOM */
int sblist_add(sblist* l, void* item);
int sblist_set(sblist* l, void* item, size_t pos);
void sblist_delete(sblist* l, size_t item);
char* sblist_item_from_index(sblist* l, size_t idx);
int sblist_grow_if_needed(sblist* l);
int sblist_insert(sblist* l, void* item, size_t pos);
/* same as sblist_add, but returns list index of new item, or -1 */
size_t sblist_addi(sblist* l, void* item);
void sblist_sort(sblist *l, int (*compar)(const void *, const void *));
/* insert element into presorted list, returns listindex of new entry or -1*/
size_t sblist_insert_sorted(sblist* l, void* o, int (*compar)(const void *, const void *));
#ifndef __COUNTER__
#define __COUNTER__ __LINE__
#endif
#define __sblist_concat_impl( x, y ) x##y
#define __sblist_macro_concat( x, y ) __sblist_concat_impl( x, y )
#define __sblist_iterator_name __sblist_macro_concat(sblist_iterator, __COUNTER__)
/* use with custom iterator variable */
#define sblist_iter_counter(LIST, ITER, PTR) \
for(size_t ITER = 0; (PTR = sblist_get(LIST, ITER)), ITER < sblist_getsize(LIST); ITER++)
/* use with custom iterator variable, which is predeclared */
#define sblist_iter_counter2(LIST, ITER, PTR) \
for(ITER = 0; (PTR = sblist_get(LIST, ITER)), ITER < sblist_getsize(LIST); ITER++)
/* use with custom iterator variable, which is predeclared and signed */
/* useful for a loop which can delete items from the list, and then decrease the iterator var. */
#define sblist_iter_counter2s(LIST, ITER, PTR) \
for(ITER = 0; (PTR = sblist_get(LIST, ITER)), ITER < (ssize_t) sblist_getsize(LIST); ITER++)
/* uses "magic" iterator variable */
#define sblist_iter(LIST, PTR) sblist_iter_counter(LIST, __sblist_iterator_name, PTR)
#ifdef __cplusplus
}
#endif
#endif

View File

@ -33,6 +33,18 @@
#include "sock.h" #include "sock.h"
#include "text.h" #include "text.h"
#include "conf.h" #include "conf.h"
#include "loop.h"
/*
* Return a human readable error for getaddrinfo() and getnameinfo().
*/
static const char * get_gai_error (int n)
{
if (n == EAI_SYSTEM)
return strerror (errno);
else
return gai_strerror (n);
}
/* /*
* Bind the given socket to the supplied address. The socket is * Bind the given socket to the supplied address. The socket is
@ -43,6 +55,7 @@ static int
bind_socket (int sockfd, const char *addr, int family) bind_socket (int sockfd, const char *addr, int family)
{ {
struct addrinfo hints, *res, *ressave; struct addrinfo hints, *res, *ressave;
int n;
assert (sockfd >= 0); assert (sockfd >= 0);
assert (addr != NULL && strlen (addr) != 0); assert (addr != NULL && strlen (addr) != 0);
@ -51,9 +64,13 @@ bind_socket (int sockfd, const char *addr, int family)
hints.ai_family = family; hints.ai_family = family;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
/* The local port it not important */ /* The local port is not important */
if (getaddrinfo (addr, NULL, &hints, &res) != 0) n = getaddrinfo (addr, NULL, &hints, &res);
if (n != 0) {
log_message (LOG_INFO,
"bind_socket: getaddrinfo failed for %s: ", addr, get_gai_error (n));
return -1; return -1;
}
ressave = res; ressave = res;
@ -96,7 +113,7 @@ int opensock (const char *host, int port, const char *bind_to)
n = getaddrinfo (host, portstr, &hints, &res); n = getaddrinfo (host, portstr, &hints, &res);
if (n != 0) { if (n != 0) {
log_message (LOG_ERR, log_message (LOG_ERR,
"opensock: Could not retrieve info for %s", host); "opensock: Could not retrieve address info for %s:%d: %s", host, port, get_gai_error (n));
return -1; return -1;
} }
@ -117,16 +134,25 @@ int opensock (const char *host, int port, const char *bind_to)
close (sockfd); close (sockfd);
continue; /* can't bind, so try again */ continue; /* can't bind, so try again */
} }
} else if (config.bind_address) { } else if (config->bind_address) {
if (bind_socket (sockfd, config.bind_address, if (bind_socket (sockfd, config->bind_address,
res->ai_family) < 0) { res->ai_family) < 0) {
close (sockfd); close (sockfd);
continue; /* can't bind, so try again */ continue; /* can't bind, so try again */
} }
} }
if (connect (sockfd, res->ai_addr, res->ai_addrlen) == 0) if (connect (sockfd, res->ai_addr, res->ai_addrlen) == 0) {
union sockaddr_union *p = (void*) res->ai_addr, u;
int af = res->ai_addr->sa_family;
unsigned dport = ntohs(af == AF_INET ? p->v4.sin_port : p->v6.sin6_port);
socklen_t slen = sizeof u;
if (dport == config->port) {
getsockname(sockfd, (void*)&u, &slen);
loop_records_add(&u);
}
break; /* success */ break; /* success */
}
close (sockfd); close (sockfd);
} while ((res = res->ai_next) != NULL); } while ((res = res->ai_next) != NULL);
@ -134,8 +160,9 @@ int opensock (const char *host, int port, const char *bind_to)
freeaddrinfo (ressave); freeaddrinfo (ressave);
if (res == NULL) { if (res == NULL) {
log_message (LOG_ERR, log_message (LOG_ERR,
"opensock: Could not establish a connection to %s", "opensock: Could not establish a connection to %s:%d",
host); host,
port);
return -1; return -1;
} }
@ -186,8 +213,7 @@ static int listen_on_one_socket(struct addrinfo *ad)
ret = getnameinfo(ad->ai_addr, ad->ai_addrlen, ret = getnameinfo(ad->ai_addr, ad->ai_addrlen,
numerichost, NI_MAXHOST, NULL, 0, flags); numerichost, NI_MAXHOST, NULL, 0, flags);
if (ret != 0) { if (ret != 0) {
log_message(LOG_ERR, "error calling getnameinfo: %s", log_message(LOG_ERR, "getnameinfo failed: %s", get_gai_error (ret));
gai_strerror(errno));
return -1; return -1;
} }
@ -256,6 +282,7 @@ int listen_sock (const char *addr, uint16_t port, vector_t listen_fds)
struct addrinfo hints, *result, *rp; struct addrinfo hints, *result, *rp;
char portstr[6]; char portstr[6];
int ret = -1; int ret = -1;
int n;
assert (port > 0); assert (port > 0);
assert (listen_fds != NULL); assert (listen_fds != NULL);
@ -270,10 +297,13 @@ int listen_sock (const char *addr, uint16_t port, vector_t listen_fds)
snprintf (portstr, sizeof (portstr), "%d", port); snprintf (portstr, sizeof (portstr), "%d", port);
if (getaddrinfo (addr, portstr, &hints, &result) != 0) { n = getaddrinfo (addr, portstr, &hints, &result);
if (n != 0) {
log_message (LOG_ERR, log_message (LOG_ERR,
"Unable to getaddrinfo() because of %s", "Unable to getaddrinfo() for %s:%d because of %s",
strerror (errno)); addr,
port,
get_gai_error (n));
return -1; return -1;
} }
@ -334,27 +364,9 @@ int getsock_ip (int fd, char *ipaddr)
/* /*
* Return the peer's socket information. * Return the peer's socket information.
*/ */
int getpeer_information (int fd, char *ipaddr, char *string_addr) void getpeer_information (union sockaddr_union* addr, char *ipaddr, size_t ipaddr_len)
{ {
struct sockaddr_storage sa; int af = addr->v4.sin_family;
socklen_t salen = sizeof sa; void *ipdata = af == AF_INET ? (void*)&addr->v4.sin_addr : (void*)&addr->v6.sin6_addr;
inet_ntop(af, ipdata, ipaddr, ipaddr_len);
assert (fd >= 0);
assert (ipaddr != NULL);
assert (string_addr != NULL);
/* Set the strings to default values */
ipaddr[0] = '\0';
strlcpy (string_addr, "[unknown]", HOSTNAME_LENGTH);
/* Look up the IP address */
if (getpeername (fd, (struct sockaddr *) &sa, &salen) != 0)
return -1;
if (get_ip_string ((struct sockaddr *) &sa, ipaddr, IP_LENGTH) == NULL)
return -1;
/* Get the full host name */
return getnameinfo ((struct sockaddr *) &sa, salen,
string_addr, HOSTNAME_LENGTH, NULL, 0, 0);
} }

View File

@ -28,8 +28,14 @@
#define MAXLINE (1024 * 4) #define MAXLINE (1024 * 4)
#include "common.h"
#include "vector.h" #include "vector.h"
union sockaddr_union {
struct sockaddr_in v4;
struct sockaddr_in6 v6;
};
extern int opensock (const char *host, int port, const char *bind_to); extern int opensock (const char *host, int port, const char *bind_to);
extern int listen_sock (const char *addr, uint16_t port, vector_t listen_fds); extern int listen_sock (const char *addr, uint16_t port, vector_t listen_fds);
@ -37,6 +43,6 @@ extern int socket_nonblocking (int sock);
extern int socket_blocking (int sock); extern int 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 void getpeer_information (union sockaddr_union *addr, char *ipaddr, size_t ipaddr_len);
#endif #endif

View File

@ -33,6 +33,7 @@
#include "stats.h" #include "stats.h"
#include "utils.h" #include "utils.h"
#include "conf.h" #include "conf.h"
#include <pthread.h>
struct stat_s { struct stat_s {
unsigned long int num_reqs; unsigned long int num_reqs;
@ -43,14 +44,16 @@ struct stat_s {
}; };
static struct stat_s *stats; static struct stat_s *stats;
static pthread_mutex_t stats_update_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t stats_file_lock = PTHREAD_MUTEX_INITIALIZER;
/* /*
* Initialize the statistics information to zero. * Initialize the statistics information to zero.
*/ */
void init_stats (void) void init_stats (void)
{ {
stats = (struct stat_s *) malloc_shared_memory (sizeof (struct stat_s)); stats = (struct stat_s *) safemalloc (sizeof (struct stat_s));
if (stats == MAP_FAILED) if (!stats)
return; return;
memset (stats, 0, sizeof (struct stat_s)); memset (stats, 0, sizeof (struct stat_s));
@ -72,10 +75,15 @@ showstats (struct conn_s *connptr)
snprintf (denied, sizeof (denied), "%lu", stats->num_denied); snprintf (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")))) { pthread_mutex_lock(&stats_file_lock);
if (!config->statpage || (!(statfile = fopen (config->statpage, "r")))) {
message_buffer = (char *) safemalloc (MAXBUFFSIZE); message_buffer = (char *) safemalloc (MAXBUFFSIZE);
if (!message_buffer) if (!message_buffer) {
err_minus_one:
pthread_mutex_unlock(&stats_file_lock);
return -1; return -1;
}
snprintf snprintf
(message_buffer, MAXBUFFSIZE, (message_buffer, MAXBUFFSIZE,
@ -105,13 +113,13 @@ showstats (struct conn_s *connptr)
if (send_http_message (connptr, 200, "OK", if (send_http_message (connptr, 200, "OK",
message_buffer) < 0) { message_buffer) < 0) {
safefree (message_buffer); safefree (message_buffer);
return -1; goto err_minus_one;
} }
safefree (message_buffer); safefree (message_buffer);
pthread_mutex_unlock(&stats_file_lock);
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);
@ -121,6 +129,7 @@ showstats (struct conn_s *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);
pthread_mutex_unlock(&stats_file_lock);
return 0; return 0;
} }
@ -131,6 +140,9 @@ showstats (struct conn_s *connptr)
*/ */
int update_stats (status_t update_level) int update_stats (status_t update_level)
{ {
int ret = 0;
pthread_mutex_lock(&stats_update_lock);
switch (update_level) { switch (update_level) {
case STAT_BADCONN: case STAT_BADCONN:
++stats->num_badcons; ++stats->num_badcons;
@ -149,8 +161,9 @@ int update_stats (status_t update_level)
++stats->num_denied; ++stats->num_denied;
break; break;
default: default:
return -1; ret = -1;
} }
pthread_mutex_unlock(&stats_update_lock);
return 0; return ret;
} }

View File

@ -64,11 +64,16 @@ do_transparent_proxy (struct conn_s *connptr, hashmap_t hashofheaders,
length = hashmap_entry_by_key (hashofheaders, "host", (void **) &data); length = hashmap_entry_by_key (hashofheaders, "host", (void **) &data);
if (length <= 0) { if (length <= 0) {
struct sockaddr_in dest_addr; union sockaddr_union dest_addr;
const void *dest_inaddr;
char namebuf[INET6_ADDRSTRLEN+1];
int af;
length = sizeof(dest_addr);
if (getsockname if (getsockname
(connptr->client_fd, (struct sockaddr *) &dest_addr, (connptr->client_fd, (void *) &dest_addr,
&length) < 0) { &length) < 0 || length > sizeof(dest_addr)) {
addr_err:;
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);
@ -78,10 +83,16 @@ do_transparent_proxy (struct conn_s *connptr, hashmap_t hashofheaders,
return 0; return 0;
} }
request->host = (char *) safemalloc (17); af = length == sizeof(dest_addr.v4) ? AF_INET : AF_INET6;
strlcpy (request->host, inet_ntoa (dest_addr.sin_addr), 17); if (af == AF_INET) dest_inaddr = &dest_addr.v4.sin_addr;
else dest_inaddr = &dest_addr.v6.sin6_addr;
request->port = ntohs (dest_addr.sin_port); if (!inet_ntop(af, dest_inaddr, namebuf, sizeof namebuf))
goto addr_err;
request->host = safestrdup (namebuf);
request->port = ntohs (af == AF_INET ? dest_addr.v4.sin_port
: dest_addr.v6.sin6_port);
request->path = (char *) safemalloc (ulen + 1); request->path = (char *) safemalloc (ulen + 1);
strlcpy (request->path, *url, ulen + 1); strlcpy (request->path, *url, ulen + 1);

View File

@ -123,7 +123,7 @@ static struct upstream *upstream_build (const char *host, int port, const char *
log_message (LOG_INFO, "Added no-upstream for %s", domain); log_message (LOG_INFO, "Added no-upstream for %s", domain);
} else { } else {
if (!host || host[0] == '\0' || port < 1 || !domain if (!host || host[0] == '\0' || !domain
|| domain[0] == '\0') { || domain[0] == '\0') {
log_message (LOG_WARNING, log_message (LOG_WARNING,
"Nonsense upstream rule: invalid parameters"); "Nonsense upstream rule: invalid parameters");
@ -234,7 +234,7 @@ struct upstream *upstream_get (char *host, struct upstream *up)
up = up->next; up = up->next;
} }
if (up && (!up->host || !up->port)) if (up && (!up->host))
up = NULL; up = NULL;
if (up) if (up)

View File

@ -30,6 +30,7 @@ TINYPROXY_USER=$(id -un)
TINYPROXY_PID_DIR=$TESTENV_DIR/var/run/tinyproxy TINYPROXY_PID_DIR=$TESTENV_DIR/var/run/tinyproxy
TINYPROXY_PID_FILE=$TINYPROXY_PID_DIR/tinyproxy.pid TINYPROXY_PID_FILE=$TINYPROXY_PID_DIR/tinyproxy.pid
TINYPROXY_LOG_DIR=$LOG_DIR/tinyproxy TINYPROXY_LOG_DIR=$LOG_DIR/tinyproxy
TINYPROXY_LOG_FILE=$TINYPROXY_LOG_DIR/tinyproxy.log
TINYPROXY_DATA_DIR=$TESTENV_DIR/usr/share/tinyproxy TINYPROXY_DATA_DIR=$TESTENV_DIR/usr/share/tinyproxy
TINYPROXY_CONF_DIR=$TESTENV_DIR/etc/tinyproxy TINYPROXY_CONF_DIR=$TESTENV_DIR/etc/tinyproxy
TINYPROXY_CONF_FILE=$TINYPROXY_CONF_DIR/tinyproxy.conf TINYPROXY_CONF_FILE=$TINYPROXY_CONF_DIR/tinyproxy.conf
@ -80,14 +81,10 @@ Timeout 600
StatHost "$TINYPROXY_STATHOST_IP" StatHost "$TINYPROXY_STATHOST_IP"
DefaultErrorFile "$TINYPROXY_DATA_DIR/debug.html" DefaultErrorFile "$TINYPROXY_DATA_DIR/debug.html"
StatFile "$TINYPROXY_DATA_DIR/stats.html" StatFile "$TINYPROXY_DATA_DIR/stats.html"
Logfile "$TINYPROXY_LOG_DIR/tinyproxy.log" Logfile "$TINYPROXY_LOG_FILE"
PidFile "$TINYPROXY_PID_FILE" PidFile "$TINYPROXY_PID_FILE"
LogLevel Info LogLevel Info
MaxClients 100 MaxClients 100
MinSpareServers 5
MaxSpareServers 20
StartServers 10
MaxRequestsPerChild 0
Allow 127.0.0.0/8 Allow 127.0.0.0/8
ViaProxyName "tinyproxy" ViaProxyName "tinyproxy"
#DisableViaHeader Yes #DisableViaHeader Yes
@ -109,11 +106,17 @@ start_tinyproxy() {
stop_tinyproxy() { stop_tinyproxy() {
echo -n "killing tinyproxy..." echo -n "killing tinyproxy..."
kill $(cat $TINYPROXY_PID_FILE) pid=$(cat $TINYPROXY_PID_FILE)
kill $pid
if test "x$?" = "x0" ; then if test "x$?" = "x0" ; then
echo " ok" echo " ok"
else else
echo " error" echo " error killing pid $pid"
ps aux | grep tinyproxy
echo "### printing logfile"
cat $TINYPROXY_LOG_FILE
echo "### printing stderr logfile"
cat $TINYPROXY_STDERR_LOG
fi fi
} }