Compare commits

...

424 Commits
1.8 ... master

Author SHA1 Message Date
rofl0r
cea0ebe657 tinyproxy.conf.5: explain what a site_spec looks like 2025-01-06 19:25:57 +00:00
rofl0r
05f6e4e000 basic auth: fix error status 401 vs 407
if tinyproxy serves as a HTTP server (i.e. when serving stats),
use error code 401, else error code 407.

fixes #532
2024-11-04 12:57:04 +01:00
rofl0r
c04ba4711a reqs: don't compile upstream code if feature disabled
fixes warning about implicit function declaration which is by default
treated as an error starting with GCC14.

closes #560
2024-10-19 09:26:37 +00:00
Robert Grumann
73da8a35a3
conf: add BasicAuthRealm feature (#547)
makes BasicAuth realm string editable in config file.

closes #235
2024-07-14 13:38:25 +02:00
James McKinney
d652ed8538
Omit the version number from headers and HTML responses (#543)
Omit the version number from headers, HTML responses, and templates
2024-06-20 10:51:29 +02:00
rofl0r
72b93f6d4b CI: update release workflow to non-deprecated actions
github continues to deprecate actions and idioms in their CI system.
hopefully these changes will last for a while and maintaining a simple
CI task doesn't turn into a neverending story.
2024-06-16 13:12:25 +00:00
Mohamed Akram
942d0c6b03 Use appropriate installation path variables 2024-06-02 20:42:49 +02:00
rofl0r
dd49e975a0 release 1.11.2 2024-05-08 18:22:55 +00:00
rofl0r
e69788b761
Add SECURITY.md
given the catastrophic way TALOS Intelligence "communicated" with upstream
(i.e. by probably sending a single mail to an unused email address),
it's probably best to explicitly document how to approach upstream
when a security issue is discovered.
2024-05-05 20:56:17 +02:00
rofl0r
12a8484265 fix potential UAF in header handling (CVE-2023-49606)
https://talosintelligence.com/vulnerability_reports/TALOS-2023-1889

this bug was brought to my attention today by the debian tinyproxy
package maintainer. the above link states that the issue was known
since last year and that maintainers have been contacted, but if
that is even true then it probably was done via a private email
to a potentially outdated email address of one of the maintainers,
not through the channels described clearly on the tinyproxy homepage:

> Feel free to report a new bug or suggest features via github issues.
> Tinyproxy developers hang out in #tinyproxy on irc.libera.chat.

no github issue was filed, and nobody mentioned a vulnerability on
the mentioned IRC chat. if the issue had been reported on github or
IRC, the bug would have been fixed within a day.
2024-05-05 10:37:29 +00:00
rofl0r
92289d5a4c main: print filename of config file used on (re)load 2024-05-01 23:48:37 +00:00
strongleong
c4df45b7e4 BasicAuth: Added logging for failed login attemps
closes #514
2023-11-12 15:05:38 +00:00
Victor Kislov
84285b640d
BasicAuth: Accept special chars in username and password (#516)
Co-authored-by: Victor Kislov <victork@primis.tech>
2023-11-02 19:24:42 +00:00
rofl0r
c834073968 fix CI by running apt update 2023-10-16 00:06:15 +01:00
rofl0r
1289d8afc8 conf: use case-independent match for Filtertype parameter 2023-10-13 19:54:26 +00:00
rofl0r
2935519eb7 fix omission to reset socklen parameter for accept()
since accept() uses the socklen parameter as in/out, after processing
an IPv4 the socklen fed to it waiting for the next client was only
the length of sockaddr_in, so if a connection from an IPv6 came in
the client sockaddr was only partially filled in.
this caused wrongly printed ipv6 addresses in log, and failure to
match them correctly against the acl.

closes #495
2023-06-07 18:57:05 +00:00
rofl0r
d7c20e663f tinyproxy.conf.5: document syntax for upstream IPv6 addresses
follow-up to 2bec15ee40
2023-05-25 19:42:02 +00:00
ivanwick
1e615e66a9
tinyproxy.conf.5: document config strings that require double quotes (#493)
* tinyproxy.conf.5: document config strings that require double quotes

String config values matched by the STR regex must be enclosed in double
quotes

Edit descriptions for brevity

conf.c: move boolean arguments comment before BOOL group

addresses #491

* Revert conf.c: move boolean arguments comment before BOOL group
2023-05-24 15:06:15 +01:00
Mario-Klebsch
2bec15ee40
Allow configuring IPv6 address for upstream proxy (#492)
* Added support to configure IPv6 upstream proxy servers using bracket syntax.
* Added regular expression for IPv6 scope identifier to re for IPv6 address.
2023-05-23 14:04:48 +01:00
rofl0r
ef60434b39 docs: typo fix
closes #487
2023-03-20 20:36:01 +00:00
rofl0r
31339cb161 tinyproxy.conf.5: update text for bind directive
the existing text was sort of misleading as it was written in a
pre-HTTPS era.

addressing #475
2023-02-01 15:46:58 +00:00
rofl0r
470cc0863d conf: fix potential crash with invalid input data
closes #474
2023-02-01 13:32:45 +00:00
rofl0r
6ffd9af2c7 hsearch: fix potential UB (pointer arithmetics on nullptr)
closes #471
addresses #470
2023-02-01 13:16:58 +00:00
rofl0r
3764b85514 prevent junk from showing up in error page in invalid requests
fixes #457
2022-09-08 15:18:04 +00:00
rofl0r
84f203fb1c fix reversepath directive using https url giving misleading error
it's not possible to use a https url in a ReversePath directive, without
removing the security provided by https, and would require adding a
dependency on a TLS library like openssl and a lot of code complexity
to fetch the requested resource via https and relay it back to the client.

in case the reversepath directive kicked in, but the protocol wasn't
recognized, and support for transparent proxying built-in, the code
wrongfully tried to turn the request into a trans request, leading
to a bogus rewritten url like http://localhost:8888https://www.endpoint.com
and an error message that we're trying to connect to the machine the
proxy runs on.

now instead use the generic code that signals an invalid protocol/url
was used.

closes #419
2022-08-20 14:43:24 +00:00
rofl0r
121be4a74e echo http protocol version on CONNECT request response
while at it, the function doing it was renamed from the misleading
ssl name to what it actually does.
also inlined the strings that were previously defined as macros.

addressing #152
2022-07-15 23:43:13 +00:00
rofl0r
8b373f804e update html documentation, add quickstart section 2022-05-30 19:10:27 +00:00
rofl0r
90adf28663 release 1.11.1 2022-05-27 14:08:03 +00:00
rofl0r
ea75e79609 CI: add github workflow to build release tarball 2022-05-27 14:06:16 +00:00
rofl0r
d6ee3835f0 configure: use release tarball provided gperf file 2022-05-27 14:06:16 +00:00
rofl0r
d9e38babb7 add gperf generated files to make dist 2022-05-27 14:06:16 +00:00
rofl0r
77cd87efef print error message if errorfile is configured but can't be opened
also, don't use fopen(NULL) if usage of errorfiles is not configured.
2022-05-27 12:48:41 +00:00
rofl0r
7d1e86ccae don't try to send 408 error to closed client socket
read_request_line() is exercised on the client's fd, and it fails
when the client closed the connection. therefore it's wrong
to send an error message to the client in this situation.
additionally, the error message states that the server closed
the connection.

might fix #383
2022-05-02 14:50:42 +00:00
rofl0r
235b1c10a7 implement filtertype keyword and fnmatch-based filtering
as suggested in #212, it seems the majority of people don't understand
that input was expected to be in regex format and people were using
filter lists containing plain hostnames, e.g. `www.google.com`.

apart from that, using fnmatch() for matching is actually a lot less
computationally expensive and allows to use big blacklists without
incurring a huge performance hit.

the config file now understands a new option `FilterType` which can
be one of `bre`, `ere` and `fnmatch`.
The `FilterExtended` option was deprecated in favor of it.
It still works, but will be removed in the release after the next.
2022-05-02 13:13:40 +00:00
Tristan Stenner
26db3f6cc9 Allow "XTinyProxy No" with xtinyproxy disabled 2022-05-02 08:32:54 +01:00
dr|z3d
14d31ed63f Prettify stats.html 2022-04-22 06:13:38 +00:00
rofl0r
c63028d675 bind_socket(): improve log output
bind_socket most often fails due to having wrong address family,
so it's helpful to see which one was used.
2022-03-01 15:17:32 +00:00
rofl0r
9718be09c1 reload_logging: fix returning possibly uninitialized value
introduced in 17d3733be3

may fix #422
2022-02-16 02:28:11 +00:00
Malte S. Stretz
479df8ecec Add test case for unavailable upstream 2022-02-13 21:46:03 +00:00
Malte S. Stretz
1576ee279f Return 5xx when upstream is unreachable
Currently a 404 is returned for a misconfigured or unavailable upstream
server.  Since that's a server error it should be a 5xx instead; a 404
is confusing when used as a forward proxy and might even be harmful when
used as a reverse proxy.

It is debatable if another 5xx code might be better; the misconfigured
situation might better be a 500 whereas the connection issue could be
a 503 instead (as used eg. in haproxy).
2022-02-13 21:46:03 +00:00
rofl0r
eced6822f8 properly deal with client sending chunked data
this fixes OPTIONS requests sent from apache SVN client using their
native HTTP proxy support.

closes #421

tested with `svn info http://svnmir.bme.freebsd.org/ports/`
2022-02-13 21:11:37 +00:00
rofl0r
17d3733be3 main: fix logging being disabled after reload conf fails
fixes #417
2022-01-26 12:13:22 +00:00
rofl0r
79d0b0fa79 fix timeout not being applied to outgoing connections
the fix in 0b9a74c290 was incomplete, as it
applied the socket timeout only to the socket received from accept(), but
not to sockets created for outgoing connections.
2022-01-20 20:25:42 +00:00
rofl0r
d3d8943fe4 update website footer re: freenode 2021-09-22 12:14:58 +00:00
rofl0r
f0c291e1da README.md: freenode is dead, long live libera
since freenode.net doesn't even serve a website anymore, it's safe to
finally announce the switch.
2021-09-22 12:07:33 +00:00
Aaron M. Ucko
207f790314 Fix FTBFS on hurd-i386: run_tests.sh duplicates build path. 2021-09-22 13:05:12 +01:00
rofl0r
c1023f6821 fix regression failing to parse dotted netmask in upstream
introduced in 979c737f9b.
when refactoring the "site-spec" parsing code i failed to realize that
the code dealing with acl allow/deny directives didn't provide the
option to specify netmasks in dotted ipv4 notation, unlike the code
in the upstream parser. since both scenarios now use the same parsing,
both dotted notation and CIDR slash-notation are possible.

while at it, removed the len parameter from fill_netmask_array() which
provided the illusion the array length could be of variable size.

fixes #394
2021-08-23 14:00:05 +00:00
rofl0r
39d7bf6c70 improve error message for "Error reading readable client_fd"
maybe this helps to track down the cause of #383.
2021-07-23 20:17:18 +01:00
rofl0r
e91e48dd60
add an issue template (#387)
as it's unproductive to be getting the same bug report for old tinyproxy versions over and over, and people not even stating which version they're using, this new issue template makes people
aware of what information to include when filing an issue request.
2021-07-21 14:29:20 +01:00
rofl0r
563978a3ea socks4 upstream: add safety check for hostname length 2021-06-25 02:55:22 +01:00
rofl0r
7ea9f80d3f fix segfault in socks4 upstream with unresolvable hostname
using a socks4 tor upstream with an .onion url resulted in
gethostbyname() returning NULL and a subsequent segfault.
not only did the code not check the return value of gethostbyname(),
that resolver API itself isn't threadsafe.

as pure SOCKS4 supports only IPv4 addresses, and the main SOCKS4
user to this date is tor, we just use SOCKS4a unconditionally and
pass the hostname to the proxy without trying to do any local name
resolving.

i suspect in 2021 almost all SOCKS4 proxy servers in existence use
SOCKS4a extension, but should i be wrong on this, i prefer issue
reports to show up and implement plain SOCKS4 fallback only when
i see it is actually used in practice.
2021-06-25 02:43:00 +01:00
rofl0r
bc81b4d9e8 put an end to LINE_MAX issues
for some reason, getting this macro is really hard across platforms,
requiring either different feature test macros or even the right order
of included headers, and its usage caused several build failures in the
past. fix it once and for all by just using 1024 as max line length if
the macro can't be retrieved.

closes #382
2021-06-24 22:55:33 +01:00
rofl0r
558e3f748d configure.ac: remove unneeded "defs" linker flag
the flag was added in 753010f571 without
explanation, and according to my research it is used to make the linker
report undefined symbols when linking a shared library.
since we don't build any shared libs, this isn't needed at all, but
reportedly causes issues with cygwin (#382).
2021-06-23 16:59:55 +01:00
Alex Wied
7168a42624 Include limits.h to fix build on OSX 2021-05-13 01:15:13 +01:00
Michael Adam
60bf8b9e4d github actions: don't run "make test" on macos it currenctly fails
Signed-off-by: Michael Adam <obnox@samba.org>
2021-05-13 01:02:43 +01:00
Michael Adam
2db9a2a00f github actions: add macos tests
Signed-off-by: Michael Adam <obnox@samba.org>
2021-05-13 01:38:10 +02:00
Michael Adam
132a55cefb Fix github actions
Signed-off-by: Michael Adam <obnox@samba.org>
2021-05-12 22:50:15 +02:00
Michael Adam
e81d7767d3 Add github actions workflow for new CI
Signed-off-by: Michael Adam <obnox@samba.org>
2021-05-12 22:39:30 +02:00
rofl0r
7af5b1a553 manpage: improve FilterDefaultDeny paragraph 2021-05-10 00:25:50 +01:00
rofl0r
aeb7b19c53 conf: do not warn about missing user directive unless root
there's no point in printing a warning if the program is already started
as a restricted user.
2021-05-10 00:03:43 +01:00
rofl0r
9d815f69a4 filter: hard error when filter file doesn't exist 2021-05-09 23:41:49 +01:00
rofl0r
cc47fbf1f7 manpage: URL-based filtering is no longer recommended 2021-05-09 17:53:52 +01:00
rofl0r
a869e71ac3 add support for outgoing connections with HTTP/1.1
since there are numerous changes in HTTP/1.1, the proxyserver will
stick to using HTTP/1.0 for internal usage, however when a connection
is requested with HTTP/1.x from now on we will duplicate the minor revision
the client requested, because apparently some servers refuse to accept
HTTP/1.0

addresses #152.
2021-04-16 14:51:01 +01:00
rofl0r
979c737f9b make upstream site-spec ipv6 compatible, refactor acl code
the acl.c code parsing a site-spec has been factored out into a
new TU: hostspec. it was superior to the parsing code in
upstream.c in that it properly deals with both ipv4 and ipv6.

both upstream and acl now use the new code for parsing, and upstream
also for checking for a match.
acl.c still uses the old matching code as it has a lot of special case
code for specifications containing a hostname, and in case such
a spec is encountered, tries to do reverse name lookup to see if
a numeric ip matches that spec.

removing that code could break existing usecases, however since
that was never implemented for upstream nobody will miss it there.
2021-04-16 14:46:02 +01:00
rofl0r
2529597ea0 reverse: redirect if path without trailing slash is detected
if for example:

ReversePath = "/foo/"

and user requests "http://tinyproxy/foo" the common behaviour for HTTP
servers is to send a http 301 redirect to the correct url.
we now do the same.
2021-04-16 14:41:40 +01:00
rofl0r
bc87de3482 Release 1.11.0 2021-04-16 13:47:21 +01:00
rofl0r
11a4f6c5cf reverse: ensure paths always end with a slash 2021-03-28 20:36:55 +01:00
rofl0r
64badd6b37 htab: prevent filling up of table with tombstones
as pointed out by @craigbarnes [0], using the latest fix for
the tombstone issue, it's possible to provoke a situation
that causes an endless loop when all free slots in the table
are filled up with tombstones and htab_find() is called.

therefore we need to account for those as well when deciding
if there's a need to call resize() so there's never more than
75% of the table used by either dead or live items.
the resize() serves as a rehash which gets rid of all deleted
entries, and it might cause the table size to shrink if
htab_insert() is called after a lot of items have been removed.

[0]: https://github.com/rofl0r/htab/issues/1#issuecomment-800094442

testcase:

    #include <assert.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include "hsearch.h"

    #define HTAB_OOM_TEST
    #include "hsearch.c"

    static char *xstrdup(const char *str)
    {
        char *dup = strdup(str);
        assert(dup);
        return dup;
    }

    void utoa(unsigned number, char* buffer) {
            int lentest, len = 0, i, start = 0;

            lentest = number;
            do {
                    len++;
                    lentest /= 10;
            } while(lentest);
            buffer[start+len] = 0;
            do {
                    i = number % 10;
                    buffer[start+len - 1] = '0' + i;
                    number -= i;
                    len -= 1;
                    number /= 10;
            } while (number);
    }

    #define TESTSIZE 8
    #define KEEP 1

    static char* notorious[TESTSIZE];

    static void prep() {
    	srand(0);
    	char buf[16];
    	size_t filled = 0;
    	while(filled < TESTSIZE) {
    		utoa(rand(), buf);
    		size_t idx = keyhash(buf) & (TESTSIZE-1);
    		if(!notorious[idx]) {
    			notorious[idx] = xstrdup(buf);
    			++filled;
    		}
    	}
    }

    int main(void)
    {
    	struct htab *h = htab_create(TESTSIZE);
    	size_t i;
    	assert(h);

    	prep();
    	for(i=0; i<TESTSIZE; ++i) {
    		char *key = notorious[i];
    		printf("[%zu] = \"%s\"\n", i, key);
    		int r = htab_insert(h, key, HTV_N(42));
    		if(!r == 1) {
    			printf("element %zu couldn't be inserted\n", i);
    			break;
    		}
    		assert(r == 1);
    		// Ensure newly inserted entry can be found
    		assert(htab_find(h, key));
    		if(i >= KEEP) htab_delete(h, key);
    	}

    	htab_find(h, "looooop");

    	return 0;
    }
2021-03-28 20:33:17 +01:00
rofl0r
48860bbe26 refactor html-error so send_http_headers() can take extra arg
we already required an extra argument inside the headers sent
for 401 and 407 error responses, move those to sent_http_error_message()
and refactor send_http_headers() to always take the extra argument.
in calling sites where the extra arg isn't needed, use "".
2021-03-28 20:24:23 +01:00
rofl0r
c4231e58bf orderedmap: fix memory leak when using orderedmap_remove()
closes #351
2021-03-14 16:06:10 +00:00
rofl0r
38934921c4 htab_delete(): fix failure to set tombstone
we can't just set an item's key to zero and be done with a deletion,
because this will break the item search chain.
a deleted item requires a special marker, also known as tombstone.
when searching for an item, all slots with a tombstone need to treated
as if they were in use, but when inserting an item such a slot needs
to be filled with the new item.

a common procedure is to rehash the table when the number of deleted
items crosses a certain threshold, though for simplicity we leave this
task to the resize() function which does the same thing anyway when
the hashtable grows.

this allows to fix the issue quite elegantly and with almost no
additional overhead, so we don't penalize applications that do very
few deletions.
2021-03-14 01:57:21 +00:00
rofl0r
4147e917d6 configure: check whether gperf is compatible
closes #337
2021-02-13 09:53:55 +00:00
rofl0r
adad565c03 http-message: fix UB passing long to format string expecting int 2020-10-19 20:33:04 +01:00
rofl0r
db5c0e99b4 reqs: fix UB passing ssize_t to format string expecting int 2020-10-19 20:30:10 +01:00
rofl0r
8ebbd50cb2 log: replace non-mt-safe localtime() with localtime_r() 2020-10-19 20:26:12 +01:00
rofl0r
732bdd0f56 replace usage of non-threadsafe gmtime() with gmtime_r()
the latter is a standard POSIX function too.
2020-10-19 20:21:26 +01:00
Anton Khirnov
3bb14e0440 Allow multiple Bind directives.
Try all the addresses specified with Bind in order. This is necessary
e.g. for maintaining IPv4+6 connectivity while still being restricted to
one interface.
2020-10-19 20:08:31 +01:00
Anton Khirnov
2b49ef0e0f sock: add missing format specifier to log_message() 2020-10-19 20:08:31 +01:00
rofl0r
f7c616d2b9 log.c: fix format string args 2020-10-19 20:08:31 +01:00
rofl0r
cc0a7eb9a2 html-error: move common.h inclusion back to top
this seems to cause an implicit declaration of snprintf() thanks to
feature test macro hell.
2020-10-19 20:08:31 +01:00
rofl0r
e20aa221ff conf: move inclusion of common.h back to the start
otherwise the feature-test-macros won't kick in as they should.

should fix #329
2020-10-01 15:25:35 +01:00
rofl0r
8d27503cc3 acl: fix regression using ipv6 with netmask
introduced in 0ad8904b40

closes #327
2020-09-30 19:23:34 +01:00
rofl0r
3950a606a4 conf: only treat space and tab as whitespace
other characters in the [[:space:]] set can't possibly be encountered,
and this speeds up parsing by approximately 10%.
2020-09-30 05:31:56 +01:00
rofl0r
a8944b93e7 conf: use [0-9] instead of [[:digit:]] for shorter re strings 2020-09-30 05:28:00 +01:00
rofl0r
960972865c print linenumber from all conf-emitted warnings 2020-09-30 05:21:26 +01:00
rofl0r
f55c46eb39 log: print timestamps with millisecond precision
this allows easier time measurements for benchmarks.
2020-09-30 05:20:09 +01:00
rofl0r
10494cab8c change loglevel of "Not running as root" message to INFO
there's no reason to display this as warning.
2020-09-30 05:19:16 +01:00
rofl0r
4f1a1663ff conf: remove bogus support for hex literals
the INT regex macro supported a 0x prefix (used e.g. for port numbers),
however following that, only digits were accepted, and not the full
range of hexdigits. it's unlikely this was used, so remove it.

note that the () expression is kept, so we don't have to adjust match
number indices all over the place.
2020-09-30 05:14:57 +01:00
rofl0r
35c8edcf73 speed up build by only including regex.h where needed 2020-09-30 05:13:45 +01:00
rofl0r
7c664ad0b2 Release 1.11.0-rc1 2020-09-27 16:22:21 +01:00
rofl0r
8594e9b8cc add conf-tokens.gperf to EXTRA_DIST
otherwise it will be missing in `make dist`-generated tarballs.
2020-09-27 15:55:23 +01:00
rofl0r
094db9d670 version.sh: relax regex for release tag detection
this allows to use tag names with a custom suffix too.
2020-09-27 15:44:50 +01:00
rofl0r
4dfac863a5 version.sh: replace -g with -git-
git describe prefixes the sha1 commit hash with -g, which is exactly what
we're after. this change gets rid of the confusing "g" in the commit hash
and allows tag names that include "-".
2020-09-27 15:41:54 +01:00
rofl0r
c74fe57262 transparent: workaround old glibc bug on RHEL7
it's been reported[0] that RHEL7 fails to properly set the length
parameter of the getsockname() call to the length of the required
struct sockaddr type, and always returns the length passed if it
is big enough.

the SOCKADDR_UNION_* macros originate from my microsocks[1] project,
and facilitate handling of the sockaddr mess without nasty casts.

[0]: https://github.com/tinyproxy/tinyproxy/issues/45#issuecomment-694594990
[1]: https://github.com/rofl0r/microsocks
2020-09-18 12:12:14 +01:00
rofl0r
d4ef2cfa62 child_kill_children(): use method that actually works
it turned out that close()ing an fd behind the back of a thread
doesn't actually cause blocking operations to get a read/write event,
because the fd will stay valid to in-progress operations.
2020-09-17 21:24:45 +01:00
rofl0r
da1bc1425d tune error messages to show select or poll depending on what is used 2020-09-17 21:03:51 +01:00
rofl0r
22e4898519 add autoconf test and fallback code for systems without gperf 2020-09-16 23:04:12 +01:00
rofl0r
45b238fc6f main: print error when config_init() fails 2020-09-16 21:01:02 +01:00
rofl0r
45323584a0 speed up big config parsing by 2x using gperf 2020-09-16 21:01:02 +01:00
rofl0r
caeab31fca conf.c: simplify the huge IPV6 regex
even though the existing IPV6 regex caught (almost?) all invalid
ipv6 addresses, it did so with a huge performance penalty.
parsing a file with 32K allow or deny statement took 30 secs in
a test setup, after this change less than 3.

the new regex is sufficient to recognize all valid ipv6 addresses,
and hands down the responsibility to detect corner cases to the
system's inet_pton() function, which is e.g. called from insert_acl(),
which now causes a warning to be printed in the log if a seemingly
valid address is in fact invalid.

the new regex has been tested with 486 testcases from
http://download.dartware.com/thirdparty/test-ipv6-regex.pl
and accepts all valid ones and rejects most of the invalid ones.

note that the IPV4 regex already did a similar thing and checked only
whether the ip looks like [0-9]+.[0-9]+.[0-9]+.[0-9]+ without pedantry.
2020-09-16 21:01:02 +01:00
rofl0r
0ad8904b40 acl.c: detect invalid ipv6 string 2020-09-16 21:00:50 +01:00
rofl0r
99ed66cbc4 conf.c: warn when encountering invalid address 2020-09-16 21:00:50 +01:00
rofl0r
880a8b0ab6 conf: use cpp stringification for STDCONF macro 2020-09-16 21:00:04 +01:00
rofl0r
551e914d24 conf: merge upstream/upstream_none into single regex/handler 2020-09-16 21:00:04 +01:00
rofl0r
bad36cd9cd move config reload message to reload_config()
move it to before disabling logging, so a message with the correct
timestamp is printed if logging was already enabled.
also add a message when loading finished, so one can see from the
timestamp how long it took.

note that this only works on a real config reload triggered by
SIGHUP/SIGUSR1, because on startup we don't know yet where to log to.
2020-09-16 21:00:04 +01:00
rofl0r
683a354196 remove vector remains 2020-09-16 02:39:09 +01:00
rofl0r
06c96761d5 log_message_storage: use sblist 2020-09-16 02:39:09 +01:00
rofl0r
54ae2d2a19 tests: add some AddHeader directives 2020-09-16 02:39:09 +01:00
rofl0r
e843519fb8 listen_addrs: use sblist 2020-09-16 02:39:09 +01:00
rofl0r
a5381223df basicauth: use sblist 2020-09-16 02:39:09 +01:00
rofl0r
487f2aba47 connect_ports: use sblist 2020-09-16 02:39:09 +01:00
rofl0r
e929e81a55 add_header: use sblist
note that the old code inserted added headers at the beginning of the
list, reasoning unknown. this seems counter-intuitive as the headers
would end up in the request in the reverse order they were added,
but this was irrelevant, as the headers were originally first put
into the hashmap hashofheaders before sending it to the client.
since the hashmap didn't preserve ordering, the headers would appear
in random order anyway.
2020-09-16 02:39:09 +01:00
rofl0r
7d33fc8e8a listen_fds: use sblist 2020-09-16 01:05:58 +01:00
rofl0r
a5890b621b run_tests_valgrind: use tougher valgrind settings 2020-09-15 23:39:04 +01:00
rofl0r
2037bc64f5 free a mem leak by statically allocating global statsbuf 2020-09-15 23:28:33 +01:00
rofl0r
d453a4c2a4 main: include loop header 2020-09-15 23:20:14 +01:00
rofl0r
192f8194e1 free() loop records too 2020-09-15 23:12:00 +01:00
rofl0r
bd92446184 use poll() where available 2020-09-15 23:12:00 +01:00
rofl0r
10cdee3bc5 prepare transition to poll()
usage of select() is inefficient (because a huge fd_set array has to
be initialized on each call) and insecure (because an fd >= FD_SETSIZE
will cause out-of-bounds accesses using the FD_*SET macros, and a system
can be set up to allow more than that number of fds using ulimit).
for the moment we prepared a poll-like wrapper that still runs select()
to test for regressions, and so we have fallback code for systems without
poll().
2020-09-15 23:12:00 +01:00
rofl0r
0c8275a90e refactor conns.[ch], put conn_s into child struct
this allows to access the conn member from the main thread handling
the childs, plus simplifies the code.
2020-09-15 23:12:00 +01:00
rofl0r
5779ba8697 hsearch: add seed to prevent another CVE-2012-3505 instance 2020-09-15 23:12:00 +01:00
rofl0r
155bfbbe87 replace leftover users of hashmap with htab
also fixes a bug where the ErrorFile directive would create a
new hashmap on every added item, effectively allowing only
the use of the last specified errornumber, and producing memory
leaks on each config reload.
2020-09-15 23:12:00 +01:00
rofl0r
34a8b28414 save headers in an ordered dictionary
due to the usage of a hashmap to store headers, when relaying them
to the other side the order was not prevented.
even though correct from a standards point-of-view, this caused
issues with various programs, and it allows to fingerprint the use
of tinyproxy.

to implement this, i imported the MIT-licensed hsearch.[ch] from
https://github.com/rofl0r/htab which was originally taken from
musl libc. it's a simple and efficient hashtable implementation
with far better performance characteristic than the one previously
used by tinyproxy. additionally it has an API much more well-suited
for this purpose.

orderedmap.[ch] was implemented from scratch to address this issue.
behind the scenes it uses an sblist to store string values, and a htab
to store keys and the indices into the sblist.
this allows us to iterate linearly over the sblist and then find the
corresponding key in the hash table, so the headers can be reproduced
in the order they were received.

closes #73
2020-09-15 23:11:59 +01:00
rofl0r
9d5ee85c3e fix free()ing of config items
- we need to free the config after it has been succesfully loaded,
  not unconditionally before reloading.
- we also need to free them before exiting from the main program
  to have clean valgrind output.
2020-09-15 23:11:59 +01:00
rofl0r
372d7ff824 shutdown: free children from right place 2020-09-15 22:32:42 +01:00
rofl0r
2f3a3828ac Revert "childs.c: fix minor memory leak"
This reverts commit 6dd3806f7d.
2020-09-15 22:25:53 +01:00
rofl0r
6dd3806f7d childs.c: fix minor memory leak
this would leak only once on program termination, so it's no big
deal apart from having spurious reachable memory in valgrind logs.
2020-09-15 20:02:12 +01:00
rofl0r
7eb6600aeb main: orderly shutdown on SIGINT too
the appropriate code in the signal handler was already set up,
but for some reason the signal itself not being handled.
2020-09-14 20:59:02 +01:00
rofl0r
7014d050d9 run_tests: make travis happy, use signal nr instead of name 2020-09-14 17:02:36 +01:00
rofl0r
ff23f3249b conf.c: include common.h 2020-09-14 17:02:36 +01:00
rofl0r
17e19a67cf run_tests: do some more extensive testing
1) force a config reload after some initial tests.
   this will allow to identify memleaks using the valgrind test,
   as this will free all structures allocated for the config, and
   recreate them.
2) test ErrorFile directive by adding several of them.
   this should help catch regressions such as the one fixed in
   4847d8cdb3.
   it will also test memleaks in the related code paths.
3) test some scenarios that should produce errors and use the
   configured ErrorFile directives.
2020-09-13 01:09:21 +01:00
rofl0r
c64ac9edbe fix get_request_entity()
get_request_entity()'s purpose is to drain remaining unread bytes
in the request read pipe before handing out an error page,
and kinda surprisingly, also when connection to the stathost is
done.

in the stathost case tinyproxy just skipped proper processing and
jumped to the error handler code, and remembering whether a
connection to the stathost was desired in a variable, then doing
things a bit differently depending on whether it's set.

i tried to fix issues with get_request_entity in
88153e944f (which is basically the
right fix for the issue it tried to solve, but incomplete),
and resulting from there in 78cc5b72b1.
the latter fix wasn't quite right since we're not supposed to check
whether the socket is ready for writing, and having a return value
of 2 instead of 1 got resulted in some of the if statements not
kicking in when they should have.
this also resulted in the stathost page no longer working.

after in-depth study of the issue i realized that we only need to
call get_request_entity() when the headers aren't completely read,
additional to setting the proper connection timeout as
88153e944f already implemented.
the changes of 78cc5b72b1 have been
reverted.
2020-09-13 00:37:19 +01:00
rofl0r
bfe59856b2 tests/webclient: return error when HTTP status > 399 2020-09-13 00:35:38 +01:00
rofl0r
4847d8cdb3 add_new_errorpage(): fix segfault accessing global config
another fallout of the config refactoring finished by
2e02dce0c3.

apparently no one using the ErrorFile directive used git master
during the last months, as there have been no reports about this issue.
2020-09-12 21:38:04 +01:00
rofl0r
df9074db6e vector.h: missing include <unistd.h> for ssize_t 2020-09-12 15:56:36 +01:00
rofl0r
9e40f8311f handle_connection(): print process_*_headers errno information 2020-09-10 21:13:31 +01:00
rofl0r
f1bd259e6e handle_connection: replace "goto fail" with func call
this allows to see in a backtrace from where the error was
triggered.
2020-09-10 14:48:39 +01:00
rofl0r
e94cbdb3a5 handle_connection(): factor out failure code
this allows us in a next step to replace goto fail with a call to that
function, so we can see in a backtrace from where the failure was
triggered.
2020-09-10 14:37:56 +01:00
rofl0r
b549ba5af3 remove bogus custom timeout handling code
in networking, hitting a timeout requires that *nothing* happens during the
interval. whenever anything happens, the timeout is reset.
there's no need to do custom time calculations, it's perfectly fine to let
the kernel handle it using the select() syscall.

additionally the code added in 0b9a74c290
assures that read and write syscalls() don't block indefinitely and return
on the timeout too, so there's no need to switch sockets back and forth
between blocking/nonblocking.
2020-09-09 12:37:23 +01:00
rofl0r
b4e3f1a896 fix negative timeout resulting in select() EINVAL 2020-09-09 11:59:40 +01:00
rofl0r
78cc5b72b1 get_request_entity: fix regression w/ CONNECT method
introduced in 88153e944f.
when connect method is used (HTTPS), and e.g. a filtered domain requested,
there's no data on readfds, only on writefds.

this caused the response from the connection to hang until the timeout was
hit. in the past in such scenario always a "no entity" response
was produced in tinyproxy logs.
2020-09-08 14:45:24 +01:00
rofl0r
58cfaf2659 make acl lookup 450x faster by using sblist
tested with 32K acl rules, generated by

    for x in `seq 128` ; do for y in `seq 255` ; do \
    echo "Deny 10.$x.$y.0/24" ; done ; done

after loading the config (which is dogslow too), tinyproxy
required 9.5 seconds for the acl check on every request.
after switching the list implementation to sblist, a request
with the full acl check now takes only 0.025 seconds.
the time spent for loading the config file is identical for both
list implementations, roughly 30 seconds.

(in a previous test, 65K acl rules were generated, but every
connection required almost 2 minutes to crunch through the list...)
2020-09-07 22:09:35 +01:00
rofl0r
ebc7f15ec7 acl: typedef access_list to acl_list_t
this allows to switch the underlying implementation easily.
2020-09-07 21:53:14 +01:00
rofl0r
efa5892011 check_acl: do full_inet_pton() only once per ip
if there's a long list of acl's, doing full_inet_pton() over
and over with the same IP isn't really efficient.
2020-09-07 20:57:16 +01:00
rofl0r
88153e944f get_request_entity: respect user-set timeout
get_request_entity() is only called on error, for example if a client
doesn't pass a check_acl() check. in such a case it's possible that
the client fd isn't yet ready to read from.
using select() with a timeout timeval of {0,0} causes it to return
immediately and return 0 if there's no data ready to be read.
this resulted in immediate connection termination rather than returning
the 403 access denied error page to the client and a confusing
"no entity" message displayed in the proxy log.
2020-09-07 20:49:07 +01:00
rofl0r
f720244baa README.md: describe how transparent proxying can be used
addressing #45
2020-09-07 18:08:57 +01:00
rofl0r
487a062fcc change loglevel of start/stop/reload messages to NOTICE
this allows to see them when the verbose INFO loglevel is not desired.

closes #78
2020-09-07 16:59:37 +01:00
rofl0r
23b0c84653 upstream: fix ip/mask calculation for types other than none
the code wrongly processed the site_spec (here: domain) parameter
only when PT_TYPE == PT_NONE.
re-arranged code to process it correctly whenever passed.
additionally the mask is now also applied to the passed subnet/ip,
so a site_spec like 127.0.0.1/8 is converted into 127.0.0.0/8.
also the case where inet_aton fails now produces a proper error
message.

note that the code still doesn't process ipv6 addresses and mask.
to support it, we should use the existing code in acl.c and refactor
it so it can be used from both call sites.

closes #83
closes #165
2020-09-07 16:11:51 +01:00
Brett Randall
559faf7957 website stylesheet: added pre margin-bottom: 20px.
this improves rendering of literal code paragraphs.
2020-09-07 12:34:35 +01:00
rofl0r
a8848d4bd8 html-error: substitute template variables via a regex
previously, in order to detect and insert {variables} into error/stats
templates, tinyproxy iterated char-by-char over the input file, and would
try to parse anything inside {} pairs and treat it like a variable name.
this breaks CSS, and additionally it's dog slow as tinyproxy wrote every
single character to the client via a write syscall.
now we process line-by-line, and inspect all matches of the regex
\{[a-z]{1,32}\}. if the contents of the regex are a known variable name,
substitution is taking place. if not, the contents are passed as-is to
the client. also the chunks before and after matches are written in
a single syscall.

closes #108
2020-09-07 04:32:13 +01:00
[anp/hsw]
17ae1b512c Do not give error while storing invalid header 2020-09-07 01:12:50 +01:00
rofl0r
d0fae11760 config parser: increase possible line length limit
let's use POSIX LINE_MAX (usually 4KB) instead of 1KB.

closes #226
2020-09-07 01:07:00 +01:00
rofl0r
7c37a61e00 manpages: update copyright years 2020-09-06 23:16:29 +01:00
rofl0r
65e79b84a4 update documentation about signals 2020-09-06 23:15:41 +01:00
rofl0r
8c86e8b3ae allow SIGUSR1 to be used as an alternative to SIGHUP
this allows a tinyproxy session in terminal foreground mode to reload
its configuration without dropping active connections.
2020-09-06 23:11:22 +01:00
rofl0r
95b1a8ea06 main.c: remove set_signal_handler code duplication 2020-09-06 23:08:10 +01:00
rofl0r
8ba0ac4e86 do not catch SIGHUP in foreground-mode
it's quite unexpected for an application running foreground in a
terminal to keep running when the terminal is closed.
also in such a case (if file logging is disabled) there's no way to
see what's happening to the proxy.
2020-09-06 22:46:26 +01:00
rofl0r
3da66364de configure.ac: fail if version script returns empty string 2020-09-06 20:32:52 +01:00
rofl0r
0d71223a1d send_html_file(): also set empty variables to "(unknown)" 2020-09-06 20:06:59 +01:00
rofl0r
f1a6d063b0 version.sh: fix empty result when git describe fails
fixes an error in travis, which makes a shallow clone of 50 commits.
if the last tag is older than 50 commits, we get:
"fatal: No names found, cannot describe anything."

this caused a premature exit due to an assert error in safe_write()
on this line: assert (count > 0);

because the version variable in tinyproxy was empty.
2020-09-06 20:04:01 +01:00
rofl0r
0d26fab317 run_tests.sh: print more diagnostic if killing tp fails 2020-09-06 17:48:14 +01:00
rofl0r
55208eb2f6 run_tests.sh: print pid if killing tp fails 2020-09-06 17:20:06 +01:00
rofl0r
ab27e4c68b configure.ac: check for all "desired" CFLAGS at once
in case they're all accepted, which would be the case with any
halfways recent GCC, we save a lot of time over testing each flag
sequentially.
2020-09-06 16:58:28 +01:00
rofl0r
f20681e0c6 configure.ac: remove unused checks for malloc/realloc 2020-09-06 16:40:52 +01:00
rofl0r
8685d23225 configure.ac: remove check for strdup()
it was being used unconditionally anyway.
2020-09-06 16:32:37 +01:00
rofl0r
36c9b93cfe transparent: remove usage of inet_ntoa(), make IPv6 ready
inet_ntoa() uses a static buffer and is therefore not threadsafe.
additionally it has been deprecated by POSIX.

by using inet_ntop() instead the code has been made ipv6 aware.

note that this codepath was only entered in the unlikely event that
no hosts header was being passed to the proxy, i.e. pre-HTTP/1.1.
2020-09-06 16:22:11 +01:00
rofl0r
51b8be3ee4 add tinyproxy website template to docs/web
this allows to automatically generate the website from the current
tinyproxy.conf.5 template.

    make
    cd docs/web
    make
2020-09-06 13:45:40 +01:00
Brett Randall
5e594e593a Added BasicAuth to tinyproxy.conf man page. 2020-09-06 12:25:46 +01:00
rofl0r
233ce6de3b filter: reduce memory usage, fix OOM crashes
* check return values of memory allocation and abort gracefully
  in out-of-memory situations

* use sblist (linear dynamic array) instead of linked list
  - this removes one pointer per filter rule
  - removes need to manually allocate/free every single list item
    (instead block allocation is used)
  - simplifies code

* remove storage of (unused) input rule
  - removes one char* pointer per filter rule
  - removes storage of the raw bytes of each filter rule

* add line number to display on out-of-memory/invalid regex situation

* replace duplicate filter_domain()/filter_host() code with a single
  function filter_run()
  - reduces code size and management effort

with these improvements, >1 million regex rules can be loaded with
4 GB of RAM, whereas previously it crashed with about 950K.

the list for testing was assembled from
http://www.shallalist.de/Downloads/shallalist.tar.gz

closes #20
2020-09-05 19:42:34 +01:00
rofl0r
c4dc3ba007 filter: fix documentation about rules
the file docs/filter-howto.txt was removed, as it contained misleading
information since it was first checked in.

it suggests the syntax for filter rules is fnmatch()-like, when in
fact they need to be specified as posix regular expressions.

additionally it contained a lot of utterly unrelated and irrelevant/
outdated text.

a few examples with the correct syntax have now been added to
tinyproxy.conf.5 manpage.

closes #212
2020-09-05 17:33:53 +01:00
Nicolai Søborg
281488a729 Change loglevel for "Maximum number of connections reached"
I was hit by this, and did not see anything in the log, connections was just hanging.
Think warning is a better log level
2020-09-01 15:07:03 +01:00
rofl0r
f825bea4c1 travis: asciidoc is no longer needed 2020-08-20 14:32:16 +01:00
rofl0r
a547a298c7 generate manpages with pod2man instead of a2x/asciidoc(tor)
it turned out that the upstream section in tinyproxy.conf.5 wasn't rendered
properly, because in asciidoc items following a list item are always explicitly
appended to the last list item.
after several hours of finding a workaround, it was decided to change the
manpage generator to pod2man instead.

as pod2man ships together with any perl base install, it should be available
on almost every UNIX system, unlike asciidoc which requires installation
of a huge set of dependencies (more than 1.3 GB on Ubuntu 16.04), and the
replacement asciidoctor requires a ruby installation plus a "gem" (which is
by far better than asciidoc, but still more effort than using the already
available pod2man).

tinyproxy's hard requirement of a2x (asciidoctor) for building  from source
caused rivers of tears (and dozens of support emails/issues) in the past, but
finally we get rid of it. a tool such as a2x with its XML based bloat-
technology isn't really suited to go along with a supposedly lightweight
C program.

if it ever turns out that even pod2man is too heavy a dependency, we could
still write our own replacement in less than 50 lines of awk, as the pod
syntax is very low level and easy to parse.
2020-08-19 22:45:37 +01:00
rofl0r
3fa53f8660 tinyproxy.conf.5: fix typo 2020-08-19 20:37:03 +01:00
rofl0r
fc681e26b8 tinyproxy.conf.5: document upstream null-routing 2020-08-19 12:16:02 +01:00
rofl0r
c984122acd tinyproxy.conf.5: clarify that upstream none makes direct connection 2020-08-19 12:07:19 +01:00
rofl0r
335477b16e upstream: allow port 0 to be specified
this is useful to use upstream directive to null-route a specific target
domain.

e.g.
upstream http 0.0.0.0:0 ".adserver.com"
2020-08-19 12:01:20 +01:00
rofl0r
5ba958829f add configure option to disable manpage generation
using --disable-manpage-support it's finally possibly to disable
the formerly obligatory use of a2x to generate the manpage
documentation.

this is the final solution to the decade old problem that users need
to install the enormous asciidoc package to compile TINYproxy from
source, or otherwise get a build error, even though the vast majority
is only interested in the program itself.

solution was inspired by PR #179.

closes #179
closes #111

note that since 1.10.0 release the generated release tarball includes
the generated manpages too; in which case neither the use of a2x
nor --disable-manpage-support is required.
2020-08-12 00:15:45 +01:00
rofl0r
e1d36d3f74 configure.ac: remove obsolete check for xsltproc
xsltproc was once[1] used to generate AUTHORS from xml input, but
fortunately this is no longer the case.

[1]: in a time when everybody thought XML would be a Good Idea (TM)
2020-08-11 23:35:50 +01:00
rofl0r
8a251023b2 travis: remove make distcheck
distcheck chokes on man5/8 files still in the file tree, while the input
files (.txt) are not. these are generated by the configure script and
it would require quite some effort to get this test working.
as it is non-essential, we simply disable it.
2020-08-11 19:51:48 +01:00
rofl0r
77853caa0a move manpages to maintainer-clean make target
according to https://www.gnu.org/prep/standards/html_node/Standard-Targets.html#Standard-Targets
`maintainer-clean` is the proper make target for files that are distributed
in a release tarball:

> The ‘maintainer-clean’ target is intended to be used by a maintainer of the
> package, not by ordinary users.
> You may need special tools to reconstruct some of the files that
> ‘make maintainer-clean’ deletes.

this prevents users without a2x or asciidoctor from losing their ability to
recompile tinyproxy after `make clean`, but it also means that users wanting
to regenerate the documentation need to run `make maintainer-clean`.
2020-08-11 18:00:34 +01:00
rofl0r
8ceebbf0c6 configure.ac: fix typo checking for asciidoctor in release 2020-08-11 17:50:07 +01:00
rofl0r
d9953d795d travis: run make clean before second compile
otherwise object files will not be rebuilt with the new configure options.
this will prevent cases like db4bd162a3
where it turned out there was a build error with --enable-debug since several
git revisions.
2020-08-11 15:51:15 +01:00
rofl0r
c2d4114427 add support for asciidoctor to generate manpages
asciidoctor is a modern replacement for asciidoc and much more lightweight,
issuing "apt-get install asciidoc" on ubuntu 16.04 results in an attempt to
install more than 1.3 GB of dependencies.
2020-08-11 15:25:02 +01:00
rofl0r
0b9a74c290 enforce socket timeout on new sockets via setsockopt()
the timeout option set by the config file wasn't respected at all
so it could happen that connections became stale and were never released,
which eventually caused tinyproxy to hit the limit of open connections and
never accepting new ones.

addresses #274
2020-07-15 09:59:25 +01:00
LucasVerneyDGE
25e2cc330c add a comment example in the filter file example 2020-07-06 12:20:58 +01:00
xiejianjun
db4bd162a3 fix check_acl compilation with --enable-debug
regression introduced in f6d4da5d81.
this has been overlooked due to the assert macro being optimized out in
non-debug builds.
2020-07-06 11:37:35 +01:00
rofl0r
d98aabf47f transparent: fix invalid memory access
getsockname() requires addrlen to be set to the size of the sockaddr struct
passed as the addr, and a check whether the returned addrlen exceeds the
initially passed size (to determine whether the address returned is truncated).

with a request like "GET /\r\n\r\n" where length is 0 this caused the code
to assume success and use the values of the uninitialized sockaddr struct.
2020-03-18 12:31:15 +00:00
rofl0r
3230ce0bc2 anonymous: fix segfault loading config item
unlike other functions called from the config parser code,
anonymous_insert() accesses the global config variable rather than
passing it as an argument. however the global variable is only set
after successful loading of the entire config.

we fix this by adding a conf argument to each anonymous_* function,
passing the global pointer in calls done from outside the config
parser.

fixes #292
2020-03-16 13:19:39 +00:00
rofl0r
2e02dce0c3 conf: use 2 swappable conf slots, so old config can stay valid
... in case reloading of it after SIGHUP fails, the old config can
continue working.

(apart from the logging-related issue mentioned in 27d96df999 )
2020-01-15 17:03:47 +00:00
rofl0r
5dd514af93 conf: fix loading of default values
previously, default values were stored once into a static struct,
then on each reload item by item copied manually into a "new"
config struct.
this has proven to be errorprone, as additions in one of the 2
locations were not propagated to the second one, apart from
being simply a lot of gratuitous code.

we now simply load the default values directly into the config
struct to be used on each reload.

closes #283
2020-01-15 16:57:03 +00:00
rofl0r
27d96df999 remove duplicate code calling reload_config_file()
as a side effect of not updating the config pointer when loading
the config file fails, the "FIXME" level comment to take appropriate
action in that case has been removed. the only issue remaining
when receiving a SIGHUP and encountering a malformed config file would
now be the case that output to syslog/logfile won't be resumed, if
initially so configured.
2020-01-15 16:35:43 +00:00
rofl0r
c63d5d26b4 access config via a pointer, not a hardcoded struct address
this is required so we can elegantly swap out an old config for a
new one in the future and remove lots of boilerplate from config
initialization code.

unfortunately this is a quite intrusive change as the config struct
was accessed in numerous places, but frankly it should have been
done via a pointer right from the start.

right now, we simply point to a static struct in main.c, so there
shouldn't be any noticeable changes in behaviour.
2020-01-15 16:09:41 +00:00
rofl0r
bffa705005 remove config file name item from conf struct
since this is set via command line, we can deal with it easily
from where it is actually needed.
2020-01-15 15:42:24 +00:00
rofl0r
180c0664aa remove godaemon member from config structure
since this option can't be set via config file, it makes sense
to factor it out and use it only where strictly needed, e.g. in
startup code.
2020-01-15 15:26:40 +00:00
rofl0r
eb2104e1ff log: remove special case code for daemonized mode without logfile
if daemon mode is used and neither logfile nor syslog options specified,
this is clearly a misconfiguration issue. don't try to be smart and work
around that, so less global state information is required.
also, this case is already checked for in main.c:334.
2020-01-15 15:22:43 +00:00
rofl0r
4fb2c14039 syslog: always use LOG_USER facility
LOG_DAEMON isn't specified in POSIX and the gratuitously different
treatment is in the way of a planned cleanup.
2020-01-15 15:09:37 +00:00
rofl0r
40afaeb637 move commandline parsing to main() 2020-01-15 14:45:23 +00:00
rofl0r
25205fd1f3 move initialize_config_defaults to conf.c 2020-01-15 14:17:13 +00:00
rofl0r
cd005a94ce implement detection and denial of endless connection loops
it is quite easy to bring down a proxy server by forcing it to make
connections to one of its own ports, because this will result in an endless
loop spawning more and more connections, until all available fds are exhausted.
since there's a potentially infinite number of potential DNS/ip addresses
resolving to the proxy, it is impossible to detect an endless loop by simply
looking at the destination ip address and port.

what *is* possible though is to record the ip/port tuples assigned to outgoing
connections, and then compare them against new incoming connections. if they
match, the sender was the proxy itself and therefore needs to reject that
connection.

fixes #199.
2019-12-21 00:43:45 +00:00
rofl0r
f6d4da5d81 do hostname resolution only when it is absolutely necessary for ACL check
tinyproxy used to do a full hostname resolution whenever a new client
connection happened, which could cause very long delays (as reported in #198).

there's only a single place/scenario that actually requires a hostname, and
that is when an Allow/Deny rule exists for a hostname or domain, rather than
a raw IP address. since it is very likely this feature is not very widely used,
it makes absolute sense to only do the costly resolution when it is unavoidable.
2019-12-21 00:43:45 +00:00
rofl0r
82e10935d2 move sockaddr_union to sock.h 2019-12-21 00:43:45 +00:00
rofl0r
fa2ad0cf9a log.c: protect logging facility with a mutex
since the write syscall is used instead of stdio, accesses have been
safe already, but it's better to use a mutex anyway to prevent out-
of-order writes.
2019-12-21 00:43:45 +00:00
rofl0r
b09d8d927d conf.c: merely warn on encountering recently obsoleted config items
if we don't handle these gracefully, pretty much every existing config
file will fail with an error, which is probably not very friendly.

the obsoleted config items can be made hard errors after the next
release.
2019-12-21 00:43:45 +00:00
rofl0r
1186c297b4 conf.c: pass lineno to handler funcs 2019-12-21 00:43:45 +00:00
rofl0r
b935dc85c3 simplify codebase by using one thread/conn, instead of preforked procs
the existing codebase used an elaborate and complex approach for
its parallelism:

5 different config file options, namely

- MaxClients
- MinSpareServers
- MaxSpareServers
- StartServers
- MaxRequestsPerChild

were used to steer how (and how many) parallel processes tinyproxy
would spin up at start, how many processes at each point needed to
be idle, etc.
it seems all preforked processes would listen on the server port
and compete with each other about who would get assigned the new
incoming connections.
since some data needs to be shared across those processes, a half-
baked "shared memory" implementation was provided for this purpose.
that implementation used to use files in the filesystem, and since
it had a big FIXME comment, the author was well aware of how hackish
that approach was.

this entire complexity is now removed. the main thread enters
a loop which polls on the listening fds, then spins up a new
thread per connection, until the maximum number of connections
(MaxClients) is hit. this is the only of the 5 config options
left after this cleanup. since threads share the same address space,
the code necessary for shared memory access has been removed.
this means that the other 4 mentioned config option will now
produce a parse error, when encountered.

currently each thread uses a hardcoded default of 256KB per thread
for the thread stack size, which is quite lavish and should be
sufficient for even the worst C libraries, but people may want
to tweak this value to the bare minimum, thus we may provide a new
config option for this purpose in the future.
i suspect that on heavily optimized C libraries such a musl, a
stack size of 8-16 KB per thread could be sufficient.

since the existing list implementation in vector.c did not provide
a way to remove a single item from an existing list, i added my
own list implementation from my libulz library which offers this
functionality, rather than trying to add an ad-hoc, and perhaps
buggy implementation to the vector_t list code. the sblist
code is contained in an 80 line C file and as simple as it can get,
while offering good performance and is proven bugfree due to years
of use in other projects.
2019-12-21 00:43:45 +00:00
rofl0r
3a7aa15834 start work on 1.11.x 2019-12-21 00:43:45 +00:00
Martin Kutschker
69c86b987b Use gai_strerror() to report errors of getaddrinfo() and getnameinfo() 2019-11-27 20:31:48 +00:00
Andre Mas
c2d3470a35 Fixes #256 Provides ::1 as allowed 2019-08-20 21:52:02 +01:00
rofl0r
734ba1d970 fix usage of stathost in combination with basic auth
http protocol requires different treatment of proxy auth vs server auth.

fixes #246
2019-06-14 01:18:19 +01:00
Janosch Hoffmann
e666e4a35b filter file: Don't ignore lines with leading whitespace (#239)
The new code skips leading whitespaces before removing trailing
whitespaces and comments.
Without doing this, lines with leading whitespace are treated like empty
lines (i.e. they are ignored).
2019-05-05 19:13:38 +01:00
rofl0r
b131f45cbb
child.c: properly initialize fdset for each select() call (#216)
it was reported that because the fdset was only initialized once,
tinyproxy would fail to properly listen on more than one interface.

closes #214
closes #127
2018-12-15 17:09:04 +00:00
Vasily
dc41b35333 Basic Auth: allow almost all possible characters for user/pass
previously was restricted to alphanumeric chars only.
2018-11-23 14:59:03 +00:00
Michael Adam
f44d0f387b build: Remove now unused TINYPROXY_UNSTABLE variable from configure
Signed-off-by: Michael Adam <obnox@samba.org>
2018-09-05 01:34:51 +02:00
rofl0r
04f68e21e7 tinyproxy.8: remove l flag from short options 2018-09-05 00:57:23 +02:00
Michael Adam
9f4ed46c0a build: add new version mechanism based on VERSION file and a version.sh script
If this is a git checkout, and git is available, then git describe is
used. Otherwise, the new checked in VERSION file is taken for the version.

This mechanism uses a version.sh script inspired by
http://git.musl-libc.org/cgit/musl/tree/tools/version.sh

Signed-off-by: Michael Adam <obnox@samba.org>
2018-09-05 00:56:31 +02:00
Michael Adam
a662c11b8d Release 1.10.0
Signed-off-by: Michael Adam <obnox@samba.org>
2018-09-01 04:06:59 +02:00
Michael Adam
8333a4bb1a docs: update the copyright notice in the manpages
Signed-off-by: Michael Adam <obnox@samba.org>
2018-09-01 04:06:59 +02:00
Michael Adam
e2a92ea334 Update AUTHORS
Signed-off-by: Michael Adam <obnox@samba.org>
2018-09-01 04:06:59 +02:00
Michael Adam
4c9289802a scripts: add a script to generate the AUTHORS file from git
Signed-off-by: Michael Adam <obnox@samba.org>
2018-09-01 04:06:59 +02:00
Michael Adam
3eb3c2c099 Remove xml-based AUTHORS mechanism. AUTHORS is checked in.
Signed-off-by: Michael Adam <obnox@samba.org>
2018-09-01 04:06:59 +02:00
Michael Adam
a5e6d343d7 Remove unused authors.c/authors.h and generation mechanism.
Signed-off-by: Michael Adam <obnox@samba.org>
2018-09-01 04:06:59 +02:00
Michael Adam
56f30a4386 main: remove the "-l" switch to display the license and authors
Signed-off-by: Michael Adam <obnox@samba.org>
2018-09-01 04:06:59 +02:00
Michael Adam
89b2c68b2b NEWS: just mention to use git log
Signed-off-by: Michael Adam <obnox@samba.org>
2018-09-01 04:06:59 +02:00
Michael Adam
ec95d8b553 configure: remove unused variables
Signed-off-by: Michael Adam <obnox@samba.org>
2018-09-01 04:06:59 +02:00
rofl0r
c651664720 fix socks5 upstream user/pass subnegotiation check
RFC 1929 specifies that the user/pass auth subnegotation repurposes the version
field for the version of that specification, which is 1, not 5.
however there's quite a good deal of software out there which got it wrong and
replies with version 5 to a successful authentication, so let's just accept both
forms - other socks5 client programs like curl do the same.

closes #172
2018-05-29 21:59:11 +02:00
rofl0r
0aad2f5b92 fix basicauth string comparison
closes #160
2018-03-29 00:40:18 +01:00
Michael Adam
ae0cbfe3f2 html-error: Make a switch fallthrough explicit
This silences a gcc v7 compile warning.

Signed-off-by: Michael Adam <obnox@samba.org>
2018-03-27 09:36:53 +02:00
Michael Adam
badb6435d6 upstream: Fix case of empty string domain.
Found by compiler note.

Signed-off-by: Michael Adam <obnox@samba.org>
2018-03-23 01:45:10 +01:00
rofl0r
8d0ea71486 install tinyproxy to bin/, not /sbin
sbin/ is meant for programs only usable by root, but in tinyproxy's
case, regular users can and *should* use tinyproxy; meaning it is
preferable from a security PoV to use tinyproxy as regular user.
2018-03-23 01:44:53 +01:00
rofl0r
09979629c0 make bind option usable with transparent proxy too
closes #15 for real.
the previous patch that was merged[0] was halfbaked and only removed
the warning part of the original patch from openwrt[1], but didn't
actually activate bind support. further it invoked UB by removing
the return value from the function, if transparent proxy support was
compiled in.

[0]: d97d486d53
[1]: 7c01da4a72
2018-02-27 20:52:09 +00:00
rofl0r
a53f62a212 enable transparent proxy by default
by having all features turned on by default, the binary is only
slightly bigger, but users of binary distros get the whole package
and don't need to compile tinyproxy by hand if they need a feature
that wasn't compiled in.
it also prevents the confusion from getting syntax errors when a
config file using those features is parsed.
another advantage is that by enabling them these features may
actually get some more testing.
2018-02-27 20:13:35 +00:00
rofl0r
b8c6a2127d implement user/password auth for socks5 upstream proxy
just like the rest of the socks code, this was stolen from
proxychains-ng, of which i'm happen to be the maintainer of,
so it's not an issue (the licenses are identical, too).
2018-02-27 20:13:07 +00:00
rofl0r
e78b461607 update upstream syntax in manpage template 2018-02-25 23:52:23 +00:00
rofl0r
057cf06805 config: unify upstream syntax for http,socks4,socks5 and none
closes #50
2018-02-25 23:52:23 +00:00
rofl0r
9cde492d68 configure.ac: remove -pedantic 2018-02-25 23:52:23 +00:00
rofl0r
39132b9787 rename members of proxy_type enum to have a common prefix
and add a NONE member.
2018-02-25 23:52:23 +00:00
rofl0r
86632a91eb fix early logging
tinyproxy uses a curious mechanism to log those early messages
that result from parsing the config file before the logging mechanism
has been properly set up yet by finishing parsing of the config file:
those early messages are written into a memory buffer and then
are printed later on. this slipped my attention when making it possible
to log to stdout in ccbbb81a.
2018-02-25 18:35:34 +00:00
rofl0r
e1c11c47db make send_stored_logs static 2018-02-25 18:35:34 +00:00
rofl0r
bf76aeeba1 implement HTTP basic auth for upstream proxies
loosely based on @valenbg1's code from PR #38

closes #38
closes #96
2018-02-25 15:13:45 +00:00
rofl0r
4d9891e59e basicauth.[ch]: refactor to make basicauth_string() reusable 2018-02-25 15:13:45 +00:00
bertliao
81ea4feb2e fix possible memory leak 2018-02-25 14:56:50 +00:00
John Weldon
c7c88e9c59 Remove #ifdef for HAVE_SYSLOG_H
- syslog.h is a standard posix header, this #ifdef is an artifact
  accidentally left in.
2018-02-23 05:32:33 +00:00
Michael Adam
9acb0cb16c Fix CVE-2017-11747: Create PID file before dropping privileges.
Resolves #106

Signed-off-by: Michael Adam <obnox@samba.org>
2018-02-09 15:21:14 +01:00
rofl0r
af1d7ab510 move base64 code into own file
it will be needed to add support for upstream proxy auth.
2018-02-06 16:57:02 +00:00
rofl0r
bd04ed00d8 Basic Auth: send correct response codes and headers acc. to rfc7235
as reported by @natedogith1
2018-02-06 16:57:02 +00:00
rofl0r
8db511b9bf add support for basic HTTP authentication
using the "BasicAuth" keyword in tinyproxy.conf.

base64 code was written by myself and taken from my own library "libulz".
for this purpose it is relicensed under the usual terms of the tinyproxy
license.
2018-02-06 16:57:02 +00:00
rofl0r
1ebfd2a2d1 tinyproxy.conf.in: add example for SOCKS upstream 2018-02-06 16:11:39 +00:00
rofl0r
7a3fd81a8d fix types used in SOCKS4/5 support code
the line

    len = buff[0]; /* max = 255 */

could lead to a negative length if the value in buff[0] is > 127.
2018-02-06 16:11:39 +00:00
Gonzalo Tornaria
8906b0734e add SOCKS upstream proxy support (socks4/socks5)
original patch submitted in 2006 to debian mailing list:
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=392848%29#12

this version was rebased to git and updated by Russ Dill <russ.dill@gmail.com>
in 2015 (the original patch used a different config file format).

as discussed in #40.

commit message by @rofl0r.
2018-02-06 16:11:39 +00:00
rofl0r
116e59e933 activate reverse proxy support by default
closes #17
2018-02-06 15:22:22 +00:00
rofl0r
9bb699628f safe_write/read: take void* buffer for generic use
if using one of unsigned or signed char for the function prototype, one
gets nasty warnings when using it with the other type. the only proper
solution is to put void* into the prototype, and then specialize the pointer
inside the function using an automatic variable.
for exactly this reason, libc functions like read(), write(), etc use void*
too.
2017-12-04 11:33:01 +00:00
rofl0r
e9e0f0b4f0 configure.ac: remove -Wc++-compat 2017-11-29 19:11:04 +01:00
Brian Cain
08a9fbb041 Add example value for Log Level setting 2017-11-27 18:49:53 +01:00
rofl0r
ccbbb81aa9 log to stdout if no logfile specified
some users want to run tinyproxy on an as-needed basis in a terminal,
without setting it up permanently to run as a daemon/service.
in such use case, it is very annoying that tinyproxy didn't have
an option to log to stdout, so the user has to keep a second terminal
open to `tail -f` the log.

additionally, this precluded usage with runit service supervisor,
which runs all services in foreground and creates logfiles from the
service's stdout/stderr.

since logging to stdout doesn't make sense when daemonized, now if
no logfile is specified and daemon mode activated, a warning is
printed to stderr once, and nothing is logged.
the original idea was to fail with an error message, though some users
might actually want to run tinyproxy as daemon and no logging at all.
2017-11-16 19:26:14 +01:00
rofl0r
64b29c5f4e do not create a pidfile, if none is specified in config
some people want to run tinyproxy with minimal configuration from
the command line (and as non-root), but tinyproxy insists on writing
a pid file, which only makes sense for usage as a service, hereby
forcing the user to either run it as root so it can write to the
default location, or start editing the default config file to work
around it.
and if no pidfile is specified in the config, it frankly doesn't
make sense to force creation of one anyway.
2017-11-16 19:26:14 +01:00
Pablo Panero
d97d486d53 Issue 15 fix. PR 2017-11-16 01:21:43 +01:00
rofl0r
5062b78740 tinyproxy.conf.in: default to allow CONNECT method more broadly
tinyproxy conservatively defaulted to allow CONNECT method only
on two ports used by SSL in the ancient past, but since HTTPS usage
got much more widespread (actually, it's now the default for the
majority of websites), it makes sense now to allow it without
restriction by default to accomodate for the new situation.
2017-11-16 01:08:08 +01:00
Steven Conaway
caf0ff3be8 Update README.md 2017-11-16 01:05:54 +01:00
Michael Adam
86a9a6729e Merge pull request #100 from dmd/patch-1
tinyproxy configure does not actually support enable-static
2017-08-24 22:48:46 +02:00
Daniel M. Drucker
d67c48a49b tinyproxy configure does not actually support enable-static
See #90 and #99
2017-07-18 17:56:03 -04:00
Michael Adam
cb6f868739 Merge pull request #86 from obnoxxx/getipstring1
honor result of inet_ntop in get_ip_string()
2017-03-30 00:52:43 +02:00
Michael Adam
46cbe5357c network: honour result of inet_ntop in get_ip_string()
Signed-off-by: Michael Adam <obnox@samba.org>
2017-03-29 23:57:08 +02:00
Michael Adam
a71cebb094 network: let get_ip_string() return const char * instead of const char
Signed-off-by: Michael Adam <obnox@samba.org>
2017-03-29 23:56:48 +02:00
Michael Adam
f149b62475 Merge pull request #66 from rofl0r/configure_trim
Configure trim
2017-03-29 23:26:45 +02:00
Michael Adam
3f6179a1fd Merge pull request #67 from dmz-uk/patch-1
Prevent child from calling exit() on interrupt
2017-03-29 09:19:55 +02:00
Michael Adam
2c574e9afe Merge pull request #85 from rofl0r/fix_72
src/Makefile.am: fix spaces vs TAB
2017-03-29 09:15:22 +02:00
rofl0r
aade379acb src/Makefile.am: fix spaces vs TAB
this causes a build failure on several platforms using older versions
of autotools or GNU make.

make[2]: Entering directory `src'
Makefile:670: *** missing separator (did you mean TAB instead of 8 spaces?).  Stop.
make[2]: Leaving directory `src'

fixes #72
2017-03-27 18:57:01 +01:00
Michael Adam
1e93411810 Merge pull request #59 from gmp216/pull
Fix OS X build
2017-01-04 16:21:27 +01:00
Greg
1b8a6ef561 configure.ac: add conditional for -z,defs 2017-01-04 09:19:04 -05:00
Greg
802a6aa93f Added conditional for xmllint by testing "a2x" without the -L flag. 2017-01-04 09:18:20 -05:00
Michael Adam
302738dee5 Merge pull request #71 from gmp216/lookup
Move lookup_variable from html-error.c to hashmap.c
2017-01-04 13:59:14 +01:00
dmz-uk
f9024c7186 Prevent child from calling exit() on interrupt
A proposed fix for the logrotate SIGHUP issue.
2016-12-30 10:16:19 +00:00
Greg
fdb1aae3a9 Move lookup_variable into hashmap.c / hashmap.h 2016-12-27 12:23:51 -05:00
Greg
540069551d Change signature for lookup_variable to take map instead of connptr 2016-12-27 12:18:16 -05:00
Michael Adam
e4ae0d10d2 Merge pull request #70 from obnoxxx/compile
add 'compile' to gitignore
2016-12-24 03:09:33 +01:00
Michael Adam
06281857a4 Merge pull request #69 from obnoxxx/readme
README: mention --enable-reverse configure option
2016-12-24 03:07:57 +01:00
Michael Adam
6c98f2cc27 add 'compile' to gitignore
it's generated by autogen

Signed-off-by: Michael Adam <obnox@samba.org>
2016-12-24 03:04:50 +01:00
Michael Adam
ff880a5e13 README: mention --enable-reverse configure option
Signed-off-by: Michael Adam <obnox@samba.org>
2016-12-24 03:02:27 +01:00
Michael Adam
67fc3b54ba Merge pull request #68 from obnoxxx/travis
Enable travis-ci: add .travis.yml
2016-12-24 03:01:34 +01:00
Michael Adam
841fed4afd Enable travis-ci: add .travis.yml
Signed-off-by: Michael Adam <obnox@samba.org>
2016-12-24 02:54:26 +01:00
rofl0r
c98f1cd25a configure: remove checks for standard types 2016-12-21 21:16:06 +00:00
rofl0r
c3cc308cc8 configure: remove checks for standard POSIX funcs
addresses #65
2016-12-21 21:16:06 +00:00
rofl0r
81a93f9e4f configure: do not check for standard POSIX headers
addresses #65
2016-12-21 21:01:37 +00:00
rofl0r
39c92a3f70 configure: do not check for standard C headers
addresses #65
2016-12-21 20:45:06 +00:00
Michael Adam
90df510932 Merge pull request #63 from rofl0r/fix_manpage_gen
ship manpages as part of the dist tarball
2016-12-20 22:27:11 +01:00
rofl0r
909528e29c ship manpages as part of the dist tarball
`make dist` now creates the manpages and puts them into the tarball, so
the user does not need to have `a2x` installed to build them.

closes #2
2016-12-20 21:06:20 +00:00
Michael Adam
4e0951afd9 Merge pull request #62 from obnoxxx/bundle-readme-md
build: add README.md to the bundled files
2016-12-20 19:28:59 +01:00
Michael Adam
2f4e6403f9 build: add README.md to the bundled files
Signed-off-by: Michael Adam <obnox@samba.org>
2016-12-20 19:26:48 +01:00
Michael Adam
1bb255c3f8 Merge pull request #61 from obnoxxx/cleanup-readme
Cleanup readme and fix installation instructions
2016-12-20 19:24:28 +01:00
Michael Adam
85c73d68bd Add placeholder ChangeLog to keep automake happy
Closes #60

Signed-off-by: Michael Adam <obnox@samba.org>
2016-12-20 19:22:17 +01:00
Michael Adam
7f3fe02d47 README: update install instructions
Closes #30

Signed-off-by: Michael Adam <obnox@samba.org>
2016-12-20 19:22:17 +01:00
Michael Adam
271c4ecdcb README: add README pointing to README.md to make automake happy
Signed-off-by: Michael Adam <obnox@samba.org>
2016-12-20 19:22:17 +01:00
Michael Adam
0d3ca13398 README: Rename README --> README.md
Signed-off-by: Michael Adam <obnox@samba.org>
2016-12-20 19:04:56 +01:00
Michael Adam
82ce991b3d REAMDE: convert README to github markdown
Signed-off-by: Michael Adam <obnox@samba.org>
2016-12-20 19:04:47 +01:00
Michael Adam
af6e236521 README: cleanup readme from legacy content
Signed-off-by: Michael Adam <obnox@samba.org>
2016-12-20 19:04:43 +01:00
Michael Adam
e541456ee7 Merge pull request #36 from obnoxxx/issue-21
build: remove foreign from AM_INIT_AUTOMAKE - generate INSTALL again
2016-09-11 00:16:30 +02:00
Michael Adam
60e5fc7c57 build: remove foreign from AM_INIT_AUTOMAKE - generate INSTALL again
Closes #21

Signed-off-by: Michael Adam <obnox@samba.org>
2016-09-11 00:11:21 +02:00
Stephan Leemburg
c5da1cc934 Continue with forward proxy if ReverseOnly is not true and no mapping available (#35)
allow non-reverse mappings if reverseonly is not enabled
2016-09-10 19:22:45 +02:00
John Spencer
186bbc3efb configure.ac: remove check for broken regex which breaks crosscompilation
in the unlikely case that the user's C library has broken regex support,
she should probably update to a bugfree version.
in its full consequence, checking if individual functions works basically
require to test every single function in use, which is nonsensical.
since this check required to compile and run a code sample on the host,
it cannot be checked in cross-compile scenarios and as it defaulted to yes
(broken), causes build failure in any such scenario.

closes #1

Signed-off-by: John Spencer <maillist-tinyproxy@barfooze.de>
Reviewed-by: Michael Adam <obnox@samba.org>
2016-01-19 22:50:20 +01:00
Michael Adam
b036d2e055 Merge pull request #6 from rofl0r/url
update URL
2016-01-03 23:58:17 +01:00
rofl0r
ee83293285 update URL 2016-01-03 21:16:21 +00:00
Michael Adam
800c3a250c BB#110 Increase number of hash buckets from 32 to 256.
This should make hash processing generally faster.

There is a treadeoff between memory footprint and
speed of processing. 10 KB instead of 1.2 KB of
hash table per process should not be a huge problem
even on very limited current systems.

Who really needs to stick to 32 buckets could
recompile. We could also think about making
this configurable at some point.

Signed-off-by: Michael Adam <obnox@samba.org>
2014-12-13 01:41:56 +01:00
Michael Adam
545463c75d BB#110 limit the number of headers per request to prevent DoS
Based on patch provided by gpernot@praksys.org on bugzilla.

Signed-off-by: Michael Adam <obnox@samba.org>
2014-12-13 01:28:07 +01:00
Michael Adam
308305d827 BB#110 secure the hashmaps by adding a seed
Based on a patch provided by gpernot@praksys.org on bugzilla.

Signed-off-by: Michael Adam <obnox@samba.org>
2014-12-13 01:21:02 +01:00
Peter H. Froehlich
ab6255393d BB#110 Replace hash function with Dan Bernstein's.
This hash function distributes much better than the
original one. The effect is not as visible with
hashes taken modulo 32 than with a bigger modulus,
but it is there. And larger number of buckets migh
become possible in the future...

Reviewed-by: Michael Adam <obnox@samba.org>
2014-12-13 01:20:56 +01:00
Mukund Sivaraman
24087f743a Remove suggester (see Banu RT #138) 2014-05-01 13:58:54 +05:30
Michael Adam
b59ecd0c66 buffer: fix log message in read_buffer().
Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-23 12:21:59 +01:00
Michael Adam
ffa3a56ab8 buffer: reduce indentation in read_buffer()
Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-23 12:09:59 +01:00
Michael Adam
76bd008cf9 reqs: fix typo in a debug message in get_request_entity()
Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-23 11:59:47 +01:00
Michael Adam
b3ac7d2c7b transparent: make transparent support compile after introduction of multi Listen
I seem to have forgotten to compile with transparent support enabled...
This belongs to the fix for bug BB#63.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-23 00:18:04 +01:00
Michael Adam
b92d70be07 child: remove use of config.listen_addrs in child_listening_sockets()
This was accidentially used instead of the function parameter listen_addrs
This still belongs to the fix for bug BB#63.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-23 00:17:55 +01:00
Michael Adam
3710accf72 reqs: Fix CID 1130969 (part 3) - unchecked return value from library.
Check the return value of socket_blocking (fcntl) at the
end of relay_connection() for client socket.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-22 21:56:39 +01:00
Michael Adam
e07c363df2 reqs: Fix CID 1130969 (part 2) - unchecked return value from library.
Check the return value of socket_blocking (fcntl) at the
end of relay_connection().

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-22 21:44:12 +01:00
Michael Adam
c82840bfcb reqs: Fix CID 1130972 - remove logically dead code.
url == NULL is caught above.

Found by coverity.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-22 18:58:19 +01:00
Michael Adam
49c55ed26c network: Fix CID 113095 - unchecked return value from library
Check return of "recv" in readline().

Found by coverity.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-22 18:54:22 +01:00
Michael Adam
198600ce42 child: check return code of socket_blocking for accept in child_main
Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-22 18:49:45 +01:00
Michael Adam
38ef36d998 child: Fix CID 1130966 - unchecked return value from library
check the return code of fcntl via socket_nonblocking
on the listen sockets in child_main()

Found by coverity.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-22 18:49:45 +01:00
Michael Adam
0a99803425 reqs: Fix CID 1130967 - unchecked return value from library.
Check the return code of fcntl via socket_blocking
in pull_client_data().

Found by coverity.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-22 18:49:45 +01:00
Michael Adam
9efa5799f0 reqs: Fix CID 1130968 - unchecked return value from library
Check the return code of fcntl via socket_nonblocking
in pull_client_data()

Found by coverity.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-22 18:49:45 +01:00
Michael Adam
c27b6d15e2 reqs: rename a variable.
ret will be used in enclosing scope.
so rename this special varibale.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-22 18:49:45 +01:00
Michael Adam
68bd0b61b5 reqs: fix CID 1130969 - unchecked return code from library
Effectively, the return code of fcntl was not checked
by not checking the return code of socket_nonblocking()
for the server fd.

Found by coverity.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-22 17:35:59 +01:00
Michael Adam
2004abc1e3 reqs: fix CID 1130970 - unchecked return code from library
Effectively, the return code of fcntl was not checked
by not checking the return code of socket_nonblocking()
for the client fd.

Found by coverity.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-22 17:35:54 +01:00
Michael Adam
a244c1d4aa conf: Fix CID 1130973 - resource leak.
Found by coverity.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-22 16:08:47 +01:00
Michael Adam
0f18e4fc3a BB#106: remove now unused extract_ssl_url.
Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-16 15:26:06 +01:00
Michael Adam
9f43cfd488 BB#106: fix CONNECT requsts with IPv6 literal addresses as host.
Use extract_url instead of the old extract_ssl_url:
extract_url is generic and handles ipv6 literal addresses correctly.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-16 15:25:44 +01:00
Michael Adam
98f77ef8c7 BB#106: add default_port argument to extract_http_url and rename it to extract_url
There is in fact nothing http-specific any more about this function, hence
the rename. The input has been stripped of the <proto>:// header anyways.

This in preparation of fixing bug BB#106: ssl fails with literal ipv6 addrs.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-16 15:09:48 +01:00
Michael Adam
69c348ce6d req: move a variable into the scope where it is used in extract_http_url()
Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-16 13:10:03 +01:00
Michael Adam
bb2e894e0d BB#116: fix invalid free when connecting to ipv6 literal address
When removing the '[' and ']' characers from the ipv6 literal address, make sure
the pointer that is later free'd stays a malloced pointer by memmoving the
string one place left.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-16 13:07:19 +01:00
Michael Adam
7e1d8154de build: check for memmove
Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-16 13:07:09 +01:00
Michael Adam
836d4534d6 sock: add debug messages to opensock()
log entering opensock and successful return of getaddrinfo.
This allows to detect dns timeouts from looking at the logs.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-16 11:30:07 +01:00
Michael Adam
e82080a5f6 [BB#63] conf: Allow multiple Listen statements in the config.
This introduces a list (vector) of addresses instead of
having just one address string.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-09 13:34:33 +01:00
Michael Adam
d0732f9ade [BB#81] allow listening on multiple families when no Listen is provided in config
This is achieved by not stopping at the first result of getaddrinfo
that we managed to listen on: Without "Listen" in the config, we
call getraddrinfo with NULL address. With AI_PASSIVE, this gives results
for both IPv4 and IPv6 wildcard addresses (if both are supported).

This lets tinyproxy listen on both IPv4 and IPv6 wildcard if the system
supports them.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-09 13:34:33 +01:00
Michael Adam
e40b91974a sock: add a starting debug message to listen_sock()
Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-09 13:34:33 +01:00
Michael Adam
22587d3d41 sock: update introductory comment for listen_sock()
Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-09 13:34:33 +01:00
Michael Adam
0698e4d180 sock: set IPV6_V6ONLY on the socket before binding an IPv6 address
so that we can bind wildcard for both IPv4 and IPv6.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-09 13:34:33 +01:00
Michael Adam
d7f67768eb sock: factor listening on one socket out of the gai-result-loop in listen_sock()
for clarity of the code

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-09 13:34:33 +01:00
Michael Adam
157879d4f6 sock: in listen_sock(), move variable for setsockopt() into scope
Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-09 13:34:33 +01:00
Michael Adam
a7898a2c98 sock: log each result of getaddrinfo() in listen_sock()
Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-09 13:34:33 +01:00
Michael Adam
947e255d19 sock: in listen_sock(), add a log message for when bind() has failed
Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-09 13:34:33 +01:00
Michael Adam
b41d140984 sock: in listen_sock(), detect and log failure to call setsockopt()
Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-09 13:34:33 +01:00
Michael Adam
5392e9829c sock: in listen_sock(), add debug message when socket() call failed.
Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-09 13:34:33 +01:00
Michael Adam
fa26ad4d56 sock: move listen() into the getaddrinfo result loop in listen_sock()
This also reverses the exit logic of the loop.
It prepares listening on multiple addresses.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-09 13:34:33 +01:00
Michael Adam
2ebfd456ef child: use a list of listen_fds instead of one single listenfd.
This prepares listenting on multiple sockets, which will be ussed to
fix listening on the wildcard (listen on both ipv6 and ipv4) and
help add the support for multiple Listen statements in the config

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-09 13:34:33 +01:00
Michael Adam
070d621534 child: add addr argument to child_listening_sock().
Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-09 13:34:33 +01:00
Michael Adam
2bd919f01e sock: add addr argument to listen_sock()
instead of using config.ipAddr internally.
This is in preparation to make it possible
to call it for multiple addresses.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-09 13:34:33 +01:00
Michael Adam
7eea1638bc sock/child: remove global variable addrlen.
This changes listen_sock() to not return the
addrlen of the used address from getaddrinfo call
to the caller, stored in global addrlen in child.c.

This was only used to be able to allocate enough space for the
arguments to the later accept call depending on whether
IPv4 or IPv6 is used.

This removes the need to pass this info by always allocating
sizeof(struct sockaddr_storage) instead, which is enough
to carry both sockaddr_in and sockaddr_in6.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-09 13:34:33 +01:00
Michael Adam
4bbd6e8626 [BB#109] Fix crash (infinite loop) when writing to log file fails.
Fall back to syslog logging in that case.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-09 13:34:33 +01:00
Michael Adam
d652681e8a log: remove extra newline characters in log messages.
Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-09 13:34:33 +01:00
Gaudenz Steinlin
c8b8247f70 [BB#115] Drop supplementary groups
Supplementary groups are inherited from the calling process. Drop all
supplementary groups if the "Group" configuration directive is set to
change to a different user. Otherwise the process may have more rights
than expected.

Reviewed-by: Michael Adam <obnox@samba.org>
2013-11-01 06:38:53 +01:00
Michael Adam
3cc59ec3be [BB#112] build: fix build with autoconf >= 2.69
Use AC_CONFIG_HEADERS instead of obsolete AM_CONFIG_HEADER.

Signed-off-by: Michael Adam <obnox@samba.org>
2013-04-09 22:35:20 +02:00
Mukund Sivaraman
983d241db6 Update authors 2012-01-23 16:32:35 +05:30
Mukund Sivaraman
2e4ea496b3 build: Prepend to LDFLAGS instead of replacing its contents 2012-01-23 16:25:24 +05:30
Mukund Sivaraman
e5fd1fd556 Make .xz compressed dist targets too 2011-09-11 11:52:21 +05:30
Mukund Sivaraman
32563a4ed6 Bug #103: Move files installed in /etc/ to /etc/tinyproxy/ 2011-08-23 14:46:04 +05:30
Mukund Sivaraman
ec86c15d5a Minor whitespace fix 2011-08-16 17:44:17 +05:30
Mukund Sivaraman
e8426f6662 [BB#90]: Fix bug in ACL netmask generation
Thanks to John Horne who diagnosed this issue and found the problem.
2011-03-04 14:47:05 +05:30
Mukund Sivaraman
97b9984484 Validate port number specified in Port directive
This was asked in bug #90 comment #8.
2011-03-04 14:07:07 +05:30
Mukund Sivaraman
5ad24ba99d Update URLs of Tinyproxy 2011-02-28 12:36:05 +05:30
Mukund Sivaraman
7378c97524 Surround IPv6 literals with [] in Host: headers 2011-02-07 18:00:39 +05:30
Mukund Sivaraman
2d02e2211e Handle IPv6 literals in URLs correctly 2011-02-04 20:28:48 +05:30
Michael Adam
8fd3808ad3 [BB#95] remove two comments that have become wrong by the fix. 2010-12-01 22:25:44 +01:00
Daniel Egger
62443a6391 [BB#95] Fix FilterURLs with transparent proxy support.
Pass a pointer to a char pointer to do_transparent_proxy so the reassembled URL
will actually end up back in the caller where it is needed for filtering
decisions. This fixes the problem that a tinyproxy configured with the
transparent proxy functionality and "FilterURLs Yes" would filter on everything
but the domain.

Signed-off-by: daniel.egger@sphairon.com
Signed-off-by: Michael Adam <obnox@samba.org>
2010-12-01 22:25:44 +01:00
Michael Adam
58ac635a17 README: correctly list --enable-transparent
--enable-transparent-proxy was renamed to --enable-transparent
in August 2004... :-)
2010-12-01 21:40:07 +01:00
Michael Adam
b672ca03fe [BB#91] Fix upstream proxy support.
Patch by Jordi Mallach.
2010-08-24 22:47:35 +02:00
Michael Adam
52cad36352 upstream: clarify debug messages
There are frequent questions "what does 'No proxy for ...' mean?"
on the mailing list and IRC. Be more specific. (No upstream proxy ...)
Correspondingly, log "Found upstream proxy ... for ..."
2010-08-24 22:46:10 +02:00
Mukund Sivaraman
691415a8f9 [BB#74] Create log and pid files after we drop privs 2010-06-02 10:36:05 +05:30
Mukund Sivaraman
56257d3da3 Remove excessive code 2010-06-02 10:11:17 +05:30
Mukund Sivaraman
df9e6d3163 Add authors to authors.xml and regen 2010-06-01 08:16:53 +05:30
John van der Kamp
a7933bdd36 [BB#89] Don't recompile regular expressions
This is a modification of a patch originally written by
John van der Kamp <john@kirika.demon.nl> at
<http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=579427#12>

The modification was done by the committer.
2010-06-01 07:54:55 +05:30
Mukund Sivaraman
50e3a0f6d9 Fix typo in manpage 2010-05-30 10:01:36 +05:30
Mukund Sivaraman
f84fb98766 Precompute network addresses for increased performance 2010-05-30 08:08:04 +05:30
Mukund Sivaraman
0963c4c71f Minor indent of code 2010-05-30 08:01:14 +05:30
Mukund Sivaraman
7c39cdfe1d Use complete sentences in the manpages 2010-04-22 21:14:27 +05:30
Jordi Mallach
7da3e4854c [BB#83] Use output of id instead of $USER 2010-04-21 21:02:57 +05:30
Dmitry Semyonov
7edb6e047c Fix crash in send_stored_logs() 2010-04-21 20:35:50 +05:30
Mukund Sivaraman
d30ee746a7 Revert "main: drop privileges right after reading the config"
This reverts commit 7a9abc2a04. It should
fix the issue in bug #87.
2010-04-21 20:10:56 +05:30
Mukund Sivaraman
1e0ad98f7f Just fix the support URLs 2010-03-28 10:33:38 +05:30
Mukund Sivaraman
34088e2175 Revert "Update Tinyproxy website URLs"
This reverts commit b108162dfb.
2010-03-28 10:27:40 +05:30
Mukund Sivaraman
b108162dfb Update Tinyproxy website URLs 2010-03-27 08:22:51 +05:30
Mukund Sivaraman
d37a6b2c12 Revert "Revert "Pass address family when binding to outgoing socket""
This reverts commit 70885bf029. It looks
like bug #69 needs this fix after all.
2010-03-09 17:08:37 +05:30
Mukund Sivaraman
2f86b79d85 Use AI_PASSIVE flag to make tinyproxy listen on wildcard interface
Signed-off-by: Michael Adam <obnox@samba.org>
2010-03-03 01:03:45 +01:00
Michael Adam
243526d493 Comment out the LogFile and PidFile options in the example tinyproxy.conf.
These are compiled in defaults now.

Michael
2010-03-02 23:41:37 +01:00
Michael Adam
e87c856487 change the default pid file location to "@LOCALSTATEDIR@/run/tinyproxy/tinyproxy.pid"
I.e., add a tinyproxy subdirectory.
This is meant to ease running tinyproxy as non-root user.
The subdirectory can be used to give the tinyproxy user
write permission.

Michael
2010-03-02 23:39:30 +01:00
Michael Adam
cfa5792880 change the default log file location to "@LOCALSTATEDIR@/log/tinyproxy/tinyproxy.log"
i.e. add a tinyproxy subdirectory.
This is meant to ease running tinyproxy as non-root user
the subdirectory can be used to give the tinyproxy user
write permission.

Michael
2010-03-02 23:39:21 +01:00
Michael Adam
f640c6fbb1 main: some tabs->spaces 2010-03-02 22:17:34 +01:00
Michael Adam
fcf5fd2129 main: move a log message. 2010-03-02 22:05:28 +01:00
Michael Adam
7a9abc2a04 main: drop privileges right after reading the config
This is the second part of fixing bug #74.
I lets tinyproxy create its log and pid files as the
user as which it is running, so that later on at SIGHUP,
the log file can successfully be reopened.

Michael
2010-03-02 22:02:46 +01:00
Michael Adam
2235a97f05 main: separate loading of config and setup_logging at startup
This is the first part of a fix for bug #74
(making reloading of config work if running as non-privileged user)

Michael
2010-03-02 21:57:03 +01:00
Michael Adam
f923649a11 tinyproxy.conf: fix LogFile to proper CamelCase for consistency 2010-02-23 08:06:55 +01:00
Michael Adam
3f1632603c tests: fix bug #80: keep track of errors in return codes
Michael
2010-02-23 00:18:29 +01:00
Michael Adam
7c15563430 tests: fix a typo in run_tests.sh 2010-02-23 00:17:21 +01:00
Michael Adam
ee1f1e38f2 test: add make target "make valgrind-test-wait" 2010-02-22 23:31:40 +01:00
Michael Adam
752e4419a6 tests: add a new make target "make test-wait" 2010-02-22 23:17:39 +01:00
Michael Adam
d133eee36b tests: Fix bug #79. Finish "make test" without waiting for user input.
Old behaviour is preserved by passing in the environment variable
TINYPROXY_TESTS_WAIT=yes.

Michael
2010-02-22 23:12:51 +01:00
Mukund Sivaraman
2f6840fd85 Use format string when logging messages 2010-02-19 21:04:02 +05:30
Mukund Sivaraman
719b5f6049 Fix pkgdatadir path in tinyproxy.conf 2010-02-18 00:30:19 +05:30
Mukund Sivaraman
907a6c2420 Display upstream proxy support in usage message 2010-02-17 23:11:45 +05:30
Mukund Sivaraman
c6d4c68c99 Update help text a little 2010-02-17 00:29:29 +05:30
Mukund Sivaraman
3efcfd2345 Also install AUTHORS file in doc directory 2010-01-26 13:26:32 +05:30
Mukund Sivaraman
a9b42c491f Install README and NEWS in doc directory 2010-01-26 12:24:41 +05:30
Mukund Sivaraman
9434b27cc1 Remove bogus static qualifiers for these auto variables 2010-01-25 23:40:03 +05:30
Mukund Sivaraman
2d85a69e49 Remove unnecessary cast to (void **)
Fixes a compiler warning about dereferencing type-punned pointers.
2010-01-25 21:36:18 +05:30
Mukund Sivaraman
0697a9047d Remove obsolete ChangeLog file 2010-01-25 21:16:39 +05:30
Mukund Sivaraman
41d80861e3 Fix typo in manpage 2010-01-25 19:35:30 +05:30
Mukund Sivaraman
19b9bff888 Fix compiler warning about dereferencing type-punned pointers 2010-01-25 13:06:58 +05:30
Mukund Sivaraman
13eb2258e0 Don't check for overlength strings 2010-01-25 12:47:13 +05:30
Mukund Sivaraman
2447e38618 Minor text updates 2010-01-24 11:17:20 +05:30
Mukund Sivaraman
d9d66e6280 Minor text updates to the patch section 2010-01-24 11:07:28 +05:30
Mukund Sivaraman
70885bf029 Revert "Pass address family when binding to outgoing socket"
This reverts commit 65ef313cc4.
This patch could've been the reason for BB#69.
2010-01-21 06:29:15 +05:30
Mukund Sivaraman
9b9d8c10f8 Reformat README for asciidoc 2010-01-19 19:12:24 +05:30
Mukund Sivaraman
b08420ec13 List all authors when license is requested
(Not just recent authors.)
2010-01-19 19:04:39 +05:30
Mukund Sivaraman
e11a01f0b1 Add more authors 2010-01-19 19:03:36 +05:30
Mukund Sivaraman
7ac13d7924 Change to for loop 2010-01-19 05:14:06 +05:30
Mukund Sivaraman
edea7e37d0 Show authors and documenters when license is requested 2010-01-18 19:05:31 +05:30
Mukund Sivaraman
f3fe3d8e79 Add authors.* files to EXTRA_DIST 2010-01-18 18:45:13 +05:30
Mukund Sivaraman
510655e447 Make note about auto-generated file an asciidoc comment 2010-01-18 18:42:10 +05:30
Mukund Sivaraman
9e7dac7996 Update AUTHORS file for dual roles 2010-01-18 18:35:59 +05:30
Mukund Sivaraman
b21270ffa5 Move documenters under Documentation section 2010-01-18 17:45:32 +05:30
Mukund Sivaraman
84d5926bdb Do better asciidoc formatting of AUTHORS file 2010-01-18 17:41:42 +05:30
Mukund Sivaraman
8631a9d531 Update the AUTHORS file
This uses an XML based system now to store author names.
We also keep a pre-generated AUTHORS file checked in.
2010-01-18 17:35:51 +05:30
Mukund Sivaraman
d396e5592b Remove completed items from TODO list 2010-01-18 16:01:08 +05:30
Mukund Sivaraman
ff2a7a2bdc Reset the NEWS file for the master branch 2010-01-17 21:05:13 +05:30
Mukund Sivaraman
26cfec06a0 Bump master version to 1.9.0 2010-01-17 19:21:22 +05:30
103 changed files with 5632 additions and 6441 deletions

View File

@ -0,0 +1,20 @@
---
name: New Issue, Bug report, Question
about: New Issue, Bug report, Question
title: ''
labels: ''
assignees: ''
---
# IMPORTANT NOTICE
Before filing an issue here PLEASE keep in mind that **tinyproxy 1.10.0 and older are no longer supported**.
Do not report issues with 1.10.0 or older, first try latest release 1.11.0, or even better, git master, and see whether the issue is already fixed.
## Tinyproxy version
State the tinyproxy version you're using; whether git master or 1.11.0 stable.
## Issue
Fill in your Issue text here.
A good issue report is detailed and includes full error messages from tinyproxy's output, not "X doesn't work".

36
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,36 @@
name: CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: ./autogen.sh
- run: ./configure
- run: make
- run: make test
test-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- run: brew install automake
- run: ./autogen.sh
- run: ./configure
- run: make
valgrind-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: sudo apt update
- run: sudo apt install --assume-yes valgrind
- run: ./autogen.sh
- run: ./configure --enable-debug --enable-transparent --enable-reverse
- run: make
- run: make test
- run: make valgrind-test

40
.github/workflows/release_tarball.yml vendored Normal file
View File

@ -0,0 +1,40 @@
name: Generate Source Tarball
# Trigger whenever a release is created
on:
release:
types:
- created
jobs:
build:
name: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: archive
id: archive
run: |
sudo apt install -y gperf
rm -rf .git
autoreconf -i
VERSION=$(cat VERSION)
PKGNAME="tinyproxy-$VERSION"
./configure
make dist
echo "tarball_xz=${PKGNAME}.tar.xz" >> "$GITHUB_OUTPUT"
echo "tarball_gz=${PKGNAME}.tar.gz" >> "$GITHUB_OUTPUT"
echo "tarball_bz2=${PKGNAME}.tar.bz2" >> "$GITHUB_OUTPUT"
- name: upload tarballs
uses: softprops/action-gh-release@v2
with:
files: |
${{ steps.archive.outputs.tarball_xz }}
${{ steps.archive.outputs.tarball_gz }}
${{ steps.archive.outputs.tarball_bz2 }}

1
.gitignore vendored
View File

@ -18,5 +18,6 @@ stamp-h1
autom4te.cache
cscope.files
cscope.out
compile
*~
tags

18
.travis.yml Normal file
View File

@ -0,0 +1,18 @@
language: C
dist: trusty
sudo: true
before_install:
- sudo apt-get update -qq
- sudo apt-get install --assume-yes valgrind
script:
- ./autogen.sh
- ./configure
- make
- make test
- make clean
- ./configure --enable-debug --enable-transparent --enable-reverse
- make
- make test
- make valgrind-test

58
AUTHORS
View File

@ -1,19 +1,39 @@
Early History
-------------
Steven Young is the original author and was the primary maintainer of
tinyproxy until version 1.1. He also maintained the project from
version 1.3.1 until 1.4.0.
Robert James Kaes is the current maintainer and lead programmer. He
also maintained the project between versions 1.2 and 1.3.0, and again
starting with version 1.4.0.
Major Additions/Improvements
----------------------------
James E. Flemer <jflemer@acm.jhu.edu> - URL/domain filtering
Kim Holviala <kim@holviala.com> - Reverse proxy support
Petr Lampa <lampa@fit.vutbr.cz> - Transparent proxy support
George Talusan <gstalusan@uwaterloo.ca> - URL/domain filtering
Andrew Stribblehill
bertliao
Bob Showalter
Brian Cain
cvs2svn
Daniel Egger
Daniel M. Drucker
David Shanks
Dmitry Semyonov
dmz-uk
Drew G. Wallace
Frank Morgner
gary-wzl77
Gaudenz Steinlin
goba62000374
Gonzalo Tornaria
Greg
Jeremy Hinegardner
John Spencer
John van der Kamp
John Weldon
Jordi
Jordi Mallach
Julien Hartmann
kikuchan
Mathew Mrosko
Matthew Dempsky
Michael Adam
Mike Mead
Mukund Sivaraman
Pablo Panero
Peter H. Froehlich
Robert James Kaes
rofl0r
Stephan Leemburg
Steven Conaway
Steven Young
Valen Blanco
Vladimir Belov

2156
ChangeLog

File diff suppressed because it is too large Load Diff

View File

@ -4,18 +4,34 @@ SUBDIRS = \
etc \
docs \
m4macros \
tests
tests \
scripts
# tools want this on a single line
ACLOCAL_AMFLAGS = -I m4macros
all-local:
dist_doc_DATA = \
AUTHORS \
NEWS \
README \
README.md
EXTRA_DIST = \
autogen.sh \
tinyproxy-indent.sh \
TODO
TODO \
VERSION
test: all
./tests/scripts/run_tests.sh
test-wait:
TINYPROXY_TESTS_WAIT=yes $(MAKE) test
valgrind-test: all
./tests/scripts/run_tests_valgrind.sh
valgrind-test-wait:
TINYPROXY_TESTS_WAIT=yes $(MAKE) valgrind-test

82
NEWS
View File

@ -1,81 +1 @@
Tinyproxy NEWS
==============
Version 1.8.0
-------------
* Tinyproxy now reloads its configuration upon SIGHUP signal.
* Tinyproxy reopens its log file (instead of truncation) upon SIGHUP
signal. This is to play more nicely with logrotate.
* File logging is now the default.
Syslog is chosen if and only if "SysLog Yes" is in the config,
i.e., a present "SysLog Yes" in the config file now overrides
any LogFile setting.
* The XTinyProxy option is now documented as a global boolean.
Before it was documented to build a list of sites to add a
X-Tinyproxy header for, but it was implemented as global boolean.
* A new config option AddHeader allows the user to configure a list of
custom headers to send in outgoing HTTP requests.
* A new config option DisableViaHeader allows the user to disable
sending of the "Via:" header.
* Tinyproxy is now IPv6 capable.
* The config option PidFile now has a compiled in default.
Bugs fixed since version 1.7.1
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* BB#9: Add support for the IPv6 protocol
* BB#17: Add support for custom headers
* BB#55: Error message response omits body when request has a body
* BB#60: Add config option to disable Via header
* BB#61: SIGHUP does not refresh filter list
* BB#62: Make tinyproxy reload the config upon SIGHUP
* BB#64: Config parsing error with reverse proxy option
* BB#65: Format string compile warnings
* BB#67: ACL processing error with multiple Allow statements
Contributors
~~~~~~~~~~~~
David Shanks, Mathew Mrosko, Michael Adam, Mukund Sivaraman.
Version 1.7.1
-------------
* Fixed all warnings reported by GCC.
* The tinyproxy manpage has been extended and converted to asciidoc.
* There is a new tinyproxy.conf manpage that describes all the options.
* The build system has been considerably cleaned up.
* Various other bugs have been fixed.
Bugs fixed since version 1.7.0
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* BB#2: Fix Tinyproxy for requests like www.site.com:8001
* BB#5: Move templates from the doc directory to its own directory
* BB#8: Update README, INSTALL, NEWS and the manpage
* BB#10: Do not filter out transfer-encoding header
* BB#18: Fix pointer aliasing issues
* BB#53: Add a GPLv2 COPYING file
Contributors
~~~~~~~~~~~~
Andrew Stribblehill, Jeremy Hinegardner, Matthew Dempsky, Michael Adam,
Mukund Sivaraman, Robert James Kaes.
Version 1.7.0
-------------
* There is now support for reverse proxying.
* Tinyproxy does not bundle a vendor regular expressions library
anymore. It uses the system installed regular expressions library.
* The documentation has been updated.
* Tinyproxy now contains some code optimizations such as the use of a
hashmap internally for looking up error pages.
* Various other bugs have been fixed.
Contributors
~~~~~~~~~~~~
Kim Holviala, Marc Silver, Robert James Kaes, Steven Young.
See git log for recent changes in Tinyproxy.

98
README
View File

@ -1,97 +1 @@
Tinyproxy
=========
Tinyproxy is a small, efficient HTTP/SSL proxy daemon released under the
GNU General Public License. Tinyproxy is very useful in a small network
setting, where a larger proxy would either be too resource intensive, or
a security risk. One of the key features of Tinyproxy is the buffering
connection concept. In effect, Tinyproxy will buffer a high speed
response from a server, and then relay it to a client at the highest
speed the client will accept. This feature greatly reduces the problems
with sluggishness on the Internet. If you are sharing an Internet
connection with a small network, and you only want to allow HTTP
requests to be allowed, then Tinyproxy is a great tool for the network
administrator.
For more info, please visit:
https://www.banu.com/tinyproxy/
Installation
------------
To install this package under a UNIX derivative, read the INSTALL file.
Tinyproxy uses a standard GNU `configure` script. Basically you should
be able to do:
----
./configure
make
make install
----
in the top level directory to compile and install Tinyproxy. There are
additional command line arguments you can supply to `configure`. They
include:
--enable-debug If you would like to turn on full
debugging support
--enable-xtinyproxy Compile in support for the XTinyproxy
header, which is sent to any web
server in your domain.
--enable-filter Allows Tinyproxy to filter out certain
domains and URLs.
--enable-upstream Enable support for proxying connections
through another proxy server.
--enable-transparent-proxy
Allow Tinyproxy to be used as a
transparent proxy daemon
--enable-static Compile a static version of Tinyproxy
--with-stathost=HOST Set the default name of the stats host
Support
-------
If you are having problems with Tinyproxy, please submit a bug report
using Tinyproxy as the product at:
https://www.banu.com/bugzilla/
You may also wish to subscribe to the Tinyproxy mailing lists. To do so
please visit:
https://www.banu.com/mailman/listinfo/tinyproxy-announce-list
https://www.banu.com/mailman/listinfo/tinyproxy-users-list
https://www.banu.com/mailman/listinfo/tinyproxy-developers-list
for more information on how to subscribe and post messages to the lists.
Contributing
------------
If you would like to contribute a feature, or a bug fix to the Tinyproxy
source, please send a patch (preferably as a unified diff. i.e. `diff
-u` against the "master" branch of the Tinyproxy source code git
repository to tinyproxy-developers-list. Please include a description
of what your patch does.
Tinyproxy's git repository is git://www.banu.com/tinyproxy.git. The
following command creates a local copy:
----
git clone git://www.banu.com/tinyproxy.git
----
The easiest and preferred way to create a patch for submission is to
check in your changes locally and use `git format-patch` to generate a
mbox-style commit file that contains the diff along with the commit
message and author information. Such a format-patch file can be
integrated into the upstream repository, automatically keeping the
commit message and author information.
You can also meet developers and discuss development issues and patches
in the #tinyproxy IRC channel on Freenode (irc.freenode.net).
see README.md

92
README.md Normal file
View File

@ -0,0 +1,92 @@
# Tinyproxy
Tinyproxy is a small, efficient HTTP/SSL proxy daemon released under the
GNU General Public License. Tinyproxy is very useful in a small network
setting, where a larger proxy would either be too resource intensive, or
a security risk. One of the key features of Tinyproxy is the buffering
connection concept. In effect, Tinyproxy will buffer a high speed
response from a server, and then relay it to a client at the highest
speed the client will accept. This feature greatly reduces the problems
with sluggishness on the Internet. If you are sharing an Internet
connection with a small network, and you only want to allow HTTP
requests to be allowed, then Tinyproxy is a great tool for the network
administrator.
For more info, please visit [the Tinyproxy web site](https://tinyproxy.github.io/).
## Installation
Tinyproxy uses a standard GNU `configure` script based on the automake
system. If compiling from a git checkout, you need to first run
```
./autogen.sh
```
from the top level directory to generate the `configure` script.
The release tarball contains the pre-created `configure` script,
so when building from a release, you can skip this step.
Then basically all you need to do is
```
./configure
make
make install
```
in the top level directory to compile and install Tinyproxy. There are
additional command line arguments you can supply to `configure`. They
include:
- `--enable-debug`:
If you would like to turn on full debugging support.
- `--enable-xtinyproxy`:
Compile in support for the XTinyproxy header, which is sent to any
web server in your domain.
- `--enable-filter`:
Allows Tinyproxy to filter out certain domains and URLs.
- `--enable-upstream`:
Enable support for proxying connections through another proxy server.
- `--enable-transparent`:
Allow Tinyproxy to be used as a transparent proxy daemon.
Unlike other work modes, transparent proxying doesn't require explicit
configuration and works automatically when traffic is redirected to
the proxy using the appropriate firewall rules.
- `--enable-reverse`:
Enable reverse proxying.
- `--with-stathost=HOST`:
Set the default name of the stats host.
For more information about the build system, read the INSTALL file
that is generated by `autogen.sh` and comes with the release tar ball.
## Support
If you are having problems with Tinyproxy, please raise an
[issue on github](https://github.com/tinyproxy/tinyproxy/issues).
## Contributing
If you would like to contribute a feature, or a bug fix to the Tinyproxy
source, please clone the
[git repository from github](https://github.com/tinyproxy/tinyproxy.git)
and create a [pull request](https://github.com/tinyproxy/tinyproxy/pulls).
## Community
You can meet developers and users to discuss development,
patches and deployment issues in the `#tinyproxy` IRC channel on
libera (`irc.libera.chat`).

28
SECURITY.md Normal file
View File

@ -0,0 +1,28 @@
# Security Policy
## Supported Versions
| Version | Supported |
| --------- | ------------------ |
| 1.11.x | :white_check_mark: |
| <= 1.10.x | :x: |
## Reporting a Vulnerability
Open a public issue on github. The issue will most likely be fixed
within a day, unless all maintainers happen to just be taking a
vacation at the same time, which is unlikely.
Even then, having the bug publicly known will allow competent people
to come up with custom patches for distros, most likely quicker
than black hats can craft a remote execution exploit.
If you really really do not want to make the issue public, come
to the tinyproxy IRC channel and ask for a maintainer, which you
can then contact via private messages.
Do not, however, like ["TALOS Intelligence"](https://talosintelligence.com/vulnerability_reports/TALOS-2023-1889)
pull a random email address out of git log, then send an email
nobody reads or responds to, and wait for 6 months for publication.
this only gives black hats plenty time to sell, use and circulate
zero days and get the best possible ROI.

8
TODO
View File

@ -38,16 +38,10 @@ against the current tree and I'll integrate it if possible.
* Include user authentication for accessing tinyproxy itself.
Administrators should be allowed to selectively allow certain users
access to tinyproxy via a user name/password pair. Check the
HTTP/1.1 RFC for more information. [Suggested: Tyrone Tranmer]
HTTP/1.1 RFC for more information.
==> https://www.banu.com/bugzilla/show_bug.cgi?id=13
* Fix paths inside etc/tinyproxy.conf
* Finish manpages
* Move defaults handling to conf.c
* Remove common.h and fix order of headers
* Remove memory debugging functions (Valgrind is good enough)

1
VERSION Normal file
View File

@ -0,0 +1 @@
1.11.2

View File

@ -3,57 +3,27 @@
AC_PREREQ(2.54)
m4_define([tinyproxy_major_version], [1])
m4_define([tinyproxy_minor_version], [8])
m4_define([tinyproxy_micro_version], [0])
m4_define([tinyproxy_real_version],
[tinyproxy_major_version.tinyproxy_minor_version.tinyproxy_micro_version])
m4_define([tinyproxy_version], [tinyproxy_real_version])
# For overriding the version string. Comment out if not needed.
# m4_define([tinyproxy_version], [1.8.0])
m4_define([tinyproxy_unstable],
m4_if(m4_eval(tinyproxy_minor_version % 2), [1], [yes], [no]))
m4_define([tinyproxy_stable],
m4_if(m4_eval(tinyproxy_minor_version % 2), [0], [yes], [no]))
m4_define([tinyproxy_version], esyscmd(sh scripts/version.sh | tr -d '\n'))
AC_INIT([Tinyproxy], [tinyproxy_version],
[https://www.banu.com/bugzilla/enter_bug.cgi?product=tinyproxy],
[https://tinyproxy.github.io/],
[tinyproxy])
tpv=tinyproxy_version
if test "x$tpv" = "x" ; then
AC_MSG_ERROR([got empty result from version script!])
fi
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([dist-bzip2])
AM_CONFIG_HEADER(config.h)
AM_INIT_AUTOMAKE([dist-bzip2 dist-xz])
AC_CONFIG_HEADERS(config.h)
AC_CONFIG_MACRO_DIR([m4macros])
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
TINYPROXY_MAJOR_VERSION=tinyproxy_major_version
TINYPROXY_MINOR_VERSION=tinyproxy_minor_version
TINYPROXY_MICRO_VERSION=tinyproxy_micro_version
TINYPROXY_REAL_VERSION=tinyproxy_real_version
TINYPROXY_VERSION=tinyproxy_version
TINYPROXY_UNSTABLE=tinyproxy_unstable
AC_SUBST(TINYPROXY_MAJOR_VERSION)
AC_SUBST(TINYPROXY_MINOR_VERSION)
AC_SUBST(TINYPROXY_MICRO_VERSION)
AC_SUBST(TINYPROXY_REAL_VERSION)
AC_SUBST(TINYPROXY_VERSION)
AC_SUBST(TINYPROXY_UNSTABLE)
dnl Temporarily defined here until we get tinyproxy-version.h
AC_DEFINE(TINYPROXY_VERSION, "tinyproxy_version", [Tinyproxy version number])
# The symbol TINYPROXY_UNSTABLE is defined above for substitution in
# Makefiles and conditionally defined here as a preprocessor symbol
# and automake conditional.
if test "x$TINYPROXY_UNSTABLE" = "xyes"; then
AC_DEFINE(TINYPROXY_UNSTABLE, 1,
[Define to 1 if this is an unstable version of Tinyproxy])
fi
AM_CONDITIONAL(TINYPROXY_UNSTABLE, test "x$TINYPROXY_UNSTABLE" = "xyes")
dnl Check if we're compiling on a weird platform :)
AC_USE_SYSTEM_EXTENSIONS
@ -113,8 +83,8 @@ dnl Include support for reverse proxy?
AH_TEMPLATE([REVERSE_SUPPORT],
[Include support for reverse proxy.])
TP_ARG_ENABLE(reverse,
[Enable reverse proxying (default is NO)],
no)
[Enable reverse proxying (default is YES)],
yes)
if test x"$reverse_enabled" = x"yes"; then
ADDITIONAL_OBJECTS="$ADDITIONAL_OBJECTS reverse-proxy.o"
AC_DEFINE(REVERSE_SUPPORT)
@ -124,17 +94,21 @@ dnl Include the transparent proxy support
AH_TEMPLATE([TRANSPARENT_PROXY],
[Include support for using tinyproxy as a transparent proxy.])
TP_ARG_ENABLE(transparent,
[Enable transparent proxying code (default is NO)],
no)
[Enable transparent proxying code (default is YES)],
yes)
if test x"$transparent_enabled" = x"yes"; then
ADDITIONAL_OBJECTS="$ADDITIONAL_OBJECTS transparent-proxy.o"
AC_DEFINE(TRANSPARENT_PROXY)
fi
dnl Check for broken regex library
TP_ARG_ENABLE(regexcheck,
[Check for working regex library (default is YES)],
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
AC_PROG_CC
@ -166,128 +140,78 @@ dnl
AC_HEADER_STDC
AC_HEADER_TIME
AC_HEADER_SYS_WAIT
AC_CHECK_HEADERS([sys/ioctl.h sys/mman.h sys/resource.h \
sys/select.h sys/socket.h sys/time.h sys/uio.h \
sys/un.h arpa/inet.h netinet/in.h \
assert.h ctype.h errno.h fcntl.h grp.h io.h libintl.h \
netdb.h pwd.h regex.h signal.h stdarg.h stddef.h stdio.h \
sysexits.h syslog.h time.h wchar.h wctype.h \
values.h])
dnl OpenBSD machines don't like having malloc included (even if it's present)
dnl as they expect you to use stdlib.h
case "$target" in
*-openbsd*) ;;
*) AC_CHECK_HEADER(malloc.h);;
esac
dnl Checks for types
AC_TYPE_SIZE_T
AC_TYPE_PID_T
AC_UNP_CHECK_TYPE(uint8_t, unsigned char)
AC_UNP_CHECK_TYPE(int16_t, short)
AC_UNP_CHECK_TYPE(uint16_t, unsigned short)
AC_UNP_CHECK_TYPE(int32_t, int)
AC_UNP_CHECK_TYPE(uint32_t, unsigned int)
AC_UNP_CHECK_TYPE(ssize_t, int)
AC_UNP_CHECK_TYPE(socklen_t, unsigned int)
AC_UNP_CHECK_TYPE(in_addr_t, uint32_t)
AC_CHECK_HEADERS([sys/ioctl.h alloca.h memory.h malloc.h sysexits.h \
values.h poll.h])
dnl Checks for libary functions
AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK
AC_FUNC_MALLOC
AC_FUNC_REALLOC
AC_CHECK_FUNCS([gethostname inet_ntoa memchr memset select socket strcasecmp \
strchr strdup strerror strncasecmp strpbrk strstr strtol])
AC_CHECK_FUNCS([isascii memcpy setrlimit ftruncate regcomp regexec])
AC_CHECK_FUNCS([strlcpy strlcat])
AC_CHECK_FUNCS([strlcpy strlcat setgroups])
dnl Enable extra warnings
DESIRED_FLAGS="-fdiagnostics-show-option -Wall -Wextra -Wno-unused-parameter -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations -Wfloat-equal -Wundef -Wformat=2 -Wlogical-op -Wmissing-include-dirs -Wformat-nonliteral -Wold-style-definition -Wpointer-arith -Waggregate-return -Winit-self -Wpacked --std=c89 -ansi -pedantic -Wc++-compat -Wno-long-long -Wno-overlength-strings -Wdeclaration-after-statement -Wredundant-decls -Wmissing-noreturn -Wshadow -Wendif-labels -Wcast-qual -Wcast-align -Wwrite-strings -Wp,-D_FORTIFY_SOURCE=2 -fno-common"
DESIRED_FLAGS="-fdiagnostics-show-option -Wall -Wextra -Wno-unused-parameter -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations -Wfloat-equal -Wundef -Wformat=2 -Wlogical-op -Wmissing-include-dirs -Wformat-nonliteral -Wold-style-definition -Wpointer-arith -Waggregate-return -Winit-self -Wpacked --std=c89 -ansi -Wno-overlength-strings -Wno-long-long -Wno-overlength-strings -Wdeclaration-after-statement -Wredundant-decls -Wmissing-noreturn -Wshadow -Wendif-labels -Wcast-qual -Wcast-align -Wwrite-strings -Wp,-D_FORTIFY_SOURCE=2 -fno-common"
if test -n "${MAINTAINER_MODE_FALSE}"; then
DESIRED_FLAGS="-Werror $DESIRED_FLAGS"
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
AS_COMPILER_FLAG([$flag], [CFLAGS="$CFLAGS $flag"])
done
fi
dnl Disable debugging if it's not specified
if test x"$debug_enabled" != x"yes" ; then
CFLAGS="-DNDEBUG $CFLAGS"
fi
LDFLAGS="-Wl,-z,defs"
dnl
dnl Make sure we can actually handle the "--with-*" and "--enable-*" stuff.
dnl
dnl Handle the REGEX library
if test x"$ac_cv_func_regexec" != x"yes"; then
AC_MSG_ERROR([Could not locate the regexec() function])
else
if test x"$regexcheck_enabled" = x"yes" ; then
AC_MSG_CHECKING([whether the system's regex library is broken])
AC_CACHE_VAL(tinyproxy_cv_regex_broken,
[AC_TRY_RUN([
#if HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#if HAVE_REGEX_H
# include <regex.h>
#endif
#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
int main(void)
{
regex_t blah;
if (regcomp(&blah, "foo.*bar", REG_NOSUB) != 0)
exit(1);
if (regexec(&blah, "foobar", 0, NULL, 0) != 0)
exit(1);
else
exit(0);
return 0;
}
],
tinyproxy_cv_regex_broken=no,
tinyproxy_cv_regex_broken=yes,
tinyproxy_cv_regex_broken=yes)])
AC_MSG_RESULT([$tinyproxy_cv_regex_broken])
if test x"$tinyproxy_cv_regex_broken" = x"yes" ; then
AC_MSG_ERROR([Your system's regexec() function is broken.])
fi
fi
fi
dnl
dnl Substitute the variables into the various Makefiles
dnl
# runstatedir isn't available for Autoconf < 2.70
AS_IF([test -z "${runstatedir}"], [runstatedir='${localstatedir}/run'])
AC_SUBST([runstatedir])
AC_SUBST(CFLAGS)
AC_SUBST(LDFLAGS)
AC_SUBST(CPPFLAGS)
AC_SUBST(LIBS)
AC_SUBST(ADDITIONAL_OBJECTS)
# Check for asciidoc
AC_PATH_PROG(A2X, a2x, no)
AM_CONDITIONAL(HAVE_A2X, test "x$A2X" != "xno")
if test x"$A2X" = x"no"; then
AC_MSG_ERROR([Test for asciidoc failed. See the file 'INSTALL' for help.])
if test x"$manpage_support_enabled" = x"yes"; then
AC_PATH_PROG(POD2MAN, pod2man, no)
if test "x$POD2MAN" = "xno" && \
! test -e docs/man5/tinyproxy.conf.5 -a -e docs/man8/tinyproxy.8 ; then
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
AM_CONDITIONAL(HAVE_POD2MAN, test "x$POD2MAN" != "x" -a "x$POD2MAN" != "xno")
AC_PATH_PROG(GPERF, gperf, no)
AH_TEMPLATE([HAVE_GPERF],
[Whether you have gperf installed for faster config parsing.])
tmp_gperf=false
if test "x$GPERF" != "x" -a "x$GPERF" != "xno" ; then
AS_ECHO_N(["checking whether gperf is recent enough... "])
if "$GPERF" < src/conf-tokens.gperf >/dev/null 2>&1 ; then
AS_ECHO("yes")
AC_DEFINE(HAVE_GPERF)
tmp_gperf=true
else
AS_ECHO("no")
fi
fi
AM_CONDITIONAL(HAVE_GPERF, $tmp_gperf)
AC_CONFIG_FILES([
Makefile
@ -299,10 +223,31 @@ docs/Makefile
docs/man5/Makefile
docs/man5/tinyproxy.conf.txt
docs/man8/Makefile
docs/man8/tinyproxy.txt
m4macros/Makefile
tests/Makefile
tests/scripts/Makefile
scripts/Makefile
])
AC_OUTPUT
# the manpages are shipped in the release tarball and we don't want them to
# get regenerated if pod2man is not available. the intermediate files from
# 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
# make from rebuild them.
if test "x$POD2MAN" = "xno" ; then
touch docs/man5/tinyproxy.conf.txt
touch docs/man8/tinyproxy.txt
if test -e docs/man5/tinyproxy.conf.5 ; then
touch docs/man5/tinyproxy.conf.5
fi
if test -e docs/man8/tinyproxy.8 ; then
touch docs/man8/tinyproxy.8
fi
fi
if test "x$HAVE_GPERF" = "xno" && test -e src/conf-tokens-gperf.inc ; then
touch src/conf-tokens-gperf.inc
fi

View File

@ -30,9 +30,6 @@
<dt>clienthost</dt>
<dd>{clienthost}</dd>
<dt>version</dt>
<dd>{version}</dd>
<dt>package</dt>
<dd>{package}</dd>
@ -49,7 +46,7 @@
<hr />
<p><em>Generated by <a href="{website}">{package}</a> version {version}.</em></p>
<p><em>Generated by <a href="{website}">{package}</a>.</em></p>
</body>

View File

@ -16,7 +16,7 @@
<hr />
<p><em>Generated by <a href="{website}">{package}</a> version {version}.</em></p>
<p><em>Generated by <a href="{website}">{package}</a>.</em></p>
</body>

View File

@ -1,69 +1,95 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<html lang="en">
<head>
<title>Stats [{package}]</title>
<meta charset="UTF-8" />
<style type="text/css">
body {
color: #eee;
background: #110d0d;
text-align: center;
font: 12pt/1.6 Open Sans, Segoe UI, sans-serif;
}
#container {
position: absolute;
top: 0;
left: 0;
margin: 0;
width: 100%;
height: 100%;
display: table;
}
#inner {
width: 100%;
display: table-cell;
vertical-align: middle;
}
table {
width: auto;
margin: auto;
height: auto;
background: #222020;
border: 1px solid #777373;
border-spacing: 3px;
}
th,
td {
padding: 6px 18px;
}
th {
font-weight: 700;
background: linear-gradient(to bottom, #777373, #555151);
}
.odd {
background: #444040;
}
.even {
background: #555151;
}
.center {
text-align: center;
}
.right {
text-align: right;
font-weight: 600;
}
</style>
</head>
<head>
<title>{package} version {version} run-time statistics</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<body>
<div id="container">
<div id="inner">
<table>
<tr>
<th colspan="2">{package} statistics</th>
</tr>
<tr class="odd">
<td class="right">Open connections</td>
<td class="center">{opens}</td>
</tr>
<style type="text/css" media="screen">
<!--/*--><![CDATA[<!--*/
<tr class="even">
<td class="right">Bad connections</td>
<td class="center">{badconns}</td>
</tr>
th, td
{
text-align: left;
padding: 0.5em;
border: 1px solid gray;
}
<tr class="odd">
<td class="right">Denied connections</td>
<td class="center">{deniedconns}</td>
</tr>
/*]]>*/-->
</style>
</head>
<body>
<h1>{package} version {version} run-time statistics</h1>
<table>
<tr>
<th>Name</th>
<th>Value</th>
</tr>
<tr>
<td>Number of open connections</td>
<td>{opens}</td>
</tr>
<tr>
<td>Number of requests</td>
<td>{reqs}</td>
</tr>
<tr>
<td>Number of bad connections</td>
<td>{badconns}</td>
</tr>
<tr>
<td>Number of denied connections</td>
<td>{deniedconns}</td>
</tr>
<tr>
<td>Number of refused connections due to high load</td>
<td>{refusedconns}</td>
</tr>
</table>
<hr />
<p><em>Generated by <a href="{website}">{package}</a> version {version}.</em></p>
</body>
<tr class="even">
<td class="right">Refused (high load)</td>
<td class="center">{refusedconns}</td>
</tr>
<tr class="odd">
<td class="right">Total requests</td>
<td class="center">{reqs}</td>
</tr>
</table>
</div>
</div>
</body>
</html>

View File

@ -4,6 +4,5 @@ SUBDIRS = \
EXTRA_DIST = \
http-error-codes.txt \
http-rfcs.txt \
filter-howto.txt
http-rfcs.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,20 +1,25 @@
if HAVE_MANPAGE_INTEREST
MAN5_FILES = \
tinyproxy.conf.txt
endif
A2X_ARGS = \
-d manpage \
-f manpage
M_SECTION=5
M_NAME=TINYPROXY.CONF
man_MANS = \
$(MAN5_FILES:.txt=.5)
.txt.5:
if HAVE_A2X
$(AM_V_GEN) $(A2X) $(A2X_ARGS) $<
if HAVE_POD2MAN
$(AM_V_GEN) $(POD2MAN) --center="Tinyproxy manual" \
--section=$(M_SECTION) --name=$(M_NAME) --release="Version @VERSION@" \
$< > $@
else
@echo "*** a2x (asciidoc) is required to regenerate $(@) ***"; exit 1;
@echo "*** pod2man is required to regenerate $(@) ***"; exit 1;
endif
CLEANFILES = \
$(MAN5_FILES:.txt=.5) \
$(MAN5_FILES:.txt=.xml)
MAINTAINERCLEANFILES = \
$(MAN5_FILES:.txt=.5)
EXTRA_DIST = \
$(MAN5_FILES:.txt=.5)

View File

@ -1,24 +1,20 @@
TINYPROXY.CONF(5)
=================
:man source: Version @VERSION@
:man manual: Tinyproxy manual
=pod
NAME
----
=encoding utf8
=head1 NAME
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
command line). This manpage describes the syntax and contents of the
configuration file.
@ -26,345 +22,419 @@ configuration file.
The Tinyproxy configuration file contains key-value pairs, one per
line. Lines starting with `#` and empty lines are comments and are
ignored. Keywords are case-insensitive, whereas values are
case-sensitive. Values may be enclosed in double-quotes (") if they
contain spaces.
case-sensitive. Some string values must be enclosed in double
quotes (") as noted below.
The possible keywords and their descriptions are as follows:
*User*::
=over 4
The user which the Tinyproxy process should run as, after the
initial port-binding has been done as the `root` user. Either the
user name or the UID may be specified.
=item B<User>
*Group*::
The user which the Tinyproxy process should run as, after the
initial port-binding has been done as the `root` user. Either the
user name or the UID may be specified.
The group which the Tinyproxy process should run as, after the
initial port-binding has been done as the `root` user. Either the
group name or the GID may be specified.
=item B<Group>
*Port*::
The group which the Tinyproxy process should run as, after the
initial port-binding has been done as the `root` user. Either the
group name or the GID may be specified.
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
`root` user.
=item B<Port>
*Listen*::
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
`root` user.
By default, Tinyproxy listens for connections on all available
interfaces (i.e. it listens on the wildcard address `0.0.0.0`).
With this configuration parameter, Tinyproxy can be told to listen
only on one specific address.
=item B<Listen>
*Bind*::
By default, Tinyproxy listens for connections on all available
interfaces (i.e. it listens on the wildcard address `0.0.0.0`).
With this configuration parameter, Tinyproxy can be told to listen
only on one specific address.
This allows you to specify which address Tinyproxy will bind
to for outgoing connections to web servers or upstream proxies.
=item B<Bind>
*BindSame*::
This allows you to specify which address Tinyproxy will bind
to for outgoing connections.
This parameter may be specified multiple times, then Tinyproxy
will try all the specified addresses in order.
If this boolean parameter is set to `yes`, then Tinyproxy will
bind the outgoing connection to the IP address of the incoming
connection that triggered the outgoing request.
=item B<BindSame>
*Timeout*::
If this boolean parameter is set to `yes`, then Tinyproxy will
bind the outgoing connection to the IP address of the incoming
connection that triggered the outgoing request.
The maximum number of seconds of inactivity a connection is
allowed to have before it is closed by Tinyproxy.
=item B<Timeout>
*ErrorFile*::
The maximum number of seconds of inactivity a connection is
allowed to have before it is closed by Tinyproxy.
This parameter controls which HTML file Tinyproxy returns when a
given HTTP error occurs. It takes two arguments, the error number
and the location of the HTML error file.
=item B<ErrorFile>
*DefaultErrorFile*::
This parameter controls which HTML file Tinyproxy returns when a
given HTTP error occurs. It takes two arguments, the error number
and the location of the HTML error file. Enclose the file location
in double quotes.
This parameter controls the HTML template file returned when an
error occurs for which no specific error file has been set.
=item B<DefaultErrorFile>
*StatHost*::
The HTML template file returned when an error occurs for which no
specific error file has been set. Enclose in double quotes.
This configures the host name or IP address that is treated
as the `stat host`: Whenever a request for this host is received,
Tinyproxy will return an internal statistics page instead of
forwarding the request to that host. The template for this
page can be configured with the `StatFile` configuration option.
The default value of `StatHost` is `@TINYPROXY_STATHOST@`.
=item B<StatHost>
*StatFile*::
The host name or IP address that is treated as the `stat host`.
Enclose in double quotes. Whenever Tinyproxy receives a request for
the `stat host` it returns an internal statistics page instead of
forwarding the request to that host. The template for this page can be
configured with the `StatFile` configuration option. The default value
of `StatHost` is `@TINYPROXY_STATHOST@`.
This configures the HTML file that Tinyproxy sends when
a request for the stathost is received. If this parameter is
not set, Tinyproxy returns a hard-coded basic statistics page.
See the STATHOST section in the `tinyproxy(8)` manual page
for details.
+
Note that the StatFile and the error files configured with ErrorFile
and DefaultErrorFile are template files that can contain a few
template variables that Tinyproxy expands prior to delivery.
Examples are "\{cause}" for an abbreviated error description and
"\{detail}" for a detailed error message. The `tinyproxy(8)`
manual page contains a description of all template variables.
=item B<StatFile>
*LogFile*::
The HTML file that Tinyproxy sends in response to a request for the
`stat host`. Enclose in double quotes. If this parameter is not set,
Tinyproxy returns a hard-coded basic statistics page. See the STATHOST
section in the L<tinyproxy(8)> manual page for details.
This controls the location of the file to which Tinyproxy
writes its debug output. Alternatively, Tinyproxy can log
to syslog -- see the Syslog option.
Note that the StatFile and the error files configured with ErrorFile
and DefaultErrorFile are template files that can contain a few
template variables that Tinyproxy expands prior to delivery.
Examples are "{cause}" for an abbreviated error description and
"{detail}" for a detailed error message. The L<tinyproxy(8)>
manual page contains a description of all template variables.
*Syslog*::
=item B<LogFile>
When set to `On`, this option tells Tinyproxy to write its
debug messages to syslog instead of to a log file configured
with `LogFile`. These two options are mutually exclusive.
The location of the file to which Tinyproxy writes its debug output.
Enclose in double quotes. Alternatively, Tinyproxy can log to syslog
-- see the Syslog option.
*LogLevel*::
=item B<Syslog>
Sets the log level. Messages from the set level and above are
logged. For example, if the LogLevel was set to Warning, then all
log messages from Warning to Critical would be output, but Notice
and below would be suppressed. Allowed values are:
When set to `On`, this option tells Tinyproxy to write its
debug messages to syslog instead of to a log file configured
with `LogFile`. These two options are mutually exclusive.
* Critical (least verbose)
* Error
* Warning
* Notice
* Connect (log connections without Info's noise)
* Info (most verbose)
=item B<LogLevel>
*PidFile*::
Sets the log level. Messages from the set level and above are
logged. For example, if the LogLevel was set to Warning, then all
log messages from Warning to Critical would be output, but Notice
and below would be suppressed. Allowed values are:
This option controls the location of the file where the main
Tinyproxy process stores its process ID for signaling purposes.
=over 4
*XTinyproxy*::
=item * Critical (least verbose)
Setting this option to `Yes` tells Tinyproxy to add a header
`X-Tinyproxy` containing the client's IP address to the request.
=item * Error
*Upstream*::
*No Upstream*::
=item * Warning
This option allows you to set up a set of rules for deciding
whether an upstream a proxy server is to be used, based on the
host or domain of the site being accessed. The rules are stored
in the order encountered in the configuration file and the
LAST matching rule wins. There are three possible forms for
specifying upstream rules:
=item * Notice
* 'upstream host:port' turns proxy upstream support on generally.
=item * Connect (log connections without Info's noise)
* 'upstream host:port "site_spec"' turns on the upstream proxy for
the sites matching `site_spec`.
=item * Info (most verbose)
* 'no upstream "site_spec"' turns off upstream support for sites
matching `site_spec`.
=back
The site can be specified in various forms as a hostname, domain
name or as an IP range:
=item B<PidFile>
* 'name' matches host exactly
* '.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
The location of the file where the main Tinyproxy process stores its
process ID for signaling purposes. Enclose in double quotes.
*MaxClients*::
=item B<XTinyproxy>
Tinyproxy creates one child process for each connected client.
This options specifies the absolute highest number processes that
will be created. With other words, only MaxClients clients can be
connected to Tinyproxy simultaneously.
Setting this option to `Yes` tells Tinyproxy to add a header
`X-Tinyproxy` containing the client's IP address to the request.
*MinSpareServers*::
*MaxSpareServers*::
=item B<Upstream>
Tinyproxy always keeps a certain number of idle child processes
so that it can handle new incoming client requests quickly.
`MinSpareServer` and `MaxSpareServers` control the lower and upper
limits for the number of spare processes. I.e. when the number of
spare servers drops below `MinSpareServers` then Tinyproxy will
start forking new spare processes in the background and when the
number of spare processes exceeds `MaxSpareServers` then Tinyproxy
will kill off extra processes.
*StartServers*::
The number of servers to start initially. This should usually be
set to a value between MinSpareServers and MaxSpareServers.
*MaxRequestsPerChild*::
This limits the number of connections that a child process
will handle before it is killed. The default value is `0`
which disables this feature. This option is meant as an
emergency measure in the case of problems with memory leakage.
In that case, setting `MaxRequestsPerChild` to a value of e.g.
1000, or 10000 can be useful.
*Allow*::
*Deny*::
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
host or domain of the site being accessed. The rules are stored
in the order encountered in the configuration file and the
LAST matching rule wins. The following forms for specifying upstream
rules exist:
The `Allow` and `Deny` options provide a means to customize
which clients are allowed to access Tinyproxy. `Allow` and `Deny`
lines can be specified multiple times to build the access control
list for Tinyproxy. The order in the config file is important.
If there are no `Access` or `Deny` lines, then all clients are
allowed. Otherwise, the default action is to deny access.
The argument to `Access` or `Deny` can be a single IP address
of a client host, like `127.0.0.1`, an IP address range, like
`192.168.0.1/24` or a string that will be matched against the
end of the client host name, i.e, this can be a full host name
like `host.example.com` or a domain name like `.example.com` or
even a top level domain name like `.com`.
*AddHeader*::
Configure one or more HTTP request headers to be added to outgoing
HTTP requests that Tinyproxy makes. Note that this option will not
work for HTTPS traffic, as Tinyproxy has no control over what
headers are exchanged.
+
----
AddHeader "X-My-Header" "Powered by Tinyproxy"
----
*ViaProxyName*::
RFC 2616 requires proxies to add a `Via` header to the HTTP
requests, but using the real host name can be a security
concern. If the `ViaProxyname` option is present, then its
string value will be used as the host name in the Via header.
Otherwise, the server's host name will be used.
*DisableViaHeader*::
When this is set to yes, Tinyproxy does NOT add the `Via` header
to the requests. This virtually puts Tinyproxy into stealth mode.
Note that RFC 2616 requires proxies to set the `Via` header, so by
enabling this option, you break compliance.
Don't disable the `Via` header unless you know what you are doing...
*Filter*::
Tinyproxy supports filtering of web sites based on URLs or
domains. This option specifies the location of the file
containing the filter rules, one rule per line.
*FilterURLs*::
If this boolean option is set to `Yes` or `On`, filtering is
performed for URLs rather than for domains. The default is to
filter based on domains.
*FilterExtended*::
If this boolean option is set to `Yes`, then extended POSIX
regular expressions are used for matching the filter rules.
The default is to use basic POSIX regular expressions.
*FilterCaseSensitive*::
If this boolean option is set to `Yes`, then the filter rules
are matched in a case sensitive manner. The default is to
match case-insensitively.
*FilterDefaultDeny*::
The default filtering policy is to allow everything that is
not matched by a filtering rule. Setting `FilterDefaultDeny`
to `Yes` changes the policy do deny everything but the domains
or URLs matched by the filtering rules.
*Anonymous*::
If an `Anonymous` keyword is present, then anonymous proxying
is enabled. The headers listed with `Anonymous` are allowed
through, while all others are denied. If no Anonymous keyword
is present, then all headers are allowed through. You must
include quotes around the headers.
+
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.
+
Example:
+
----
Anonymous "Host"
Anonymous "Authorization"
Anonymous "Cookie"
----
*ConnectPort*::
This option can be used to specify the ports allowed for the
CONNECT method. If no `ConnectPort` line is found, then all
ports are allowed. To disable CONNECT altogether, include a
single ConnectPort line with a value of `0`.
*ReversePath*::
Configure one or more ReversePath directives to enable reverse proxy
support. With reverse proxying it's possible to make a number of
sites appear as if they were part of a single site.
+
If you uncomment the following two directives and run Tinyproxy
on your own computer at port 8888, you can access example.com,
using http://localhost:8888/example/.
+
----
ReversePath "/example/" "http://www.example.com/"
----
*ReverseOnly*::
=over 4
When using Tinyproxy as a reverse proxy, it is STRONGLY
recommended that the normal proxy is turned off by setting
this boolean option to `Yes`.
*ReverseMagic*::
Setting this option to `Yes`, makes Tinyproxy use a cookie to
track reverse proxy mappings. If you need to reverse proxy
sites which have absolute links you must use this option.
=item * I<upstream type host:port> turns proxy upstream support on generally.
*ReverseBaseURL*::
=item * I<upstream type user:pass@host:port>
does the same, but uses the supplied credentials for authentication.
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
proxy. If you have a chain of reverse proxies, you'll need to
put the outermost URL here (the address which the end user
types into his/her browser). If this option is not set then
no rewriting of redirects occurs.
=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`.
BUGS
----
a `site_spec` is either a full domain name, a domain name starting with a
`.`, in which case it is treated as a suffix, or an ip/mask tuple.
the `site_spec` needs to be double-quoted.
=item * I<upstream none "site_spec">
turns off upstream support for sites matching `site_spec`, that means the
connection is done directly.
=back
It's recommended to use raw IP addresses to specify the upstream host, so
no costly DNS lookup has to be done everytime it is used.
IPv6 addresses need to be enclosed in square brackets.
The site can be specified in various forms as a hostname, domain
name or as an IP range:
=over 4
=item * I<name> matches host exactly
=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
will be created. With other words, only MaxClients clients can be
connected to Tinyproxy simultaneously.
=item B<Allow>
=item B<Deny>
The `Allow` and `Deny` options provide a means to customize
which clients are allowed to access Tinyproxy. `Allow` and `Deny`
lines can be specified multiple times to build the access control
list for Tinyproxy. The order in the config file is important.
If there are no `Allow` or `Deny` lines, then all clients are
allowed. Otherwise, the default action is to deny access.
The argument to `Allow` or `Deny` can be a single IP address
of a client host, like `127.0.0.1`, an IP address range, like
`192.168.0.1/24` or a string that will be matched against the
end of the client host name, i.e, this can be a full host name
like `host.example.com` or a domain name like `.example.com` or
even a top level domain name like `.com`.
Note that by adding a rule using a host or domain name, a costly name
lookup has to be done for every new connection, which could slow down
the service considerably.
=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<BasicAuthRealm>
In case "BasicAuth" is configured, the "realm" information.
"Proxy Authentication Required" status http 407 "error-response" can be
customized.
- defaults in code to "Tinyproxy" (PACKAGE_NAME), if not configured.
=item B<AddHeader>
Configure one or more HTTP request headers to be added to outgoing
HTTP requests that Tinyproxy makes. Note that this option will not
work for HTTPS traffic, as Tinyproxy has no control over what
headers are exchanged.
AddHeader "X-My-Header" "Powered by Tinyproxy"
=item B<ViaProxyName>
RFC 2616 requires proxies to add a `Via` header to the HTTP
requests, but using the real host name can be a security
concern. If the `ViaProxyname` option is present, then its
string value will be used as the host name in the Via header.
Otherwise, the server's host name will be used. Enclose in double
quotes.
=item B<DisableViaHeader>
When this is set to yes, Tinyproxy does NOT add the `Via` header
to the requests. This virtually puts Tinyproxy into stealth mode.
Note that RFC 2616 requires proxies to set the `Via` header, so by
enabling this option, you break compliance.
Don't disable the `Via` header unless you know what you are doing...
=item B<Filter>
Tinyproxy supports filtering of web sites based on URLs or
domains. This option specifies the location of the file
containing the filter rules, one rule per line.
Rules are specified as POSIX basic regular expressions (BRE), unless
another FilterType is specified.
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<FilterType>
This option can be set to one of `bre`, `ere`, or `fnmatch`.
If `bre` is set, the rules specified in the filter file are matched
using POSIX basic regular expressions, when set to `ere`, using
POSIX extended regular expressions, and when set to `fnmatch` using
the `fnmatch` function as specified in the manpage `man 3p fnmatch`.
`fnmatch` matching is identical to what's used in the shell to match
filenames, so for example `*.google.com` matches everything that
ends with `.google.com`.
If you don't know what regular expressions are or you're using filter
lists from 3rd party sources, `fnmatch` is probably what you want.
It's also the fastest matching method of the three.
=item B<FilterURLs>
If this boolean option is set to `Yes` or `On`, filtering is
performed for URLs rather than for domains. The default is to
filter based on domains.
Note that filtering for URLs works only in plain HTTP scenarios.
Since HTTPS has become ubiquitous during the last years, this
will only work on a tiny fraction of websites, so it is
recommended not to use this option.
=item B<FilterExtended>
Deprecated. Use `FilterType ere` instead.
If this boolean option is set to `Yes`, then extended POSIX
regular expressions are used for matching the filter rules.
The default is to use basic POSIX regular expressions.
=item B<FilterCaseSensitive>
If this boolean option is set to `Yes`, then the filter rules
are matched in a case sensitive manner. The default is to
match case-insensitively, unfortunately.
If you set this to `Yes`, then your matching will be almost
twice as fast.
This setting affects only `bre` and `ere` FilterTypes, fnmatch
is always case sensitive.
=item B<FilterDefaultDeny>
The default filtering policy is to allow everything that is
not matched by a filtering rule. Setting `FilterDefaultDeny`
to `Yes` changes the policy do deny everything but the domains
or URLs matched by the filtering rules.
In other words, if set to `No` the Filter list acts as a
blacklist, if set to `Yes` as a whitelist.
=item B<Anonymous>
If an `Anonymous` keyword is present, then anonymous proxying
is enabled. The headers listed with `Anonymous` are allowed
through, while all others are denied. If no Anonymous keyword
is present, then all headers are allowed through. You must
include double quotes around the headers.
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.
Example:
Anonymous "Host"
Anonymous "Authorization"
Anonymous "Cookie"
=item B<ConnectPort>
This option can be used to specify the ports allowed for the
CONNECT method. If no `ConnectPort` line is found, then all
ports are allowed. To disable CONNECT altogether, include a
single ConnectPort line with a value of `0`.
=item B<ReversePath>
Configure one or more ReversePath directives to enable reverse proxy
support. With reverse proxying it's possible to make a number of
sites appear as if they were part of a single site.
If you uncomment the following two directives and run Tinyproxy
on your own computer at port 8888, you can access example.com,
using http://localhost:8888/example/.
ReversePath "/example/" "http://www.example.com/"
=item B<ReverseOnly>
When using Tinyproxy as a reverse proxy, it is STRONGLY
recommended that the normal proxy is turned off by setting
this boolean option to `Yes`.
=item B<ReverseMagic>
Setting this option to `Yes`, makes Tinyproxy use a cookie to
track reverse proxy mappings. If you need to reverse proxy
sites which have absolute links you must use this option.
=item B<ReverseBaseURL>
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
proxy. If you have a chain of reverse proxies, you'll need to
put the outermost URL here (the address which the end user
types into his/her browser). If this option is not set then
no rewriting of redirects occurs.
=back
=head1 BUGS
To report bugs in Tinyproxy, please visit
<https://www.banu.com/tinyproxy/support/[https://www.banu.com/tinyproxy/support/]>.
L<https://tinyproxy.github.io/>.
SEE ALSO
--------
tinyproxy(8)
=head1 SEE ALSO
L<tinyproxy(8)>
AUTHOR
------
=head1 AUTHOR
Written by the Tinyproxy project team.
This manpage was written by the Tinyproxy project team.
COPYRIGHT
---------
=head1 COPYRIGHT
Copyright (c) 1998-2000 Steven Young;
Copyright (c) 2000-2001 Robert James Kaes;
Copyright (c) 2009-2010 Mukund Sivaraman;
Copyright (c) 2009-2010 Michael Adam.
Copyright (c) 1998-2024 the Tinyproxy authors.
This program is distributed under the terms of the GNU General Public
License version 2 or above. See the COPYING file for additional
information.

View File

@ -1,20 +1,36 @@
if HAVE_MANPAGE_INTEREST
MAN8_FILES = \
tinyproxy.txt
endif
A2X_ARGS = \
-d manpage \
-f manpage
M_SECTION=8
M_NAME=TINYPROXY
man_MANS = \
$(MAN8_FILES:.txt=.8)
edit = sed \
-e 's|@localstatedir[@]|$(localstatedir)|g' \
-e 's|@runstatedir[@]|$(runstatedir)|g' \
-e 's|@sysconfdir[@]|$(sysconfdir)|g' \
-e 's|@TINYPROXY_STATHOST[@]|$(TINYPROXY_STATHOST)|g'
tinyproxy.txt: $(top_srcdir)/docs/man8/tinyproxy.txt.in Makefile
@rm -f $@ $@.tmp
$(AM_V_GEN) $(edit) $(top_srcdir)/docs/man8/$@.in > $@.tmp
@mv $@.tmp $@
.txt.8:
if HAVE_A2X
$(AM_V_GEN) $(A2X) $(A2X_ARGS) $<
if HAVE_POD2MAN
$(AM_V_GEN) $(POD2MAN) --center="Tinyproxy manual" \
--section=$(M_SECTION) --name=$(M_NAME) --release="Version @VERSION@" \
$< > $@
else
@echo "*** a2x (asciidoc) is required to regenerate $(@) ***"; exit 1;
@echo "*** pod2man is required to regenerate $(@) ***"; exit 1;
endif
CLEANFILES = \
$(MAN8_FILES:.txt=.8) \
$(MAN8_FILES:.txt=.xml)
MAINTAINERCLEANFILES = \
$(MAN8_FILES:.txt=.8)
EXTRA_DIST = \
$(MAN8_FILES:.txt=.8)

View File

@ -1,24 +1,20 @@
TINYPROXY(8)
============
:man source: Version @VERSION@
:man manual: Tinyproxy manual
=pod
NAME
----
=encoding utf8
=head1 NAME
tinyproxy - A light-weight HTTP proxy daemon
SYNOPSIS
--------
=head1 SYNOPSIS
*tinyproxy* [-vldch]
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
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
@ -26,49 +22,66 @@ deployments where a full featured HTTP proxy is required, but the system
resources for a larger proxy are unavailable.
OPTIONS
-------
=head1 OPTIONS
*tinyproxy* accepts the following options:
B<tinyproxy> accepts the following options:
*-c <config-file>*::
Use an alternate configuration file.
=over 4
*-d*::
Don't daemonize and stay in the foreground. Useful for debugging purposes.
=item B<-c <config-file>>
*-h*::
Display a short help screen of command line arguments and exit.
Use an alternate configuration file.
*-l*::
Display the licensing agreement.
=item B<-d>
*-v*::
Display version information and exit.
Don't daemonize and stay in the foreground. Useful for debugging purposes.
=item B<-h>
SIGNALS
-------
Display a short help screen of command line arguments and exit.
=item B<-v>
Display version information and exit.
=back
=head1 SIGNALS
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.
*SIGHUP*::
Force Tinyproxy to do a garbage collection on the current
connections linked list. This is usually done automatically after a
certain number of connections have been handled.
=over 4
=item B<SIGHUP>
TEMPLATE FILES
--------------
Force Tinyproxy to do a garbage collection on the current
connections linked list. This is usually done automatically after a
certain number of connections have been handled.
(Daemon mode only)
=item B<SIGUSR1>
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
the client on it's own right:
. When an error occurred, a corresponding error page is returned.
. When a request for the stathost is made, a page summarizing the
connection statistics is returned. (See STATHOST below.)
=over 4
=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.)
=back
The layout of both error pages and the statistics page can be
controlled via configurable HTML template files that are plain
@ -76,46 +89,60 @@ HTML files that additionally understand a few template
variables.
TEMPLATE VARIABLES
------------------
=head1 TEMPLATE VARIABLES
There are several standard HTML variables that are available in every
template file:
*request*::
The full HTTP request line.
=over 4
*cause*::
The abbreviated cause of the error condition.
=item B<request>
*clientip*::
The IP address of the client making the request.
The full HTTP request line.
*clienthost*::
The hostname of the client making the request.
=item B<cause>
*version*::
The version of Tinyproxy.
The abbreviated cause of the error condition.
*package*::
The package name. Presently, resolves to 'tinyproxy'.
=item B<clientip>
*date*::
The current date/time in HTTP format.
The IP address of the client making the request.
=item B<clienthost>
The hostname of the client making the request.
=item B<version>
The version of Tinyproxy.
=item B<package>
The package name. Presently, resolves to 'tinyproxy'.
=item B<date>
The current date/time in HTTP format.
=back
In addition, almost all templates support:
*detail*::
A detailed, plain English explanation of the error and possible
causes.
=over 4
=item B<detail>
A detailed, plain English explanation of the error and possible
causes.
=back
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.
STATHOST
--------
=head1 STATHOST
Tinyproxy returns a HTML page with connection statistics when it
receives a HTTP request for a certain host -- the stathost. The
@ -127,36 +154,33 @@ The stat file template can be changed at runtime through the
configuration variable `StatFile`.
FILES
-----
=head1 FILES
`/etc/tinyproxy/tinyproxy.conf`, `/var/run/tinyproxy.pid`, `/var/log/tinyproxy.log`
F<@sysconfdir@/tinyproxy/tinyproxy.conf>
BUGS
----
F<@runstatedir@/tinyproxy/tinyproxy.pid>
F<@localstatedir@/log/tinyproxy/tinyproxy.log>
=head1 BUGS
To report bugs in Tinyproxy, please visit
<https://www.banu.com/tinyproxy/support/[https://www.banu.com/tinyproxy/support/]>.
L<https://tinyproxy.github.io/>.
SEE ALSO
--------
tinyproxy.conf(5)
=head1 SEE ALSO
L<tinyproxy.conf(5)>
AUTHOR
------
=head1 AUTHOR
Written by the Tinyproxy project team.
This manpage was written by the Tinyproxy project team.
COPYRIGHT
---------
=head1 COPYRIGHT
Copyright (c) 1998-2000 Steven Young;
Copyright (c) 2000-2001 Robert James Kaes;
Copyright (c) 2009-2010 Mukund Sivaraman;
Copyright (c) 2009-2010 Michael Adam.
Copyright (c) 1998-2020 the Tinyproxy authors.
This program is distributed under the terms of the GNU General Public
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,426 @@
/*******************************************************************************
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;
margin-bottom: 20px;
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.libera.chat.</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>

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

@ -0,0 +1,98 @@
<!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, which you should disable, unless you want to restrict the users).</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 (HTTP only).</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). Note that these features do not affect HTTPS connections.</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. In fact, it is <b>recommended</b> to run it as a regular/restricted user. 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>
<p>Note that many distributions ship horribly outdated versions of tinyproxy, therefore it is recommended to compile it from source.</p>
<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 (likely), please contact the package maintainer for that particular operating system. If this fails, you can always compile the latest stable, or even better, the latest git master 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="quickstart" class="anchor" href="#quickstart" aria-hidden="true"><span class="octicon octicon-link"></span></a>Quickstart</h2>
<p>The quickest way to get started is using a minimal config file like the below:</p>
<pre><code>
Port 8888
Listen 127.0.0.1
Timeout 600
Allow 127.0.0.1
</code></pre>
<p>And then simply run <code>tinyproxy -d -c tinyproxy.conf</code> as your current user. This starts tinyproxy in foreground mode with <code>tinyproxy.conf</code> as its config, while logging to stdout. Now, all programs supporting a HTTP proxy can use 127.0.0.1:8888 as a proxy. You can try it out using <code>http_proxy=127.0.0.1:8888 curl example.com</code>.</p>
<h2>
<a id="documentation" class="anchor" href="#documentation" aria-hidden="true"><span class="octicon octicon-link"></span></a>Documentation</h2>

View File

@ -1,4 +1,6 @@
sysconf_DATA = \
pkgsysconfdir = $(sysconfdir)/$(PACKAGE)
pkgsysconf_DATA = \
tinyproxy.conf
EXTRA_DIST = \
@ -8,8 +10,9 @@ edit = sed \
-e 's|@bindir[@]|$(bindir)|g' \
-e 's|@datadir[@]|$(datadir)|g' \
-e 's|@datarootdir[@]|$(datarootdir)|g' \
-e 's|@sysconfdir[@]|$(sysconfdir)|g' \
-e 's|@pkgsysconfdir[@]|$(pkgsysconfdir)|g' \
-e 's|@localstatedir[@]|$(localstatedir)|g' \
-e 's|@runstatedir[@]|$(runstatedir)|g' \
-e 's|@pkgdatadir[@]|$(pkgdatadir)|g' \
-e 's|@prefix[@]|$(prefix)|g' \
-e 's|@TINYPROXY_STATHOST[@]|$(TINYPROXY_STATHOST)|g'

View File

@ -3,7 +3,7 @@
##
## This example tinyproxy.conf file contains example settings
## with explanations in comments. For decriptions of all
## parameters, see the tinproxy.conf(5) manual page.
## parameters, see the tinyproxy.conf(5) manual page.
##
#
@ -56,18 +56,18 @@ Timeout 600
# /usr/share/tinyproxy
# /etc/tinyproxy
#
#ErrorFile 404 "@datadir@/404.html"
#ErrorFile 400 "@datadir@/400.html"
#ErrorFile 503 "@datadir@/503.html"
#ErrorFile 403 "@datadir@/403.html"
#ErrorFile 408 "@datadir@/408.html"
#ErrorFile 400 "@pkgdatadir@/400.html"
#ErrorFile 502 "@pkgdatadir@/502.html"
#ErrorFile 503 "@pkgdatadir@/503.html"
#ErrorFile 403 "@pkgdatadir@/403.html"
#ErrorFile 408 "@pkgdatadir@/408.html"
#
# DefaultErrorFile: The HTML file that gets sent if there is no
# HTML file defined with an ErrorFile keyword for the HTTP error
# that has occured.
#
DefaultErrorFile "@datadir@/default.html"
DefaultErrorFile "@pkgdatadir@/default.html"
#
# StatHost: This configures the host name or IP address that is treated
@ -84,15 +84,16 @@ DefaultErrorFile "@datadir@/default.html"
# for the stathost. If this file doesn't exist a basic page is
# hardcoded in tinyproxy.
#
StatFile "@datadir@/stats.html"
StatFile "@pkgdatadir@/stats.html"
#
# Logfile: Allows you to specify the location where information should
# LogFile: Allows you to specify the location where information should
# be logged to. If you would prefer to log to syslog, then disable this
# and enable the Syslog directive. These directives are mutually
# exclusive.
# exclusive. If neither Syslog nor LogFile are specified, output goes
# to stdout.
#
Logfile "@localstatedir@/log/tinyproxy.log"
#LogFile "@localstatedir@/log/tinyproxy/tinyproxy.log"
#
# Syslog: Tell tinyproxy to use syslog instead of a logfile. This
@ -102,7 +103,7 @@ Logfile "@localstatedir@/log/tinyproxy.log"
#Syslog On
#
# LogLevel:
# LogLevel: Warning
#
# Set the logging level. Allowed settings are:
# Critical (least verbose)
@ -121,8 +122,9 @@ LogLevel Info
#
# PidFile: Write the PID of the main tinyproxy thread to this file so it
# can be used for signalling purposes.
# If not specified, no pidfile will be written.
#
PidFile "@localstatedir@/run/tinyproxy.pid"
#PidFile "@runstatedir@/tinyproxy/tinyproxy.pid"
#
# XTinyproxy: Tell Tinyproxy to include the X-Tinyproxy header, which
@ -138,25 +140,37 @@ PidFile "@localstatedir@/run/tinyproxy.pid"
# The upstream rules allow you to selectively route upstream connections
# based on the host/domain of the site being accessed.
#
# Syntax: upstream type (user:pass@)ip:port ("domain")
# Or: upstream none "domain"
# The parts in parens are optional.
# Possible types are http, socks4, socks5, none
#
# For example:
# # connection to test domain goes through testproxy
# upstream testproxy:8008 ".test.domain.invalid"
# upstream testproxy:8008 ".our_testbed.example.com"
# upstream testproxy:8008 "192.168.128.0/255.255.254.0"
# upstream http testproxy:8008 ".test.domain.invalid"
# upstream http testproxy:8008 ".our_testbed.example.com"
# upstream http testproxy:8008 "192.168.128.0/255.255.254.0"
#
# # upstream proxy using basic authentication
# upstream http user:pass@testproxy:8008 ".test.domain.invalid"
#
# # no upstream proxy for internal websites and unqualified hosts
# no upstream ".internal.example.com"
# no upstream "www.example.com"
# no upstream "10.0.0.0/8"
# no upstream "192.168.0.0/255.255.254.0"
# no upstream "."
# upstream none ".internal.example.com"
# upstream none "www.example.com"
# upstream none "10.0.0.0/8"
# upstream none "192.168.0.0/255.255.254.0"
# upstream none "."
#
# # connection to these boxes go through their DMZ firewalls
# upstream cust1_firewall:8008 "testbed_for_cust1"
# upstream cust2_firewall:8008 "testbed_for_cust2"
# upstream http cust1_firewall:8008 "testbed_for_cust1"
# upstream http cust2_firewall:8008 "testbed_for_cust2"
#
# # default upstream is internet firewall
# upstream firewall.internal.example.com:80
# upstream http firewall.internal.example.com:80
#
# You may also use SOCKS4/SOCKS5 upstream proxies:
# upstream socks4 127.0.0.1:9050
# upstream socks5 socksproxy:1080
#
# The LAST matching rule wins the route decision. As you can see, you
# can use a host, or a domain:
@ -166,7 +180,7 @@ PidFile "@localstatedir@/run/tinyproxy.pid"
# IP/bits matches network/mask
# IP/mask matches network/mask
#
#Upstream some.remote.proxy:port
#Upstream http some.remote.proxy:port
#
# MaxClients: This is the absolute highest number of threads which will
@ -175,30 +189,6 @@ PidFile "@localstatedir@/run/tinyproxy.pid"
#
MaxClients 100
#
# MinSpareServers/MaxSpareServers: These settings set the upper and
# lower limit for the number of spare servers which should be available.
#
# If the number of spare servers falls below MinSpareServers then new
# server processes will be spawned. If the number of servers exceeds
# MaxSpareServers then the extras will be killed off.
#
MinSpareServers 5
MaxSpareServers 20
#
# StartServers: The number of servers to start initially.
#
StartServers 10
#
# MaxRequestsPerChild: The number of connections a thread will handle
# before it is killed. In practise this should be set to 0, which
# disables thread reaping. If you do notice problems with memory
# leakage, then set this to something like 10000.
#
MaxRequestsPerChild 0
#
# Allow: Customization of authorization controls. If there are any
# access control keywords then the default action is to DENY. Otherwise,
@ -208,6 +198,19 @@ MaxRequestsPerChild 0
# tested against the controls based on order.
#
Allow 127.0.0.1
Allow ::1
# BasicAuth: HTTP "Basic Authentication" for accessing the proxy.
# If there are any entries specified, access is only granted for authenticated
# users.
#BasicAuth user password
# BasicAuthRealm : In case BasicAuth is configured, the "realm" information.
# "Proxy Authentication Required" status http 407 "error-response" can be
# customized.
#
# - defaults in code to "Tinyproxy" (PACKAGE_NAME), if not configured.
#BasicAuthRealm "Tinyproxy"
#
# AddHeader: Adds the specified headers to outgoing HTTP requests that
@ -236,7 +239,7 @@ ViaProxyName "tinyproxy"
#
# Filter: This allows you to specify the location of the filter file.
#
#Filter "@sysconfdir@/filter"
#Filter "@pkgsysconfdir@/filter"
#
# FilterURLs: Filter based on URLs rather than domains.
@ -244,10 +247,9 @@ ViaProxyName "tinyproxy"
#FilterURLs On
#
# FilterExtended: Use POSIX Extended regular expressions rather than
# basic.
# FilterType: Use bre (default), ere, or fnmatch for filtering.
#
#FilterExtended On
#FilterType fnmatch
#
# FilterCaseSensitive: Use case sensitive regular expressions.
@ -283,12 +285,12 @@ ViaProxyName "tinyproxy"
# ConnectPort: This is a list of ports allowed by tinyproxy when the
# CONNECT method is used. To disable the CONNECT method altogether, set
# the value to 0. If no ConnectPort line is found, all ports are
# allowed (which is not very secure.)
# allowed.
#
# The following two ports are used by SSL.
#
ConnectPort 443
ConnectPort 563
#ConnectPort 443
#ConnectPort 563
#
# Configure one or more ReversePath directives to enable reverse proxy
@ -325,6 +327,3 @@ ConnectPort 563
# If not set then no rewriting occurs.
#
#ReverseBaseURL "http://localhost:8888/"

View File

@ -1,4 +1,3 @@
EXTRA_DIST = \
as-compiler-flag.m4 \
argenable.m4 \
typecheck.m4
argenable.m4

View File

@ -1,103 +0,0 @@
dnl Taken from Unix Network Programming, W. Richard Stevens
dnl ##################################################################
dnl We cannot use the AC_CHECK_TYPE macros becasue AC_CHECK_TYPE
dnl #includes only <sys/types.h>, <stdlib.h>, and <stddef.h>.
dnl Unfortunately, many implementations today hide typedefs in wierd
dnl locations: Solaris 2.5.1 has uint8_t and uint32_t in <pthread.h>.
dnl SunOS 4.1.x has int8_t in <sys/bittypes.h>.
dnl So we define our own macro AC_UNP_CHECK_TYPE that does the same
dnl #includes as "unp.h", and then looks for the typedef.
dnl
dnl This macro should be invoked after all the header checks have been
dnl performed, since we #include "confdefs.h" below, and then use the
dnl HAVE_foo_H values that is can #define.
dnl
AC_DEFUN([AC_UNP_CHECK_TYPE],
[AC_MSG_CHECKING(if $1 defined)
AC_CACHE_VAL(ac_cv_type_$1,
[AC_TRY_COMPILE(
[
#include "confdefs.h" /* the header built by configure so far */
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#ifdef HAVE_ERRNO_H
# include <errno.h>
#endif
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#ifdef HAVE_NETDB_H
# include <netdb.h>
#endif
#ifdef HAVE_SIGNAL_H
# include <signal.h>
#endif
#ifdef HAVE_STDIO_H
# include <stdio.h>
#endif
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#ifdef HAVE_SYS_UIO_H
# include <sys/uio.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
#ifdef HAVE_SYS_UN_H
# include <sys/un.h>
#endif
#ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#ifdef HAVE_SYS_FILIO_H
# include <sys/filio.h>
#endif
#ifdef HAVE_SYS_SOCKIO_H
# include <sys/sockio.h>
#endif
#ifdef HAVE_PTHREAD_H
# include <pthread.h>
#endif
#ifdef HAVE_STDINT_H
# include <stdint.h>
#endif
],
[ $1 foo ],
[ac_cv_type_$1=yes],
[ac_cv_type_$1=no])])
AC_MSG_RESULT([$ac_cv_type_$1])
if test $ac_cv_type_$1 = no ; then
AH_TEMPLATE([$1], [Defined with the proper type.])
AC_DEFINE($1, $2)
fi
])

2
scripts/Makefile.am Normal file
View File

@ -0,0 +1,2 @@
EXTRA_DIST = \
version.sh

10
scripts/gen-authors.sh Executable file
View File

@ -0,0 +1,10 @@
#!/bin/sh
SCRIPT_DIR="$(cd "$(dirname "${0}")" && pwd)"
BASE_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
AUTHORS_FILE="${BASE_DIR}/AUTHORS"
type git > /dev/null || exit
test -d "${BASE_DIR}/.git" || exit
git log --all --format='%aN' | sort -u > "${AUTHORS_FILE}"

19
scripts/version.sh Executable file
View File

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

View File

@ -15,22 +15,25 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
sbin_PROGRAMS = tinyproxy
pkgsysconfdir = $(sysconfdir)/$(PACKAGE)
bin_PROGRAMS = tinyproxy
AM_CPPFLAGS = \
-DSYSCONFDIR=\"${sysconfdir}\" \
-DSYSCONFDIR=\"${pkgsysconfdir}\" \
-DLOCALSTATEDIR=\"${localstatedir}\"
tinyproxy_SOURCES = \
hostspec.c hostspec.h \
acl.c acl.h \
anonymous.c anonymous.h \
buffer.c buffer.h \
child.c child.h \
common.h \
conf-tokens.c conf-tokens.h \
conf.c conf.h \
conns.c conns.h \
daemon.c daemon.h \
hashmap.c hashmap.h \
heap.c heap.h \
html-error.c html-error.h \
http-message.c http-message.h \
@ -42,12 +45,27 @@ tinyproxy_SOURCES = \
text.c text.h \
main.c main.h \
utils.c utils.h \
vector.c vector.h \
upstream.c upstream.h \
basicauth.c basicauth.h \
base64.c base64.h \
sblist.c sblist.h \
hsearch.c hsearch.h \
orderedmap.c orderedmap.h \
loop.c loop.h \
mypoll.c mypoll.h \
connect-ports.c connect-ports.h
EXTRA_tinyproxy_SOURCES = filter.c filter.h \
reverse-proxy.c reverse-proxy.h \
transparent-proxy.c transparent-proxy.h
tinyproxy_DEPENDENCIES = @ADDITIONAL_OBJECTS@
tinyproxy_LDADD = @ADDITIONAL_OBJECTS@
tinyproxy_LDADD = @ADDITIONAL_OBJECTS@ -lpthread
if HAVE_GPERF
conf-tokens.c: conf-tokens-gperf.inc
conf-tokens-gperf.inc: conf-tokens.gperf
$(GPERF) $< > $@
endif
EXTRA_DIST = conf-tokens.gperf conf-tokens-gperf.inc

210
src/acl.c
View File

@ -28,17 +28,8 @@
#include "log.h"
#include "network.h"
#include "sock.h"
#include "vector.h"
#include <limits.h>
/* Define how long an IPv6 address is in bytes (128 bits, 16 bytes) */
#define IPV6_LEN 16
enum acl_type {
ACL_STRING,
ACL_NUMERIC
};
#include "sblist.h"
#include "hostspec.h"
/*
* Hold the information about a particular access control. We store
@ -47,67 +38,17 @@ enum acl_type {
*/
struct acl_s {
acl_access_t access;
enum acl_type type;
union {
char *string;
struct {
unsigned char octet[IPV6_LEN];
unsigned char mask[IPV6_LEN];
} ip;
} address;
struct hostspec h;
};
/*
* Fills in the netmask array given a numeric value.
*
* Returns:
* 0 on success
* -1 on failure (invalid mask value)
*
*/
static int
fill_netmask_array (char *bitmask_string, unsigned char array[],
size_t len)
{
unsigned int i;
unsigned long int mask;
char *endptr;
errno = 0; /* to distinguish success/failure after call */
mask = strtoul (bitmask_string, &endptr, 10);
/* check for various conversion errors */
if ((errno == ERANGE && mask == ULONG_MAX)
|| (errno != 0 && mask == 0) || (endptr == bitmask_string))
return -1;
/* valid range for a bit mask */
if (mask > (8 * len))
return -1;
/* we have a valid range to fill in the array */
for (i = 0; i != len; ++i) {
if (mask >= 8) {
array[i] = 0xff;
mask -= 8;
} else if (mask > 0) {
array[i] = (unsigned char) (0xff << (8 - mask));
mask = 0;
} else {
array[i] = 0;
}
}
return 0;
}
/**
* If the access list has not been set up, create it.
*/
static int init_access_list(vector_t *access_list)
static int init_access_list(acl_list_t *access_list)
{
if (!*access_list) {
*access_list = vector_create ();
*access_list = sblist_new(sizeof(struct acl_s), 16);
if (!*access_list) {
log_message (LOG_ERR,
"Unable to allocate memory for access list");
@ -127,65 +68,26 @@ static int init_access_list(vector_t *access_list)
* -1 on failure
* 0 otherwise.
*/
int insert_acl (char *location, acl_access_t access_type, vector_t *access_list)
int
insert_acl (char *location, acl_access_t access_type, acl_list_t *access_list)
{
struct acl_s acl;
int ret;
char *p, ip_dst[IPV6_LEN];
assert (location != NULL);
ret = init_access_list(access_list);
if (ret != 0) {
if (init_access_list(access_list) != 0)
return -1;
}
/*
* Start populating the access control structure.
*/
memset (&acl, 0, sizeof (struct acl_s));
acl.access = access_type;
/*
* Check for a valid IP address (the simplest case) first.
*/
if (full_inet_pton (location, ip_dst) > 0) {
acl.type = ACL_NUMERIC;
memcpy (acl.address.ip.octet, ip_dst, IPV6_LEN);
memset (acl.address.ip.mask, 0xff, IPV6_LEN);
} else {
/*
* At this point we're either a hostname or an
* IP address with a slash.
*/
p = strchr (location, '/');
if (p != NULL) {
/*
* We have a slash, so it's intended to be an
* IP address with mask
*/
*p = '\0';
if (full_inet_pton (location, ip_dst) <= 0)
if(hostspec_parse(location, &acl.h) || acl.h.type == HST_NONE)
return -1;
acl.type = ACL_NUMERIC;
memcpy (acl.address.ip.octet, ip_dst, IPV6_LEN);
if (fill_netmask_array
(p + 1, &(acl.address.ip.mask[0]), IPV6_LEN)
< 0)
return -1;
} else {
/* In all likelihood a string */
acl.type = ACL_STRING;
acl.address.string = safestrdup (location);
if (!acl.address.string)
return -1;
}
}
ret = vector_append (*access_list, &acl, sizeof (struct acl_s));
return ret;
if(!sblist_add(*access_list, &acl)) return -1;
return 0;
}
/*
@ -198,28 +100,27 @@ int insert_acl (char *location, acl_access_t access_type, vector_t *access_list)
* -1 if no tests match, so skip
*/
static int
acl_string_processing (struct acl_s *acl,
const char *ip_address, const char *string_address)
acl_string_processing (struct acl_s *acl, const char *ip_address,
union sockaddr_union *addr, char *string_addr)
{
int match;
struct addrinfo hints, *res, *ressave;
size_t test_length, match_length;
char ipbuf[512];
assert (acl && acl->type == ACL_STRING);
assert (acl && acl->h.type == HST_STRING);
assert (ip_address && strlen (ip_address) > 0);
assert (string_address && strlen (string_address) > 0);
/*
* If the first character of the ACL string is a period, we need to
* do a string based test only; otherwise, we can do a reverse
* lookup test as well.
*/
if (acl->address.string[0] != '.') {
if (acl->h.address.string[0] != '.') {
memset (&hints, 0, sizeof (struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo (acl->address.string, NULL, &hints, &res) != 0)
if (getaddrinfo (acl->h.address.string, NULL, &hints, &res) != 0)
goto STRING_TEST;
ressave = res;
@ -244,8 +145,16 @@ acl_string_processing (struct acl_s *acl,
}
STRING_TEST:
test_length = strlen (string_address);
match_length = strlen (acl->address.string);
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->h.address.string);
/*
* If the string length is shorter than AC string, return a -1 so
@ -255,8 +164,8 @@ STRING_TEST:
return -1;
if (strcasecmp
(string_address + (test_length - match_length),
acl->address.string) == 0) {
(string_addr + (test_length - match_length),
acl->h.address.string) == 0) {
if (acl->access == ACL_DENY)
return 0;
else
@ -275,20 +184,16 @@ STRING_TEST:
* 0 IP address is denied
* -1 neither allowed nor denied.
*/
static int check_numeric_acl (const struct acl_s *acl, const char *ip)
static int check_numeric_acl (const struct acl_s *acl, uint8_t addr[IPV6_LEN])
{
uint8_t addr[IPV6_LEN], x, y;
uint8_t x, y;
int i;
assert (acl && acl->type == ACL_NUMERIC);
assert (ip && strlen (ip) > 0);
if (full_inet_pton (ip, &addr) <= 0)
return -1;
assert (acl && acl->h.type == HST_NUMERIC);
for (i = 0; i != IPV6_LEN; ++i) {
x = addr[i] & acl->address.ip.mask[i];
y = acl->address.ip.octet[i] & acl->address.ip.mask[i];
x = addr[i] & acl->h.address.ip.mask[i];
y = acl->h.address.ip.network[i];
/* If x and y don't match, the IP addresses don't match */
if (x != y)
@ -306,14 +211,18 @@ static int check_numeric_acl (const struct acl_s *acl, const char *ip)
* 1 if allowed
* 0 if denied
*/
int check_acl (const char *ip, const char *host, vector_t access_list)
int check_acl (const char *ip, union sockaddr_union *addr, acl_list_t access_list)
{
struct acl_s *acl;
int perm = 0;
int perm = 0, is_numeric_addr;
size_t i;
char string_addr[HOSTNAME_LENGTH];
uint8_t numeric_addr[IPV6_LEN];
assert (ip != NULL);
assert (host != NULL);
assert (addr != NULL);
string_addr[0] = 0;
/*
* If there is no access list allow everything.
@ -321,17 +230,26 @@ int check_acl (const char *ip, const char *host, vector_t access_list)
if (!access_list)
return 1;
for (i = 0; i != (size_t) vector_length (access_list); ++i) {
acl = (struct acl_s *) vector_getentry (access_list, i, NULL);
switch (acl->type) {
case ACL_STRING:
perm = acl_string_processing (acl, ip, host);
is_numeric_addr = (full_inet_pton (ip, &numeric_addr) > 0);
for (i = 0; i < sblist_getsize (access_list); ++i) {
acl = sblist_get (access_list, i);
switch (acl->h.type) {
case HST_STRING:
perm = acl_string_processing (acl, ip, addr, string_addr);
break;
case ACL_NUMERIC:
case HST_NUMERIC:
if (ip[0] == '\0')
continue;
perm = check_numeric_acl (acl, ip);
perm = is_numeric_addr
? check_numeric_acl (acl, numeric_addr)
: -1;
break;
case HST_NONE:
perm = -1;
break;
}
@ -348,12 +266,12 @@ int check_acl (const char *ip, const char *host, vector_t access_list)
/*
* Deny all connections by default.
*/
log_message (LOG_NOTICE, "Unauthorized connection from \"%s\" [%s].",
host, ip);
log_message (LOG_NOTICE, "Unauthorized connection from \"%s\".",
ip);
return 0;
}
void flush_access_list (vector_t access_list)
void flush_access_list (acl_list_t access_list)
{
struct acl_s *acl;
size_t i;
@ -367,12 +285,12 @@ void flush_access_list (vector_t access_list)
* before we can free the acl entries themselves.
* A hierarchical memory system would be great...
*/
for (i = 0; i != (size_t) vector_length (access_list); ++i) {
acl = (struct acl_s *) vector_getentry (access_list, i, NULL);
if (acl->type == ACL_STRING) {
safefree (acl->address.string);
for (i = 0; i < sblist_getsize (access_list); ++i) {
acl = sblist_get (access_list, i);
if (acl->h.type == HST_STRING) {
safefree (acl->h.address.string);
}
}
vector_delete (access_list);
sblist_free (access_list);
}

View File

@ -21,14 +21,16 @@
#ifndef TINYPROXY_ACL_H
#define TINYPROXY_ACL_H
#include "vector.h"
#include "sblist.h"
#include "sock.h"
typedef enum { ACL_ALLOW, ACL_DENY } acl_access_t;
typedef sblist* acl_list_t;
extern int insert_acl (char *location, acl_access_t access_type,
vector_t *access_list);
extern int check_acl (const char *ip_address, const char *string_address,
vector_t access_list);
extern void flush_access_list (vector_t access_list);
acl_list_t *access_list);
extern int check_acl (const char *ip_address, union sockaddr_union *addr,
acl_list_t access_list);
extern void flush_access_list (acl_list_t access_list);
#endif

View File

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

View File

@ -21,8 +21,8 @@
#ifndef _TINYPROXY_ANONYMOUS_H_
#define _TINYPROXY_ANONYMOUS_H_
extern short int is_anonymous_enabled (void);
extern int anonymous_search (const char *s);
extern int anonymous_insert (const char *s);
extern short int is_anonymous_enabled (struct config_s *conf);
extern int anonymous_search (struct config_s *conf, const char *s);
extern int anonymous_insert (struct config_s *conf, char *s);
#endif

57
src/base64.c Normal file
View File

@ -0,0 +1,57 @@
/* tinyproxy - A fast light-weight HTTP proxy
* this file Copyright (C) 2016-2018 rofl0r
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "base64.h"
static const char base64_tbl[64] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/*
rofl0r's base64 impl (taken from libulz)
takes count bytes from src, writing base64 encoded string into dst.
dst needs to be at least BASE64ENC_BYTES(count) + 1 bytes in size.
the string in dst will be zero-terminated.
*/
void base64enc(char *dst, const void* src, size_t count)
{
unsigned const char *s = src;
char* d = dst;
while(count) {
int i = 0, n = *s << 16;
s++;
count--;
if(count) {
n |= *s << 8;
s++;
count--;
i++;
}
if(count) {
n |= *s;
s++;
count--;
i++;
}
*d++ = base64_tbl[(n >> 18) & 0x3f];
*d++ = base64_tbl[(n >> 12) & 0x3f];
*d++ = i ? base64_tbl[(n >> 6) & 0x3f] : '=';
*d++ = i == 2 ? base64_tbl[n & 0x3f] : '=';
}
*d = 0;
}

29
src/base64.h Normal file
View File

@ -0,0 +1,29 @@
/* tinyproxy - A fast light-weight HTTP proxy
* this file Copyright (C) 2016-2018 rofl0r
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef TINYPROXY_BASE64_H
#define TINYPROXY_BASE64_H
#include <stddef.h>
/* calculates number of bytes base64-encoded stream of N bytes will take. */
#define BASE64ENC_BYTES(N) (((N+2)/3)*4)
void base64enc(char *dst, const void* src, size_t count);
#endif

97
src/basicauth.c Normal file
View File

@ -0,0 +1,97 @@
/* tinyproxy - A fast light-weight HTTP proxy
* This file: Copyright (C) 2016-2017 rofl0r
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "main.h"
#include "basicauth.h"
#include "conns.h"
#include "heap.h"
#include "html-error.h"
#include "log.h"
#include "conf.h"
#include "base64.h"
/*
* Create basic-auth token in buf.
* Returns strlen of token on success,
* -1 if user/pass missing
* 0 if user/pass too long
*/
ssize_t basicauth_string(const char *user, const char *pass,
char *buf, size_t bufsize)
{
char tmp[256+2];
int l;
if (!user || !pass) return -1;
l = snprintf(tmp, sizeof tmp, "%s:%s", user, pass);
if (l < 0 || l >= (ssize_t) sizeof tmp) return 0;
if (bufsize < (BASE64ENC_BYTES((unsigned)l) + 1)) return 0;
base64enc(buf, tmp, l);
return BASE64ENC_BYTES(l);
}
/*
* Add entry to the basicauth list
*/
void basicauth_add (sblist *authlist,
const char *user, const char *pass)
{
char b[BASE64ENC_BYTES((256+2)-1) + 1], *s;
ssize_t ret;
ret = basicauth_string(user, pass, b, sizeof b);
if (ret == -1) {
log_message (LOG_WARNING,
"Illegal basicauth rule: missing user or pass");
return;
} else if (ret == 0) {
log_message (LOG_WARNING,
"User / pass in basicauth rule too long");
return;
}
if (!(s = safestrdup(b)) || !sblist_add(authlist, &s)) {
safefree(s);
log_message (LOG_ERR,
"Unable to allocate memory in basicauth_add()");
return;
}
log_message (LOG_INFO,
"Added basic auth user : %s", user);
}
/*
* Check if a user/password combination (encoded as base64)
* is in the basicauth list.
* return 1 on success, 0 on failure.
*/
int basicauth_check (sblist *authlist, const char *authstring)
{
size_t i;
char** entry;
if (!authlist) return 0;
for (i = 0; i < sblist_getsize(authlist); i++) {
entry = sblist_get (authlist, i);
if (entry && strcmp (authstring, *entry) == 0)
return 1;
}
return 0;
}

35
src/basicauth.h Normal file
View File

@ -0,0 +1,35 @@
/* tinyproxy - A fast light-weight HTTP proxy
* Copyright (C) 2005 Robert James Kaes <rjkaes@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* See 'basicauth.c' for detailed information. */
#ifndef TINYPROXY_BASICAUTH_H
#define TINYPROXY_BASICAUTH_H
#include <stddef.h>
#include "sblist.h"
extern ssize_t basicauth_string(const char *user, const char *pass,
char *buf, size_t bufsize);
extern void basicauth_add (sblist *authlist,
const char *user, const char *pass);
extern int basicauth_check (sblist *authlist, const char *authstring);
#endif

View File

@ -236,8 +236,7 @@ ssize_t read_buffer (int fd, struct buffer_s * buffptr)
"readbuff: add_to_buffer() error.");
bytesin = -1;
}
} else {
if (bytesin == 0) {
} else if (bytesin == 0) {
/* connection was closed by client */
bytesin = -1;
} else {
@ -254,13 +253,12 @@ ssize_t read_buffer (int fd, struct buffer_s * buffptr)
break;
default:
log_message (LOG_ERR,
"readbuff: recv() error \"%s\" on file descriptor %d",
strerror (errno), fd);
"read_buffer: read() failed on fd %d: %s",
fd, strerror(errno));
bytesin = -1;
break;
}
}
}
safefree (buffer);
return bytesin;

View File

@ -31,194 +31,145 @@
#include "sock.h"
#include "utils.h"
#include "conf.h"
#include "sblist.h"
#include "loop.h"
#include "conns.h"
#include "mypoll.h"
#include <pthread.h>
static int listenfd;
static socklen_t addrlen;
static sblist* listen_fds;
/*
* Stores the internal data needed for each child (connection)
*/
enum child_status_t { T_EMPTY, T_WAITING, T_CONNECTED };
struct child_s {
pid_t tid;
unsigned int connects;
enum child_status_t status;
struct client {
union sockaddr_union addr;
};
/*
* A pointer to an array of children. A certain number of children are
* created when the program is started.
*/
static struct child_s *child_ptr;
struct child {
pthread_t thread;
struct client client;
struct conn_s conn;
volatile int done;
};
static struct child_config_s {
unsigned int maxclients, maxrequestsperchild;
unsigned int maxspareservers, minspareservers, startservers;
} child_config;
static unsigned int *servers_waiting; /* servers waiting for a connection */
/*
* Lock/Unlock the "servers_waiting" variable so that two children cannot
* modify it at the same time.
*/
#define SERVER_COUNT_LOCK() _child_lock_wait()
#define SERVER_COUNT_UNLOCK() _child_lock_release()
/* START OF LOCKING SECTION */
/*
* These variables are required for the locking mechanism. Also included
* are the "private" functions for locking/unlocking.
*/
static struct flock lock_it, unlock_it;
static int lock_fd = -1;
static void _child_lock_init (void)
static void* child_thread(void* data)
{
char lock_file[] = "/tmp/tinyproxy.servers.lock.XXXXXX";
/* Only allow u+rw bits. This may be required for some versions
* of glibc so that mkstemp() doesn't make us vulnerable.
*/
umask (0177);
lock_fd = mkstemp (lock_file);
unlink (lock_file);
lock_it.l_type = F_WRLCK;
lock_it.l_whence = SEEK_SET;
lock_it.l_start = 0;
lock_it.l_len = 0;
unlock_it.l_type = F_UNLCK;
unlock_it.l_whence = SEEK_SET;
unlock_it.l_start = 0;
unlock_it.l_len = 0;
struct child *c = data;
handle_connection (&c->conn, &c->client.addr);
c->done = 1;
return NULL;
}
static void _child_lock_wait (void)
{
int rc;
static sblist *childs;
while ((rc = fcntl (lock_fd, F_SETLKW, &lock_it)) < 0) {
if (errno == EINTR)
continue;
else
return;
static void collect_threads(void)
{
size_t i;
for (i = 0; i < sblist_getsize(childs); ) {
struct child *c = *((struct child**)sblist_get(childs, i));
if (c->done) {
pthread_join(c->thread, 0);
sblist_delete(childs, i);
safefree(c);
} else i++;
}
}
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.
* This is the main loop accepting new connections.
*/
short int child_configure (child_config_t type, unsigned int val)
void child_main_loop (void)
{
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;
int connfd;
union sockaddr_union cliaddr_storage;
struct sockaddr *cliaddr = (void*) &cliaddr_storage;
socklen_t clilen;
int nfds = sblist_getsize(listen_fds);
pollfd_struct *fds = safecalloc(nfds, sizeof *fds);
ssize_t i;
int ret, listenfd, was_full = 0;
pthread_attr_t *attrp, attr;
struct child *child;
childs = sblist_new(sizeof (struct child*), config->maxclients);
for (i = 0; i < nfds; i++) {
int *fd = sblist_get(listen_fds, i);
fds[i].fd = *fd;
fds[i].events |= MYPOLL_READ;
}
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...
* We have to wait for connections on multiple fds,
* so use select/poll/whatever.
*/
reload_config ();
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;
/* Handle log rotation if it was requested */
if (received_sighup) {
reload_config (1);
#ifdef FILTER_ENABLE
filter_reload ();
#endif /* FILTER_ENABLE */
}
}
/*
* This is the main (per child) loop.
received_sighup = FALSE;
}
ret = mypoll(fds, nfds, -1);
if (ret == -1) {
if (errno == EINTR) {
continue;
}
log_message (LOG_ERR, "error calling " SELECT_OR_POLL ": %s",
strerror(errno));
continue;
} else if (ret == 0) {
log_message (LOG_WARNING, "Strange: " SELECT_OR_POLL " returned 0 "
"but we did not specify a timeout...");
continue;
}
for (i = 0; i < nfds; i++) {
if (fds[i].revents & MYPOLL_READ) {
/*
* only accept the connection on the first
* fd that we find readable. - fair?
*/
static void child_main (struct child_s *ptr)
{
int connfd;
struct sockaddr *cliaddr;
socklen_t clilen;
cliaddr = (struct sockaddr *) safemalloc (addrlen);
if (!cliaddr) {
log_message (LOG_CRIT,
"Could not allocate memory for child address.");
exit (0);
listenfd = fds[i].fd;
break;
}
}
ptr->connects = 0;
if (listenfd == -1) {
log_message(LOG_WARNING, "Strange: None of our listen "
"fds was readable after " SELECT_OR_POLL);
continue;
}
while (!config.quit) {
ptr->status = T_WAITING;
clilen = addrlen;
/*
* We have a socket that is readable.
* Continue handling this connection.
*/
clilen = sizeof(cliaddr_storage);
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...
@ -230,225 +181,41 @@ static void child_main (struct child_s *ptr)
continue;
}
ptr->status = T_CONNECTED;
SERVER_DEC ();
handle_connection (connfd);
ptr->connects++;
if (child_config.maxrequestsperchild != 0) {
DEBUG2 ("%u connections so far...", ptr->connects);
if (ptr->connects == child_config.maxrequestsperchild) {
log_message (LOG_NOTICE,
"Child has reached MaxRequestsPerChild (%u). "
"Killing child.", ptr->connects);
break;
}
child = safecalloc(1, sizeof(struct child));
if (!child) {
oom:
close(connfd);
log_message (LOG_CRIT,
"Could not allocate memory for child.");
usleep(16); /* prevent 100% CPU usage in OOM situation */
continue;
}
SERVER_COUNT_LOCK ();
if (*servers_waiting > child_config.maxspareservers) {
/*
* There are too many spare children, kill ourself
* off.
*/
log_message (LOG_NOTICE,
"Waiting servers (%d) exceeds MaxSpareServers (%d). "
"Killing child.",
*servers_waiting,
child_config.maxspareservers);
SERVER_COUNT_UNLOCK ();
child->done = 0;
break;
} else {
SERVER_COUNT_UNLOCK ();
if (!sblist_add(childs, &child)) {
free(child);
goto oom;
}
SERVER_INC ();
conn_struct_init(&child->conn);
child->conn.client_fd = connfd;
memcpy(&child->client.addr, &cliaddr_storage, sizeof(cliaddr_storage));
attrp = 0;
if (pthread_attr_init(&attr) == 0) {
attrp = &attr;
pthread_attr_setstacksize(attrp, 256*1024);
}
ptr->status = T_EMPTY;
safefree (cliaddr);
exit (0);
}
/*
* Fork a child "child" (or in our case a process) and then start up the
* child_main() function.
*/
static pid_t child_make (struct child_s *ptr)
{
pid_t pid;
if ((pid = fork ()) > 0)
return pid; /* parent */
/*
* Reset the SIGNALS so that the child can be reaped.
*/
set_signal_handler (SIGCHLD, SIG_DFL);
set_signal_handler (SIGTERM, SIG_DFL);
set_signal_handler (SIGHUP, child_sighup_handler);
child_main (ptr); /* never returns */
return -1;
}
/*
* Create a pool of children to handle incoming connections
*/
short int child_pool_create (void)
{
unsigned int i;
/*
* Make sure the number of MaxClients is not zero, since this
* variable determines the size of the array created for children
* later on.
*/
if (child_config.maxclients == 0) {
log_message (LOG_ERR,
"child_pool_create: \"MaxClients\" must be "
"greater than zero.");
return -1;
}
if (child_config.startservers == 0) {
log_message (LOG_ERR,
"child_pool_create: \"StartServers\" must be "
"greater than zero.");
return -1;
}
child_ptr =
(struct child_s *) calloc_shared_memory (child_config.maxclients,
sizeof (struct child_s));
if (!child_ptr) {
log_message (LOG_ERR,
"Could not allocate memory for children.");
return -1;
}
servers_waiting =
(unsigned int *) malloc_shared_memory (sizeof (unsigned int));
if (servers_waiting == MAP_FAILED) {
log_message (LOG_ERR,
"Could not allocate memory for child counting.");
return -1;
}
*servers_waiting = 0;
/*
* Create a "locking" file for use around the servers_waiting
* variable.
*/
_child_lock_init ();
if (child_config.startservers > child_config.maxclients) {
log_message (LOG_WARNING,
"Can not start more than \"MaxClients\" servers. "
"Starting %u servers instead.",
child_config.maxclients);
child_config.startservers = child_config.maxclients;
}
for (i = 0; i != child_config.maxclients; i++) {
child_ptr[i].status = T_EMPTY;
child_ptr[i].connects = 0;
}
for (i = 0; i != child_config.startservers; i++) {
DEBUG2 ("Trying to create child %d of %d", i + 1,
child_config.startservers);
child_ptr[i].status = T_WAITING;
child_ptr[i].tid = child_make (&child_ptr[i]);
if (child_ptr[i].tid < 0) {
log_message (LOG_WARNING,
"Could not create child number %d of %d",
i, child_config.startservers);
return -1;
} else {
log_message (LOG_INFO,
"Creating child number %d of %d ...",
i + 1, child_config.startservers);
SERVER_INC ();
}
}
log_message (LOG_INFO, "Finished creating all children.");
return 0;
}
/*
* Keep the proper number of servers running. This is the birth of the
* servers. It monitors this at least once a second.
*/
void child_main_loop (void)
{
unsigned int i;
while (1) {
if (config.quit)
return;
/* If there are not enough spare servers, create more */
SERVER_COUNT_LOCK ();
if (*servers_waiting < child_config.minspareservers) {
log_message (LOG_NOTICE,
"Waiting servers (%d) is less than MinSpareServers (%d). "
"Creating new child.",
*servers_waiting,
child_config.minspareservers);
SERVER_COUNT_UNLOCK ();
for (i = 0; i != child_config.maxclients; i++) {
if (child_ptr[i].status == T_EMPTY) {
child_ptr[i].status = T_WAITING;
child_ptr[i].tid =
child_make (&child_ptr[i]);
if (child_ptr[i].tid < 0) {
log_message (LOG_NOTICE,
"Could not create child");
child_ptr[i].status = T_EMPTY;
break;
}
SERVER_INC ();
break;
}
}
} else {
SERVER_COUNT_UNLOCK ();
}
sleep (5);
/* Handle log rotation if it was requested */
if (received_sighup) {
/*
* Ignore the return value of reload_config for now.
* This should actually be handled somehow...
*/
reload_config ();
#ifdef FILTER_ENABLE
filter_reload ();
#endif /* FILTER_ENABLE */
/* propagate filter reload to all children */
child_kill_children (SIGHUP);
received_sighup = FALSE;
if (pthread_create(&child->thread, attrp, child_thread, child) != 0) {
sblist_delete(childs, sblist_getsize(childs) -1);
free(child);
goto oom;
}
}
safefree(fds);
}
/*
@ -456,21 +223,94 @@ void child_main_loop (void)
*/
void child_kill_children (int sig)
{
unsigned int i;
size_t i, tries = 0;
for (i = 0; i != child_config.maxclients; i++) {
if (child_ptr[i].status != T_EMPTY)
kill (child_ptr[i].tid, sig);
if (sig != SIGTERM) return;
log_message (LOG_INFO,
"trying to bring down %zu threads...",
sblist_getsize(childs)
);
again:
for (i = 0; i < sblist_getsize(childs); i++) {
struct child *c = *((struct child**)sblist_get(childs, i));
if (!c->done) pthread_kill(c->thread, SIGCHLD);
}
usleep(8192);
collect_threads();
if (sblist_getsize(childs) != 0)
if(tries++ < 8) goto again;
if (sblist_getsize(childs) != 0)
log_message (LOG_CRIT,
"child_kill_children: %zu threads still alive!",
sblist_getsize(childs)
);
}
int child_listening_sock (uint16_t port)
void child_free_children(void) {
sblist_free(childs);
childs = 0;
}
/**
* Listen on the various configured interfaces
*/
int child_listening_sockets(sblist *listen_addrs, uint16_t port)
{
listenfd = listen_sock (port, &addrlen);
return listenfd;
int ret;
size_t i;
assert (port > 0);
if (listen_fds == NULL) {
listen_fds = sblist_new(sizeof(int), 16);
if (listen_fds == NULL) {
log_message (LOG_ERR, "Could not create the list "
"of listening fds");
return -1;
}
}
if (!listen_addrs || !sblist_getsize(listen_addrs))
{
/*
* no Listen directive:
* listen on the wildcard address(es)
*/
ret = listen_sock(NULL, port, listen_fds);
return ret;
}
for (i = 0; i < sblist_getsize(listen_addrs); i++) {
char **addr;
addr = sblist_get(listen_addrs, i);
if (!addr || !*addr) {
log_message(LOG_WARNING,
"got NULL from listen_addrs - skipping");
continue;
}
ret = listen_sock(*addr, port, listen_fds);
if (ret != 0) {
return ret;
}
}
return 0;
}
void child_close_sock (void)
{
close (listenfd);
size_t i;
for (i = 0; i < sblist_getsize(listen_fds); i++) {
int *fd = sblist_get(listen_fds, i);
close (*fd);
}
sblist_free(listen_fds);
listen_fds = NULL;
}

View File

@ -21,6 +21,8 @@
#ifndef TINYPROXY_CHILD_H
#define TINYPROXY_CHILD_H
#include "sblist.h"
typedef enum {
CHILD_MAXCLIENTS,
CHILD_MAXSPARESERVERS,
@ -30,10 +32,11 @@ typedef enum {
} child_config_t;
extern short int child_pool_create (void);
extern int child_listening_sock (uint16_t port);
extern int child_listening_sockets (sblist *listen_addrs, uint16_t port);
extern void child_close_sock (void);
extern void child_main_loop (void);
extern void child_kill_children (int sig);
extern void child_free_children(void);
extern short int child_configure (child_config_t type, unsigned int val);

View File

@ -32,135 +32,68 @@
/*
* Include standard headers which are used through-out tinyproxy
*/
#ifdef HAVE_SYS_TYPES_H
/* standard C headers - we can safely assume they exist. */
#include <stddef.h>
#include <stdint.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* standard POSIX headers - they need to be there as well. */
# include <errno.h>
# include <fcntl.h>
# include <netdb.h>
# include <signal.h>
# include <stdarg.h>
# include <strings.h>
# include <syslog.h>
# include <wchar.h>
# include <wctype.h>
# include <sys/mman.h>
# include <sys/select.h>
# include <sys/socket.h>
# include <sys/stat.h>
# include <sys/types.h>
#endif
# include <sys/wait.h>
# include <sys/uio.h>
# include <sys/un.h>
# include <sys/time.h>
# include <time.h>
# include <inttypes.h>
# include <sys/resource.h>
# include <netinet/in.h>
# include <assert.h>
# include <arpa/inet.h>
# include <grp.h>
# include <pwd.h>
# include <limits.h>
/* rest - some oddball headers */
#ifdef HAVE_VALUES_H
# include <values.h>
#endif
#ifdef HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#ifdef HAVE_STDDEF_H
# include <stddef.h>
#endif
#ifdef HAVE_STDINT_H
# include <stdint.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h>
#endif
#ifdef HAVE_SYS_UIO_H
# include <sys/uio.h>
#endif
#ifdef HAVE_SYS_UN_H
# include <sys/un.h>
#endif
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#ifdef HAVE_ALLOCA_H
# include <alloca.h>
#endif
#ifdef HAVE_ASSERT_H
# include <assert.h>
#endif
#ifdef HAVE_CTYPE_H
# include <ctype.h>
#endif
#ifdef HAVE_ERRNO_H
# include <errno.h>
#endif
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#ifdef HAVE_GRP_H
# include <grp.h>
#endif
#ifdef HAVE_MEMORY_H
# include <memory.h>
#endif
#ifdef HAVE_NETDB_H
# include <netdb.h>
#endif
#ifdef HAVE_PWD_H
# include <pwd.h>
#endif
#ifdef HAVE_REGEX_H
# include <regex.h>
#endif
#ifdef HAVE_SIGNAL_H
# include <signal.h>
#endif
#ifdef HAVE_STDARG_H
# include <stdarg.h>
#endif
#ifdef HAVE_STDIO_H
# include <stdio.h>
#endif
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#else
# ifdef HAVE_MALLOC_H
#ifdef HAVE_MALLOC_H
# include <malloc.h>
# endif
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#ifdef HAVE_SYSEXITS_H
# include <sysexits.h>
#endif
#ifdef HAVE_SYSLOG_H
# include <syslog.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_WCHAR_H
# include <wchar.h>
#endif
#ifdef HAVE_WCTYPE_H
# include <wctype.h>
#endif
#ifdef HAVE_SYS_MMAN_H
# include <sys/mman.h>
#endif
/*
* If MSG_NOSIGNAL is not defined, define it to be zero so that it doesn't

72
src/conf-tokens.c Normal file
View File

@ -0,0 +1,72 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <stdlib.h>
#include "conf-tokens.h"
#ifdef HAVE_GPERF
#include "conf-tokens-gperf.inc"
#else
#include <strings.h>
const struct config_directive_entry *
config_directive_find (register const char *str, register size_t len)
{
size_t i;
static const struct config_directive_entry wordlist[] =
{
{"",CD_NIL}, {"",CD_NIL},
{"allow", CD_allow},
{"stathost", CD_stathost},
{"listen", CD_listen},
{"timeout", CD_timeout},
{"statfile", CD_statfile},
{"pidfile", CD_pidfile},
{"bindsame", CD_bindsame},
{"reversebaseurl", CD_reversebaseurl},
{"viaproxyname", CD_viaproxyname},
{"upstream", CD_upstream},
{"anonymous", CD_anonymous},
{"group", CD_group},
{"defaulterrorfile", CD_defaulterrorfile},
{"startservers", CD_startservers},
{"filtercasesensitive", CD_filtercasesensitive},
{"filtertype", CD_filtertype},
{"filterurls", CD_filterurls},
{"filter", CD_filter},
{"reversemagic", CD_reversemagic},
{"errorfile", CD_errorfile},
{"minspareservers", CD_minspareservers},
{"user", CD_user},
{"disableviaheader", CD_disableviaheader},
{"deny", CD_deny},
{"xtinyproxy", CD_xtinyproxy},
{"reversepath", CD_reversepath},
{"bind", CD_bind},
{"maxclients", CD_maxclients},
{"reverseonly", CD_reverseonly},
{"port", CD_port},
{"maxspareservers", CD_maxspareservers},
{"syslog", CD_syslog},
{"filterdefaultdeny", CD_filterdefaultdeny},
{"loglevel", CD_loglevel},
{"filterextended", CD_filterextended},
{"connectport", CD_connectport},
{"logfile", CD_logfile},
{"basicauth", CD_basicauth},
{"basicauthrealm", CD_basicauthrealm},
{"addheader", CD_addheader},
{"maxrequestsperchild", CD_maxrequestsperchild}
};
for(i=0;i<sizeof(wordlist)/sizeof(wordlist[0]);++i) {
if(!strcasecmp(str, wordlist[i].name))
return &wordlist[i];
}
return 0;
}
#endif

63
src/conf-tokens.gperf Normal file
View File

@ -0,0 +1,63 @@
%{
#include <string.h>
#include <stdlib.h>
#include "conf-tokens.h"
%}
struct config_directive_entry { const char* name; enum config_directive value; };
%struct-type
%define slot-name name
%define initializer-suffix ,CD_NIL
%define lookup-function-name config_directive_find
%ignore-case
%7bit
%compare-lengths
%readonly-tables
%define constants-prefix CDS_
%omit-struct-type
%%
logfile, CD_logfile
pidfile, CD_pidfile
anonymous, CD_anonymous
viaproxyname, CD_viaproxyname
defaulterrorfile, CD_defaulterrorfile
statfile, CD_statfile
stathost, CD_stathost
xtinyproxy, CD_xtinyproxy
syslog, CD_syslog
bindsame, CD_bindsame
disableviaheader, CD_disableviaheader
port, CD_port
maxclients, CD_maxclients
maxspareservers, CD_maxspareservers
minspareservers, CD_minspareservers
startservers, CD_startservers
maxrequestsperchild, CD_maxrequestsperchild
timeout, CD_timeout
connectport, CD_connectport
user, CD_user
group, CD_group
listen, CD_listen
allow, CD_allow
deny, CD_deny
bind, CD_bind
basicauth, CD_basicauth
basicauthrealm, CD_basicauthrealm
errorfile, CD_errorfile
addheader, CD_addheader
filter, CD_filter
filterurls, CD_filterurls
filterextended, CD_filterextended
filterdefaultdeny, CD_filterdefaultdeny
filtercasesensitive, CD_filtercasesensitive
filtertype, CD_filtertype
reversebaseurl, CD_reversebaseurl
reverseonly, CD_reverseonly
reversemagic, CD_reversemagic
reversepath, CD_reversepath
upstream, CD_upstream
loglevel, CD_loglevel
%%

55
src/conf-tokens.h Normal file
View File

@ -0,0 +1,55 @@
#ifndef CONF_TOKENS_H
#define CONF_TOKENS_H
enum config_directive {
CD_NIL = 0,
CD_logfile,
CD_pidfile,
CD_anonymous,
CD_viaproxyname,
CD_defaulterrorfile,
CD_statfile,
CD_stathost,
CD_xtinyproxy,
CD_syslog,
CD_bindsame,
CD_disableviaheader,
CD_port,
CD_maxclients,
CD_maxspareservers,
CD_minspareservers,
CD_startservers,
CD_maxrequestsperchild,
CD_timeout,
CD_connectport,
CD_user,
CD_group,
CD_listen,
CD_allow,
CD_deny,
CD_bind,
CD_basicauth,
CD_basicauthrealm,
CD_errorfile,
CD_addheader,
CD_filter,
CD_filterurls,
CD_filtertype,
CD_filterextended,
CD_filterdefaultdeny,
CD_filtercasesensitive,
CD_reversebaseurl,
CD_reverseonly,
CD_reversemagic,
CD_reversepath,
CD_upstream,
CD_loglevel,
};
struct config_directive_entry { const char* name; enum config_directive value; };
const struct config_directive_entry *
config_directive_find (register const char *str, register size_t len);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -22,8 +22,9 @@
#ifndef TINYPROXY_CONF_H
#define TINYPROXY_CONF_H
#include "hashmap.h"
#include "vector.h"
#include "hsearch.h"
#include "sblist.h"
#include "acl.h"
/*
* Stores a HTTP header created using the AddHeader directive.
@ -37,21 +38,20 @@ typedef struct {
* Hold all the configuration time information.
*/
struct config_s {
sblist *basicauth_list;
char *basicauth_realm;
char *logf_name;
char *config_file;
unsigned int syslog; /* boolean */
int port;
unsigned int port;
char *stathost;
unsigned int godaemon; /* boolean */
unsigned int quit; /* boolean */
unsigned int maxclients;
char *user;
char *group;
char *ipAddr;
sblist *listen_addrs;
#ifdef FILTER_ENABLE
char *filter;
unsigned int filter_url; /* boolean */
unsigned int filter_extended; /* boolean */
unsigned int filter_casesensitive; /* boolean */
unsigned int filter_opts; /* enum filter_options */
#endif /* FILTER_ENABLE */
#ifdef XTINYPROXY_ENABLE
unsigned int add_xtinyproxy; /* boolean */
@ -67,7 +67,7 @@ struct config_s {
#endif /* UPSTREAM_SUPPORT */
char *pidpath;
unsigned int idletimeout;
char *bind_address;
sblist *bind_addrs;
unsigned int bindsame;
/*
@ -80,7 +80,7 @@ struct config_s {
/*
* Error page support. Map error numbers to file paths.
*/
hashmap_t errorpages;
struct htab *errorpages;
/*
* Error page to be displayed if appropriate page cannot be located
@ -93,26 +93,28 @@ struct config_s {
*/
char *statpage;
vector_t access_list;
acl_list_t access_list;
/*
* Store the list of port allowed by CONNECT.
*/
vector_t connect_ports;
sblist *connect_ports;
/*
* Map of headers which should be let through when the
* anonymous feature is turned on.
*/
hashmap_t anonymous_map;
struct htab *anonymous_map;
/*
* Extra headers to be added to outgoing HTTP requests.
*/
vector_t add_headers;
sblist* add_headers;
};
extern int reload_config_file (const char *config_fname, struct config_s *conf,
struct config_s *defaults);
extern int reload_config_file (const char *config_fname, struct config_s *conf);
int config_init (void);
void free_config (struct config_s *conf);
#endif

View File

@ -25,10 +25,10 @@
* Now, this routine adds a "port" to the list. It also creates the list if
* it hasn't already by done.
*/
void add_connect_port_allowed (int port, vector_t *connect_ports)
void add_connect_port_allowed (int port, sblist **connect_ports)
{
if (!*connect_ports) {
*connect_ports = vector_create ();
*connect_ports = sblist_new (sizeof(int), 16);
if (!*connect_ports) {
log_message (LOG_WARNING,
"Could not create a list of allowed CONNECT ports");
@ -38,7 +38,7 @@ void add_connect_port_allowed (int port, vector_t *connect_ports)
log_message (LOG_INFO,
"Adding Port [%d] to the list allowed by CONNECT", port);
vector_append (*connect_ports, (void **) &port, sizeof (port));
sblist_add (*connect_ports, &port);
}
/*
@ -47,7 +47,7 @@ void add_connect_port_allowed (int port, vector_t *connect_ports)
* Returns: 1 if allowed
* 0 if denied
*/
int check_allowed_connect_ports (int port, vector_t connect_ports)
int check_allowed_connect_ports (int port, sblist *connect_ports)
{
size_t i;
int *data;
@ -59,8 +59,8 @@ int check_allowed_connect_ports (int port, vector_t connect_ports)
if (!connect_ports)
return 1;
for (i = 0; i != (size_t) vector_length (connect_ports); ++i) {
data = (int *) vector_getentry (connect_ports, i, NULL);
for (i = 0; i < sblist_getsize (connect_ports); ++i) {
data = sblist_get (connect_ports, i);
if (data && *data == port)
return 1;
}
@ -71,7 +71,7 @@ int check_allowed_connect_ports (int port, vector_t connect_ports)
/**
* Free a connect_ports list.
*/
void free_connect_ports_list (vector_t connect_ports)
void free_connect_ports_list (sblist *connect_ports)
{
vector_delete (connect_ports);
sblist_free (connect_ports);
}

View File

@ -22,10 +22,10 @@
#define _TINYPROXY_CONNECT_PORTS_H_
#include "common.h"
#include "vector.h"
#include "sblist.h"
extern void add_connect_port_allowed (int port, vector_t *connect_ports);
int check_allowed_connect_ports (int port, vector_t connect_ports);
void free_connect_ports_list (vector_t connect_ports);
extern void add_connect_port_allowed (int port, sblist **connect_ports);
int check_allowed_connect_ports (int port, sblist *connect_ports);
void free_connect_ports_list (sblist *connect_ports);
#endif /* _TINYPROXY_CONNECT_PORTS_ */

View File

@ -30,14 +30,20 @@
#include "log.h"
#include "stats.h"
struct conn_s *initialize_conn (int client_fd, const char *ipaddr,
const char *string_addr,
void conn_struct_init(struct conn_s *connptr) {
connptr->error_number = -1;
connptr->client_fd = -1;
connptr->server_fd = -1;
/* There is _no_ content length initially */
connptr->content_length.server = connptr->content_length.client = -1;
}
int conn_init_contents (struct conn_s *connptr, const char *ipaddr,
const char *sock_ipaddr)
{
struct conn_s *connptr;
struct buffer_s *cbuffer, *sbuffer;
assert (client_fd >= 0);
assert (connptr->client_fd >= 0);
/*
* Allocate the memory for all the internal components
@ -48,48 +54,16 @@ struct conn_s *initialize_conn (int client_fd, const char *ipaddr,
if (!cbuffer || !sbuffer)
goto error_exit;
/*
* Allocate the space for the conn_s structure itself.
*/
connptr = (struct conn_s *) safemalloc (sizeof (struct conn_s));
if (!connptr)
goto error_exit;
connptr->client_fd = client_fd;
connptr->server_fd = -1;
connptr->cbuffer = cbuffer;
connptr->sbuffer = sbuffer;
connptr->request_line = NULL;
/* These store any error strings */
connptr->error_variables = NULL;
connptr->error_string = NULL;
connptr->error_number = -1;
connptr->connect_method = FALSE;
connptr->show_stats = FALSE;
connptr->protocol.major = connptr->protocol.minor = 0;
/* There is _no_ content length initially */
connptr->content_length.server = connptr->content_length.client = -1;
connptr->server_ip_addr = (sock_ipaddr ?
safestrdup (sock_ipaddr) : NULL);
connptr->client_ip_addr = safestrdup (ipaddr);
connptr->client_string_addr = safestrdup (string_addr);
connptr->upstream_proxy = NULL;
update_stats (STAT_OPEN);
#ifdef REVERSE_SUPPORT
connptr->reversepath = NULL;
#endif
return connptr;
return 1;
error_exit:
/*
@ -100,10 +74,10 @@ error_exit:
if (sbuffer)
delete_buffer (sbuffer);
return NULL;
return 0;
}
void destroy_conn (struct conn_s *connptr)
void conn_destroy_contents (struct conn_s *connptr)
{
assert (connptr != NULL);
@ -111,10 +85,12 @@ void destroy_conn (struct conn_s *connptr)
if (close (connptr->client_fd) < 0)
log_message (LOG_INFO, "Client (%d) close message: %s",
connptr->client_fd, strerror (errno));
connptr->client_fd = -1;
if (connptr->server_fd != -1)
if (close (connptr->server_fd) < 0)
log_message (LOG_INFO, "Server (%d) close message: %s",
connptr->server_fd, strerror (errno));
connptr->server_fd = -1;
if (connptr->cbuffer)
delete_buffer (connptr->cbuffer);
@ -124,8 +100,16 @@ void destroy_conn (struct conn_s *connptr)
if (connptr->request_line)
safefree (connptr->request_line);
if (connptr->error_variables)
hashmap_delete (connptr->error_variables);
if (connptr->error_variables) {
char *k;
htab_value *v;
size_t it = 0;
while((it = htab_next(connptr->error_variables, it, &k, &v))) {
safefree(v->p);
safefree(k);
}
htab_destroy (connptr->error_variables);
}
if (connptr->error_string)
safefree (connptr->error_string);
@ -134,15 +118,11 @@ void destroy_conn (struct conn_s *connptr)
safefree (connptr->server_ip_addr);
if (connptr->client_ip_addr)
safefree (connptr->client_ip_addr);
if (connptr->client_string_addr)
safefree (connptr->client_string_addr);
#ifdef REVERSE_SUPPORT
if (connptr->reversepath)
safefree (connptr->reversepath);
#endif
safefree (connptr);
update_stats (STAT_CLOSE);
}

View File

@ -22,7 +22,7 @@
#define TINYPROXY_CONNS_H
#include "main.h"
#include "hashmap.h"
#include "hsearch.h"
/*
* Connection Definition
@ -45,7 +45,7 @@ struct conn_s {
* This structure stores key -> value mappings for substitution
* in the error HTML files.
*/
hashmap_t error_variables;
struct htab *error_variables;
int error_number;
char *error_string;
@ -62,10 +62,9 @@ struct conn_s {
char *server_ip_addr;
/*
* Store the client's IP and hostname information
* Store the client's IP information
*/
char *client_ip_addr;
char *client_string_addr;
/*
* Store the incoming request's HTTP protocol.
@ -88,12 +87,13 @@ struct conn_s {
struct upstream *upstream_proxy;
};
/*
* Functions for the creation and destruction of a connection structure.
*/
extern struct conn_s *initialize_conn (int client_fd, const char *ipaddr,
const char *string_addr,
/* expects pointer to zero-initialized struct, set up struct
with default values for initial use */
extern void conn_struct_init(struct conn_s *connptr);
/* second stage initializiation, sets up buffers and connection details */
extern int conn_init_contents (struct conn_s *connptr, const char *ipaddr,
const char *sock_ipaddr);
extern void destroy_conn (struct conn_s *connptr);
extern void conn_destroy_contents (struct conn_s *connptr);
#endif

View File

@ -24,60 +24,66 @@
#include "main.h"
#include <regex.h>
#include <fnmatch.h>
#include "filter.h"
#include "heap.h"
#include "log.h"
#include "reqs.h"
#include "conf.h"
#include "sblist.h"
#define FILTER_BUFFER_LEN (512)
static int err;
struct filter_list {
struct filter_list *next;
char *pat;
regex_t *cpat;
union {
regex_t cpatb;
char *pattern;
} u;
};
static struct filter_list *fl = NULL;
static sblist *fl = NULL;
static int already_init = 0;
static filter_policy_t default_policy = FILTER_DEFAULT_ALLOW;
/*
* Initializes a linked list of strings containing hosts/urls to be filtered
* Initializes a list of strings containing hosts/urls to be filtered
*/
void filter_init (void)
{
FILE *fd;
struct filter_list *p;
struct filter_list fe;
char buf[FILTER_BUFFER_LEN];
char *s;
int cflags;
char *s, *start;
int cflags, lineno = 0;
if (fl || already_init) {
return;
}
fd = fopen (config.filter, "r");
fd = fopen (config->filter, "r");
if (!fd) {
return;
perror ("filter file");
exit (EX_DATAERR);
}
p = NULL;
cflags = REG_NEWLINE | REG_NOSUB;
if (config.filter_extended)
cflags |= REG_EXTENDED;
if (!config.filter_casesensitive)
cflags |= REG_ICASE;
cflags |= (REG_EXTENDED * !!(config->filter_opts & FILTER_OPT_TYPE_ERE));
cflags |= (REG_ICASE * !(config->filter_opts & FILTER_OPT_CASESENSITIVE));
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
* comments.
*/
s = buf;
while (*s) {
if (isspace ((unsigned char) *s))
break;
@ -93,34 +99,34 @@ void filter_init (void)
++s;
}
*s = '\0';
/* skip leading whitespace */
s = buf;
while (*s && isspace ((unsigned char) *s))
s++;
s = start;
/* skip blank lines and comments */
if (*s == '\0')
continue;
if (!p) /* head of list */
fl = p =
(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;
}
if (!fl) fl = sblist_new(sizeof(struct filter_list),
4096/sizeof(struct filter_list));
p->pat = safestrdup (s);
p->cpat = (regex_t *) safemalloc (sizeof (regex_t));
err = regcomp (p->cpat, p->pat, cflags);
if (config->filter_opts & FILTER_OPT_TYPE_FNMATCH) {
fe.u.pattern = safestrdup(s);
if (!fe.u.pattern) goto oom;
} else {
err = regcomp (&fe.u.cpatb, s, cflags);
if (err != 0) {
if (err == REG_ESPACE) goto oom;
fprintf (stderr,
"Bad regex in %s: %s\n",
config.filter, p->pat);
"Bad regex in %s: line %d - %s\n",
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);
}
}
@ -136,15 +142,19 @@ void filter_init (void)
/* unlink the list */
void filter_destroy (void)
{
struct filter_list *p, *q;
struct filter_list *p;
size_t i;
if (already_init) {
for (p = q = fl; p; p = q) {
regfree (p->cpat);
safefree (p->cpat);
safefree (p->pat);
q = p->next;
safefree (p);
if (fl) {
for (i = 0; i < sblist_getsize(fl); ++i) {
p = sblist_get(fl, i);
if (config->filter_opts & FILTER_OPT_TYPE_FNMATCH)
safefree(p->u.pattern);
else
regfree (&p->u.cpatb);
}
sblist_free(fl);
}
fl = NULL;
already_init = 0;
@ -156,7 +166,7 @@ void filter_destroy (void)
*/
void filter_reload (void)
{
if (config.filter) {
if (config->filter) {
log_message (LOG_NOTICE, "Re-reading filter file.");
filter_destroy ();
filter_init ();
@ -164,20 +174,25 @@ void filter_reload (void)
}
/* Return 0 to allow, non-zero to block */
int filter_domain (const char *host)
int filter_run (const char *str)
{
struct filter_list *p;
size_t i;
int result;
if (!fl || !already_init)
goto COMMON_EXIT;
for (p = fl; p; p = p->next) {
for (i = 0; i < sblist_getsize(fl); ++i) {
p = sblist_get(fl, i);
if (config->filter_opts & FILTER_OPT_TYPE_FNMATCH)
result = fnmatch (p->u.pattern, str, 0);
else
result =
regexec (p->cpat, host, (size_t) 0, (regmatch_t *) 0, 0);
regexec (&p->u.cpatb, str, (size_t) 0, (regmatch_t *) 0, 0);
if (result == 0) {
if (default_policy == FILTER_DEFAULT_ALLOW)
if (!(config->filter_opts & FILTER_OPT_DEFAULT_DENY))
return 1;
else
return 0;
@ -185,44 +200,8 @@ int filter_domain (const char *host)
}
COMMON_EXIT:
if (default_policy == FILTER_DEFAULT_ALLOW)
if (!(config->filter_opts & FILTER_OPT_DEFAULT_DENY))
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 (default_policy == FILTER_DEFAULT_ALLOW)
return 1;
else
return 0;
}
}
COMMON_EXIT:
if (default_policy == FILTER_DEFAULT_ALLOW)
return 0;
else
return 1;
}
/*
* Set the default filtering policy
*/
void filter_set_default_policy (filter_policy_t policy)
{
default_policy = policy;
}

View File

@ -21,17 +21,22 @@
#ifndef _TINYPROXY_FILTER_H_
#define _TINYPROXY_FILTER_H_
typedef enum {
FILTER_DEFAULT_ALLOW,
FILTER_DEFAULT_DENY
} filter_policy_t;
enum filter_options {
FILTER_OPT_CASESENSITIVE = 1 << 0,
FILTER_OPT_URL = 1 << 1,
FILTER_OPT_DEFAULT_DENY = 1 << 2,
FILTER_OPT_TYPE_BRE = 1 << 8,
FILTER_OPT_TYPE_ERE = 1 << 9,
FILTER_OPT_TYPE_FNMATCH = 1 << 10,
};
#define FILTER_TYPE_MASK \
(FILTER_OPT_TYPE_BRE | FILTER_OPT_TYPE_ERE | FILTER_OPT_TYPE_FNMATCH)
extern void filter_init (void);
extern void filter_destroy (void);
extern void filter_reload (void);
extern int filter_domain (const char *host);
extern int filter_url (const char *url);
extern void filter_set_default_policy (filter_policy_t policy);
extern int filter_run (const char *str);
#endif

View File

@ -1,494 +0,0 @@
/* tinyproxy - A fast light-weight HTTP proxy
* Copyright (C) 2002 Robert James Kaes <rjkaes@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* A hashmap implementation. The keys are case-insensitive NULL terminated
* strings, and the data is arbitrary lumps of data. Copies of both the
* key and the data in the hashmap itself, so you must free the original
* key and data to avoid a memory leak. The hashmap returns a pointer
* to the data when a key is searched for, so take care in modifying the
* data as it's modifying the data stored in the hashmap. (In other words,
* don't try to free the data, or realloc the memory. :)
*/
#include "main.h"
#include "hashmap.h"
#include "heap.h"
/*
* These structures are the storage for the hashmap. Entries are stored in
* struct hashentry_s (the key, data, and length), and all the "buckets" are
* grouped together in hashmap_s. The hashmap_s.size member is for
* internal use. It stores the number of buckets the hashmap was created
* with.
*/
struct hashentry_s {
char *key;
void *data;
size_t len;
struct hashentry_s *prev, *next;
};
struct hashbucket_s {
struct hashentry_s *head, *tail;
};
struct hashmap_s {
unsigned int size;
hashmap_iter end_iterator;
struct hashbucket_s *buckets;
};
/*
* A NULL terminated string is passed to this function and a "hash" value
* is produced within the range of [0 .. size) (In other words, 0 to one
* less than size.)
* The contents of the key are converted to lowercase, so this function
* is not case-sensitive.
*
* If any of the arguments are invalid a negative number is returned.
*/
static int hashfunc (const char *key, unsigned int size)
{
uint32_t hash;
if (key == NULL)
return -EINVAL;
if (size == 0)
return -ERANGE;
for (hash = tolower (*key++); *key != '\0'; key++) {
uint32_t bit = (hash & 1) ? (1 << (sizeof (uint32_t) - 1)) : 0;
hash >>= 1;
hash += tolower (*key) + bit;
}
/* Keep the hash within the table limits */
return hash % size;
}
/*
* Create a hashmap with the requested number of buckets. If "nbuckets" is
* not greater than zero a NULL is returned; otherwise, a _token_ to the
* hashmap is returned.
*
* NULLs are also returned if memory could not be allocated for hashmap.
*/
hashmap_t hashmap_create (unsigned int nbuckets)
{
struct hashmap_s *ptr;
if (nbuckets == 0)
return NULL;
ptr = (struct hashmap_s *) safecalloc (1, sizeof (struct hashmap_s));
if (!ptr)
return NULL;
ptr->size = nbuckets;
ptr->buckets = (struct hashbucket_s *) safecalloc (nbuckets,
sizeof (struct
hashbucket_s));
if (!ptr->buckets) {
safefree (ptr);
return NULL;
}
/* This points to "one" past the end of the hashmap. */
ptr->end_iterator = 0;
return ptr;
}
/*
* Follow the chain of hashentries and delete them (including the data and
* the key.)
*
* Returns: 0 if the function completed successfully
* negative number is returned if "entry" was NULL
*/
static int delete_hashbucket (struct hashbucket_s *bucket)
{
struct hashentry_s *nextptr;
struct hashentry_s *ptr;
if (bucket == NULL || bucket->head == NULL)
return -EINVAL;
ptr = bucket->head;
while (ptr) {
nextptr = ptr->next;
safefree (ptr->key);
safefree (ptr->data);
safefree (ptr);
ptr = nextptr;
}
return 0;
}
/*
* Deletes a hashmap. All the key/data pairs are also deleted.
*
* Returns: 0 on success
* negative if a NULL "map" was supplied
*/
int hashmap_delete (hashmap_t map)
{
unsigned int i;
if (map == NULL)
return -EINVAL;
for (i = 0; i != map->size; i++) {
if (map->buckets[i].head != NULL) {
delete_hashbucket (&map->buckets[i]);
}
}
safefree (map->buckets);
safefree (map);
return 0;
}
/*
* Inserts a NULL terminated string (as the key), plus any arbitrary "data"
* of "len" bytes. Both the key and the data are copied, so the original
* key/data must be freed to avoid a memory leak.
* The "data" must be non-NULL and "len" must be greater than zero. You
* cannot insert NULL data in association with the key.
*
* Returns: 0 on success
* negative number if there are errors
*/
int
hashmap_insert (hashmap_t map, const char *key, const void *data, size_t len)
{
struct hashentry_s *ptr;
int hash;
char *key_copy;
void *data_copy;
assert (map != NULL);
assert (key != NULL);
assert (data != NULL);
assert (len > 0);
if (map == NULL || key == NULL)
return -EINVAL;
if (!data || len < 1)
return -ERANGE;
hash = hashfunc (key, map->size);
if (hash < 0)
return hash;
/*
* First make copies of the key and data in case there is a memory
* problem later.
*/
key_copy = safestrdup (key);
if (!key_copy)
return -ENOMEM;
data_copy = safemalloc (len);
if (!data_copy) {
safefree (key_copy);
return -ENOMEM;
}
memcpy (data_copy, data, len);
ptr = (struct hashentry_s *) safemalloc (sizeof (struct hashentry_s));
if (!ptr) {
safefree (key_copy);
safefree (data_copy);
return -ENOMEM;
}
ptr->key = key_copy;
ptr->data = data_copy;
ptr->len = len;
/*
* Now add the entry to the end of the bucket chain.
*/
ptr->next = NULL;
ptr->prev = map->buckets[hash].tail;
if (map->buckets[hash].tail)
map->buckets[hash].tail->next = ptr;
map->buckets[hash].tail = ptr;
if (!map->buckets[hash].head)
map->buckets[hash].head = ptr;
map->end_iterator++;
return 0;
}
/*
* Get an iterator to the first entry.
*
* Returns: an negative value upon error.
*/
hashmap_iter hashmap_first (hashmap_t map)
{
assert (map != NULL);
if (!map)
return -EINVAL;
if (map->end_iterator == 0)
return -1;
else
return 0;
}
/*
* Checks to see if the iterator is pointing at the "end" of the entries.
*
* Returns: 1 if it is the end
* 0 otherwise
*/
int hashmap_is_end (hashmap_t map, hashmap_iter iter)
{
assert (map != NULL);
assert (iter >= 0);
if (!map || iter < 0)
return -EINVAL;
if (iter == map->end_iterator)
return 1;
else
return 0;
}
/*
* Return a "pointer" to the first instance of the particular key. It can
* be tested against hashmap_is_end() to see if the key was not found.
*
* Returns: negative upon an error
* an "iterator" pointing at the first key
* an "end-iterator" if the key wasn't found
*/
hashmap_iter hashmap_find (hashmap_t map, const char *key)
{
unsigned int i;
hashmap_iter iter = 0;
struct hashentry_s *ptr;
assert (map != NULL);
assert (key != NULL);
if (!map || !key)
return -EINVAL;
/*
* Loop through all the keys and look for the first occurrence
* of a particular key.
*/
for (i = 0; i != map->size; i++) {
ptr = map->buckets[i].head;
while (ptr) {
if (strcasecmp (ptr->key, key) == 0) {
/* Found it, so return the current count */
return iter;
}
iter++;
ptr = ptr->next;
}
}
return iter;
}
/*
* Retrieve the data associated with a particular iterator.
*
* Returns: the length of the data block upon success
* negative upon error
*/
ssize_t
hashmap_return_entry (hashmap_t map, hashmap_iter iter, char **key, void **data)
{
unsigned int i;
struct hashentry_s *ptr;
hashmap_iter count = 0;
assert (map != NULL);
assert (iter >= 0);
assert (iter != map->end_iterator);
assert (key != NULL);
assert (data != NULL);
if (!map || iter < 0 || !key || !data)
return -EINVAL;
for (i = 0; i != map->size; i++) {
ptr = map->buckets[i].head;
while (ptr) {
if (count == iter) {
/* This is the data so return it */
*key = ptr->key;
*data = ptr->data;
return ptr->len;
}
ptr = ptr->next;
count++;
}
}
return -EFAULT;
}
/*
* Searches for _any_ occurrences of "key" within the hashmap.
*
* Returns: negative upon an error
* zero if no key is found
* count found
*/
ssize_t hashmap_search (hashmap_t map, const char *key)
{
int hash;
struct hashentry_s *ptr;
ssize_t count = 0;
if (map == NULL || key == NULL)
return -EINVAL;
hash = hashfunc (key, map->size);
if (hash < 0)
return hash;
ptr = map->buckets[hash].head;
/* All right, there is an entry here, now see if it's the one we want */
while (ptr) {
if (strcasecmp (ptr->key, key) == 0)
++count;
/* This entry didn't contain the key; move to the next one */
ptr = ptr->next;
}
return count;
}
/*
* Get the first entry (assuming there is more than one) for a particular
* key. The data MUST be non-NULL.
*
* Returns: negative upon error
* zero if no entry is found
* length of data for the entry
*/
ssize_t hashmap_entry_by_key (hashmap_t map, const char *key, void **data)
{
int hash;
struct hashentry_s *ptr;
if (!map || !key || !data)
return -EINVAL;
hash = hashfunc (key, map->size);
if (hash < 0)
return hash;
ptr = map->buckets[hash].head;
while (ptr) {
if (strcasecmp (ptr->key, key) == 0) {
*data = ptr->data;
return ptr->len;
}
ptr = ptr->next;
}
return 0;
}
/*
* Go through the hashmap and remove the particular key.
* NOTE: This will invalidate any iterators which have been created.
*
* Remove: negative upon error
* 0 if the key was not found
* positive count of entries deleted
*/
ssize_t hashmap_remove (hashmap_t map, const char *key)
{
int hash;
struct hashentry_s *ptr, *next;
short int deleted = 0;
if (map == NULL || key == NULL)
return -EINVAL;
hash = hashfunc (key, map->size);
if (hash < 0)
return hash;
ptr = map->buckets[hash].head;
while (ptr) {
if (strcasecmp (ptr->key, key) == 0) {
/*
* Found the data, now need to remove everything
* and update the hashmap.
*/
next = ptr->next;
if (ptr->prev)
ptr->prev->next = ptr->next;
if (ptr->next)
ptr->next->prev = ptr->prev;
if (map->buckets[hash].head == ptr)
map->buckets[hash].head = ptr->next;
if (map->buckets[hash].tail == ptr)
map->buckets[hash].tail = ptr->prev;
safefree (ptr->key);
safefree (ptr->data);
safefree (ptr);
++deleted;
--map->end_iterator;
ptr = next;
continue;
}
/* This entry didn't contain the key; move to the next one */
ptr = ptr->next;
}
/* The key was not found, so return 0 */
return deleted;
}

View File

@ -1,120 +0,0 @@
/* tinyproxy - A fast light-weight HTTP proxy
* Copyright (C) 2002 Robert James Kaes <rjkaes@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* See 'hashmap.c' for detailed information. */
#ifndef _HASHMAP_H
#define _HASHMAP_H
#include "common.h"
/*
* We're using a typedef here to "hide" the implementation details of the
* hash map. Sure, it's a pointer, but the struct is hidden in the C file.
* So, just use the hashmap_t like it's a cookie. :)
*/
typedef struct hashmap_s *hashmap_t;
typedef int hashmap_iter;
/*
* hashmap_create() takes one argument, which is the number of buckets to
* use internally. hashmap_delete() is self explanatory.
*/
extern hashmap_t hashmap_create (unsigned int nbuckets);
extern int hashmap_delete (hashmap_t map);
/*
* When the you insert a key/data pair into the hashmap it will the key
* and data are duplicated, so you must free your copy if it was created
* on the heap. The key must be a NULL terminated string. "data" must be
* non-NULL and length must be greater than zero.
*
* Returns: negative on error
* 0 upon successful insert
*/
extern int hashmap_insert (hashmap_t map, const char *key,
const void *data, size_t len);
/*
* Get an iterator to the first entry.
*
* Returns: an negative value upon error.
*/
extern hashmap_iter hashmap_first (hashmap_t map);
/*
* Checks to see if the iterator is pointing at the "end" of the entries.
*
* Returns: 1 if it is the end
* 0 otherwise
*/
extern int hashmap_is_end (hashmap_t map, hashmap_iter iter);
/*
* Return a "pointer" to the first instance of the particular key. It can
* be tested against hashmap_is_end() to see if the key was not found.
*
* Returns: negative upon an error
* an "iterator" pointing at the first key
* an "end-iterator" if the key wasn't found
*/
extern hashmap_iter hashmap_find (hashmap_t map, const char *key);
/*
* Retrieve the key/data associated with a particular iterator.
* NOTE: These are pointers to the actual data, so don't mess around with them
* too much.
*
* Returns: the length of the data block upon success
* negative upon error
*/
extern ssize_t hashmap_return_entry (hashmap_t map, hashmap_iter iter,
char **key, void **data);
/*
* Get the first entry (assuming there is more than one) for a particular
* key. The data MUST be non-NULL.
*
* Returns: negative upon error
* zero if no entry is found
* length of data for the entry
*/
extern ssize_t hashmap_entry_by_key (hashmap_t map, const char *key,
void **data);
/*
* Searches for _any_ occurrances of "key" within the hashmap and returns the
* number of matching entries.
*
* Returns: negative upon an error
* zero if no key is found
* count found (positive value)
*/
extern ssize_t hashmap_search (hashmap_t map, const char *key);
/*
* Go through the hashmap and remove the particular key.
* NOTE: This will invalidate any iterators which have been created.
*
* Remove: negative upon error
* 0 if the key was not found
* positive count of entries deleted
*/
extern ssize_t hashmap_remove (hashmap_t map, const char *key);
#endif /* _HASHMAP_H */

View File

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

View File

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

179
src/hostspec.c Normal file
View File

@ -0,0 +1,179 @@
#include "common.h"
#include "hostspec.h"
#include "heap.h"
#include "network.h"
static int dotted_mask(char *bitmask_string, unsigned char array[])
{
unsigned char v4bits[4];
if (1 != inet_pton (AF_INET, bitmask_string, v4bits)) return -1;
memset (array, 0xff, IPV6_LEN-4);
memcpy (array + IPV6_LEN-4, v4bits, 4);
return 0;
}
/*
* Fills in the netmask array given a numeric value.
*
* Returns:
* 0 on success
* -1 on failure (invalid mask value)
*
*/
static int
fill_netmask_array (char *bitmask_string, int v6,
unsigned char array[])
{
unsigned int i;
unsigned long int mask;
char *endptr;
errno = 0; /* to distinguish success/failure after call */
if (strchr (bitmask_string, '.')) {
if (v6) return -1; /* ipv6 doesn't supported dotted netmasks */
return dotted_mask(bitmask_string, array);
}
mask = strtoul (bitmask_string, &endptr, 10);
/* check for various conversion errors */
if ((errno == ERANGE && mask == ULONG_MAX)
|| (errno != 0 && mask == 0) || (endptr == bitmask_string))
return -1;
if (v6 == 0) {
/* The mask comparison is done as an IPv6 address, so
* convert to a longer mask in the case of IPv4
* addresses. */
mask += 12 * 8;
}
/* check valid range for a bit mask */
if (mask > (8 * IPV6_LEN))
return -1;
/* we have a valid range to fill in the array */
for (i = 0; i != IPV6_LEN; ++i) {
if (mask >= 8) {
array[i] = 0xff;
mask -= 8;
} else if (mask > 0) {
array[i] = (unsigned char) (0xff << (8 - mask));
mask = 0;
} else {
array[i] = 0;
}
}
return 0;
}
/* parse a location string containing either an ipv4/ipv4 + hostmask tuple
or a dnsname into a struct hostspec.
returns 0 on success, non-0 on error (might be memory allocation, bogus
ip address or mask).
*/
int hostspec_parse(char *location, struct hostspec *h) {
char *mask, ip_dst[IPV6_LEN];
h->type = HST_NONE;
if(!location) return 0;
memset(h, 0, sizeof(*h));
if ((mask = strrchr(location, '/')))
*(mask++) = 0;
/*
* Check for a valid IP address (the simplest case) first.
*/
if (full_inet_pton (location, ip_dst) > 0) {
h->type = HST_NUMERIC;
memcpy (h->address.ip.network, ip_dst, IPV6_LEN);
if(!mask) memset (h->address.ip.mask, 0xff, IPV6_LEN);
else {
char dst[sizeof(struct in6_addr)];
int v6, i;
/* Check if the IP address before the netmask is
* an IPv6 address */
if (inet_pton(AF_INET6, location, dst) > 0)
v6 = 1;
else
v6 = 0;
if (fill_netmask_array
(mask, v6, &(h->address.ip.mask[0]))
< 0)
goto err;
for (i = 0; i < IPV6_LEN; i++)
h->address.ip.network[i] = ip_dst[i] &
h->address.ip.mask[i];
}
} else {
/* either bogus IP or hostname */
/* bogus ipv6 ? */
if (mask || strchr (location, ':'))
goto err;
/* In all likelihood a string */
h->type = HST_STRING;
h->address.string = safestrdup (location);
if (!h->address.string)
goto err;
}
/* restore mask */
if(mask) *(--mask) = '/';
return 0;
err:;
if(mask) *(--mask) = '/';
return -1;
}
static int string_match(const char *ip, const char *addrspec)
{
size_t test_length, match_length;
if(!strcasecmp(ip, addrspec)) return 1;
if(addrspec[0] != '.') return 0;
test_length = strlen (ip);
match_length = strlen (addrspec);
if (test_length < match_length) return 0;
return (strcasecmp
(ip + (test_length - match_length),
addrspec) == 0);
}
static int numeric_match(const uint8_t addr[], const struct hostspec *h)
{
uint8_t x, y;
int i;
for (i = 0; i != IPV6_LEN; ++i) {
x = addr[i] & h->address.ip.mask[i];
y = h->address.ip.network[i];
/* If x and y don't match, the IP addresses don't match */
if (x != y)
return 0;
}
return 1;
}
/* check whether ip matches hostspec.
return 1 on match, 0 on non-match */
int hostspec_match(const char *ip, const struct hostspec *h) {
int is_numeric_addr;
uint8_t numeric_addr[IPV6_LEN];
if (ip[0] == '\0') return 0;
is_numeric_addr = (full_inet_pton (ip, &numeric_addr) > 0);
switch (h->type) {
case HST_STRING:
if(is_numeric_addr) return 0;
return string_match (ip, h->address.string);
case HST_NUMERIC:
return numeric_match (numeric_addr, h);
case HST_NONE:
return 0;
}
return 0;
}

26
src/hostspec.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef HOSTSPEC_H
#define HOSTSPEC_H
#define IPV6_LEN 16
enum hostspec_type {
HST_NONE,
HST_STRING,
HST_NUMERIC,
};
struct hostspec {
enum hostspec_type type;
union {
char *string;
struct {
unsigned char network[IPV6_LEN];
unsigned char mask[IPV6_LEN];
} ip;
} address;
};
int hostspec_parse(char *domain, struct hostspec *h);
int hostspec_match(const char *ip, const struct hostspec *h);
#endif

222
src/hsearch.c Normal file
View File

@ -0,0 +1,222 @@
/*
musl license, hsearch.c originally written by Szabolcs Nagy
Copyright © 2005-2020 Rich Felker, et al.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include "hsearch.h"
/*
open addressing hash table with 2^n table size
quadratic probing is used in case of hash collision
tab indices and hash are size_t
after resize fails with ENOMEM the state of tab is still usable
*/
typedef struct htab_entry {
char *key;
htab_value data;
} htab_entry;
struct elem {
htab_entry item;
size_t hash;
};
struct htab {
struct elem *elems;
size_t mask;
size_t used;
size_t seed;
size_t dead;
};
#define MINSIZE 8
#define MAXSIZE ((size_t)-1/2 + 1)
#define CASE_INSENSITIVE
#ifdef CASE_INSENSITIVE
#include <ctype.h>
#include <strings.h>
#define LOWER_OR_NOT(X) tolower(X)
#define STRCMP(X, Y) strcasecmp(X, Y)
#else
#define LOWER_OR_NOT(X) X
#define STRCMP(X, Y) strcmp(X, Y)
#endif
static size_t keyhash(const char *k, size_t seed)
{
const unsigned char *p = (const void *)k;
size_t h = seed;
while (*p)
h = 31*h + LOWER_OR_NOT(*p++);
return h;
}
static int resize(struct htab *htab, size_t nel)
{
size_t newsize;
size_t i, j;
size_t oldmask = htab->mask;
struct elem *e, *newe;
struct elem *oldtab = htab->elems;
struct elem *oldend;
if (nel > MAXSIZE)
nel = MAXSIZE;
for (newsize = MINSIZE; newsize < nel; newsize *= 2);
htab->elems = calloc(newsize, sizeof *htab->elems);
if (!htab->elems) {
htab->elems = oldtab;
return 0;
}
htab->mask = newsize - 1;
if (!oldtab)
return 1;
oldend = oldtab + oldmask + 1;
for (e = oldtab; e < oldend; e++)
if (e->item.key) {
for (i=e->hash,j=1; ; i+=j++) {
newe = htab->elems + (i & htab->mask);
if (!newe->item.key)
break;
}
*newe = *e;
}
free(oldtab);
return 1;
}
static struct elem *lookup(struct htab *htab, const char *key, size_t hash, size_t dead)
{
size_t i, j;
struct elem *e;
for (i=hash,j=1; ; i+=j++) {
e = htab->elems + (i & htab->mask);
if ((!e->item.key && (!e->hash || e->hash == dead)) ||
(e->hash==hash && STRCMP(e->item.key, key)==0))
break;
}
return e;
}
struct htab *htab_create(size_t nel)
{
struct htab *r = calloc(1, sizeof *r);
if(r && !resize(r, nel)) {
free(r);
r = 0;
}
r->seed = rand();
return r;
}
void htab_destroy(struct htab *htab)
{
free(htab->elems);
free(htab);
}
static struct elem *htab_find_elem(struct htab *htab, const char* key)
{
size_t hash = keyhash(key, htab->seed);
struct elem *e = lookup(htab, key, hash, 0);
if (e->item.key) {
return e;
}
return 0;
}
htab_value* htab_find(struct htab *htab, const char* key)
{
struct elem *e = htab_find_elem(htab, key);
if(!e) return 0;
return &e->item.data;
}
htab_value* htab_find2(struct htab *htab, const char* key, char **saved_key)
{
struct elem *e = htab_find_elem(htab, key);
if(!e) return 0;
*saved_key = e->item.key;
return &e->item.data;
}
int htab_delete(struct htab *htab, const char* key)
{
struct elem *e = htab_find_elem(htab, key);
if(!e) return 0;
e->item.key = 0;
e->hash = 0xdeadc0de;
--htab->used;
++htab->dead;
return 1;
}
int htab_insert(struct htab *htab, char* key, htab_value value)
{
size_t hash = keyhash(key, htab->seed), oh;
struct elem *e = lookup(htab, key, hash, 0xdeadc0de);
if(e->item.key) {
/* it's not allowed to overwrite existing data */
return 0;
}
oh = e->hash; /* save old hash in case it's tombstone marker */
e->item.key = key;
e->item.data = value;
e->hash = hash;
if (++htab->used + htab->dead > htab->mask - htab->mask/4) {
if (!resize(htab, 2*htab->used)) {
htab->used--;
e->item.key = 0;
e->hash = oh;
return 0;
}
htab->dead = 0;
} else if (oh == 0xdeadc0de) {
/* re-used tomb */
--htab->dead;
}
return 1;
}
size_t htab_next(struct htab *htab, size_t iterator, char** key, htab_value **v)
{
size_t i;
for(i=iterator;i<htab->mask+1;++i) {
struct elem *e = htab->elems + i;
if(e->item.key) {
*key = e->item.key;
*v = &e->item.data;
return i+1;
}
}
return 0;
}

23
src/hsearch.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef HSEARCH_H
#define HSEARCH_H
#include <stdlib.h>
typedef union htab_value {
void *p;
size_t n;
} htab_value;
#define HTV_N(N) (htab_value) {.n = N}
#define HTV_P(P) (htab_value) {.p = P}
struct htab * htab_create(size_t);
void htab_destroy(struct htab *);
htab_value* htab_find(struct htab *, const char* key);
/* same as htab_find, but can retrieve the saved key (for freeing) */
htab_value* htab_find2(struct htab *htab, const char* key, char **saved_key);
int htab_insert(struct htab *, char*, htab_value);
int htab_delete(struct htab *htab, const char* key);
size_t htab_next(struct htab *, size_t iterator, char** key, htab_value **v);
#endif

View File

@ -20,9 +20,9 @@
* HTML error pages with variable substitution.
*/
#include "common.h"
#include "main.h"
#include "common.h"
#include "buffer.h"
#include "conns.h"
#include "heap.h"
@ -30,6 +30,9 @@
#include "network.h"
#include "utils.h"
#include "conf.h"
#include "log.h"
#include <regex.h>
/*
* Add an error number -> filename mapping to the errorpages list.
@ -37,19 +40,25 @@
#define ERRORNUM_BUFSIZE 8 /* this is more than required */
#define ERRPAGES_BUCKETCOUNT 16
int add_new_errorpage (char *filepath, unsigned int errornum)
int add_new_errorpage (struct config_s *conf, char *filepath,
unsigned int errornum)
{
char errornbuf[ERRORNUM_BUFSIZE];
char errornbuf[ERRORNUM_BUFSIZE], *k;
config.errorpages = hashmap_create (ERRPAGES_BUCKETCOUNT);
if (!config.errorpages)
if (!conf->errorpages)
conf->errorpages = htab_create (ERRPAGES_BUCKETCOUNT);
if (!conf->errorpages)
return (-1);
snprintf (errornbuf, ERRORNUM_BUFSIZE, "%u", errornum);
if (hashmap_insert (config.errorpages, errornbuf,
filepath, strlen (filepath) + 1) < 0)
k = safestrdup(errornbuf);
if (!k) return -1;
if (!htab_insert (conf->errorpages, k, HTV_P(filepath))) {
safefree(k);
return (-1);
}
return (0);
}
@ -59,49 +68,51 @@ int add_new_errorpage (char *filepath, unsigned int errornum)
*/
static char *get_html_file (unsigned int errornum)
{
hashmap_iter result_iter;
char errornbuf[ERRORNUM_BUFSIZE];
char *key;
static char *val;
htab_value *hv;
assert (errornum >= 100 && errornum < 1000);
if (!config.errorpages)
return (config.errorpage_undef);
if (!config->errorpages)
return (config->errorpage_undef);
snprintf (errornbuf, ERRORNUM_BUFSIZE, "%u", errornum);
result_iter = hashmap_find (config.errorpages, errornbuf);
if (hashmap_is_end (config.errorpages, result_iter))
return (config.errorpage_undef);
if (hashmap_return_entry (config.errorpages, result_iter,
&key, (void **) &val) < 0)
return (config.errorpage_undef);
return (val);
hv = htab_find (config->errorpages, errornbuf);
if (!hv) return (config->errorpage_undef);
return hv->p;
}
/*
* Look up the value for a variable.
*/
static char *lookup_variable (struct conn_s *connptr, const char *varname)
{
hashmap_iter result_iter;
char *key;
static char *data;
static char *lookup_variable (struct htab *map, const char *varname) {
htab_value *v;
v = htab_find(map, varname);
return v ? v->p : 0;
}
result_iter = hashmap_find (connptr->error_variables, varname);
if (hashmap_is_end (connptr->error_variables, result_iter))
return (NULL);
if (hashmap_return_entry (connptr->error_variables, result_iter,
&key, (void **) &data) < 0)
return (NULL);
return (data);
static void varsubst_sendline(struct conn_s *connptr, regex_t *re, char *p) {
int fd = connptr->client_fd;
while(*p) {
regmatch_t match;
char varname[32+1], *varval;
size_t l;
int st = regexec(re, p, 1, &match, 0);
if(st == 0) {
if(match.rm_so > 0) safe_write(fd, p, match.rm_so);
l = match.rm_eo - match.rm_so;
assert(l>2 && l-2 < sizeof(varname));
p += match.rm_so;
memcpy(varname, p+1, l-2);
varname[l-2] = 0;
varval = lookup_variable(connptr->error_variables, varname);
if(varval) write_message(fd, "%s", varval);
else if(varval && !*varval) write_message(fd, "(unknown)");
else safe_write(fd, p, l);
p += l;
} else {
write_message(fd, "%s", p);
break;
}
}
}
/*
@ -110,80 +121,34 @@ static char *lookup_variable (struct conn_s *connptr, const char *varname)
int
send_html_file (FILE *infile, struct conn_s *connptr)
{
char *inbuf;
char *varstart = NULL;
char *p;
const char *varval;
int in_variable = 0;
int r = 0;
regex_t re;
char *inbuf = safemalloc (4096);
(void) regcomp(&re, "{[a-z]\\{1,32\\}}", 0);
inbuf = (char *) safemalloc (4096);
while (fgets (inbuf, 4096, infile) != NULL) {
for (p = inbuf; *p; p++) {
switch (*p) {
case '}':
if (in_variable) {
*p = '\0';
varval = (const char *)
lookup_variable (connptr,
varstart);
if (!varval)
varval = "(unknown)";
r = write_message (connptr->client_fd,
"%s", varval);
in_variable = 0;
} else {
r = write_message (connptr->client_fd,
"%c", *p);
}
break;
case '{':
/* a {{ will print a single {. If we are NOT
* already in a { variable, then proceed with
* setup. If we ARE already in a { variable,
* this code will fallthrough to the code that
* just dumps a character to the client fd.
*/
if (!in_variable) {
varstart = p + 1;
in_variable++;
} else
in_variable = 0;
default:
if (!in_variable) {
r = write_message (connptr->client_fd,
"%c", *p);
}
}
if (r)
break;
}
if (r)
break;
in_variable = 0;
while (fgets (inbuf, 4096, infile)) {
varsubst_sendline(connptr, &re, inbuf);
}
regfree (&re);
safefree (inbuf);
return r;
return 1;
}
int send_http_headers (struct conn_s *connptr, int code, const char *message)
int send_http_headers (
struct conn_s *connptr, int code,
const char *message, const char *extra)
{
const char *headers =
"HTTP/1.0 %d %s\r\n"
"Server: %s/%s\r\n"
"Content-Type: text/html\r\n" "Connection: close\r\n" "\r\n";
const char headers[] =
"HTTP/1.%u %d %s\r\n"
"Server: %s\r\n"
"Content-Type: text/html\r\n"
"%s"
"Connection: close\r\n" "\r\n";
return (write_message (connptr->client_fd, headers,
code, message, PACKAGE, VERSION));
connptr->protocol.major != 1 ? 0 : connptr->protocol.minor,
code, message, PACKAGE,
extra));
}
/*
@ -204,20 +169,47 @@ int send_http_error_message (struct conn_s *connptr)
"<h1>%s</h1>\n"
"<p>%s</p>\n"
"<hr />\n"
"<p><em>Generated by %s version %s.</em></p>\n" "</body>\n"
"<p><em>Generated by %s.</em></p>\n" "</body>\n"
"</html>\n";
/* according to rfc7235, the 407 error must be accompanied by
a Proxy-Authenticate header field. */
const char *auth_str_type =
connptr->error_number == 407 ? "Proxy-Authenticate" :
(connptr->error_number == 401 ? "WWW-Authenticate" : "");
const char auth_str_tpl[] = "%s: Basic realm=\"%s\"\r\n";
char* auth_str_add = NULL;
if (auth_str_type[0] != 0) {
int auth_str_size = snprintf (NULL, 0, auth_str_tpl,
auth_str_type, config->basicauth_realm) + 1;
if (auth_str_size > 0) {
auth_str_add = safemalloc (auth_str_size);
if (auth_str_add != NULL) {
snprintf (auth_str_add, auth_str_size, auth_str_tpl,
auth_str_type, config->basicauth_realm);
}
}
}
send_http_headers (connptr, connptr->error_number,
connptr->error_string);
connptr->error_string, auth_str_add ? auth_str_add : "");
if (auth_str_add) safefree (auth_str_add);
error_file = get_html_file (connptr->error_number);
if (!(infile = fopen (error_file, "r"))) {
char *detail = lookup_variable (connptr, "detail");
if (!error_file || !(infile = fopen (error_file, "r"))) {
char *detail;
if (error_file) log_message (LOG_ERR,
"Error opening error file '%s' (%s)",
error_file, strerror (errno));
detail = lookup_variable (connptr->error_variables, "detail");
return (write_message (connptr->client_fd, fallback_error,
connptr->error_number,
connptr->error_string,
connptr->error_string,
detail, PACKAGE, VERSION));
detail, PACKAGE));
}
ret = send_html_file (infile, connptr);
@ -234,14 +226,25 @@ int send_http_error_message (struct conn_s *connptr)
int
add_error_variable (struct conn_s *connptr, const char *key, const char *val)
{
char *k, *v;
if (!connptr->error_variables)
if (!
(connptr->error_variables =
hashmap_create (ERRVAR_BUCKETCOUNT)))
htab_create (ERRVAR_BUCKETCOUNT)))
return (-1);
return hashmap_insert (connptr->error_variables, key, val,
strlen (val) + 1);
k = safestrdup(key);
v = safestrdup(val);
if (!v || !k) goto oom;
if(htab_insert (connptr->error_variables, k, HTV_P(v)))
return 1;
oom:;
safefree(k);
safefree(v);
return -1;
}
#define ADD_VAR_RET(x, y) \
@ -260,6 +263,7 @@ int add_standard_vars (struct conn_s *connptr)
char errnobuf[16];
char timebuf[30];
time_t global_time;
struct tm tm_buf;
snprintf (errnobuf, sizeof errnobuf, "%d", connptr->error_number);
ADD_VAR_RET ("errno", errnobuf);
@ -267,7 +271,6 @@ int add_standard_vars (struct conn_s *connptr)
ADD_VAR_RET ("cause", connptr->error_string);
ADD_VAR_RET ("request", connptr->request_line);
ADD_VAR_RET ("clientip", connptr->client_ip_addr);
ADD_VAR_RET ("clienthost", connptr->client_string_addr);
/* The following value parts are all non-NULL and will
* trigger warnings in ADD_VAR_RET(), so we use
@ -276,11 +279,11 @@ int add_standard_vars (struct conn_s *connptr)
global_time = time (NULL);
strftime (timebuf, sizeof (timebuf), "%a, %d %b %Y %H:%M:%S GMT",
gmtime (&global_time));
gmtime_r (&global_time, &tm_buf));
add_error_variable (connptr, "date", timebuf);
add_error_variable (connptr, "website",
"https://www.banu.com/tinyproxy/");
"https://tinyproxy.github.io/");
add_error_variable (connptr, "version", VERSION);
add_error_variable (connptr, "package", PACKAGE);

View File

@ -23,8 +23,9 @@
/* Forward declaration */
struct conn_s;
struct config_s;
extern int add_new_errorpage (char *filepath, unsigned int errornum);
extern int add_new_errorpage (struct config_s *, char *filepath, unsigned int errornum);
extern int send_http_error_message (struct conn_s *connptr);
extern int indicate_http_error (struct conn_s *connptr, int number,
const char *message, ...);
@ -32,7 +33,7 @@ extern int add_error_variable (struct conn_s *connptr, const char *key,
const char *val);
extern int send_html_file (FILE * infile, struct conn_s *connptr);
extern int send_http_headers (struct conn_s *connptr, int code,
const char *message);
const char *message, const char *extra);
extern int add_standard_vars (struct conn_s *connptr);
#endif /* !TINYPROXY_HTML_ERROR_H */

View File

@ -232,6 +232,7 @@ int http_message_send (http_message_t msg, int fd)
char timebuf[30];
time_t global_time;
unsigned int i;
struct tm tm_buf;
assert (is_http_message_valid (msg));
@ -254,11 +255,11 @@ int http_message_send (http_message_t msg, int fd)
/* Output the date */
global_time = time (NULL);
strftime (timebuf, sizeof (timebuf), "%a, %d %b %Y %H:%M:%S GMT",
gmtime (&global_time));
gmtime_r (&global_time, &tm_buf));
write_message (fd, "Date: %s\r\n", timebuf);
/* Output the content-length */
write_message (fd, "Content-length: %u\r\n", msg->body.length);
write_message (fd, "Content-length: %lu\r\n", (unsigned long) msg->body.length);
/* Write the separator between the headers and body */
safe_write (fd, "\r\n", 2);

View File

@ -27,8 +27,9 @@
#include "heap.h"
#include "log.h"
#include "utils.h"
#include "vector.h"
#include "sblist.h"
#include "conf.h"
#include <pthread.h>
static const char *syslog_level[] = {
NULL,
@ -45,6 +46,8 @@ static const char *syslog_level[] = {
#define TIME_LENGTH 16
#define STRING_LENGTH 800
static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
/*
* Global file descriptor for the log file
*/
@ -61,7 +64,7 @@ static int log_level = LOG_INFO;
* The key is the actual messages (already filled in full), and the value
* is the log level.
*/
static vector_t log_message_storage;
static sblist *log_message_storage;
static unsigned int logging_initialized = FALSE; /* boolean */
@ -70,7 +73,11 @@ static unsigned int logging_initialized = FALSE; /* boolean */
*/
int open_log_file (const char *log_file_name)
{
if (log_file_name == NULL) {
log_file_fd = fileno(stdout);
} else {
log_file_fd = create_file_safely (log_file_name, FALSE);
}
return log_file_fd;
}
@ -79,7 +86,7 @@ int open_log_file (const char *log_file_name)
*/
void close_log_file (void)
{
if (log_file_fd < 0) {
if (log_file_fd < 0 || log_file_fd == fileno(stdout)) {
return;
}
@ -101,7 +108,8 @@ void set_log_level (int level)
void log_message (int level, const char *fmt, ...)
{
va_list args;
time_t nowtime;
struct timespec nowtime;
struct tm tm_buf;
char time_string[TIME_LENGTH];
char str[STRING_LENGTH];
@ -122,7 +130,7 @@ void log_message (int level, const char *fmt, ...)
return;
#endif
if (config.syslog && level == LOG_CONN)
if (config && config->syslog && level == LOG_CONN)
level = LOG_INFO;
va_start (args, fmt);
@ -135,7 +143,7 @@ void log_message (int level, const char *fmt, ...)
char *entry_buffer;
if (!log_message_storage) {
log_message_storage = vector_create ();
log_message_storage = sblist_new (sizeof(char*), 64);
if (!log_message_storage)
goto out;
}
@ -147,30 +155,34 @@ void log_message (int level, const char *fmt, ...)
goto out;
sprintf (entry_buffer, "%d %s", level, str);
vector_append (log_message_storage, entry_buffer,
strlen (entry_buffer) + 1);
if(!sblist_add (log_message_storage, &entry_buffer))
safefree (entry_buffer);
goto out;
}
if (config.syslog) {
if(!config->syslog && log_file_fd == -1)
goto out;
if (config->syslog) {
pthread_mutex_lock(&log_mutex);
#ifdef HAVE_VSYSLOG_H
vsyslog (level, fmt, args);
#else
vsnprintf (str, STRING_LENGTH, fmt, args);
syslog (level, "%s", str);
#endif
pthread_mutex_unlock(&log_mutex);
} else {
char *p;
nowtime = time (NULL);
clock_gettime(CLOCK_REALTIME, &nowtime);
/* Format is month day hour:minute:second (24 time) */
strftime (time_string, TIME_LENGTH, "%b %d %H:%M:%S",
localtime (&nowtime));
localtime_r (&nowtime.tv_sec, &tm_buf));
snprintf (str, STRING_LENGTH, "%-9s %s [%ld]: ",
snprintf (str, STRING_LENGTH, "%-9s %s.%03lu [%ld]: ",
syslog_level[level], time_string,
(unsigned long) nowtime.tv_nsec/1000000ul,
(long int) getpid ());
/*
@ -186,13 +198,24 @@ void log_message (int level, const char *fmt, ...)
assert (log_file_fd >= 0);
pthread_mutex_lock(&log_mutex);
ret = write (log_file_fd, str, strlen (str));
pthread_mutex_unlock(&log_mutex);
if (ret == -1) {
log_message (LOG_WARNING,
"Could not write to log file");
config->syslog = TRUE;
log_message(LOG_CRIT, "ERROR: Could not write to log "
"file %s: %s.",
config->logf_name, strerror(errno));
log_message(LOG_CRIT,
"Falling back to syslog logging");
}
pthread_mutex_lock(&log_mutex);
fsync (log_file_fd);
pthread_mutex_unlock(&log_mutex);
}
out:
@ -202,23 +225,24 @@ out:
/*
* This needs to send any stored log messages.
*/
void send_stored_logs (void)
static void send_stored_logs (void)
{
char *string;
char **string;
char *ptr;
int level;
size_t i;
if (log_message_storage == NULL)
return;
log_message(LOG_DEBUG, "sending stored logs");
for (i = 0; (ssize_t) i != vector_length (log_message_storage); ++i) {
string =
(char *) vector_getentry (log_message_storage, i, NULL);
for (i = 0; i < sblist_getsize (log_message_storage); ++i) {
string = sblist_get (log_message_storage, i);
if (!string || !*string) continue;
ptr = strchr (string, ' ') + 1;
level = atoi (string);
ptr = strchr (*string, ' ') + 1;
level = atoi (*string);
#ifdef NDEBUG
if (log_level == LOG_CONN && level == LOG_INFO)
@ -230,10 +254,11 @@ void send_stored_logs (void)
continue;
#endif
log_message (level, ptr);
log_message (level, "%s", ptr);
safefree(*string);
}
vector_delete (log_message_storage);
sblist_free (log_message_storage);
log_message_storage = NULL;
log_message(LOG_DEBUG, "done sending stored logs");
@ -248,26 +273,23 @@ void send_stored_logs (void)
*/
int setup_logging (void)
{
if (!config.syslog) {
if (open_log_file (config.logf_name) < 0) {
if (!config->syslog) {
if (open_log_file (config->logf_name) < 0) {
/*
* If opening the log file fails, we try
* to fall back to syslog logging...
*/
config.syslog = TRUE;
config->syslog = TRUE;
log_message (LOG_CRIT, "ERROR: Could not create log "
"file %s: %s.\n",
config.logf_name, strerror (errno));
"file %s: %s.",
config->logf_name, strerror (errno));
log_message (LOG_CRIT,
"Falling back to syslog logging\n");
"Falling back to syslog logging.");
}
}
if (config.syslog) {
if (config.godaemon == TRUE)
openlog ("tinyproxy", LOG_PID, LOG_DAEMON);
else
if (config->syslog) {
openlog ("tinyproxy", LOG_PID, LOG_USER);
}
@ -286,7 +308,7 @@ void shutdown_logging (void)
return;
}
if (config.syslog) {
if (config->syslog) {
closelog ();
} else {
close_log_file ();

View File

@ -77,16 +77,7 @@
* don't advocate this, but it could be useful at times.)
*/
#ifdef HAVE_SYSLOG_H
# include <syslog.h>
#else
# define LOG_CRIT 2
# define LOG_ERR 3
# define LOG_WARNING 4
# define LOG_NOTICE 5
# define LOG_INFO 6
# define LOG_DEBUG 7
#endif
#include <syslog.h>
#define LOG_CONN 8 /* extra to log connections without the INFO stuff */
@ -115,7 +106,6 @@ extern void close_log_file (void);
extern void log_message (int level, const char *fmt, ...);
extern void set_log_level (int level);
extern void send_stored_logs (void);
extern int setup_logging (void);
extern void shutdown_logging (void);

81
src/loop.c Normal file
View File

@ -0,0 +1,81 @@
#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);
}
void loop_records_destroy(void) {
sblist_free(loop_records);
loop_records = 0;
}
#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;
}

12
src/loop.h Normal file
View File

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

View File

@ -38,6 +38,7 @@
#include "heap.h"
#include "filter.h"
#include "child.h"
#include "loop.h"
#include "log.h"
#include "reqs.h"
#include "sock.h"
@ -47,10 +48,18 @@
/*
* Global Structures
*/
struct config_s config;
struct config_s config_defaults;
struct config_s *config;
static struct config_s configs[2];
static const char* config_file;
unsigned int received_sighup = FALSE; /* boolean */
static struct config_s*
get_next_config(void)
{
if (config == &configs[0]) return &configs[1];
return &configs[0];
}
/*
* Handle a signal
*/
@ -61,12 +70,14 @@ takesig (int sig)
int status;
switch (sig) {
case SIGUSR1:
case SIGHUP:
received_sighup = TRUE;
break;
case SIGINT:
case SIGTERM:
config.quit = TRUE;
config->quit = TRUE;
break;
case SIGCHLD:
@ -86,37 +97,6 @@ display_version (void)
printf ("%s %s\n", PACKAGE, VERSION);
}
/*
* Display the copyright and license for this program.
*/
static void
display_license (void)
{
display_version ();
printf ("\
Copyright 1998 Steven Young (sdyoung@well.com)\n\
Copyright 1998-2002 Robert James Kaes (rjkaes@users.sourceforge.net)\n\
Copyright 1999 George Talusan (gstalusan@uwaterloo.ca)\n\
Copyright 2000 Chris Lightfoot (chris@ex-parrot.com)\n\
Copyright 2009-2010 Mukund Sivaraman (muks@banu.com)\n\
Copyright 2009-2010 Michael Adam (obnox@samba.org)\n\
\n\
This program is free software; you can redistribute it and/or modify\n\
it under the terms of the GNU General Public License as published by\n\
the Free Software Foundation; either version 2, or (at your option)\n\
any later version.\n\
\n\
This program is distributed in the hope that it will be useful,\n\
but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
GNU General Public License for more details.\n\
\n\
You should have received a copy of the GNU General Public License\n\
along with this program; if not, write to the Free Software\n\
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.\n");
}
/*
* Display usage to the user.
*/
@ -131,7 +111,6 @@ display_usage (void)
" -d Do not daemonize (run in foreground).\n"
" -c FILE Use an alternate configuration file.\n"
" -h Display this usage information.\n"
" -l Display the license.\n"
" -v Display version information.\n");
/* Display the modes compiled into tinyproxy */
@ -162,12 +141,17 @@ display_usage (void)
features++;
#endif /* REVERSE_SUPPORT */
#ifdef UPSTREAM_SUPPORT
printf (" Upstream proxy support\n");
features++;
#endif /* UPSTREAM_SUPPORT */
if (0 == features)
printf (" None\n");
printf ("\n"
"For bug reporting instructions, please see:\n"
"<https://www.banu.com/tinyproxy/support/>.\n");
"For support and bug reporting instructions, please visit\n"
"<https://tinyproxy.github.io/>.\n");
}
static int
@ -189,42 +173,153 @@ get_id (char *str)
}
/**
* process_cmdline:
* @argc: argc as passed to main()
* @argv: argv as passed to main()
* change_user:
* @program: The name of the program. Pass argv[0] here.
*
* This function parses command line arguments.
* This function tries to change UID and GID to the ones specified in
* the config file. This function is typically called during
* initialization when the effective user is root.
**/
static void
process_cmdline (int argc, char **argv, struct config_s *conf)
change_user (const char *program)
{
int opt;
if (config->group && strlen (config->group) > 0) {
int gid = get_id (config->group);
while ((opt = getopt (argc, argv, "c:vldh")) != EOF) {
if (gid < 0) {
struct group *thisgroup = getgrnam (config->group);
if (!thisgroup) {
fprintf (stderr,
"%s: Unable to find group \"%s\".\n",
program, config->group);
exit (EX_NOUSER);
}
gid = thisgroup->gr_gid;
}
if (setgid (gid) < 0) {
fprintf (stderr,
"%s: Unable to change to group \"%s\".\n",
program, config->group);
exit (EX_NOPERM);
}
#ifdef HAVE_SETGROUPS
/* Drop all supplementary groups, otherwise these are inherited from the calling process */
if (setgroups (0, NULL) < 0) {
fprintf (stderr,
"%s: Unable to drop supplementary groups.\n",
program);
exit (EX_NOPERM);
}
#endif
log_message (LOG_INFO, "Now running as group \"%s\".",
config->group);
}
if (config->user && strlen (config->user) > 0) {
int uid = get_id (config->user);
if (uid < 0) {
struct passwd *thisuser = getpwnam (config->user);
if (!thisuser) {
fprintf (stderr,
"%s: Unable to find user \"%s\".\n",
program, config->user);
exit (EX_NOUSER);
}
uid = thisuser->pw_uid;
}
if (setuid (uid) < 0) {
fprintf (stderr,
"%s: Unable to change to user \"%s\".\n",
program, config->user);
exit (EX_NOPERM);
}
log_message (LOG_INFO, "Now running as user \"%s\".",
config->user);
}
}
/**
* convenience wrapper around reload_config_file
* that also re-initializes logging.
*/
int reload_config (int reload_logging)
{
int ret, ret2;
struct config_s *c_next = get_next_config();
log_message (LOG_NOTICE, "Reloading config file (%s)", config_file);
if (reload_logging) shutdown_logging ();
ret = reload_config_file (config_file, c_next);
if (ret == 0) {
if(config) free_config (config);
config = c_next;
}
ret2 = reload_logging ? setup_logging () : 0;
if (ret != 0)
log_message (LOG_WARNING, "Reloading config file failed!");
else
log_message (LOG_NOTICE, "Reloading config file finished");
return ret ? ret : ret2;
}
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
main (int argc, char **argv)
{
int opt, daemonized = TRUE;
srand(time(NULL)); /* for hashmap seeds */
/* Only allow u+rw bits. This may be required for some versions
* of glibc so that mkstemp() doesn't make us vulnerable.
*/
umask (0177);
log_message (LOG_NOTICE, "Initializing " PACKAGE " ...");
if (config_init()) {
fprintf(stderr, "ERROR: config_init() failed\n");
exit (EX_SOFTWARE);
}
config_file = SYSCONFDIR "/tinyproxy.conf";
while ((opt = getopt (argc, argv, "c:vdh")) != EOF) {
switch (opt) {
case 'v':
display_version ();
exit (EX_OK);
case 'l':
display_license ();
exit (EX_OK);
case 'd':
conf->godaemon = FALSE;
daemonized = 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);
}
config_file = optarg;
break;
case 'h':
@ -236,134 +331,8 @@ process_cmdline (int argc, char **argv, struct config_s *conf)
exit (EX_USAGE);
}
}
}
/**
* change_user:
* @program: The name of the program. Pass argv[0] here.
*
* This function tries to change UID and GID to the ones specified in
* the config file. This function is typically called during
* initialization when the effective user is root.
**/
static void
change_user (const char *program)
{
if (config.group && strlen (config.group) > 0) {
int gid = get_id (config.group);
if (gid < 0) {
struct group *thisgroup = getgrnam (config.group);
if (!thisgroup) {
fprintf (stderr,
"%s: Unable to find group \"%s\".\n",
program, config.group);
exit (EX_NOUSER);
}
gid = thisgroup->gr_gid;
}
if (setgid (gid) < 0) {
fprintf (stderr,
"%s: Unable to change to group \"%s\".\n",
program, config.group);
exit (EX_NOPERM);
}
log_message (LOG_INFO, "Now running as group \"%s\".",
config.group);
}
if (config.user && strlen (config.user) > 0) {
int uid = get_id (config.user);
if (uid < 0) {
struct passwd *thisuser = getpwnam (config.user);
if (!thisuser) {
fprintf (stderr,
"%s: Unable to find user \"%s\".\n",
program, config.user);
exit (EX_NOUSER);
}
uid = thisuser->pw_uid;
}
if (setuid (uid) < 0) {
fprintf (stderr,
"%s: Unable to change to user \"%s\".\n",
program, config.user);
exit (EX_NOPERM);
}
log_message (LOG_INFO, "Now running as user \"%s\".",
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 = safestrdup (LOCALSTATEDIR "/log/tinyproxy.log");
conf->pidpath = safestrdup (LOCALSTATEDIR "/run/tinyproxy.pid");
}
/**
* convenience wrapper around reload_config_file
* that also re-initializes logging.
*/
int reload_config (void)
{
int ret;
shutdown_logging ();
ret = reload_config_file (config_defaults.config_file, &config,
&config_defaults);
if (ret != 0) {
goto done;
}
ret = setup_logging ();
done:
return ret;
}
int
main (int argc, char **argv)
{
int ret;
/* Only allow u+rw bits. This may be required for some versions
* of glibc so that mkstemp() doesn't make us vulnerable.
*/
umask (0177);
initialize_config_defaults (&config_defaults);
process_cmdline (argc, argv, &config_defaults);
log_message (LOG_INFO, "Initializing " PACKAGE " ...");
ret = reload_config ();
if (ret != 0) {
if (reload_config(0)) {
exit (EX_SOFTWARE);
}
@ -373,97 +342,92 @@ main (int argc, char **argv)
* in the list of allowed headers, since it is required in a
* HTTP/1.0 request. Also add the Content-Type header since it
* goes hand in hand with Content-Length. */
if (is_anonymous_enabled ()) {
anonymous_insert ("Content-Length");
anonymous_insert ("Content-Type");
if (is_anonymous_enabled (config)) {
anonymous_insert (config, safestrdup("Content-Length"));
anonymous_insert (config, safestrdup("Content-Type"));
}
if (config.godaemon == TRUE)
makedaemon ();
if (daemonized == TRUE) {
if (!config->syslog && config->logf_name == NULL)
fprintf(stderr, "WARNING: logging deactivated "
"(can't log to stdout when daemonized)\n");
if (config.pidpath) {
if (pidfile_create (config.pidpath) < 0) {
makedaemon ();
}
setup_sig(SIGPIPE, SIG_IGN, "SIGPIPE", argv[0]);
#ifdef FILTER_ENABLE
if (config->filter)
filter_init ();
#endif /* FILTER_ENABLE */
/* Start listening on the selected port. */
if (child_listening_sockets(config->listen_addrs, config->port) < 0) {
fprintf (stderr, "%s: Could not create listening sockets.\n",
argv[0]);
exit (EX_OSERR);
}
/* Create pid file before we drop privileges */
if (config->pidpath) {
if (pidfile_create (config->pidpath) < 0) {
fprintf (stderr, "%s: Could not create PID file.\n",
argv[0]);
exit (EX_OSERR);
}
}
if (set_signal_handler (SIGPIPE, SIG_IGN) == SIG_ERR) {
fprintf (stderr, "%s: Could not set the \"SIGPIPE\" signal.\n",
argv[0]);
exit (EX_OSERR);
}
#ifdef FILTER_ENABLE
if (config.filter)
filter_init ();
#endif /* FILTER_ENABLE */
/* Start listening on the selected port. */
if (child_listening_sock (config.port) < 0) {
fprintf (stderr, "%s: Could not create listening socket.\n",
argv[0]);
exit (EX_OSERR);
}
/* Switch to a different user if we're running as root */
if (geteuid () == 0)
change_user (argv[0]);
else
log_message (LOG_WARNING,
log_message (LOG_INFO,
"Not running as root, so not changing UID/GID.");
if (child_pool_create () < 0) {
fprintf (stderr,
"%s: Could not create the pool of children.\n",
argv[0]);
/* Create log file after we drop privileges */
if (setup_logging ()) {
exit (EX_SOFTWARE);
}
/* These signals are only for the parent process. */
log_message (LOG_INFO, "Setting the various signals.");
if (set_signal_handler (SIGCHLD, takesig) == SIG_ERR) {
fprintf (stderr, "%s: Could not set the \"SIGCHLD\" signal.\n",
argv[0]);
exit (EX_OSERR);
}
setup_sig (SIGCHLD, takesig, "SIGCHLD", argv[0]);
setup_sig (SIGTERM, takesig, "SIGTERM", argv[0]);
setup_sig (SIGINT, takesig, "SIGINT", argv[0]);
if (daemonized) setup_sig (SIGHUP, takesig, "SIGHUP", argv[0]);
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);
}
loop_records_init();
/* Start the main loop */
log_message (LOG_INFO, "Starting main loop. Accepting connections.");
child_main_loop ();
log_message (LOG_INFO, "Shutting down.");
log_message (LOG_NOTICE, "Shutting down.");
child_kill_children (SIGTERM);
child_close_sock ();
child_free_children();
loop_records_destroy();
/* Remove the PID file */
if (unlink (config.pidpath) < 0) {
if (config->pidpath != NULL && unlink (config->pidpath) < 0) {
log_message (LOG_WARNING,
"Could not remove PID file \"%s\": %s.",
config.pidpath, strerror (errno));
config->pidpath, strerror (errno));
}
#ifdef FILTER_ENABLE
if (config.filter)
if (config->filter)
filter_destroy ();
#endif /* FILTER_ENABLE */
free_config (config);
shutdown_logging ();
return EXIT_SUCCESS;

View File

@ -29,9 +29,9 @@
#define MAX_IDLE_TIME (60 * 10) /* 10 minutes of no activity */
/* Global Structures used in the program */
extern struct config_s config;
extern struct config_s *config;
extern unsigned int received_sighup; /* boolean */
extern int reload_config (void);
extern int reload_config (int reload_logging);
#endif /* __MAIN_H__ */

48
src/mypoll.c Normal file
View File

@ -0,0 +1,48 @@
#include "mypoll.h"
#ifdef HAVE_POLL_H
int mypoll(pollfd_struct* fds, int nfds, int timeout) {
int i, ret;
for(i=0; i<nfds; ++i) if(!fds[i].events) fds[i].fd=~fds[i].fd;
ret = poll(fds, nfds, timeout <= 0 ? timeout : timeout*1000);
for(i=0; i<nfds; ++i) if(!fds[i].events) fds[i].fd=~fds[i].fd;
return ret;
}
#else
int mypoll(pollfd_struct* fds, int nfds, int timeout) {
fd_set rset, wset, *r=0, *w=0;
int i, ret, maxfd=-1;
struct timeval tv = {0}, *t = 0;
for(i=0; i<nfds; ++i) {
if(fds[i].events & MYPOLL_READ) r = &rset;
if(fds[i].events & MYPOLL_WRITE) w = &wset;
if(r && w) break;
}
if(r) FD_ZERO(r);
if(w) FD_ZERO(w);
for(i=0; i<nfds; ++i) {
if(fds[i].fd > maxfd) maxfd = fds[i].fd;
if(fds[i].events & MYPOLL_READ) FD_SET(fds[i].fd, r);
if(fds[i].events & MYPOLL_WRITE) FD_SET(fds[i].fd, w);
}
if(timeout >= 0) t = &tv;
if(timeout > 0) tv.tv_sec = timeout;
ret = select(maxfd+1, r, w, 0, t);
switch(ret) {
case -1:
case 0:
return ret;
}
for(i=0; i<nfds; ++i) {
fds[i].revents = 0;
if(r && FD_ISSET(fds[i].fd, r)) fds[i].revents |= MYPOLL_READ;
if(w && FD_ISSET(fds[i].fd, w)) fds[i].revents |= MYPOLL_WRITE;
}
return ret;
}
#endif

31
src/mypoll.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef MYPOLL_H
#define MYPOLL_H
#include "config.h"
#ifdef HAVE_POLL_H
#define SELECT_OR_POLL "poll"
#include <poll.h>
typedef struct pollfd pollfd_struct;
#define MYPOLL_READ POLLIN
#define MYPOLL_WRITE POLLOUT
#else
#define SELECT_OR_POLL "select"
#include <sys/select.h>
typedef struct mypollfd {
int fd;
short events;
short revents;
} pollfd_struct;
#define MYPOLL_READ (1<<1)
#define MYPOLL_WRITE (1<<2)
#endif
int mypoll(pollfd_struct* fds, int nfds, int timeout);
#endif

View File

@ -32,10 +32,11 @@
* Write the buffer to the socket. If an EINTR occurs, pick up and try
* again. Keep sending until the buffer has been sent.
*/
ssize_t safe_write (int fd, const char *buffer, size_t count)
ssize_t safe_write (int fd, const void *buf, size_t count)
{
ssize_t len;
size_t bytestosend;
const char *buffer = buf;
assert (fd >= 0);
assert (buffer != NULL);
@ -67,7 +68,7 @@ ssize_t safe_write (int fd, const char *buffer, size_t count)
* Matched pair for safe_write(). If an EINTR occurs, pick up and try
* again.
*/
ssize_t safe_read (int fd, char *buffer, size_t count)
ssize_t safe_read (int fd, void *buffer, size_t count)
{
ssize_t len;
@ -191,7 +192,11 @@ ssize_t readline (int fd, char **whole_buffer)
goto CLEANUP;
}
recv (fd, line_ptr->data, diff, 0);
ret = recv (fd, line_ptr->data, diff, 0);
if (ret == -1) {
goto CLEANUP;
}
line_ptr->len = diff;
if (ptr) {
@ -245,8 +250,10 @@ CLEANUP:
* Convert the network address into either a dotted-decimal or an IPv6
* hex string.
*/
char *get_ip_string (struct sockaddr *sa, char *buf, size_t buflen)
const char *get_ip_string (struct sockaddr *sa, char *buf, size_t buflen)
{
const char *result;
assert (sa != NULL);
assert (buf != NULL);
assert (buflen != 0);
@ -257,7 +264,8 @@ char *get_ip_string (struct sockaddr *sa, char *buf, size_t buflen)
{
struct sockaddr_in *sa_in = (struct sockaddr_in *) sa;
inet_ntop (AF_INET, &sa_in->sin_addr, buf, buflen);
result = inet_ntop (AF_INET, &sa_in->sin_addr, buf,
buflen);
break;
}
case AF_INET6:
@ -265,7 +273,8 @@ char *get_ip_string (struct sockaddr *sa, char *buf, size_t buflen)
struct sockaddr_in6 *sa_in6 =
(struct sockaddr_in6 *) sa;
inet_ntop (AF_INET6, &sa_in6->sin6_addr, buf, buflen);
result = inet_ntop (AF_INET6, &sa_in6->sin6_addr, buf,
buflen);
break;
}
default:
@ -273,7 +282,7 @@ char *get_ip_string (struct sockaddr *sa, char *buf, size_t buflen)
return NULL;
}
return buf;
return result;
}
/*

View File

@ -21,13 +21,13 @@
#ifndef TINYPROXY_NETWORK_H
#define TINYPROXY_NETWORK_H
extern ssize_t safe_write (int fd, const char *buffer, size_t count);
extern ssize_t safe_read (int fd, char *buffer, size_t count);
extern ssize_t safe_write (int fd, const void *buf, size_t count);
extern ssize_t safe_read (int fd, void *buf, size_t count);
extern int write_message (int fd, const char *fmt, ...);
extern ssize_t readline (int fd, char **whole_buffer);
extern char *get_ip_string (struct sockaddr *sa, char *buf, size_t len);
extern const char *get_ip_string (struct sockaddr *sa, char *buf, size_t len);
extern int full_inet_pton (const char *ip, void *dst);
#endif

115
src/orderedmap.c Normal file
View File

@ -0,0 +1,115 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#else
# define _ALL_SOURCE
# define _GNU_SOURCE
#endif
#include <string.h>
#include "sblist.h"
#include "orderedmap.h"
static void orderedmap_destroy_contents(struct orderedmap *o) {
char **p, *q;
size_t i;
htab_value *v;
if(!o) return;
if(o->values) {
while(sblist_getsize(o->values)) {
p = sblist_get(o->values, 0);
if(p) free(*p);
sblist_delete(o->values, 0);
}
sblist_free(o->values);
}
if(o->map) {
i = 0;
while((i = htab_next(o->map, i, &q, &v)))
free(q);
htab_destroy(o->map);
}
}
struct orderedmap *orderedmap_create(size_t nbuckets) {
struct orderedmap o = {0}, *new;
o.values = sblist_new(sizeof(void*), 32);
if(!o.values) goto oom;
o.map = htab_create(nbuckets);
if(!o.map) goto oom;
new = malloc(sizeof o);
if(!new) goto oom;
memcpy(new, &o, sizeof o);
return new;
oom:;
orderedmap_destroy_contents(&o);
return 0;
}
void* orderedmap_destroy(struct orderedmap *o) {
orderedmap_destroy_contents(o);
free(o);
return 0;
}
int orderedmap_append(struct orderedmap *o, const char *key, char *value) {
size_t index;
char *nk, *nv;
nk = nv = 0;
nk = strdup(key);
nv = strdup(value);
if(!nk || !nv) goto oom;
index = sblist_getsize(o->values);
if(!sblist_add(o->values, &nv)) goto oom;
if(!htab_insert(o->map, nk, HTV_N(index))) {
sblist_delete(o->values, index);
goto oom;
}
return 1;
oom:;
free(nk);
free(nv);
return 0;
}
char* orderedmap_find(struct orderedmap *o, const char *key) {
char **p;
htab_value *v = htab_find(o->map, key);
if(!v) return 0;
p = sblist_get(o->values, v->n);
return p?*p:0;
}
int orderedmap_remove(struct orderedmap *o, const char *key) {
size_t i;
char *lk;
char *sk;
char **sv;
htab_value *lv, *v = htab_find2(o->map, key, &sk);
if(!v) return 0;
sv = sblist_get(o->values, v->n);
free(*sv);
sblist_delete(o->values, v->n);
i = 0;
while((i = htab_next(o->map, i, &lk, &lv))) {
if(lv->n > v->n) lv->n--;
}
htab_delete(o->map, key);
free(sk);
return 1;
}
size_t orderedmap_next(struct orderedmap *o, size_t iter, char** key, char** value) {
size_t h_iter;
htab_value* hval;
char **p;
if(iter < sblist_getsize(o->values)) {
h_iter = 0;
while((h_iter = htab_next(o->map, h_iter, key, &hval))) {
if(hval->n == iter) {
p = sblist_get(o->values, iter);
*value = p?*p:0;
return iter+1;
}
}
}
return 0;
}

20
src/orderedmap.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef ORDEREDMAP_H
#define ORDEREDMAP_H
#include <stdlib.h>
#include "sblist.h"
#include "hsearch.h"
typedef struct orderedmap {
sblist* values;
struct htab *map;
} *orderedmap;
struct orderedmap *orderedmap_create(size_t nbuckets);
void* orderedmap_destroy(struct orderedmap *o);
int orderedmap_append(struct orderedmap *o, const char *key, char *value );
char* orderedmap_find(struct orderedmap *o, const char *key);
int orderedmap_remove(struct orderedmap *o, const char *key);
size_t orderedmap_next(struct orderedmap *o, size_t iter, char** key, char** value);
#endif

1044
src/reqs.c

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -34,6 +34,7 @@ void reversepath_add (const char *path, const char *url,
struct reversepath **reversepath_list)
{
struct reversepath *reverse;
size_t l;
if (url == NULL) {
log_message (LOG_WARNING,
@ -65,8 +66,17 @@ void reversepath_add (const char *path, const char *url,
if (!path)
reverse->path = safestrdup ("/");
else
else {
l = strlen (path);
if (l && path[l-1] == '/')
reverse->path = safestrdup (path);
else {
reverse->path = safemalloc (l + 2);
memcpy (reverse->path, path, l);
reverse->path[l] = '/';
reverse->path[l+1] = 0;
}
}
reverse->url = safestrdup (url);
@ -83,10 +93,16 @@ void reversepath_add (const char *path, const char *url,
*/
struct reversepath *reversepath_get (char *url, struct reversepath *reverse)
{
size_t l, lu, lp;
while (reverse) {
if (strstr (url, reverse->path) == url)
lu = strlen (url);
lp = strlen (reverse->path);
if ((
(l = lu) == lp-1 ||
(l = lp) <= lu
) &&
!memcmp(url, reverse->path, l))
return reverse;
reverse = reverse->next;
}
@ -111,35 +127,41 @@ void free_reversepath_list (struct reversepath *reverse)
/*
* Rewrite the URL for reverse proxying.
*/
char *reverse_rewrite_url (struct conn_s *connptr, hashmap_t hashofheaders,
char *url)
char *reverse_rewrite_url (struct conn_s *connptr, orderedmap hashofheaders,
char *url, int *status)
{
char *rewrite_url = NULL;
char *cookie = NULL;
char *cookieval;
struct reversepath *reverse = NULL;
*status = 0;
/* Reverse requests always start with a slash */
if (*url == '/') {
/* First try locating the reverse mapping by request url */
reverse = reversepath_get (url, config.reversepath_list);
reverse = reversepath_get (url, config->reversepath_list);
if (reverse) {
rewrite_url = (char *)
safemalloc (strlen (url) + strlen (reverse->url) +
1);
strcpy (rewrite_url, reverse->url);
strcat (rewrite_url, url + strlen (reverse->path));
} else if (config.reversemagic
&& hashmap_entry_by_key (hashofheaders,
"cookie",
(void **) &cookie) > 0) {
size_t lu = strlen (url);
size_t lrp = strlen (reverse->path);
if (lrp > lu) {
rewrite_url = safestrdup (reverse->path);
*status = 301;
} else {
rewrite_url = safemalloc (
strlen (reverse->url) + lu + 1);
sprintf (rewrite_url, "%s%s", reverse->url, url + lrp);
}
} else if (config->reversemagic
&& (cookie = orderedmap_find (hashofheaders,
"cookie"))) {
/* No match - try the magical tracking cookie next */
if ((cookieval = strstr (cookie, REVERSE_COOKIE "="))
&& (reverse =
reversepath_get (cookieval +
strlen (REVERSE_COOKIE) + 1,
config.reversepath_list)))
config->reversepath_list)))
{
rewrite_url = (char *) safemalloc
@ -156,20 +178,14 @@ char *reverse_rewrite_url (struct conn_s *connptr, hashmap_t hashofheaders,
}
}
/* Forward proxy support off and no reverse path match found */
if (config.reverseonly && !rewrite_url) {
log_message (LOG_ERR, "Bad request");
indicate_http_error (connptr, 400, "Bad Request",
"detail",
"Request has an invalid URL", "url", url,
NULL);
if (rewrite_url == NULL) {
return NULL;
}
log_message (LOG_CONN, "Rewriting URL: %s -> %s", url, rewrite_url);
/* Store reverse path so that the magical tracking cookie can be set */
if (config.reversemagic && reverse)
if (config->reversemagic && reverse)
connptr->reversepath = safestrdup (reverse->path);
return rewrite_url;

View File

@ -22,6 +22,7 @@
#define TINYPROXY_REVERSE_PROXY_H
#include "conns.h"
#include "orderedmap.h"
struct reversepath {
struct reversepath *next;
@ -37,6 +38,7 @@ extern struct reversepath *reversepath_get (char *url,
struct reversepath *reverse);
void free_reversepath_list (struct reversepath *reverse);
extern char *reverse_rewrite_url (struct conn_s *connptr,
hashmap_t hashofheaders, char *url);
orderedmap hashofheaders, char *url,
int *status);
#endif

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,29 @@
#include "sock.h"
#include "text.h"
#include "conf.h"
#include "loop.h"
#include "sblist.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);
}
static const char * family_string (int af)
{
switch(af) {
case AF_UNSPEC: return "AF_UNSPEC";
case AF_INET: return "AF_INET";
case AF_INET6: return "AF_INET6";
}
return "unknown";
}
/*
* Bind the given socket to the supplied address. The socket is
@ -43,6 +66,7 @@ static int
bind_socket (int sockfd, const char *addr, int family)
{
struct addrinfo hints, *res, *ressave;
int n;
assert (sockfd >= 0);
assert (addr != NULL && strlen (addr) != 0);
@ -51,9 +75,13 @@ bind_socket (int sockfd, const char *addr, int family)
hints.ai_family = family;
hints.ai_socktype = SOCK_STREAM;
/* The local port it not important */
if (getaddrinfo (addr, NULL, &hints, &res) != 0)
/* The local port is not important */
n = getaddrinfo (addr, NULL, &hints, &res);
if (n != 0) {
log_message (LOG_INFO,
"bind_socket: getaddrinfo failed for %s: %s (af: %s)", addr, get_gai_error (n), family_string(family));
return -1;
}
ressave = res;
@ -70,6 +98,36 @@ bind_socket (int sockfd, const char *addr, int family)
return sockfd;
}
/**
* Try binding the given socket to supplied addresses, stopping when one succeeds.
*/
static int
bind_socket_list (int sockfd, sblist *addresses, int family)
{
size_t nb_addresses = sblist_getsize(addresses);
size_t i;
for (i = 0; i < nb_addresses; i++) {
const char *address = *(const char **)sblist_get(addresses, i);
if (bind_socket(sockfd, address, family) >= 0) {
log_message(LOG_INFO, "Bound to %s", address);
return 0;
}
}
return -1;
}
void set_socket_timeout(int fd) {
struct timeval tv;
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));
}
/*
* Open a connection to a remote host. It's been re-written to use
* the getaddrinfo() library function, which allows for a protocol
@ -84,6 +142,9 @@ int opensock (const char *host, int port, const char *bind_to)
assert (host != NULL);
assert (port > 0);
log_message(LOG_INFO,
"opensock: opening connection to %s:%d", host, port);
memset (&hints, 0, sizeof (struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
@ -93,10 +154,13 @@ int opensock (const char *host, int port, const char *bind_to)
n = getaddrinfo (host, portstr, &hints, &res);
if (n != 0) {
log_message (LOG_ERR,
"opensock: Could not retrieve info for %s", host);
"opensock: Could not retrieve address info for %s:%d: %s", host, port, get_gai_error (n));
return -1;
}
log_message(LOG_INFO,
"opensock: getaddrinfo returned for %s:%d", host, port);
ressave = res;
do {
sockfd =
@ -111,16 +175,27 @@ int opensock (const char *host, int port, const char *bind_to)
close (sockfd);
continue; /* can't bind, so try again */
}
} else if (config.bind_address) {
if (bind_socket (sockfd, config.bind_address,
} else if (config->bind_addrs) {
if (bind_socket_list (sockfd, config->bind_addrs,
res->ai_family) < 0) {
close (sockfd);
continue; /* can't bind, so try again */
}
}
if (connect (sockfd, res->ai_addr, res->ai_addrlen) == 0)
set_socket_timeout(sockfd);
if (connect (sockfd, res->ai_addr, res->ai_addrlen) == 0) {
union sockaddr_union *p = (void*) res->ai_addr, u;
int af = res->ai_addr->sa_family;
unsigned dport = ntohs(af == AF_INET ? p->v4.sin_port : p->v6.sin6_port);
socklen_t slen = sizeof u;
if (dport == config->port) {
getsockname(sockfd, (void*)&u, &slen);
loop_records_add(&u);
}
break; /* success */
}
close (sockfd);
} while ((res = res->ai_next) != NULL);
@ -128,8 +203,9 @@ int opensock (const char *host, int port, const char *bind_to)
freeaddrinfo (ressave);
if (res == NULL) {
log_message (LOG_ERR,
"opensock: Could not establish a connection to %s",
host);
"opensock: Could not establish a connection to %s:%d",
host,
port);
return -1;
}
@ -162,75 +238,147 @@ int socket_blocking (int sock)
return fcntl (sock, F_SETFL, flags & ~O_NONBLOCK);
}
/*
* Start listening to a socket. Create a socket with the selected port.
* The size of the socket address will be returned to the caller through
* the pointer, while the socket is returned as a default return.
* - rjkaes
/**
* Try to listen on one socket based on the addrinfo
* as returned from getaddrinfo.
*
* Return the file descriptor upon success, -1 upon error.
*/
int listen_sock (uint16_t port, socklen_t * addrlen)
static int listen_on_one_socket(struct addrinfo *ad)
{
int listenfd;
int ret;
const int on = 1;
char numerichost[NI_MAXHOST];
int flags = NI_NUMERICHOST;
ret = getnameinfo(ad->ai_addr, ad->ai_addrlen,
numerichost, NI_MAXHOST, NULL, 0, flags);
if (ret != 0) {
log_message(LOG_ERR, "getnameinfo failed: %s", get_gai_error (ret));
return -1;
}
log_message(LOG_INFO, "trying to listen on host[%s], family[%d], "
"socktype[%d], proto[%d]", numerichost,
ad->ai_family, ad->ai_socktype, ad->ai_protocol);
listenfd = socket(ad->ai_family, ad->ai_socktype, ad->ai_protocol);
if (listenfd == -1) {
log_message(LOG_ERR, "socket() failed: %s", strerror(errno));
return -1;
}
ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (ret != 0) {
log_message(LOG_ERR,
"setsockopt failed to set SO_REUSEADDR: %s",
strerror(errno));
close(listenfd);
return -1;
}
if (ad->ai_family == AF_INET6) {
ret = setsockopt(listenfd, IPPROTO_IPV6, IPV6_V6ONLY, &on,
sizeof(on));
if (ret != 0) {
log_message(LOG_ERR,
"setsockopt failed to set IPV6_V6ONLY: %s",
strerror(errno));
close(listenfd);
return -1;
}
}
ret = bind(listenfd, ad->ai_addr, ad->ai_addrlen);
if (ret != 0) {
log_message(LOG_ERR, "bind failed: %s", strerror (errno));
close(listenfd);
return -1;
}
ret = listen(listenfd, MAXLISTEN);
if (ret != 0) {
log_message(LOG_ERR, "listen failed: %s", strerror(errno));
close(listenfd);
return -1;
}
log_message(LOG_INFO, "listening on fd [%d]", listenfd);
return listenfd;
}
/*
* Start listening on a socket. Create a socket with the selected port.
* If the provided address is NULL, we may listen on multiple sockets,
* e.g. the wildcard addresse for IPv4 and IPv6, depending on what is
* supported. If the address is not NULL, we only listen on the first
* address reported by getaddrinfo that works.
*
* Upon success, the listen-fds are added to the listen_fds list
* and 0 is returned. Upon error, -1 is returned.
*/
int listen_sock (const char *addr, uint16_t port, sblist* listen_fds)
{
struct addrinfo hints, *result, *rp;
char portstr[6];
int listenfd;
const int on = 1;
int ret = -1;
int n;
assert (port > 0);
assert (addrlen != NULL);
assert (listen_fds != NULL);
log_message(LOG_INFO, "listen_sock called with addr = '%s'",
addr == NULL ? "(NULL)" : addr);
memset (&hints, 0, sizeof (struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
snprintf (portstr, sizeof (portstr), "%d", port);
if (getaddrinfo (config.ipAddr, portstr, &hints, &result) != 0) {
n = getaddrinfo (addr, portstr, &hints, &result);
if (n != 0) {
log_message (LOG_ERR,
"Unable to getaddrinfo() because of %s",
strerror (errno));
"Unable to getaddrinfo() for %s:%d because of %s",
addr,
port,
get_gai_error (n));
return -1;
}
for (rp = result; rp != NULL; rp = rp->ai_next) {
listenfd = socket (rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if (listenfd == -1)
int listenfd;
listenfd = listen_on_one_socket(rp);
if (listenfd == -1) {
continue;
setsockopt (listenfd, SOL_SOCKET, SO_REUSEADDR, &on,
sizeof (on));
if (bind (listenfd, rp->ai_addr, rp->ai_addrlen) == 0)
break; /* success */
close (listenfd);
}
if (rp == NULL) {
/* was not able to bind to any address */
log_message (LOG_ERR,
"Unable to bind listening socket "
"to any address.");
sblist_add (listen_fds, &listenfd);
freeaddrinfo (result);
return -1;
/* success */
ret = 0;
if (addr != NULL) {
/*
* Unless wildcard is requested, only listen
* on the first result that works.
*/
break;
}
}
if (listen (listenfd, MAXLISTEN) < 0) {
log_message (LOG_ERR,
"Unable to start listening socket because of %s",
strerror (errno));
close (listenfd);
freeaddrinfo (result);
return -1;
if (ret != 0) {
log_message (LOG_ERR, "Unable to listen on any address.");
}
*addrlen = rp->ai_addrlen;
freeaddrinfo (result);
return listenfd;
return ret;
}
/*
@ -259,27 +407,9 @@ int getsock_ip (int fd, char *ipaddr)
/*
* Return the peer's socket information.
*/
int getpeer_information (int fd, char *ipaddr, char *string_addr)
void getpeer_information (union sockaddr_union* addr, char *ipaddr, size_t ipaddr_len)
{
struct sockaddr_storage sa;
socklen_t salen = sizeof sa;
assert (fd >= 0);
assert (ipaddr != NULL);
assert (string_addr != NULL);
/* Set the strings to default values */
ipaddr[0] = '\0';
strlcpy (string_addr, "[unknown]", HOSTNAME_LENGTH);
/* Look up the IP address */
if (getpeername (fd, (struct sockaddr *) &sa, &salen) != 0)
return -1;
if (get_ip_string ((struct sockaddr *) &sa, ipaddr, IP_LENGTH) == NULL)
return -1;
/* Get the full host name */
return getnameinfo ((struct sockaddr *) &sa, salen,
string_addr, HOSTNAME_LENGTH, NULL, 0, 0);
int af = addr->v4.sin_family;
void *ipdata = af == AF_INET ? (void*)&addr->v4.sin_addr : (void*)&addr->v6.sin6_addr;
inet_ntop(af, ipdata, ipaddr, ipaddr_len);
}

View File

@ -28,13 +28,37 @@
#define MAXLINE (1024 * 4)
#include "common.h"
#include "sblist.h"
#define SOCKADDR_UNION_AF(PTR) (PTR)->v4.sin_family
#define SOCKADDR_UNION_LENGTH(PTR) ( \
( SOCKADDR_UNION_AF(PTR) == AF_INET ) ? sizeof((PTR)->v4) : ( \
( SOCKADDR_UNION_AF(PTR) == AF_INET6 ) ? sizeof((PTR)->v6) : 0 ) )
#define SOCKADDR_UNION_ADDRESS(PTR) ( \
( SOCKADDR_UNION_AF(PTR) == AF_INET ) ? (void*) &(PTR)->v4.sin_addr : ( \
( SOCKADDR_UNION_AF(PTR) == AF_INET6 ) ? (void*) &(PTR)->v6.sin6_addr : (void*) 0 ) )
#define SOCKADDR_UNION_PORT(PTR) ( \
( SOCKADDR_UNION_AF(PTR) == AF_INET ) ? (PTR)->v4.sin_port : ( \
( SOCKADDR_UNION_AF(PTR) == AF_INET6 ) ? (PTR)->v6.sin6_port : 0 ) )
union sockaddr_union {
struct sockaddr_in v4;
struct sockaddr_in6 v6;
};
extern int opensock (const char *host, int port, const char *bind_to);
extern int listen_sock (uint16_t port, socklen_t * addrlen);
extern int listen_sock (const char *addr, uint16_t port, sblist* listen_fds);
extern int socket_nonblocking (int sock);
extern int socket_blocking (int sock);
extern void set_socket_timeout(int fd);
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

View File

@ -33,6 +33,7 @@
#include "stats.h"
#include "utils.h"
#include "conf.h"
#include <pthread.h>
struct stat_s {
unsigned long int num_reqs;
@ -42,18 +43,16 @@ struct stat_s {
unsigned long int num_denied;
};
static struct stat_s *stats;
static struct stat_s stats_buf, *stats;
static pthread_mutex_t stats_update_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t stats_file_lock = PTHREAD_MUTEX_INITIALIZER;
/*
* Initialize the statistics information to zero.
*/
void init_stats (void)
{
stats = (struct stat_s *) malloc_shared_memory (sizeof (struct stat_s));
if (stats == MAP_FAILED)
return;
memset (stats, 0, sizeof (struct stat_s));
stats = &stats_buf;
}
/*
@ -72,10 +71,15 @@ showstats (struct conn_s *connptr)
snprintf (denied, sizeof (denied), "%lu", stats->num_denied);
snprintf (refused, sizeof (refused), "%lu", stats->num_refused);
if (!config.statpage || (!(statfile = fopen (config.statpage, "r")))) {
pthread_mutex_lock(&stats_file_lock);
if (!config->statpage || (!(statfile = fopen (config->statpage, "r")))) {
message_buffer = (char *) safemalloc (MAXBUFFSIZE);
if (!message_buffer)
if (!message_buffer) {
err_minus_one:
pthread_mutex_unlock(&stats_file_lock);
return -1;
}
snprintf
(message_buffer, MAXBUFFSIZE,
@ -83,9 +87,9 @@ showstats (struct conn_s *connptr)
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" "
"\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
"<html>\n"
"<head><title>%s version %s run-time statistics</title></head>\n"
"<head><title>%s run-time statistics</title></head>\n"
"<body>\n"
"<h1>%s version %s run-time statistics</h1>\n"
"<h1>%s run-time statistics</h1>\n"
"<p>\n"
"Number of open connections: %lu<br />\n"
"Number of requests: %lu<br />\n"
@ -94,33 +98,34 @@ showstats (struct conn_s *connptr)
"Number of refused connections due to high load: %lu\n"
"</p>\n"
"<hr />\n"
"<p><em>Generated by %s version %s.</em></p>\n" "</body>\n"
"<p><em>Generated by %s.</em></p>\n" "</body>\n"
"</html>\n",
PACKAGE, VERSION, PACKAGE, VERSION,
PACKAGE, PACKAGE,
stats->num_open,
stats->num_reqs,
stats->num_badcons, stats->num_denied,
stats->num_refused, PACKAGE, VERSION);
stats->num_refused, PACKAGE);
if (send_http_message (connptr, 200, "OK",
message_buffer) < 0) {
safefree (message_buffer);
return -1;
goto err_minus_one;
}
safefree (message_buffer);
pthread_mutex_unlock(&stats_file_lock);
return 0;
}
add_error_variable (connptr, "opens", opens);
add_error_variable (connptr, "reqs", reqs);
add_error_variable (connptr, "badconns", badconns);
add_error_variable (connptr, "deniedconns", denied);
add_error_variable (connptr, "refusedconns", refused);
add_standard_vars (connptr);
send_http_headers (connptr, 200, "Statistic requested");
send_http_headers (connptr, 200, "Statistic requested", "");
send_html_file (statfile, connptr);
fclose (statfile);
pthread_mutex_unlock(&stats_file_lock);
return 0;
}
@ -131,6 +136,9 @@ showstats (struct conn_s *connptr)
*/
int update_stats (status_t update_level)
{
int ret = 0;
pthread_mutex_lock(&stats_update_lock);
switch (update_level) {
case STAT_BADCONN:
++stats->num_badcons;
@ -149,8 +157,9 @@ int update_stats (status_t update_level)
++stats->num_denied;
break;
default:
return -1;
ret = -1;
}
pthread_mutex_unlock(&stats_update_lock);
return 0;
return ret;
}

View File

@ -45,7 +45,7 @@ static int build_url (char **url, const char *host, int port, const char *path)
assert (path != NULL);
len = strlen (host) + strlen (path) + 14;
*url = (char *) safemalloc (len);
*url = (char *) saferealloc (*url, len);
if (*url == NULL)
return -1;
@ -53,46 +53,54 @@ static int build_url (char **url, const char *host, int port, const char *path)
}
int
do_transparent_proxy (struct conn_s *connptr, hashmap_t hashofheaders,
do_transparent_proxy (struct conn_s *connptr, orderedmap hashofheaders,
struct request_s *request, struct config_s *conf,
char *url)
char **url)
{
socklen_t length;
char *data;
size_t ulen = strlen (url);
size_t ulen = strlen (*url);
size_t i;
length = hashmap_entry_by_key (hashofheaders, "host", (void **) &data);
if (length <= 0) {
struct sockaddr_in dest_addr;
data = orderedmap_find (hashofheaders, "host");
if (!data) {
union sockaddr_union dest_addr;
const void *dest_inaddr;
char namebuf[INET6_ADDRSTRLEN+1];
int af;
length = sizeof(dest_addr);
if (getsockname
(connptr->client_fd, (struct sockaddr *) &dest_addr,
&length) < 0) {
(connptr->client_fd, (void *) &dest_addr,
&length) < 0 || length > sizeof(dest_addr)) {
addr_err:;
log_message (LOG_ERR,
"process_request: cannot get destination IP for %d",
connptr->client_fd);
indicate_http_error (connptr, 400, "Bad Request",
"detail", "Unknown destination",
"url", url, NULL);
"url", *url, NULL);
return 0;
}
request->host = (char *) safemalloc (17);
strlcpy (request->host, inet_ntoa (dest_addr.sin_addr), 17);
af = SOCKADDR_UNION_AF(&dest_addr);
dest_inaddr = SOCKADDR_UNION_ADDRESS(&dest_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 (SOCKADDR_UNION_PORT(&dest_addr));
request->path = (char *) safemalloc (ulen + 1);
strlcpy (request->path, url, ulen + 1);
strlcpy (request->path, *url, ulen + 1);
/* url overwritten by the call below is the url passed
* to this function, and is not the url variable in the
* caller. */
build_url (&url, request->host, request->port, request->path);
build_url (url, request->host, request->port, request->path);
log_message (LOG_INFO,
"process_request: trans IP %s %s for %d",
request->method, url, connptr->client_fd);
request->method, *url, connptr->client_fd);
} else {
length = strlen (data);
request->host = (char *) safemalloc (length + 1);
if (sscanf (data, "%[^:]:%hu", request->host, &request->port) !=
2) {
@ -101,27 +109,36 @@ do_transparent_proxy (struct conn_s *connptr, hashmap_t hashofheaders,
}
request->path = (char *) safemalloc (ulen + 1);
strlcpy (request->path, url, ulen + 1);
strlcpy (request->path, *url, ulen + 1);
/* url overwritten by the call below is the url passed
* to this function, and is not the url variable in the
* caller. */
build_url (&url, request->host, request->port, request->path);
build_url (url, request->host, request->port, request->path);
log_message (LOG_INFO,
"process_request: trans Host %s %s for %d",
request->method, url, connptr->client_fd);
request->method, *url, connptr->client_fd);
}
if (conf->ipAddr && strcmp (request->host, conf->ipAddr) == 0) {
log_message (LOG_ERR,
"process_request: destination IP is localhost %d",
connptr->client_fd);
indicate_http_error (connptr, 400, "Bad Request",
if (conf->listen_addrs == NULL) {
return 1;
}
for (i = 0; i < sblist_getsize(conf->listen_addrs); i++) {
char **addr;
addr = sblist_get(conf->listen_addrs, i);
if (addr && *addr && strcmp(request->host, *addr) == 0) {
log_message(LOG_ERR,
"transparent: destination IP %s is local "
"on socket fd %d",
request->host, connptr->client_fd);
indicate_http_error(connptr, 400, "Bad Request",
"detail",
"You tried to connect to the machine "
"the proxy is running on", "url", url,
NULL);
"You tried to connect to the "
"machine the proxy is running on",
"url", *url, NULL);
return 0;
}
}
return 1;
}

View File

@ -26,13 +26,13 @@
#ifdef TRANSPARENT_PROXY
#include "conns.h"
#include "hashmap.h"
#include "orderedmap.h"
#include "reqs.h"
extern int do_transparent_proxy (struct conn_s *connptr,
hashmap_t hashofheaders,
orderedmap hashofheaders,
struct request_s *request,
struct config_s *config, char *url);
struct config_s *config, char **url);
#endif

View File

@ -27,89 +27,120 @@
#include "upstream.h"
#include "heap.h"
#include "log.h"
#include "base64.h"
#include "basicauth.h"
#ifdef UPSTREAM_SUPPORT
const char *
proxy_type_name(proxy_type type)
{
switch(type) {
case PT_NONE: return "none";
case PT_HTTP: return "http";
case PT_SOCKS4: return "socks4";
case PT_SOCKS5: return "socks5";
default: return "unknown";
}
}
const char* upstream_build_error_string(enum upstream_build_error ube) {
static const char *emap[] = {
[UBE_SUCCESS] = "",
[UBE_OOM] = "Unable to allocate memory in upstream_build()",
[UBE_USERLEN] = "User / pass in upstream config too long",
[UBE_EDOMAIN] = "Nonsense upstream none rule: empty domain",
[UBE_INVHOST] = "Nonsense upstream rule: invalid host or port",
[UBE_INVPARAMS] = "Nonsense upstream rule: invalid parameters",
[UBE_NETMASK] = "Nonsense upstream rule: failed to parse netmask",
};
return emap[ube];
}
/**
* Construct an upstream struct from input data.
*/
static struct upstream *upstream_build (const char *host, int port, const char *domain)
static struct upstream *upstream_build (const char *host, int port, char *domain,
const char *user, const char *pass,
proxy_type type, enum upstream_build_error *ube)
{
char *ptr;
struct upstream *up;
*ube = UBE_SUCCESS;
up = (struct upstream *) safemalloc (sizeof (struct upstream));
if (!up) {
log_message (LOG_ERR,
"Unable to allocate memory in upstream_build()");
*ube = UBE_OOM;
return NULL;
}
up->host = up->domain = NULL;
up->ip = up->mask = 0;
up->type = type;
up->target.type = HST_NONE;
up->host = up->ua.user = up->pass = NULL;
if (user) {
if (type == PT_HTTP) {
char b[BASE64ENC_BYTES((256+2)-1) + 1];
ssize_t ret;
ret = basicauth_string(user, pass, b, sizeof b);
if (ret == 0) {
*ube = UBE_USERLEN;
return NULL;
}
up->ua.authstr = safestrdup (b);
} else {
up->ua.user = safestrdup (user);
up->pass = safestrdup (pass);
}
}
if (domain == NULL) {
if (!host || host[0] == '\0' || port < 1) {
log_message (LOG_WARNING,
"Nonsense upstream rule: invalid host or port");
if (type == PT_NONE) {
e_nonedomain:;
*ube = UBE_EDOMAIN;
goto fail;
}
if (!host || !host[0] || port < 1) {
*ube = UBE_INVHOST;
goto fail;
}
up->host = safestrdup (host);
up->port = port;
log_message (LOG_INFO, "Added upstream %s:%d for [default]",
host, port);
} else if (host == NULL) {
if (!domain || domain[0] == '\0') {
log_message (LOG_WARNING,
"Nonsense no-upstream rule: empty domain");
log_message (LOG_INFO, "Added upstream %s %s:%d for [default]",
proxy_type_name(type), host, port);
} else {
if (type == PT_NONE) {
if (!domain[0]) goto e_nonedomain;
} else {
if (!host || !host[0] || !domain[0]) {
*ube = UBE_INVPARAMS;
goto fail;
}
ptr = strchr (domain, '/');
if (ptr) {
struct in_addr addrstruct;
*ptr = '\0';
if (inet_aton (domain, &addrstruct) != 0) {
up->ip = ntohl (addrstruct.s_addr);
*ptr++ = '/';
if (strchr (ptr, '.')) {
if (inet_aton (ptr, &addrstruct) != 0)
up->mask =
ntohl (addrstruct.s_addr);
} else {
up->mask =
~((1 << (32 - atoi (ptr))) - 1);
}
}
} else {
up->domain = safestrdup (domain);
}
log_message (LOG_INFO, "Added no-upstream for %s", domain);
} else {
if (!host || host[0] == '\0' || port < 1 || !domain
|| domain == '\0') {
log_message (LOG_WARNING,
"Nonsense upstream rule: invalid parameters");
goto fail;
}
up->host = safestrdup (host);
up->port = port;
up->domain = safestrdup (domain);
}
log_message (LOG_INFO, "Added upstream %s:%d for %s",
host, port, domain);
if (hostspec_parse(domain, &up->target)
|| up->target.type == HST_NONE) {
*ube = UBE_NETMASK;
goto fail;
}
if (type == PT_NONE)
log_message (LOG_INFO, "Added upstream none for %s", domain);
else
log_message (LOG_INFO, "Added upstream %s %s:%d for %s",
proxy_type_name(type), host, port, domain);
}
return up;
fail:
safefree (up->ua.user);
safefree (up->pass);
safefree (up->host);
safefree (up->domain);
if(up->target.type == HST_STRING)
safefree (up->target.address.string);
safefree (up);
return NULL;
@ -118,21 +149,24 @@ fail:
/*
* Add an entry to the upstream list
*/
void upstream_add (const char *host, int port, const char *domain,
struct upstream **upstream_list)
enum upstream_build_error upstream_add (
const char *host, int port, char *domain,
const char *user, const char *pass,
proxy_type type, struct upstream **upstream_list)
{
struct upstream *up;
enum upstream_build_error ube;
up = upstream_build (host, port, domain);
up = upstream_build (host, port, domain, user, pass, type, &ube);
if (up == NULL) {
return;
return ube;
}
if (!up->domain && !up->ip) { /* always add default to end */
if (up->target.type == HST_NONE) { /* always add default to end */
struct upstream *tmp = *upstream_list;
while (tmp) {
if (!tmp->domain && !tmp->ip) {
if (tmp->target.type == HST_NONE) {
log_message (LOG_WARNING,
"Duplicate default upstream");
goto upstream_cleanup;
@ -141,7 +175,7 @@ void upstream_add (const char *host, int port, const char *domain,
if (!tmp->next) {
up->next = NULL;
tmp->next = up;
return;
return ube;
}
tmp = tmp->next;
@ -151,14 +185,15 @@ void upstream_add (const char *host, int port, const char *domain,
up->next = *upstream_list;
*upstream_list = up;
return;
return ube;
upstream_cleanup:
safefree (up->host);
safefree (up->domain);
if(up->target.type == HST_STRING)
safefree (up->target.address.string);
safefree (up);
return;
return ube;
}
/*
@ -166,46 +201,24 @@ upstream_cleanup:
*/
struct upstream *upstream_get (char *host, struct upstream *up)
{
in_addr_t my_ip = INADDR_NONE;
while (up) {
if (up->domain) {
if (strcasecmp (host, up->domain) == 0)
break; /* exact match */
if (up->domain[0] == '.') {
char *dot = strchr (host, '.');
if (!dot && !up->domain[1])
break; /* local host matches "." */
while (dot && strcasecmp (dot, up->domain))
dot = strchr (dot + 1, '.');
if (dot)
break; /* subdomain match */
}
} else if (up->ip) {
if (my_ip == INADDR_NONE)
my_ip = ntohl (inet_addr (host));
if ((my_ip & up->mask) == up->ip)
if (up->target.type == HST_NONE)
break;
if (hostspec_match(host, &up->target))
break;
} else {
break; /* No domain or IP, default upstream */
}
up = up->next;
}
if (up && (!up->host || !up->port))
if (up && (!up->host))
up = NULL;
if (up)
log_message (LOG_INFO, "Found proxy %s:%d for %s",
up->host, up->port, host);
log_message (LOG_INFO, "Found upstream proxy %s %s:%d for %s",
proxy_type_name(up->type), up->host, up->port, host);
else
log_message (LOG_INFO, "No proxy for %s", host);
log_message (LOG_INFO, "No upstream proxy for %s", host);
return up;
}
@ -215,7 +228,8 @@ void free_upstream_list (struct upstream *up)
while (up) {
struct upstream *tmp = up;
up = up->next;
safefree (tmp->domain);
if(tmp->target.type == HST_STRING)
safefree (tmp->target.address.string);
safefree (tmp->host);
safefree (tmp);
}

View File

@ -26,24 +26,51 @@
#define _TINYPROXY_UPSTREAM_H_
#include "common.h"
#include "hostspec.h"
enum upstream_build_error {
UBE_SUCCESS = 0,
UBE_OOM,
UBE_USERLEN,
UBE_EDOMAIN,
UBE_INVHOST,
UBE_INVPARAMS,
UBE_NETMASK,
};
/*
* Even if upstream support is not compiled into tinyproxy, this
* structure still needs to be defined.
*/
typedef enum proxy_type {
PT_NONE = 0,
PT_HTTP,
PT_SOCKS4,
PT_SOCKS5
} proxy_type;
struct upstream {
struct upstream *next;
char *domain; /* optional */
char *host;
union {
char *user;
char *authstr;
} ua;
char *pass;
int port;
in_addr_t ip, mask;
struct hostspec target;
proxy_type type;
};
#ifdef UPSTREAM_SUPPORT
extern void upstream_add (const char *host, int port, const char *domain,
struct upstream **upstream_list);
const char *proxy_type_name(proxy_type type);
extern enum upstream_build_error upstream_add (
const char *host, int port, char *domain,
const char *user, const char *pass,
proxy_type type, struct upstream **upstream_list);
extern struct upstream *upstream_get (char *host, struct upstream *up);
extern void free_upstream_list (struct upstream *up);
extern const char* upstream_build_error_string(enum upstream_build_error);
#endif /* UPSTREAM_SUPPORT */
#endif /* _TINYPROXY_UPSTREAM_H_ */

View File

@ -39,7 +39,7 @@ send_http_message (struct conn_s *connptr, int http_code,
const char *error_title, const char *message)
{
static const char *headers[] = {
"Server: " PACKAGE "/" VERSION,
"Server: " PACKAGE,
"Content-type: text/html",
"Connection: close"
};

View File

@ -1,214 +0,0 @@
/* tinyproxy - A fast light-weight HTTP proxy
* Copyright (C) 2002 Robert James Kaes <rjkaes@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* A vector implementation. The vector can be of an arbitrary length, and
* the data for each entry is an lump of data (the size is stored in the
* vector.)
*/
#include "main.h"
#include "heap.h"
#include "vector.h"
/*
* These structures are the storage for the "vector". Entries are
* stored in struct vectorentry_s (the data and the length), and the
* "vector" structure is implemented as a linked-list. The struct
* vector_s stores a pointer to the first vector (vector[0]) and a
* count of the number of entries (or how long the vector is.)
*/
struct vectorentry_s {
void *data;
size_t len;
struct vectorentry_s *next;
};
struct vector_s {
size_t num_entries;
struct vectorentry_s *head;
struct vectorentry_s *tail;
};
/*
* Create an vector. The vector initially has no elements and no
* storage has been allocated for the entries.
*
* A NULL is returned if memory could not be allocated for the
* vector.
*/
vector_t vector_create (void)
{
vector_t vector;
vector = (vector_t) safemalloc (sizeof (struct vector_s));
if (!vector)
return NULL;
vector->num_entries = 0;
vector->head = vector->tail = NULL;
return vector;
}
/*
* Deletes an vector. All the entries when this function is run.
*
* Returns: 0 on success
* negative if a NULL vector is supplied
*/
int vector_delete (vector_t vector)
{
struct vectorentry_s *ptr, *next;
if (!vector)
return -EINVAL;
ptr = vector->head;
while (ptr) {
next = ptr->next;
safefree (ptr->data);
safefree (ptr);
ptr = next;
}
safefree (vector);
return 0;
}
/*
* Appends an entry into the vector. The entry is an arbitrary
* collection of bytes of _len_ octets. The data is copied into the
* vector, so the original data must be freed to avoid a memory leak.
* The "data" must be non-NULL and the "len" must be greater than zero.
* "pos" is either 0 to prepend the data, or 1 to append the data.
*
* Returns: 0 on success
* negative number if there are errors
*/
typedef enum {
INSERT_PREPEND,
INSERT_APPEND
} vector_pos_t;
static int
vector_insert (vector_t vector,
void *data,
size_t len,
vector_pos_t pos)
{
struct vectorentry_s *entry;
if (!vector || !data || len <= 0 ||
(pos != INSERT_PREPEND && pos != INSERT_APPEND))
return -EINVAL;
entry =
(struct vectorentry_s *) safemalloc (sizeof (struct vectorentry_s));
if (!entry)
return -ENOMEM;
entry->data = safemalloc (len);
if (!entry->data) {
safefree (entry);
return -ENOMEM;
}
memcpy (entry->data, data, len);
entry->len = len;
entry->next = NULL;
/* If there is no head or tail, create them */
if (!vector->head && !vector->tail)
vector->head = vector->tail = entry;
else if (pos == INSERT_PREPEND) {
/* prepend the entry */
entry->next = vector->head;
vector->head = entry;
} else {
/* append the entry */
vector->tail->next = entry;
vector->tail = entry;
}
vector->num_entries++;
return 0;
}
/*
* The following two function are used to make the API clearer. As you
* can see they simply call the vector_insert() function with appropriate
* arguments.
*/
int vector_append (vector_t vector, void *data, size_t len)
{
return vector_insert (vector, data, len, INSERT_APPEND);
}
int vector_prepend (vector_t vector, void *data, size_t len)
{
return vector_insert (vector, data, len, INSERT_PREPEND);
}
/*
* A pointer to the data at position "pos" (zero based) is returned.
* If the vector is out of bound, data is set to NULL.
*
* Returns: negative upon an error
* length of data if position is valid
*/
void *vector_getentry (vector_t vector, size_t pos, size_t * size)
{
struct vectorentry_s *ptr;
size_t loc;
if (!vector || pos >= vector->num_entries)
return NULL;
loc = 0;
ptr = vector->head;
while (loc != pos) {
ptr = ptr->next;
loc++;
}
if (size)
*size = ptr->len;
return ptr->data;
}
/*
* Returns the number of entries (or the length) of the vector.
*
* Returns: negative if vector is not valid
* positive length of vector otherwise
*/
ssize_t vector_length (vector_t vector)
{
if (!vector)
return -EINVAL;
return vector->num_entries;
}

View File

@ -1,75 +0,0 @@
/* tinyproxy - A fast light-weight HTTP proxy
* Copyright (C) 2002 Robert James Kaes <rjkaes@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* See 'vector.c' for detailed information. */
#ifndef _VECTOR_H
#define _VECTOR_H
/*
* We're using a typedef here to "hide" the implementation details of the
* vector. Sure, it's a pointer, but the struct is hidden in the C file.
* So, just use the vector_t like it's a cookie. :)
*/
typedef struct vector_s *vector_t;
/*
* vector_create() takes no arguments.
* vector_delete() is self explanatory.
*/
extern vector_t vector_create (void);
extern int vector_delete (vector_t vector);
/*
* When you insert a piece of data into the vector, the data will be
* duplicated, so you must free your copy if it was created on the heap.
* The data must be non-NULL and the length must be greater than zero.
*
* Returns: negative on error
* 0 upon successful insert.
*/
extern int vector_append (vector_t vector, void *data, size_t len);
extern int vector_prepend (vector_t vector, void *data, size_t len);
/*
* A pointer to the data at position "pos" (zero based) is returned and the
* size pointer contains the length of the data stored.
*
* The pointer points to the actual data in the vector, so you have
* the power to modify the data, but do it responsibly since the
* library doesn't take any steps to prevent you from messing up the
* vector. (A better rule is, don't modify the data since you'll
* likely mess up the "length" parameter of the data.) However, DON'T
* try to realloc or free the data; doing so will break the vector.
*
* If "size" is NULL the size of the data is not returned.
*
* Returns: NULL on error
* valid pointer to data
*/
extern void *vector_getentry (vector_t vector, size_t pos, size_t * size);
/*
* Returns the number of enteries (or the length) of the vector.
*
* Returns: negative if vector is not valid
* positive length of vector otherwise
*/
extern ssize_t vector_length (vector_t vector);
#endif /* _VECTOR_H */

View File

@ -18,7 +18,7 @@
# this program; if not, see <http://www.gnu.org/licenses/>.
SCRIPTS_DIR=$(pwd)/$(dirname $0)
SCRIPTS_DIR=$(cd $(dirname $0) && pwd)
BASEDIR=$SCRIPTS_DIR/../..
TESTS_DIR=$SCRIPTS_DIR/..
TESTENV_DIR=$TESTS_DIR/env
@ -26,10 +26,11 @@ LOG_DIR=$TESTENV_DIR/var/log
TINYPROXY_IP=127.0.0.2
TINYPROXY_PORT=12321
TINYPROXY_USER=$USER
TINYPROXY_USER=$(id -un)
TINYPROXY_PID_DIR=$TESTENV_DIR/var/run/tinyproxy
TINYPROXY_PID_FILE=$TINYPROXY_PID_DIR/tinyproxy.pid
TINYPROXY_LOG_DIR=$LOG_DIR
TINYPROXY_LOG_DIR=$LOG_DIR/tinyproxy
TINYPROXY_LOG_FILE=$TINYPROXY_LOG_DIR/tinyproxy.log
TINYPROXY_DATA_DIR=$TESTENV_DIR/usr/share/tinyproxy
TINYPROXY_CONF_DIR=$TESTENV_DIR/etc/tinyproxy
TINYPROXY_CONF_FILE=$TINYPROXY_CONF_DIR/tinyproxy.conf
@ -79,26 +80,32 @@ Listen $TINYPROXY_IP
Timeout 600
StatHost "$TINYPROXY_STATHOST_IP"
DefaultErrorFile "$TINYPROXY_DATA_DIR/debug.html"
ErrorFile 400 "$TINYPROXY_DATA_DIR/debug.html"
ErrorFile 403 "$TINYPROXY_DATA_DIR/debug.html"
ErrorFile 501 "$TINYPROXY_DATA_DIR/debug.html"
ErrorFile 502 "$TINYPROXY_DATA_DIR/debug.html"
StatFile "$TINYPROXY_DATA_DIR/stats.html"
Logfile "$TINYPROXY_LOG_DIR/tinyproxy.log"
Logfile "$TINYPROXY_LOG_FILE"
PidFile "$TINYPROXY_PID_FILE"
LogLevel Info
MaxClients 100
MinSpareServers 5
MaxSpareServers 20
StartServers 10
MaxRequestsPerChild 0
Allow 127.0.0.0/8
ViaProxyName "tinyproxy"
#DisableViaHeader Yes
ConnectPort 443
ConnectPort 563
FilterURLs On
#FilterURLs On
Filter "$TINYPROXY_FILTER_FILE"
XTinyproxy Yes
AddHeader "X-My-Header1" "Powered by Tinyproxy"
AddHeader "X-My-Header2" "Powered by Tinyproxy"
AddHeader "X-My-Header3" "Powered by Tinyproxy"
Upstream http 255.255.255.255:65535 ".invalid"
EOF
touch $TINYPROXY_FILTER_FILE
cat << 'EOF' > $TINYPROXY_FILTER_FILE
.*\.google-analytics\.com$
EOF
}
start_tinyproxy() {
@ -107,13 +114,26 @@ start_tinyproxy() {
echo " done (listening on $TINYPROXY_IP:$TINYPROXY_PORT)"
}
reload_config() {
echo -n "signaling tinyproxy to reload config..."
pid=$(cat $TINYPROXY_PID_FILE)
#1: SIGHUP
kill -1 $pid && echo "ok" || echo "fail"
}
stop_tinyproxy() {
echo -n "killing tinyproxy..."
kill $(cat $TINYPROXY_PID_FILE)
pid=$(cat $TINYPROXY_PID_FILE)
kill $pid
if test "x$?" = "x0" ; then
echo " ok"
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
}
@ -154,17 +174,38 @@ wait_for_some_seconds() {
}
run_basic_webclient_request() {
$WEBCLIENT_BIN $1 $2 >> $WEBCLIENT_LOG 2>&1
$WEBCLIENT_BIN $1 $2 > $WEBCLIENT_LOG 2>&1
WEBCLIENT_EXIT_CODE=$?
if test "x$WEBCLIENT_EXIT_CODE" = "x0" ; then
echo " ok"
else
echo "ERROR ($EBCLIENT_EXIT_CODE)"
echo "ERROR ($WEBCLIENT_EXIT_CODE)"
echo "webclient output:"
cat $WEBCLIENT_LOG
echo "######################################"
fi
return $WEBCLIENT_EXIT_CODE
}
run_failure_webclient_request() {
ec=$1
expected_error=$(($1 - 399))
shift
$WEBCLIENT_BIN "$1" "$2" "$3" "$4" > $WEBCLIENT_LOG 2>&1
WEBCLIENT_EXIT_CODE=$?
if test "x$WEBCLIENT_EXIT_CODE" = "x$expected_error" ; then
echo " ok, got expected error code $ec"
return 0
else
echo "ERROR ($WEBCLIENT_EXIT_CODE)"
echo "webclient output:"
cat $WEBCLIENT_LOG
echo "######################################"
fi
return 1
}
# "main"
@ -175,24 +216,58 @@ provision_webserver
start_webserver
start_tinyproxy
wait_for_some_seconds 3
wait_for_some_seconds 1
FAILED=0
basic_test() {
echo -n "checking direct connection to web server..."
run_basic_webclient_request "$WEBSERVER_IP:$WEBSERVER_PORT" /
test "x$?" = "x0" || FAILED=$((FAILED + 1))
echo -n "testing connection through tinyproxy..."
run_basic_webclient_request "$TINYPROXY_IP:$TINYPROXY_PORT" "http://$WEBSERVER_IP:$WEBSERVER_PORT/"
test "x$?" = "x0" || FAILED=$((FAILED + 1))
echo -n "requesting statspage via stathost url..."
run_basic_webclient_request "$TINYPROXY_IP:$TINYPROXY_PORT" "http://$TINYPROXY_STATHOST_IP"
test "x$?" = "x0" || FAILED=$((FAILED + 1))
}
echo "You can continue using the webserver and tinyproxy."
echo -n "hit <enter> to stop the servers and exit: "
read READ
ext_test() {
echo -n "checking bogus request..."
run_failure_webclient_request 400 --method="BIG FART" "$TINYPROXY_IP:$TINYPROXY_PORT" "http://$WEBSERVER_IP:$WEBSERVER_PORT"
test "x$?" = "x0" || FAILED=$((FAILED + 1))
echo -n "testing connection to filtered domain..."
run_failure_webclient_request 403 "$TINYPROXY_IP:$TINYPROXY_PORT" "http://badgoy.google-analytics.com/"
test "x$?" = "x0" || FAILED=$((FAILED + 1))
echo -n "requesting connect method to denied port..."
run_failure_webclient_request 403 --method=CONNECT "$TINYPROXY_IP:$TINYPROXY_PORT" "localhost:12345"
test "x$?" = "x0" || FAILED=$((FAILED + 1))
echo -n "testing unavailable backend..."
run_failure_webclient_request 502 "$TINYPROXY_IP:$TINYPROXY_PORT" "http://bogus.invalid"
test "x$?" = "x0" || FAILED=$((FAILED + 1))
}
basic_test
reload_config
basic_test
ext_test
echo "$FAILED errors"
if test "x$TINYPROXY_TESTS_WAIT" = "xyes"; then
echo "You can continue using the webserver and tinyproxy."
echo -n "hit <enter> to stop the servers and exit: "
read READ
fi
stop_tinyproxy
stop_webserver
echo "done"
exit 0
exit $FAILED

Some files were not shown because too many files have changed in this diff Show More