Compare commits

..

110 Commits
master ... 1.8

Author SHA1 Message Date
Michael Adam
1115251555 Bump version to 1.8.4.
Signed-off-by: Michael Adam <obnox@samba.org>
2014-12-13 01:57:23 +01:00
Michael Adam
72b477da9e Update NEWS for 1.8.4.
Signed-off-by: Michael Adam <obnox@samba.org>
2014-12-13 01:56:27 +01:00
Michael Adam
87e8da90b0 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>
(cherry picked from commit 800c3a250c)
2014-12-13 01:49:01 +01:00
Michael Adam
c348513095 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>
(cherry picked from commit 545463c75d)
2014-12-13 01:48:49 +01:00
Michael Adam
8845bdbff7 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>
(cherry picked from commit 308305d827)
2014-12-13 01:48:41 +01:00
Peter H. Froehlich
132a32a68d 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>
(cherry picked from commit ab6255393d)
2014-12-13 01:48:27 +01:00
Michael Adam
12c454119b Update NEWS for coverity fixes.
Signed-off-by: Michael Adam <obnox@samba.org>
2014-07-21 13:26:57 +02:00
Michael Adam
9db1b0d087 buffer: fix log message in read_buffer().
Signed-off-by: Michael Adam <obnox@samba.org>
(cherry picked from commit b59ecd0c66)
2014-07-21 13:26:57 +02:00
Michael Adam
e16844bd3b buffer: reduce indentation in read_buffer()
Signed-off-by: Michael Adam <obnox@samba.org>
(cherry picked from commit ffa3a56ab8)
2014-07-21 13:26:57 +02:00
Michael Adam
3cb25a7857 reqs: fix typo in a debug message in get_request_entity()
Signed-off-by: Michael Adam <obnox@samba.org>
(cherry picked from commit 76bd008cf9)
2014-07-21 13:26:57 +02:00
Michael Adam
ffbef661cd 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>
(cherry picked from commit b3ac7d2c7b)
2014-07-21 13:26:57 +02:00
Michael Adam
54244661f7 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>
(cherry picked from commit b92d70be07)
2014-07-21 13:26:57 +02:00
Michael Adam
5054b9b80f 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>
(cherry picked from commit 3710accf72)
2014-07-21 13:26:57 +02:00
Michael Adam
b86ecaa11b 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>
(cherry picked from commit e07c363df2)
2014-07-21 13:26:57 +02:00
Michael Adam
e467744134 reqs: Fix CID 1130972 - remove logically dead code.
url == NULL is caught above.

Found by coverity.

Signed-off-by: Michael Adam <obnox@samba.org>
(cherry picked from commit c82840bfcb)
2014-07-21 13:26:57 +02:00
Michael Adam
4ebb3c1bb6 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>
(cherry picked from commit 49c55ed26c)
2014-07-21 13:26:57 +02:00
Michael Adam
d05e801eef reqs: rename a variable.
ret will be used in enclosing scope.
so rename this special varibale.

Signed-off-by: Michael Adam <obnox@samba.org>
(cherry picked from commit c27b6d15e2)
2014-07-21 13:26:57 +02:00
Michael Adam
e1af5ffa58 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>
(cherry picked from commit 9efa5799f0)
2014-07-21 13:26:57 +02:00
Michael Adam
6c8d9de3cc 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>
(cherry picked from commit 0a99803425)
2014-07-21 13:26:57 +02:00
Michael Adam
9eec142886 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>
(cherry picked from commit 38ef36d998)
2014-07-21 13:26:56 +02:00
Michael Adam
f413620c5f child: check return code of socket_blocking for accept in child_main
Signed-off-by: Michael Adam <obnox@samba.org>
(cherry picked from commit 198600ce42)
2014-07-21 13:26:56 +02:00
Michael Adam
903c9eeb1c 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>
(cherry picked from commit 68bd0b61b5)
2014-07-21 13:26:56 +02:00
Michael Adam
5a5ae8bfe1 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>
(cherry picked from commit 2004abc1e3)
2014-07-21 13:26:56 +02:00
Michael Adam
51d4f11448 conf: Fix CID 1130973 - resource leak.
Found by coverity.

Signed-off-by: Michael Adam <obnox@samba.org>
(cherry picked from commit a244c1d4aa)
2014-07-21 13:26:56 +02:00
Mukund Sivaraman
78ff8e0f07 Remove suggester (see Banu RT #138) 2014-05-01 14:00:44 +05:30
Michael Adam
6505867f7a NEWS: fix typo
Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-20 14:25:31 +01:00
Michael Adam
0654ed6403 Start updating NEWS for 1.8.4
Signed-off-by: Michael Adam <obnox@samba.org>
2013-11-16 15:34:43 +01:00
Michael Adam
9ed48eb03b BB#106: remove now unused extract_ssl_url.
Signed-off-by: Michael Adam <obnox@samba.org>
(cherry picked from commit 0f18e4fc3a)
2013-11-16 15:27:06 +01:00
Michael Adam
578f409a03 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>
(cherry picked from commit 9f43cfd488)
2013-11-16 15:27:00 +01:00
Michael Adam
08c44a36ba 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>
(cherry picked from commit 98f77ef8c7)
2013-11-16 15:26:53 +01:00
Michael Adam
e0ad093b0f 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>
(cherry picked from commit bb2e894e0d)
2013-11-16 13:13:18 +01:00
Michael Adam
4ef81ff95d build: check for memmove
Signed-off-by: Michael Adam <obnox@samba.org>
(cherry picked from commit 7e1d8154de)
2013-11-16 13:13:17 +01:00
Michael Adam
3cdb1bf7aa [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>
(cherry picked from commit e82080a5f6)
2013-11-16 11:35:58 +01:00
Michael Adam
d11d23c14c [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>
(cherry picked from commit d0732f9ade)
2013-11-16 11:35:58 +01:00
Michael Adam
fdfa365bae sock: add a starting debug message to listen_sock()
Signed-off-by: Michael Adam <obnox@samba.org>
(cherry picked from commit e40b91974a)
2013-11-16 11:35:58 +01:00
Michael Adam
954d427565 sock: update introductory comment for listen_sock()
Signed-off-by: Michael Adam <obnox@samba.org>
(cherry picked from commit 22587d3d41)
2013-11-16 11:35:58 +01:00
Michael Adam
bb5d6af78f 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>
(cherry picked from commit 0698e4d180)
2013-11-16 11:35:58 +01:00
Michael Adam
79f34cd113 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>
(cherry picked from commit d7f67768eb)
2013-11-16 11:35:58 +01:00
Michael Adam
4f600654d2 sock: in listen_sock(), move variable for setsockopt() into scope
Signed-off-by: Michael Adam <obnox@samba.org>
(cherry picked from commit 157879d4f6)
2013-11-16 11:35:58 +01:00
Michael Adam
37ffa6986d sock: log each result of getaddrinfo() in listen_sock()
Signed-off-by: Michael Adam <obnox@samba.org>
(cherry picked from commit a7898a2c98)
2013-11-16 11:35:58 +01:00
Michael Adam
5608382be9 sock: in listen_sock(), add a log message for when bind() has failed
Signed-off-by: Michael Adam <obnox@samba.org>
(cherry picked from commit 947e255d19)
2013-11-16 11:35:58 +01:00
Michael Adam
04cc18d0f8 sock: in listen_sock(), detect and log failure to call setsockopt()
Signed-off-by: Michael Adam <obnox@samba.org>
(cherry picked from commit b41d140984)
2013-11-16 11:35:58 +01:00
Michael Adam
7b1801d8e7 sock: in listen_sock(), add debug message when socket() call failed.
Signed-off-by: Michael Adam <obnox@samba.org>
(cherry picked from commit 5392e9829c)
2013-11-16 11:35:58 +01:00
Michael Adam
844403afbe 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>
(cherry picked from commit fa26ad4d56)
2013-11-16 11:35:58 +01:00
Michael Adam
866f1c3e56 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>
(cherry picked from commit 2ebfd456ef)
2013-11-16 11:35:58 +01:00
Michael Adam
425ce3beb6 child: add addr argument to child_listening_sock().
Signed-off-by: Michael Adam <obnox@samba.org>
(cherry picked from commit 070d621534)
2013-11-16 11:35:58 +01:00
Michael Adam
a8a2342527 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>
(cherry picked from commit 2bd919f01e)
2013-11-16 11:35:58 +01:00
Michael Adam
ccb987e8e0 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>
(cherry picked from commit 7eea1638bc)
2013-11-16 11:35:58 +01:00
Michael Adam
0adf359245 [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>
(cherry picked from commit 4bbd6e8626)
2013-11-09 13:47:18 +01:00
Michael Adam
54eaac8e76 log: remove extra newline characters in log messages.
Signed-off-by: Michael Adam <obnox@samba.org>
(cherry picked from commit d652681e8a)
2013-11-09 13:47:16 +01:00
Gaudenz Steinlin
8963739e4b [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>
(cherry picked from commit c8b8247f70)
2013-11-01 06:40:22 +01:00
Michael Adam
a747617c05 [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>
(cherry picked from commit 3cc59ec3be)

Conflicts:

	configure.ac
2013-04-10 00:01:44 +02:00
Mukund Sivaraman
9d7a95bd56 build: Prepend to LDFLAGS instead of replacing its contents 2012-01-23 16:26:07 +05:30
Mukund Sivaraman
3933daf793 Make .xz compressed dist targets too 2011-09-11 11:53:46 +05:30
Mukund Sivaraman
18db1675a0 Update NEWS 2011-08-16 17:56:04 +05:30
Mukund Sivaraman
4d979df636 Bump version to 1.8.3 2011-08-16 17:45:06 +05:30
Mukund Sivaraman
fc354343f9 Minor whitespace fix 2011-08-16 17:43:59 +05:30
Mukund Sivaraman
1db982793d [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:54 +05:30
Mukund Sivaraman
95a6f8259c Validate port number specified in Port directive
This was asked in bug #90 comment #8.
2011-03-04 14:10:11 +05:30
Mukund Sivaraman
8b76f1a939 Update URLs of Tinyproxy 2011-02-28 12:46:46 +05:30
Mukund Sivaraman
121a11d8e5 Surround IPv6 literals with [] in Host: headers 2011-02-07 18:01:03 +05:30
Mukund Sivaraman
736e052dc1 Handle IPv6 literals in URLs correctly 2011-02-04 21:03:12 +05:30
Michael Adam
aa197d6dc8 [BB#95] remove two comments that have become wrong by the fix. 2010-12-01 22:21:11 +01:00
Daniel Egger
d13d575d29 [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:18:08 +01:00
Michael Adam
c4b187c8ab README: correctly list --enable-transparent
--enable-transparent-proxy was renamed to --enable-transparent
in August 2004... :-)
2010-12-01 21:44:32 +01:00
Michael Adam
5012ce690f [BB#91] Fix upstream proxy support.
Patch by Jordi Mallach.
2010-08-24 22:50:53 +02:00
Michael Adam
4b75b634d9 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:50:48 +02:00
Mukund Sivaraman
4b64de4c31 Change wording in NEWS 2010-06-05 09:29:58 +05:30
Mukund Sivaraman
7fa544c3d8 Bump version in configure.ac 2010-06-02 10:40:23 +05:30
Mukund Sivaraman
a5a3741291 Add BB#74 to NEWS 2010-06-02 10:39:56 +05:30
Mukund Sivaraman
784d458b82 [BB#74] Create log and pid files after we drop privs 2010-06-02 10:19:49 +05:30
Mukund Sivaraman
94edc4f4c5 Remove excessive code 2010-06-02 10:11:10 +05:30
Mukund Sivaraman
be63d2ca19 Add BB#89 to NEWS 2010-06-02 05:05:30 +05:30
Mukund Sivaraman
a905437242 Add NEWS for 1.8.2 2010-06-01 08:12:29 +05:30
John van der Kamp
3127e726d0 [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:58:02 +05:30
Mukund Sivaraman
5bb184c54d Fix typo in manpage 2010-05-30 10:02:45 +05:30
Jordi Mallach
505ff803e9 [BB#83] Use output of id instead of $USER 2010-04-21 21:01:59 +05:30
Dmitry Semyonov
d03e3a52e5 Fix crash in send_stored_logs() 2010-04-21 20:32:41 +05:30
Mukund Sivaraman
fe9444d585 Fix compiler warning about dereferencing type-punned pointers
This is a backport of commit 19b9bff888
from the master branch.
2010-04-21 20:21:22 +05:30
Mukund Sivaraman
b37135524d Revert "main: drop privileges right after reading the config"
This reverts commit 965664798c. It should
fix the issue in bug #87.
2010-04-21 20:09:20 +05:30
Mukund Sivaraman
7f053670c0 Just fix the support URLs
These should always point to the top-level Tinyproxy homepage.
2010-03-28 10:29:56 +05:30
Mukund Sivaraman
b28e438cdf Revert "Update Tinyproxy website URLs"
This reverts commit e495bdf129.
2010-03-28 10:27:45 +05:30
Mukund Sivaraman
e495bdf129 Update Tinyproxy website URLs 2010-03-27 08:19:53 +05:30
Mukund Sivaraman
287a7ae649 Revert "Revert "Pass address family when binding to outgoing socket""
This reverts commit 577ac16cf1. It looks
like bug #69 needs this fix after all.
2010-03-09 17:07:14 +05:30
Mukund Sivaraman
315e129f12 Pull BB#69 out of NEWS for 1.8.1 2010-03-09 06:58:24 +05:30
Mukund Sivaraman
7cb30dd9ee Separate different versions in NEWS with newlines 2010-03-09 06:55:25 +05:30
Mukund Sivaraman
9927d411bf Add the contributors section to 1.8.1 NEWS 2010-03-03 15:37:10 +05:30
Mukund Sivaraman
0f28221ec5 Minor grammatical change 2010-03-03 15:33:40 +05:30
Mukund Sivaraman
57d90c8bf7 Update NEWS for 1.8.1 2010-03-03 15:31:46 +05:30
Mukund Sivaraman
8de8634b8b Bump version to 1.8.1 2010-03-03 15:21:25 +05:30
Mukund Sivaraman
ce149cc7a6 Use AI_PASSIVE flag to make tinyproxy listen on wildcard interface
Signed-off-by: Michael Adam <obnox@samba.org>
2010-03-03 09:27:22 +01:00
Michael Adam
410eaf107c Comment out the LogFile and PidFile options in the example tinyproxy.conf.
These are compiled in defaults now.

Michael
2010-03-03 01:06:25 +01:00
Michael Adam
e6cbaf7b6e 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-03 01:06:25 +01:00
Michael Adam
2d0192b8a8 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-03 01:06:25 +01:00
Michael Adam
643d52ac5a main: some tabs->spaces 2010-03-03 01:06:25 +01:00
Michael Adam
b92792fd8d main: move a log message. 2010-03-03 01:06:25 +01:00
Michael Adam
965664798c 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-03 01:06:25 +01:00
Michael Adam
6d5709de38 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-03 01:06:25 +01:00
Michael Adam
107f9117d0 tinyproxy.conf: fix LogFile to proper CamelCase for consistency 2010-03-03 01:06:24 +01:00
Michael Adam
1b3dd058d1 tests: fix bug #80: keep track of errors in return codes
Michael
(cherry picked from commit 3f1632603c)
2010-02-23 00:19:26 +01:00
Michael Adam
333d722d56 tests: fix a typo in run_tests.sh
(cherry picked from commit 7c15563430)
2010-02-23 00:19:23 +01:00
Michael Adam
6f0abb7339 test: add make target "make valgrind-test-wait"
(cherry picked from commit ee1f1e38f2)
2010-02-22 23:32:08 +01:00
Michael Adam
a19f758743 tests: add a new make target "make test-wait"
(cherry picked from commit 752e4419a6)
2010-02-22 23:21:18 +01:00
Michael Adam
f63730c77e 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
(cherry picked from commit d133eee36b)
2010-02-22 23:21:13 +01:00
Mukund Sivaraman
ed3ada7c26 Use format string when logging messages 2010-02-19 21:08:00 +05:30
Mukund Sivaraman
b9e6d9742d Fix pkgdatadir path in tinyproxy.conf 2010-02-19 21:07:58 +05:30
Mukund Sivaraman
fbc434e26d Display upstream proxy support in usage message 2010-02-17 23:18:50 +05:30
Mukund Sivaraman
cc74869e71 Fix typo in manpage 2010-01-25 19:35:41 +05:30
Mukund Sivaraman
577ac16cf1 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-24 11:18:27 +05:30
Mukund Sivaraman
810b9ae49a Remove completed items from TODO list 2010-01-18 16:01:16 +05:30
101 changed files with 6447 additions and 5159 deletions

View File

@ -1,20 +0,0 @@
---
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".

View File

@ -1,36 +0,0 @@
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

View File

@ -1,40 +0,0 @@
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,6 +18,5 @@ stamp-h1
autom4te.cache
cscope.files
cscope.out
compile
*~
tags

View File

@ -1,18 +0,0 @@
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,39 +1,19 @@
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
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

2156
ChangeLog

File diff suppressed because it is too large Load Diff

View File

@ -4,25 +4,15 @@ SUBDIRS = \
etc \
docs \
m4macros \
tests \
scripts
tests
# 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 \
VERSION
TODO
test: all
./tests/scripts/run_tests.sh

180
NEWS
View File

@ -1 +1,179 @@
See git log for recent changes in Tinyproxy.
Tinyproxy NEWS
==============
Version 1.8.4
-------------
Most notably, this release removes the limitation of
a single Listen address of not listening on the
wildcard address (BB#63) and a DoS (BB#110, CVE-2012-3505).
Among several other bug fixes, this release fixes a bunch of issues found
by coverity (scan.coverity.com).
Bugs resolved since version 1.8.3
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* BB#110: fix algorithmic complexity DoS in hashmap
* BB#106: fix failing CONNECT requests with IPv6 literal addresses
* BB#116: fix invalid free for GET requests to IPv6 literal addresses
* BB#63: support multiple Listen statements in configuration
* BB#81: support listening on ipv4 and ipv6 wildcard if no Listen specified
* BB#109: fix crash when writing to log file fails
* BB#112: fix build with autoconf >= 2.69
Contributors:
~~~~~~~~~~~~~
Mukund Sivaraman, Michael Adam, Gaudenz Steinlin, Peter H. Froehlich
Version 1.8.3
-------------
This release mostly fixes support for IPv6, and also some security
bugs. Fixes to messages, etc. were also made.
Bugs resolved since version 1.8.2
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* BB#91: Fix upstream proxy support
* BB#95: Fix FilterURLs with transparent proxy support
* BB#90: Fix bug in ACL netmask generation
Contributors
~~~~~~~~~~~~
Daniel Egger, John Horne, Michael Adam, Mukund Sivaraman.
Version 1.8.2
-------------
* Minor formatting changes and typo fixes were made.
Bugs resolved since version 1.8.1
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* BB#69: INET6 not available when configured to Listen and Bind in v4,
and vice versa
* BB#74: tinyproxy unable to reopen log files after receiving HUP
* BB#78: Warn if configuration results in an open proxy
* BB#82: https access not working
* BB#83: run_tests.sh relies on $USER
* BB#84: Unaligned access error on ia64 and alpha
* BB#87: Unable to listen on ports less than 1024 (regression in 1.8.1)
* BB#88: Crashes when reloading configuration
* BB#89: tinyproxy leaks memory over time
Contributors
~~~~~~~~~~~~
Dmitry Semyonov, John van der Kamp, Jordi Mallach, Michael Adam,
Mukund Sivaraman.
Version 1.8.1
-------------
* Tinyproxy now drops `root` user privileges more quickly.
* The log and pid files are now stored in a sub-directory in `/var/`.
* A format string vulnerability was fixed.
* Minor formatting changes and typo fixes were made.
Bugs fixed since version 1.8.0
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* BB#74: tinyproxy unable to reopen log files after receiving HUP
* BB#79: Make the testsuite uninteractive
* BB#80: Handle errors in testsuite
* BB#81: Listen directive doesn't work as expected
* BB#72: upstream support is not reported with tinyproxy -h
* BB#73: generated tinyproxy.conf has the wrong location for the html
file installation
Contributors
~~~~~~~~~~~~
Michael Adam, Mukund Sivaraman.
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.

98
README
View File

@ -1 +1,97 @@
see README.md
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://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
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://banu.com/bugzilla/
You may also wish to subscribe to the Tinyproxy mailing lists. To do so
please visit:
https://banu.com/mailman/listinfo/tinyproxy-announce-list
https://banu.com/mailman/listinfo/tinyproxy-users-list
https://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://banu.com/tinyproxy.git. The
following command creates a local copy:
----
git clone git://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).

View File

@ -1,92 +0,0 @@
# 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`).

View File

@ -1,28 +0,0 @@
# 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.

View File

@ -1 +0,0 @@
1.11.2

View File

@ -3,17 +3,25 @@
AC_PREREQ(2.54)
m4_define([tinyproxy_version], esyscmd(sh scripts/version.sh | tr -d '\n'))
m4_define([tinyproxy_major_version], [1])
m4_define([tinyproxy_minor_version], [8])
m4_define([tinyproxy_micro_version], [4])
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.2])
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]))
AC_INIT([Tinyproxy], [tinyproxy_version],
[https://tinyproxy.github.io/],
[https://banu.com/tinyproxy/],
[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 dist-xz])
AC_CONFIG_HEADERS(config.h)
@ -21,9 +29,31 @@ 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
@ -83,8 +113,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 YES)],
yes)
[Enable reverse proxying (default is NO)],
no)
if test x"$reverse_enabled" = x"yes"; then
ADDITIONAL_OBJECTS="$ADDITIONAL_OBJECTS reverse-proxy.o"
AC_DEFINE(REVERSE_SUPPORT)
@ -94,21 +124,17 @@ 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 YES)],
yes)
[Enable transparent proxying code (default is NO)],
no)
if test x"$transparent_enabled" = x"yes"; then
ADDITIONAL_OBJECTS="$ADDITIONAL_OBJECTS transparent-proxy.o"
AC_DEFINE(TRANSPARENT_PROXY)
fi
dnl Let user decide whether he wants support for manpages
dnl Which require either pod2man or a tarball release
AH_TEMPLATE([MANPAGE_SUPPORT],
[Build manpages with pod2man if they are missing from the distribution.])
TP_ARG_ENABLE(manpage_support,
[Enable support for building manpages (default is YES)],
dnl Check for broken regex library
TP_ARG_ENABLE(regexcheck,
[Check for working regex library (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
@ -140,78 +166,130 @@ dnl
AC_HEADER_STDC
AC_HEADER_TIME
AC_HEADER_SYS_WAIT
AC_CHECK_HEADERS([sys/ioctl.h alloca.h memory.h malloc.h sysexits.h \
values.h poll.h])
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)
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 memmove setrlimit ftruncate regcomp regexec])
AC_CHECK_FUNCS([strlcpy strlcat setgroups])
AC_CHECK_FUNCS([time rand srand])
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 -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"
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"
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 $LDFLAGS"
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)
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.])
# 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.])
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
@ -223,31 +301,10 @@ 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,6 +30,9 @@
<dt>clienthost</dt>
<dd>{clienthost}</dd>
<dt>version</dt>
<dd>{version}</dd>
<dt>package</dt>
<dd>{package}</dd>
@ -46,7 +49,7 @@
<hr />
<p><em>Generated by <a href="{website}">{package}</a>.</em></p>
<p><em>Generated by <a href="{website}">{package}</a> version {version}.</em></p>
</body>

View File

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

View File

@ -1,95 +1,69 @@
<!DOCTYPE html>
<?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">
<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>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<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>
<head>
<title>{package} version {version} run-time statistics</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<tr class="even">
<td class="right">Bad connections</td>
<td class="center">{badconns}</td>
</tr>
<style type="text/css" media="screen">
<!--/*--><![CDATA[<!--*/
<tr class="odd">
<td class="right">Denied connections</td>
<td class="center">{deniedconns}</td>
</tr>
th, td
{
text-align: left;
padding: 0.5em;
border: 1px solid gray;
}
<tr class="even">
<td class="right">Refused (high load)</td>
<td class="center">{refusedconns}</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="odd">
<td class="right">Total requests</td>
<td class="center">{reqs}</td>
</tr>
</table>
</div>
</div>
</body>
</html>

View File

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

52
docs/filter-howto.txt Normal file
View File

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

View File

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

View File

@ -1,20 +1,24 @@
=pod
TINYPROXY.CONF(5)
=================
:man source: Version @VERSION@
:man manual: Tinyproxy manual
=encoding utf8
=head1 NAME
NAME
----
tinyproxy.conf - Tinyproxy HTTP proxy daemon configuration file
=head1 SYNOPSIS
SYNOPSIS
--------
B<tinyproxy.conf>
*tinyproxy.conf*
=head1 DESCRIPTION
DESCRIPTION
-----------
L<tinyproxy(8)> reads its configuration file, typically stored in
`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.
@ -22,419 +26,345 @@ 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. Some string values must be enclosed in double
quotes (") as noted below.
case-sensitive. Values may be enclosed in double-quotes (") if they
contain spaces.
The possible keywords and their descriptions are as follows:
=over 4
*User*::
=item B<User>
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 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.
*Group*::
=item B<Group>
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 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.
*Port*::
=item B<Port>
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.
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.
*Listen*::
=item B<Listen>
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.
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.
*Bind*::
=item B<Bind>
This allows you to specify which address Tinyproxy will bind
to for outgoing connections to web servers or upstream proxies.
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.
*BindSame*::
=item B<BindSame>
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.
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.
*Timeout*::
=item B<Timeout>
The maximum number of seconds of inactivity a connection is
allowed to have before it is closed by Tinyproxy.
The maximum number of seconds of inactivity a connection is
allowed to have before it is closed by Tinyproxy.
*ErrorFile*::
=item B<ErrorFile>
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.
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.
*DefaultErrorFile*::
=item B<DefaultErrorFile>
This parameter controls the HTML template file returned when an
error occurs for which no specific error file has been set.
The HTML template file returned when an error occurs for which no
specific error file has been set. Enclose in double quotes.
*StatHost*::
=item B<StatHost>
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@`.
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@`.
*StatFile*::
=item B<StatFile>
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.
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.
*LogFile*::
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.
This controls the location of the file to which Tinyproxy
writes its debug output. Alternatively, Tinyproxy can log
to syslog -- see the Syslog option.
=item B<LogFile>
*Syslog*::
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.
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.
=item B<Syslog>
*LogLevel*::
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.
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:
=item B<LogLevel>
* Critical (least verbose)
* Error
* Warning
* Notice
* Connect (log connections without Info's noise)
* Info (most verbose)
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:
*PidFile*::
=over 4
This option controls the location of the file where the main
Tinyproxy process stores its process ID for signaling purposes.
=item * Critical (least verbose)
*XTinyproxy*::
=item * Error
Setting this option to `Yes` tells Tinyproxy to add a header
`X-Tinyproxy` containing the client's IP address to the request.
=item * Warning
*Upstream*::
*No Upstream*::
=item * Notice
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. There are three possible forms for
specifying upstream rules:
=item * Connect (log connections without Info's noise)
* 'upstream host:port' turns proxy upstream support on generally.
=item * Info (most verbose)
* 'upstream host:port "site_spec"' turns on the upstream proxy for
the sites matching `site_spec`.
=back
* 'no upstream "site_spec"' turns off upstream support for sites
matching `site_spec`.
=item B<PidFile>
The site can be specified in various forms as a hostname, domain
name or as an IP range:
The location of the file where the main Tinyproxy process stores its
process ID for signaling purposes. Enclose in double quotes.
* '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
=item B<XTinyproxy>
*MaxClients*::
Setting this option to `Yes` tells Tinyproxy to add a header
`X-Tinyproxy` containing the client's IP address to the request.
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.
=item B<Upstream>
*MinSpareServers*::
*MaxSpareServers*::
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:
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*::
=over 4
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`.
*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*::
=item * I<upstream type host:port> turns proxy upstream support on generally.
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 user:pass@host:port>
does the same, but uses the supplied credentials for authentication.
*ReverseBaseURL*::
=item * I<upstream type host:port "site_spec">
turns on the upstream proxy for the sites matching `site_spec`.
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.
`type` can be one of `http`, `socks4`, `socks5`, `none`.
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
BUGS
----
To report bugs in Tinyproxy, please visit
L<https://tinyproxy.github.io/>.
<https://www.banu.com/tinyproxy/[https://www.banu.com/tinyproxy/]>.
=head1 SEE ALSO
L<tinyproxy(8)>
SEE ALSO
--------
tinyproxy(8)
=head1 AUTHOR
AUTHOR
------
This manpage was written by the Tinyproxy project team.
Written by the Tinyproxy project team.
=head1 COPYRIGHT
COPYRIGHT
---------
Copyright (c) 1998-2024 the Tinyproxy authors.
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.
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,36 +1,20 @@
if HAVE_MANPAGE_INTEREST
MAN8_FILES = \
MAN8_FILES = \
tinyproxy.txt
endif
M_SECTION=8
M_NAME=TINYPROXY
A2X_ARGS = \
-d manpage \
-f manpage
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_POD2MAN
$(AM_V_GEN) $(POD2MAN) --center="Tinyproxy manual" \
--section=$(M_SECTION) --name=$(M_NAME) --release="Version @VERSION@" \
$< > $@
if HAVE_A2X
$(AM_V_GEN) $(A2X) $(A2X_ARGS) $<
else
@echo "*** pod2man is required to regenerate $(@) ***"; exit 1;
@echo "*** a2x (asciidoc) is required to regenerate $(@) ***"; exit 1;
endif
MAINTAINERCLEANFILES = \
$(MAN8_FILES:.txt=.8)
EXTRA_DIST = \
$(MAN8_FILES:.txt=.8)
CLEANFILES = \
$(MAN8_FILES:.txt=.8) \
$(MAN8_FILES:.txt=.xml)

View File

@ -1,20 +1,24 @@
=pod
TINYPROXY(8)
============
:man source: Version @VERSION@
:man manual: Tinyproxy manual
=encoding utf8
=head1 NAME
NAME
----
tinyproxy - A light-weight HTTP proxy daemon
=head1 SYNOPSIS
SYNOPSIS
--------
B<tinyproxy> [-vdch]
*tinyproxy* [-vldch]
=head1 DESCRIPTION
DESCRIPTION
-----------
B<tinyproxy> is a light-weight HTTP proxy daemon designed to consume a
*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
@ -22,66 +26,49 @@ deployments where a full featured HTTP proxy is required, but the system
resources for a larger proxy are unavailable.
=head1 OPTIONS
OPTIONS
-------
B<tinyproxy> accepts the following options:
*tinyproxy* accepts the following options:
=over 4
*-c <config-file>*::
Use an alternate configuration file.
=item B<-c <config-file>>
*-d*::
Don't daemonize and stay in the foreground. Useful for debugging purposes.
Use an alternate configuration file.
*-h*::
Display a short help screen of command line arguments and exit.
=item B<-d>
*-l*::
Display the licensing agreement.
Don't daemonize and stay in the foreground. Useful for debugging purposes.
*-v*::
Display version information and exit.
=item B<-h>
Display a short help screen of command line arguments and exit.
=item B<-v>
Display version information and exit.
=back
=head1 SIGNALS
SIGNALS
-------
In addition to command-line options, there are also several signals that
can be sent to B<tinyproxy> while it is running to generate debugging
can be sent to *tinyproxy* while it is running to generate debugging
information and to force certain events.
=over 4
*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.
=item B<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.
(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
TEMPLATE FILES
--------------
There are two occasions when Tinyproxy delivers HTML pages to
the client on it's own right:
=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
. 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.)
The layout of both error pages and the statistics page can be
controlled via configurable HTML template files that are plain
@ -89,60 +76,46 @@ HTML files that additionally understand a few template
variables.
=head1 TEMPLATE VARIABLES
TEMPLATE VARIABLES
------------------
There are several standard HTML variables that are available in every
template file:
=over 4
*request*::
The full HTTP request line.
=item B<request>
*cause*::
The abbreviated cause of the error condition.
The full HTTP request line.
*clientip*::
The IP address of the client making the request.
=item B<cause>
*clienthost*::
The hostname of the client making the request.
The abbreviated cause of the error condition.
*version*::
The version of Tinyproxy.
=item B<clientip>
*package*::
The package name. Presently, resolves to 'tinyproxy'.
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
*date*::
The current date/time in HTTP format.
In addition, almost all templates support:
=over 4
=item B<detail>
A detailed, plain English explanation of the error and possible
causes.
=back
*detail*::
A detailed, plain English explanation of the error and possible
causes.
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.
=head1 STATHOST
STATHOST
--------
Tinyproxy returns a HTML page with connection statistics when it
receives a HTTP request for a certain host -- the stathost. The
@ -154,33 +127,36 @@ The stat file template can be changed at runtime through the
configuration variable `StatFile`.
=head1 FILES
FILES
-----
F<@sysconfdir@/tinyproxy/tinyproxy.conf>
`/etc/tinyproxy/tinyproxy.conf`, `/var/run/tinyproxy/tinyproxy.pid`, `/var/log/tinyproxy/tinyproxy.log`
F<@runstatedir@/tinyproxy/tinyproxy.pid>
F<@localstatedir@/log/tinyproxy/tinyproxy.log>
=head1 BUGS
BUGS
----
To report bugs in Tinyproxy, please visit
L<https://tinyproxy.github.io/>.
<https://www.banu.com/tinyproxy/[https://www.banu.com/tinyproxy/]>.
=head1 SEE ALSO
L<tinyproxy.conf(5)>
SEE ALSO
--------
tinyproxy.conf(5)
=head1 AUTHOR
AUTHOR
------
This manpage was written by the Tinyproxy project team.
Written by the Tinyproxy project team.
=head1 COPYRIGHT
COPYRIGHT
---------
Copyright (c) 1998-2020 the Tinyproxy authors.
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.
This program is distributed under the terms of the GNU General Public
License version 2 or above. See the COPYING file for additional

View File

@ -1,15 +0,0 @@
# 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

@ -1,5 +0,0 @@
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

@ -1,426 +0,0 @@
/*******************************************************************************
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;
}
}

View File

@ -1,21 +0,0 @@
<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>

View File

@ -1,98 +0,0 @@
<!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,6 +1,4 @@
pkgsysconfdir = $(sysconfdir)/$(PACKAGE)
pkgsysconf_DATA = \
sysconf_DATA = \
tinyproxy.conf
EXTRA_DIST = \
@ -10,9 +8,8 @@ edit = sed \
-e 's|@bindir[@]|$(bindir)|g' \
-e 's|@datadir[@]|$(datadir)|g' \
-e 's|@datarootdir[@]|$(datarootdir)|g' \
-e 's|@pkgsysconfdir[@]|$(pkgsysconfdir)|g' \
-e 's|@sysconfdir[@]|$(sysconfdir)|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 tinyproxy.conf(5) manual page.
## parameters, see the tinproxy.conf(5) manual page.
##
#
@ -56,8 +56,8 @@ Timeout 600
# /usr/share/tinyproxy
# /etc/tinyproxy
#
#ErrorFile 404 "@pkgdatadir@/404.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"
@ -90,8 +90,7 @@ StatFile "@pkgdatadir@/stats.html"
# 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. If neither Syslog nor LogFile are specified, output goes
# to stdout.
# exclusive.
#
#LogFile "@localstatedir@/log/tinyproxy/tinyproxy.log"
@ -103,7 +102,7 @@ StatFile "@pkgdatadir@/stats.html"
#Syslog On
#
# LogLevel: Warning
# LogLevel:
#
# Set the logging level. Allowed settings are:
# Critical (least verbose)
@ -122,9 +121,8 @@ 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 "@runstatedir@/tinyproxy/tinyproxy.pid"
#PidFile "@localstatedir@/run/tinyproxy/tinyproxy.pid"
#
# XTinyproxy: Tell Tinyproxy to include the X-Tinyproxy header, which
@ -140,37 +138,25 @@ LogLevel Info
# 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 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"
# upstream testproxy:8008 ".test.domain.invalid"
# upstream testproxy:8008 ".our_testbed.example.com"
# upstream testproxy:8008 "192.168.128.0/255.255.254.0"
#
# # no upstream proxy for internal websites and unqualified hosts
# 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 "."
# 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 "."
#
# # connection to these boxes go through their DMZ firewalls
# upstream http cust1_firewall:8008 "testbed_for_cust1"
# upstream http cust2_firewall:8008 "testbed_for_cust2"
# upstream cust1_firewall:8008 "testbed_for_cust1"
# upstream cust2_firewall:8008 "testbed_for_cust2"
#
# # default upstream is internet firewall
# 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
# upstream firewall.internal.example.com:80
#
# The LAST matching rule wins the route decision. As you can see, you
# can use a host, or a domain:
@ -180,7 +166,7 @@ LogLevel Info
# IP/bits matches network/mask
# IP/mask matches network/mask
#
#Upstream http some.remote.proxy:port
#Upstream some.remote.proxy:port
#
# MaxClients: This is the absolute highest number of threads which will
@ -189,6 +175,30 @@ LogLevel Info
#
MaxClients 100
#
# MinSpareServers/MaxSpareServers: These settings set the upper and
# lower limit for the number of spare servers which should be available.
#
# If the number of spare servers falls below MinSpareServers then new
# server processes will be spawned. If the number of servers exceeds
# MaxSpareServers then the extras will be killed off.
#
MinSpareServers 5
MaxSpareServers 20
#
# StartServers: The number of servers to start initially.
#
StartServers 10
#
# MaxRequestsPerChild: The number of connections a thread will handle
# before it is killed. In practise this should be set to 0, which
# disables thread reaping. If you do notice problems with memory
# leakage, then set this to something like 10000.
#
MaxRequestsPerChild 0
#
# Allow: Customization of authorization controls. If there are any
# access control keywords then the default action is to DENY. Otherwise,
@ -198,19 +208,6 @@ MaxClients 100
# 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
@ -239,7 +236,7 @@ ViaProxyName "tinyproxy"
#
# Filter: This allows you to specify the location of the filter file.
#
#Filter "@pkgsysconfdir@/filter"
#Filter "@sysconfdir@/filter"
#
# FilterURLs: Filter based on URLs rather than domains.
@ -247,9 +244,10 @@ ViaProxyName "tinyproxy"
#FilterURLs On
#
# FilterType: Use bre (default), ere, or fnmatch for filtering.
# FilterExtended: Use POSIX Extended regular expressions rather than
# basic.
#
#FilterType fnmatch
#FilterExtended On
#
# FilterCaseSensitive: Use case sensitive regular expressions.
@ -285,12 +283,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.
# allowed (which is not very secure.)
#
# 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
@ -327,3 +325,6 @@ ViaProxyName "tinyproxy"
# If not set then no rewriting occurs.
#
#ReverseBaseURL "http://localhost:8888/"

View File

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

103
m4macros/typecheck.m4 Normal file
View File

@ -0,0 +1,103 @@
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
])

View File

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

View File

@ -1,10 +0,0 @@
#!/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}"

View File

@ -1,19 +0,0 @@
#!/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,25 +15,22 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
pkgsysconfdir = $(sysconfdir)/$(PACKAGE)
bin_PROGRAMS = tinyproxy
sbin_PROGRAMS = tinyproxy
AM_CPPFLAGS = \
-DSYSCONFDIR=\"${pkgsysconfdir}\" \
-DSYSCONFDIR=\"${sysconfdir}\" \
-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 \
@ -45,27 +42,12 @@ 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@ -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
tinyproxy_LDADD = @ADDITIONAL_OBJECTS@

229
src/acl.c
View File

@ -28,8 +28,17 @@
#include "log.h"
#include "network.h"
#include "sock.h"
#include "sblist.h"
#include "hostspec.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
};
/*
* Hold the information about a particular access control. We store
@ -38,17 +47,74 @@
*/
struct acl_s {
acl_access_t access;
struct hostspec h;
enum acl_type type;
union {
char *string;
struct {
unsigned char octet[IPV6_LEN];
unsigned char mask[IPV6_LEN];
} ip;
} address;
};
/*
* 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[], 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;
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 * 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(acl_list_t *access_list)
static int init_access_list(vector_t *access_list)
{
if (!*access_list) {
*access_list = sblist_new(sizeof(struct acl_s), 16);
*access_list = vector_create ();
if (!*access_list) {
log_message (LOG_ERR,
"Unable to allocate memory for access list");
@ -68,26 +134,75 @@ static int init_access_list(acl_list_t *access_list)
* -1 on failure
* 0 otherwise.
*/
int
insert_acl (char *location, acl_access_t access_type, acl_list_t *access_list)
int insert_acl (char *location, acl_access_t access_type, vector_t *access_list)
{
struct acl_s acl;
int ret;
char *p, ip_dst[IPV6_LEN];
assert (location != NULL);
if (init_access_list(access_list) != 0)
ret = init_access_list(access_list);
if (ret != 0) {
return -1;
}
/*
* Start populating the access control structure.
*/
memset (&acl, 0, sizeof (struct acl_s));
acl.access = access_type;
if(hostspec_parse(location, &acl.h) || acl.h.type == HST_NONE)
return -1;
if(!sblist_add(*access_list, &acl)) return -1;
return 0;
/*
* 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) {
char dst[sizeof(struct in6_addr)];
int v6;
/*
* 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)
return -1;
acl.type = ACL_NUMERIC;
memcpy (acl.address.ip.octet, ip_dst, IPV6_LEN);
/* 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
(p + 1, v6, &(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;
}
/*
@ -100,27 +215,28 @@ insert_acl (char *location, acl_access_t access_type, acl_list_t *access_list)
* -1 if no tests match, so skip
*/
static int
acl_string_processing (struct acl_s *acl, const char *ip_address,
union sockaddr_union *addr, char *string_addr)
acl_string_processing (struct acl_s *acl,
const char *ip_address, const char *string_address)
{
int match;
struct addrinfo hints, *res, *ressave;
size_t test_length, match_length;
char ipbuf[512];
assert (acl && acl->h.type == HST_STRING);
assert (acl && acl->type == ACL_STRING);
assert (ip_address && strlen (ip_address) > 0);
assert (string_address && strlen (string_address) > 0);
/*
* If the first character of the ACL string is a period, we need to
* do a string based test only; otherwise, we can do a reverse
* lookup test as well.
*/
if (acl->h.address.string[0] != '.') {
if (acl->address.string[0] != '.') {
memset (&hints, 0, sizeof (struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo (acl->h.address.string, NULL, &hints, &res) != 0)
if (getaddrinfo (acl->address.string, NULL, &hints, &res) != 0)
goto STRING_TEST;
ressave = res;
@ -145,16 +261,8 @@ acl_string_processing (struct acl_s *acl, const char *ip_address,
}
STRING_TEST:
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);
test_length = strlen (string_address);
match_length = strlen (acl->address.string);
/*
* If the string length is shorter than AC string, return a -1 so
@ -164,8 +272,8 @@ STRING_TEST:
return -1;
if (strcasecmp
(string_addr + (test_length - match_length),
acl->h.address.string) == 0) {
(string_address + (test_length - match_length),
acl->address.string) == 0) {
if (acl->access == ACL_DENY)
return 0;
else
@ -184,16 +292,20 @@ STRING_TEST:
* 0 IP address is denied
* -1 neither allowed nor denied.
*/
static int check_numeric_acl (const struct acl_s *acl, uint8_t addr[IPV6_LEN])
static int check_numeric_acl (const struct acl_s *acl, const char *ip)
{
uint8_t x, y;
uint8_t addr[IPV6_LEN], x, y;
int i;
assert (acl && acl->h.type == HST_NUMERIC);
assert (acl && acl->type == ACL_NUMERIC);
assert (ip && strlen (ip) > 0);
if (full_inet_pton (ip, &addr) <= 0)
return -1;
for (i = 0; i != IPV6_LEN; ++i) {
x = addr[i] & acl->h.address.ip.mask[i];
y = acl->h.address.ip.network[i];
x = addr[i] & acl->address.ip.mask[i];
y = acl->address.ip.octet[i] & acl->address.ip.mask[i];
/* If x and y don't match, the IP addresses don't match */
if (x != y)
@ -211,18 +323,14 @@ static int check_numeric_acl (const struct acl_s *acl, uint8_t addr[IPV6_LEN])
* 1 if allowed
* 0 if denied
*/
int check_acl (const char *ip, union sockaddr_union *addr, acl_list_t access_list)
int check_acl (const char *ip, const char *host, vector_t access_list)
{
struct acl_s *acl;
int perm = 0, is_numeric_addr;
int perm = 0;
size_t i;
char string_addr[HOSTNAME_LENGTH];
uint8_t numeric_addr[IPV6_LEN];
assert (ip != NULL);
assert (addr != NULL);
string_addr[0] = 0;
assert (host != NULL);
/*
* If there is no access list allow everything.
@ -230,26 +338,17 @@ int check_acl (const char *ip, union sockaddr_union *addr, acl_list_t access_lis
if (!access_list)
return 1;
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);
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);
break;
case HST_NUMERIC:
case ACL_NUMERIC:
if (ip[0] == '\0')
continue;
perm = is_numeric_addr
? check_numeric_acl (acl, numeric_addr)
: -1;
break;
case HST_NONE:
perm = -1;
perm = check_numeric_acl (acl, ip);
break;
}
@ -266,12 +365,12 @@ int check_acl (const char *ip, union sockaddr_union *addr, acl_list_t access_lis
/*
* Deny all connections by default.
*/
log_message (LOG_NOTICE, "Unauthorized connection from \"%s\".",
ip);
log_message (LOG_NOTICE, "Unauthorized connection from \"%s\" [%s].",
host, ip);
return 0;
}
void flush_access_list (acl_list_t access_list)
void flush_access_list (vector_t access_list)
{
struct acl_s *acl;
size_t i;
@ -285,12 +384,12 @@ void flush_access_list (acl_list_t access_list)
* before we can free the acl entries themselves.
* A hierarchical memory system would be great...
*/
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);
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);
}
}
sblist_free (access_list);
vector_delete (access_list);
}

View File

@ -21,16 +21,14 @@
#ifndef TINYPROXY_ACL_H
#define TINYPROXY_ACL_H
#include "sblist.h"
#include "sock.h"
#include "vector.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,
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);
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);
#endif

View File

@ -23,26 +23,26 @@
#include "main.h"
#include "anonymous.h"
#include "hsearch.h"
#include "hashmap.h"
#include "heap.h"
#include "log.h"
#include "conf.h"
short int is_anonymous_enabled (struct config_s *conf)
short int is_anonymous_enabled (void)
{
return (conf->anonymous_map != NULL) ? 1 : 0;
return (config.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 (struct config_s *conf, const char *s)
int anonymous_search (const char *s)
{
assert (s != NULL);
assert (conf->anonymous_map != NULL);
assert (config.anonymous_map != NULL);
return !!htab_find (conf->anonymous_map, s);
return hashmap_search (config.anonymous_map, s);
}
/*
@ -51,21 +51,23 @@ int anonymous_search (struct config_s *conf, const char *s)
* Return -1 if there is an error, otherwise a 0 is returned if the insert was
* successful.
*/
int anonymous_insert (struct config_s *conf, char *s)
int anonymous_insert (const char *s)
{
char data = 1;
assert (s != NULL);
if (!conf->anonymous_map) {
conf->anonymous_map = htab_create (32);
if (!conf->anonymous_map)
if (!config.anonymous_map) {
config.anonymous_map = hashmap_create (32);
if (!config.anonymous_map)
return -1;
}
if (htab_find (conf->anonymous_map, s)) {
/* The key was already found. */
if (hashmap_search (config.anonymous_map, s) > 0) {
/* The key was already found, so return a positive number. */
return 0;
}
/* Insert the new key */
return htab_insert (conf->anonymous_map, s, HTV_N(1)) ? 0 : -1;
return hashmap_insert (config.anonymous_map, s, &data, sizeof (data));
}

View File

@ -21,8 +21,8 @@
#ifndef _TINYPROXY_ANONYMOUS_H_
#define _TINYPROXY_ANONYMOUS_H_
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);
extern short int is_anonymous_enabled (void);
extern int anonymous_search (const char *s);
extern int anonymous_insert (const char *s);
#endif

View File

@ -1,57 +0,0 @@
/* 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;
}

View File

@ -1,29 +0,0 @@
/* 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

View File

@ -1,97 +0,0 @@
/* 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;
}

View File

@ -1,35 +0,0 @@
/* 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

@ -31,145 +31,266 @@
#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 sblist* listen_fds;
static vector_t listen_fds;
struct client {
union sockaddr_union addr;
/*
* 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 child {
pthread_t thread;
struct client client;
struct conn_s conn;
volatile int done;
};
/*
* 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;
static void* child_thread(void* data)
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)
{
struct child *c = data;
handle_connection (&c->conn, &c->client.addr);
c->done = 1;
return NULL;
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;
}
static sblist *childs;
static void collect_threads(void)
static void _child_lock_wait (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++;
}
int rc;
while ((rc = fcntl (lock_fd, F_SETLKW, &lock_it)) < 0) {
if (errno == EINTR)
continue;
else
return;
}
}
static void _child_lock_release (void)
{
if (fcntl (lock_fd, F_SETLKW, &unlock_it) < 0)
return;
}
/* END OF LOCKING SECTION */
#define SERVER_INC() do { \
SERVER_COUNT_LOCK(); \
++(*servers_waiting); \
DEBUG2("INC: servers_waiting: %d", *servers_waiting); \
SERVER_COUNT_UNLOCK(); \
} while (0)
#define SERVER_DEC() do { \
SERVER_COUNT_LOCK(); \
assert(*servers_waiting > 0); \
--(*servers_waiting); \
DEBUG2("DEC: servers_waiting: %d", *servers_waiting); \
SERVER_COUNT_UNLOCK(); \
} while (0)
/*
* Set the configuration values for the various child related settings.
*/
short int child_configure (child_config_t type, unsigned int val)
{
switch (type) {
case CHILD_MAXCLIENTS:
child_config.maxclients = val;
break;
case CHILD_MAXSPARESERVERS:
child_config.maxspareservers = val;
break;
case CHILD_MINSPARESERVERS:
child_config.minspareservers = val;
break;
case CHILD_STARTSERVERS:
child_config.startservers = val;
break;
case CHILD_MAXREQUESTSPERCHILD:
child_config.maxrequestsperchild = val;
break;
default:
DEBUG2 ("Invalid type (%d)", type);
return -1;
}
return 0;
}
/**
* child signal handler for sighup
*/
static void child_sighup_handler (int sig)
{
if (sig == SIGHUP) {
/*
* Ignore the return value of reload_config for now.
* This should actually be handled somehow...
*/
reload_config ();
#ifdef FILTER_ENABLE
filter_reload ();
#endif /* FILTER_ENABLE */
}
}
/*
* This is the main loop accepting new connections.
* This is the main (per child) loop.
*/
void child_main_loop (void)
static void child_main (struct child_s *ptr)
{
int connfd;
union sockaddr_union cliaddr_storage;
struct sockaddr *cliaddr = (void*) &cliaddr_storage;
struct sockaddr *cliaddr;
socklen_t clilen;
int nfds = sblist_getsize(listen_fds);
pollfd_struct *fds = safecalloc(nfds, sizeof *fds);
fd_set rfds;
int maxfd = 0;
ssize_t i;
int ret, listenfd, was_full = 0;
pthread_attr_t *attrp, attr;
struct child *child;
int ret;
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;
cliaddr = (struct sockaddr *)
safemalloc (sizeof(struct sockaddr_storage));
if (!cliaddr) {
log_message (LOG_CRIT,
"Could not allocate memory for child address.");
exit (0);
}
ptr->connects = 0;
srand(time(NULL));
/*
* We have to wait for connections on multiple fds,
* so use select/poll/whatever.
* so use select.
*/
while (!config->quit) {
collect_threads();
FD_ZERO(&rfds);
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;
for (i = 0; i < vector_length(listen_fds); i++) {
int *fd = (int *) vector_getentry(listen_fds, i, NULL);
ret = socket_nonblocking(*fd);
if (ret != 0) {
log_message(LOG_ERR, "Failed to set the listening "
"socket %d to non-blocking: %s",
fd, strerror(errno));
exit(1);
}
was_full = 0;
listenfd = -1;
FD_SET(*fd, &rfds);
maxfd = max(maxfd, *fd);
}
/* Handle log rotation if it was requested */
if (received_sighup) {
while (!config.quit) {
int listenfd = -1;
reload_config (1);
ptr->status = T_WAITING;
#ifdef FILTER_ENABLE
filter_reload ();
#endif /* FILTER_ENABLE */
received_sighup = FALSE;
}
ret = mypoll(fds, nfds, -1);
clilen = sizeof(struct sockaddr_storage);
ret = select(maxfd + 1, &rfds, NULL, NULL, NULL);
if (ret == -1) {
if (errno == EINTR) {
continue;
}
log_message (LOG_ERR, "error calling " SELECT_OR_POLL ": %s",
log_message (LOG_ERR, "error calling select: %s",
strerror(errno));
continue;
exit(1);
} else if (ret == 0) {
log_message (LOG_WARNING, "Strange: " SELECT_OR_POLL " returned 0 "
log_message (LOG_WARNING, "Strange: select returned 0 "
"but we did not specify a timeout...");
continue;
}
for (i = 0; i < nfds; i++) {
if (fds[i].revents & MYPOLL_READ) {
for (i = 0; i < vector_length(listen_fds); i++) {
int *fd = (int *) vector_getentry(listen_fds, i, NULL);
if (FD_ISSET(*fd, &rfds)) {
/*
* only accept the connection on the first
* fd that we find readable. - fair?
*/
listenfd = fds[i].fd;
listenfd = *fd;
break;
}
}
if (listenfd == -1) {
log_message(LOG_WARNING, "Strange: None of our listen "
"fds was readable after " SELECT_OR_POLL);
"fds was readable after select");
continue;
}
ret = socket_blocking(listenfd);
if (ret != 0) {
log_message(LOG_ERR, "Failed to set listening "
"socket %d to blocking for accept: %s",
listenfd, strerror(errno));
exit(1);
}
/*
* 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...
@ -181,41 +302,225 @@ void child_main_loop (void)
continue;
}
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;
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->done = 0;
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 ();
if (!sblist_add(childs, &child)) {
free(child);
goto oom;
break;
} else {
SERVER_COUNT_UNLOCK ();
}
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);
}
if (pthread_create(&child->thread, attrp, child_thread, child) != 0) {
sblist_delete(childs, sblist_getsize(childs) -1);
free(child);
goto oom;
}
SERVER_INC ();
}
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;
}
}
safefree(fds);
}
/*
@ -223,48 +528,27 @@ oom:
*/
void child_kill_children (int sig)
{
size_t i, tries = 0;
unsigned int i;
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)
);
for (i = 0; i != child_config.maxclients; i++) {
if (child_ptr[i].status != T_EMPTY)
kill (child_ptr[i].tid, sig);
}
}
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)
int child_listening_sockets(vector_t listen_addrs, uint16_t port)
{
int ret;
size_t i;
ssize_t i;
assert (port > 0);
if (listen_fds == NULL) {
listen_fds = sblist_new(sizeof(int), 16);
listen_fds = vector_create();
if (listen_fds == NULL) {
log_message (LOG_ERR, "Could not create the list "
"of listening fds");
@ -272,7 +556,8 @@ int child_listening_sockets(sblist *listen_addrs, uint16_t port)
}
}
if (!listen_addrs || !sblist_getsize(listen_addrs))
if ((listen_addrs == NULL) ||
(vector_length(listen_addrs) == 0))
{
/*
* no Listen directive:
@ -282,17 +567,17 @@ int child_listening_sockets(sblist *listen_addrs, uint16_t port)
return ret;
}
for (i = 0; i < sblist_getsize(listen_addrs); i++) {
char **addr;
for (i = 0; i < vector_length(listen_addrs); i++) {
const char *addr;
addr = sblist_get(listen_addrs, i);
if (!addr || !*addr) {
addr = (char *)vector_getentry(listen_addrs, i, NULL);
if (addr == NULL) {
log_message(LOG_WARNING,
"got NULL from listen_addrs - skipping");
continue;
}
ret = listen_sock(*addr, port, listen_fds);
ret = listen_sock(addr, port, listen_fds);
if (ret != 0) {
return ret;
}
@ -303,14 +588,14 @@ int child_listening_sockets(sblist *listen_addrs, uint16_t port)
void child_close_sock (void)
{
size_t i;
ssize_t i;
for (i = 0; i < sblist_getsize(listen_fds); i++) {
int *fd = sblist_get(listen_fds, i);
for (i = 0; i < vector_length(listen_fds); i++) {
int *fd = (int *) vector_getentry(listen_fds, i, NULL);
close (*fd);
}
sblist_free(listen_fds);
vector_delete(listen_fds);
listen_fds = NULL;
}

View File

@ -21,7 +21,7 @@
#ifndef TINYPROXY_CHILD_H
#define TINYPROXY_CHILD_H
#include "sblist.h"
#include "vector.h"
typedef enum {
CHILD_MAXCLIENTS,
@ -32,11 +32,10 @@ typedef enum {
} child_config_t;
extern short int child_pool_create (void);
extern int child_listening_sockets (sblist *listen_addrs, uint16_t port);
extern int child_listening_sockets (vector_t 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,67 +32,134 @@
/*
* Include standard headers which are used through-out tinyproxy
*/
/* 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>
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
# 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 */
#endif
#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_MALLOC_H
# include <malloc.h>
#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
# 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>
# 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
/*

View File

@ -1,72 +0,0 @@
#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

View File

@ -1,63 +0,0 @@
%{
#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
%%

View File

@ -1,55 +0,0 @@
#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,9 +22,8 @@
#ifndef TINYPROXY_CONF_H
#define TINYPROXY_CONF_H
#include "hsearch.h"
#include "sblist.h"
#include "acl.h"
#include "hashmap.h"
#include "vector.h"
/*
* Stores a HTTP header created using the AddHeader directive.
@ -38,20 +37,21 @@ 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 */
unsigned int port;
char *stathost;
unsigned int godaemon; /* boolean */
unsigned int quit; /* boolean */
unsigned int maxclients;
char *user;
char *group;
sblist *listen_addrs;
vector_t listen_addrs;
#ifdef FILTER_ENABLE
char *filter;
unsigned int filter_opts; /* enum filter_options */
unsigned int filter_url; /* boolean */
unsigned int filter_extended; /* boolean */
unsigned int filter_casesensitive; /* boolean */
#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;
sblist *bind_addrs;
char *bind_address;
unsigned int bindsame;
/*
@ -80,7 +80,7 @@ struct config_s {
/*
* Error page support. Map error numbers to file paths.
*/
struct htab *errorpages;
hashmap_t errorpages;
/*
* Error page to be displayed if appropriate page cannot be located
@ -93,28 +93,28 @@ struct config_s {
*/
char *statpage;
acl_list_t access_list;
vector_t access_list;
/*
* Store the list of port allowed by CONNECT.
*/
sblist *connect_ports;
vector_t connect_ports;
/*
* Map of headers which should be let through when the
* anonymous feature is turned on.
*/
struct htab *anonymous_map;
hashmap_t anonymous_map;
/*
* Extra headers to be added to outgoing HTTP requests.
*/
sblist* add_headers;
vector_t add_headers;
};
extern int reload_config_file (const char *config_fname, struct config_s *conf);
extern int reload_config_file (const char *config_fname, struct config_s *conf,
struct config_s *defaults);
int config_init (void);
void free_config (struct config_s *conf);
int config_compile_regex (void);
#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, sblist **connect_ports)
void add_connect_port_allowed (int port, vector_t *connect_ports)
{
if (!*connect_ports) {
*connect_ports = sblist_new (sizeof(int), 16);
*connect_ports = vector_create ();
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, sblist **connect_ports)
log_message (LOG_INFO,
"Adding Port [%d] to the list allowed by CONNECT", port);
sblist_add (*connect_ports, &port);
vector_append (*connect_ports, (void **) &port, sizeof (port));
}
/*
@ -47,7 +47,7 @@ void add_connect_port_allowed (int port, sblist **connect_ports)
* Returns: 1 if allowed
* 0 if denied
*/
int check_allowed_connect_ports (int port, sblist *connect_ports)
int check_allowed_connect_ports (int port, vector_t connect_ports)
{
size_t i;
int *data;
@ -59,8 +59,8 @@ int check_allowed_connect_ports (int port, sblist *connect_ports)
if (!connect_ports)
return 1;
for (i = 0; i < sblist_getsize (connect_ports); ++i) {
data = sblist_get (connect_ports, i);
for (i = 0; i != (size_t) vector_length (connect_ports); ++i) {
data = (int *) vector_getentry (connect_ports, i, NULL);
if (data && *data == port)
return 1;
}
@ -71,7 +71,7 @@ int check_allowed_connect_ports (int port, sblist *connect_ports)
/**
* Free a connect_ports list.
*/
void free_connect_ports_list (sblist *connect_ports)
void free_connect_ports_list (vector_t connect_ports)
{
sblist_free (connect_ports);
vector_delete (connect_ports);
}

View File

@ -22,10 +22,10 @@
#define _TINYPROXY_CONNECT_PORTS_H_
#include "common.h"
#include "sblist.h"
#include "vector.h"
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);
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);
#endif /* _TINYPROXY_CONNECT_PORTS_ */

View File

@ -30,20 +30,14 @@
#include "log.h"
#include "stats.h"
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,
struct conn_s *initialize_conn (int client_fd, const char *ipaddr,
const char *string_addr,
const char *sock_ipaddr)
{
struct conn_s *connptr;
struct buffer_s *cbuffer, *sbuffer;
assert (connptr->client_fd >= 0);
assert (client_fd >= 0);
/*
* Allocate the memory for all the internal components
@ -54,16 +48,48 @@ int conn_init_contents (struct conn_s *connptr, 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);
return 1;
#ifdef REVERSE_SUPPORT
connptr->reversepath = NULL;
#endif
return connptr;
error_exit:
/*
@ -74,10 +100,10 @@ error_exit:
if (sbuffer)
delete_buffer (sbuffer);
return 0;
return NULL;
}
void conn_destroy_contents (struct conn_s *connptr)
void destroy_conn (struct conn_s *connptr)
{
assert (connptr != NULL);
@ -85,12 +111,10 @@ void conn_destroy_contents (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);
@ -100,16 +124,8 @@ void conn_destroy_contents (struct conn_s *connptr)
if (connptr->request_line)
safefree (connptr->request_line);
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_variables)
hashmap_delete (connptr->error_variables);
if (connptr->error_string)
safefree (connptr->error_string);
@ -118,11 +134,15 @@ void conn_destroy_contents (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 "hsearch.h"
#include "hashmap.h"
/*
* Connection Definition
@ -45,7 +45,7 @@ struct conn_s {
* This structure stores key -> value mappings for substitution
* in the error HTML files.
*/
struct htab *error_variables;
hashmap_t error_variables;
int error_number;
char *error_string;
@ -62,9 +62,10 @@ struct conn_s {
char *server_ip_addr;
/*
* Store the client's IP information
* Store the client's IP and hostname information
*/
char *client_ip_addr;
char *client_string_addr;
/*
* Store the incoming request's HTTP protocol.
@ -87,13 +88,12 @@ struct conn_s {
struct upstream *upstream_proxy;
};
/* 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,
/*
* Functions for the creation and destruction of a connection structure.
*/
extern struct conn_s *initialize_conn (int client_fd, const char *ipaddr,
const char *string_addr,
const char *sock_ipaddr);
extern void conn_destroy_contents (struct conn_s *connptr);
extern void destroy_conn (struct conn_s *connptr);
#endif

View File

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

495
src/hashmap.c Normal file
View File

@ -0,0 +1,495 @@
/* 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 {
uint32_t seed;
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.
*
* This is Dan Bernstein's hash function as described, for example, here:
* http://www.cse.yorku.ca/~oz/hash.html
*
* If any of the arguments are invalid a negative number is returned.
*/
static int hashfunc (const char *key, unsigned int size, uint32_t seed)
{
uint32_t hash;
if (key == NULL)
return -EINVAL;
if (size == 0)
return -ERANGE;
for (hash = seed; *key != '\0'; key++) {
hash = ((hash << 5) + hash) ^ tolower (*key);
}
/* 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->seed = (uint32_t)rand();
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, map->seed);
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, map->seed);
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, map->seed);
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, map->seed);
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;
}

120
src/hashmap.h Normal file
View File

@ -0,0 +1,120 @@
/* 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,3 +97,61 @@ 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,4 +52,10 @@ 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

View File

@ -1,179 +0,0 @@
#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;
}

View File

@ -1,26 +0,0 @@
#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

View File

@ -1,222 +0,0 @@
/*
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;
}

View File

@ -1,23 +0,0 @@
#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,9 +30,6 @@
#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.
@ -40,25 +37,19 @@
#define ERRORNUM_BUFSIZE 8 /* this is more than required */
#define ERRPAGES_BUCKETCOUNT 16
int add_new_errorpage (struct config_s *conf, char *filepath,
unsigned int errornum)
int add_new_errorpage (char *filepath, unsigned int errornum)
{
char errornbuf[ERRORNUM_BUFSIZE], *k;
char errornbuf[ERRORNUM_BUFSIZE];
if (!conf->errorpages)
conf->errorpages = htab_create (ERRPAGES_BUCKETCOUNT);
if (!conf->errorpages)
config.errorpages = hashmap_create (ERRPAGES_BUCKETCOUNT);
if (!config.errorpages)
return (-1);
snprintf (errornbuf, ERRORNUM_BUFSIZE, "%u", errornum);
k = safestrdup(errornbuf);
if (!k) return -1;
if (!htab_insert (conf->errorpages, k, HTV_P(filepath))) {
safefree(k);
if (hashmap_insert (config.errorpages, errornbuf,
filepath, strlen (filepath) + 1) < 0)
return (-1);
}
return (0);
}
@ -68,51 +59,49 @@ int add_new_errorpage (struct config_s *conf, char *filepath,
*/
static char *get_html_file (unsigned int errornum)
{
hashmap_iter result_iter;
char errornbuf[ERRORNUM_BUFSIZE];
htab_value *hv;
char *key;
static char *val;
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);
hv = htab_find (config->errorpages, errornbuf);
if (!hv) return (config->errorpage_undef);
return hv->p;
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);
}
static char *lookup_variable (struct htab *map, const char *varname) {
htab_value *v;
v = htab_find(map, varname);
return v ? v->p : 0;
}
/*
* 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 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;
}
}
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);
}
/*
@ -121,34 +110,80 @@ static void varsubst_sendline(struct conn_s *connptr, regex_t *re, char *p) {
int
send_html_file (FILE *infile, struct conn_s *connptr)
{
regex_t re;
char *inbuf = safemalloc (4096);
(void) regcomp(&re, "{[a-z]\\{1,32\\}}", 0);
char *inbuf;
char *varstart = NULL;
char *p;
const char *varval;
int in_variable = 0;
int r = 0;
while (fgets (inbuf, 4096, infile)) {
varsubst_sendline(connptr, &re, inbuf);
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;
}
regfree (&re);
safefree (inbuf);
return 1;
return r;
}
int send_http_headers (
struct conn_s *connptr, int code,
const char *message, const char *extra)
int send_http_headers (struct conn_s *connptr, int code, const char *message)
{
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";
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";
return (write_message (connptr->client_fd, headers,
connptr->protocol.major != 1 ? 0 : connptr->protocol.minor,
code, message, PACKAGE,
extra));
code, message, PACKAGE, VERSION));
}
/*
@ -169,47 +204,20 @@ int send_http_error_message (struct conn_s *connptr)
"<h1>%s</h1>\n"
"<p>%s</p>\n"
"<hr />\n"
"<p><em>Generated by %s.</em></p>\n" "</body>\n"
"<p><em>Generated by %s version %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, auth_str_add ? auth_str_add : "");
if (auth_str_add) safefree (auth_str_add);
connptr->error_string);
error_file = get_html_file (connptr->error_number);
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");
if (!(infile = fopen (error_file, "r"))) {
char *detail = lookup_variable (connptr, "detail");
return (write_message (connptr->client_fd, fallback_error,
connptr->error_number,
connptr->error_string,
connptr->error_string,
detail, PACKAGE));
detail, PACKAGE, VERSION));
}
ret = send_html_file (infile, connptr);
@ -226,25 +234,14 @@ 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 =
htab_create (ERRVAR_BUCKETCOUNT)))
hashmap_create (ERRVAR_BUCKETCOUNT)))
return (-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;
return hashmap_insert (connptr->error_variables, key, val,
strlen (val) + 1);
}
#define ADD_VAR_RET(x, y) \
@ -263,7 +260,6 @@ 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);
@ -271,6 +267,7 @@ 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
@ -279,11 +276,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_r (&global_time, &tm_buf));
gmtime (&global_time));
add_error_variable (connptr, "date", timebuf);
add_error_variable (connptr, "website",
"https://tinyproxy.github.io/");
"https://banu.com/tinyproxy/");
add_error_variable (connptr, "version", VERSION);
add_error_variable (connptr, "package", PACKAGE);

View File

@ -23,9 +23,8 @@
/* Forward declaration */
struct conn_s;
struct config_s;
extern int add_new_errorpage (struct config_s *, char *filepath, unsigned int errornum);
extern int add_new_errorpage (char *filepath, unsigned int errornum);
extern int send_http_error_message (struct conn_s *connptr);
extern int indicate_http_error (struct conn_s *connptr, int number,
const char *message, ...);
@ -33,7 +32,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 *extra);
const char *message);
extern int add_standard_vars (struct conn_s *connptr);
#endif /* !TINYPROXY_HTML_ERROR_H */

View File

@ -232,7 +232,6 @@ 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));
@ -255,11 +254,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_r (&global_time, &tm_buf));
gmtime (&global_time));
write_message (fd, "Date: %s\r\n", timebuf);
/* Output the content-length */
write_message (fd, "Content-length: %lu\r\n", (unsigned long) msg->body.length);
write_message (fd, "Content-length: %u\r\n", msg->body.length);
/* Write the separator between the headers and body */
safe_write (fd, "\r\n", 2);

View File

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

View File

@ -77,7 +77,16 @@
* don't advocate this, but it could be useful at times.)
*/
#include <syslog.h>
#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
#define LOG_CONN 8 /* extra to log connections without the INFO stuff */
@ -106,6 +115,7 @@ 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);

View File

@ -1,81 +0,0 @@
#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;
}

View File

@ -1,12 +0,0 @@
#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,7 +38,6 @@
#include "heap.h"
#include "filter.h"
#include "child.h"
#include "loop.h"
#include "log.h"
#include "reqs.h"
#include "sock.h"
@ -48,18 +47,10 @@
/*
* Global Structures
*/
struct config_s *config;
static struct config_s configs[2];
static const char* config_file;
struct config_s config;
struct config_s config_defaults;
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
*/
@ -70,14 +61,12 @@ 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:
@ -97,6 +86,37 @@ 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.
*/
@ -111,6 +131,7 @@ 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 */
@ -150,8 +171,8 @@ display_usage (void)
printf (" None\n");
printf ("\n"
"For support and bug reporting instructions, please visit\n"
"<https://tinyproxy.github.io/>.\n");
"For bug reporting instructions, please see:\n"
"<https://banu.com/tinyproxy/>.\n");
}
static int
@ -172,6 +193,56 @@ get_id (char *str)
return atoi (str);
}
/**
* process_cmdline:
* @argc: argc as passed to main()
* @argv: argv as passed to main()
*
* This function parses command line arguments.
**/
static void
process_cmdline (int argc, char **argv, struct config_s *conf)
{
int opt;
while ((opt = getopt (argc, argv, "c:vldh")) != EOF) {
switch (opt) {
case 'v':
display_version ();
exit (EX_OK);
case 'l':
display_license ();
exit (EX_OK);
case 'd':
conf->godaemon = FALSE;
break;
case 'c':
if (conf->config_file != NULL) {
safefree (conf->config_file);
}
conf->config_file = safestrdup (optarg);
if (!conf->config_file) {
fprintf (stderr,
"%s: Could not allocate memory.\n",
argv[0]);
exit (EX_SOFTWARE);
}
break;
case 'h':
display_usage ();
exit (EX_OK);
default:
display_usage ();
exit (EX_USAGE);
}
}
}
/**
* change_user:
* @program: The name of the program. Pass argv[0] here.
@ -183,16 +254,16 @@ get_id (char *str)
static void
change_user (const char *program)
{
if (config->group && strlen (config->group) > 0) {
int gid = get_id (config->group);
if (config.group && strlen (config.group) > 0) {
int gid = get_id (config.group);
if (gid < 0) {
struct group *thisgroup = getgrnam (config->group);
struct group *thisgroup = getgrnam (config.group);
if (!thisgroup) {
fprintf (stderr,
"%s: Unable to find group \"%s\".\n",
program, config->group);
program, config.group);
exit (EX_NOUSER);
}
@ -202,7 +273,7 @@ change_user (const char *program)
if (setgid (gid) < 0) {
fprintf (stderr,
"%s: Unable to change to group \"%s\".\n",
program, config->group);
program, config.group);
exit (EX_NOPERM);
}
@ -217,19 +288,19 @@ change_user (const char *program)
#endif
log_message (LOG_INFO, "Now running as group \"%s\".",
config->group);
config.group);
}
if (config->user && strlen (config->user) > 0) {
int uid = get_id (config->user);
if (config.user && strlen (config.user) > 0) {
int uid = get_id (config.user);
if (uid < 0) {
struct passwd *thisuser = getpwnam (config->user);
struct passwd *thisuser = getpwnam (config.user);
if (!thisuser) {
fprintf (stderr,
"%s: Unable to find user \"%s\".\n",
program, config->user);
program, config.user);
exit (EX_NOUSER);
}
@ -239,100 +310,78 @@ change_user (const char *program)
if (setuid (uid) < 0) {
fprintf (stderr,
"%s: Unable to change to user \"%s\".\n",
program, config->user);
program, config.user);
exit (EX_NOPERM);
}
log_message (LOG_INFO, "Now running as user \"%s\".",
config->user);
config.user);
}
}
static void initialize_config_defaults (struct config_s *conf)
{
memset (conf, 0, sizeof(*conf));
conf->config_file = safestrdup (SYSCONFDIR "/tinyproxy.conf");
if (!conf->config_file) {
fprintf (stderr, PACKAGE ": Could not allocate memory.\n");
exit (EX_SOFTWARE);
}
conf->godaemon = TRUE;
/*
* Make sure the HTML error pages array is NULL to begin with.
* (FIXME: Should have a better API for all this)
*/
conf->errorpages = NULL;
conf->stathost = safestrdup (TINYPROXY_STATHOST);
conf->idletimeout = MAX_IDLE_TIME;
conf->logf_name = safestrdup (LOCALSTATEDIR "/log/tinyproxy/tinyproxy.log");
conf->pidpath = safestrdup (LOCALSTATEDIR "/run/tinyproxy/tinyproxy.pid");
}
/**
* convenience wrapper around reload_config_file
* that also re-initializes logging.
*/
int reload_config (int reload_logging)
int reload_config (void)
{
int ret, ret2;
struct config_s *c_next = get_next_config();
int ret;
log_message (LOG_NOTICE, "Reloading config file (%s)", config_file);
shutdown_logging ();
if (reload_logging) shutdown_logging ();
ret = reload_config_file (config_file, c_next);
if (ret == 0) {
if(config) free_config (config);
config = c_next;
ret = reload_config_file (config_defaults.config_file, &config,
&config_defaults);
if (ret != 0) {
goto done;
}
ret2 = reload_logging ? setup_logging () : 0;
ret = setup_logging ();
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);
}
done:
return ret;
}
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 " ...");
log_message (LOG_INFO, "Initializing " PACKAGE " ...");
if (config_init()) {
fprintf(stderr, "ERROR: config_init() failed\n");
if (config_compile_regex()) {
exit (EX_SOFTWARE);
}
config_file = SYSCONFDIR "/tinyproxy.conf";
initialize_config_defaults (&config_defaults);
process_cmdline (argc, argv, &config_defaults);
while ((opt = getopt (argc, argv, "c:vdh")) != EOF) {
switch (opt) {
case 'v':
display_version ();
exit (EX_OK);
case 'd':
daemonized = FALSE;
break;
case 'c':
config_file = optarg;
break;
case 'h':
display_usage ();
exit (EX_OK);
default:
display_usage ();
exit (EX_USAGE);
}
}
if (reload_config(0)) {
if (reload_config_file (config_defaults.config_file,
&config,
&config_defaults)) {
exit (EX_SOFTWARE);
}
@ -342,47 +391,37 @@ 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 (config)) {
anonymous_insert (config, safestrdup("Content-Length"));
anonymous_insert (config, safestrdup("Content-Type"));
if (is_anonymous_enabled ()) {
anonymous_insert ("Content-Length");
anonymous_insert ("Content-Type");
}
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.godaemon == TRUE)
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",
if (set_signal_handler (SIGPIPE, SIG_IGN) == SIG_ERR) {
fprintf (stderr, "%s: Could not set the \"SIGPIPE\" signal.\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);
}
#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);
}
/* Switch to a different user if we're running as root */
if (geteuid () == 0)
change_user (argv[0]);
else
log_message (LOG_INFO,
log_message (LOG_WARNING,
"Not running as root, so not changing UID/GID.");
/* Create log file after we drop privileges */
@ -390,44 +429,65 @@ main (int argc, char **argv)
exit (EX_SOFTWARE);
}
/* Create pid file after 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 (child_pool_create () < 0) {
fprintf (stderr,
"%s: Could not create the pool of children.\n",
argv[0]);
exit (EX_SOFTWARE);
}
/* These signals are only for the parent process. */
log_message (LOG_INFO, "Setting the various signals.");
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 (SIGCHLD, takesig) == SIG_ERR) {
fprintf (stderr, "%s: Could not set the \"SIGCHLD\" signal.\n",
argv[0]);
exit (EX_OSERR);
}
loop_records_init();
if (set_signal_handler (SIGTERM, takesig) == SIG_ERR) {
fprintf (stderr, "%s: Could not set the \"SIGTERM\" signal.\n",
argv[0]);
exit (EX_OSERR);
}
if (set_signal_handler (SIGHUP, takesig) == SIG_ERR) {
fprintf (stderr, "%s: Could not set the \"SIGHUP\" signal.\n",
argv[0]);
exit (EX_OSERR);
}
/* Start the main loop */
log_message (LOG_INFO, "Starting main loop. Accepting connections.");
child_main_loop ();
log_message (LOG_NOTICE, "Shutting down.");
log_message (LOG_INFO, "Shutting down.");
child_kill_children (SIGTERM);
child_close_sock ();
child_free_children();
loop_records_destroy();
/* Remove the PID file */
if (config->pidpath != NULL && unlink (config->pidpath) < 0) {
if (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 (int reload_logging);
extern int reload_config (void);
#endif /* __MAIN_H__ */

View File

@ -1,48 +0,0 @@
#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

View File

@ -1,31 +0,0 @@
#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,11 +32,10 @@
* 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 void *buf, size_t count)
ssize_t safe_write (int fd, const char *buffer, size_t count)
{
ssize_t len;
size_t bytestosend;
const char *buffer = buf;
assert (fd >= 0);
assert (buffer != NULL);
@ -68,7 +67,7 @@ ssize_t safe_write (int fd, const void *buf, size_t count)
* Matched pair for safe_write(). If an EINTR occurs, pick up and try
* again.
*/
ssize_t safe_read (int fd, void *buffer, size_t count)
ssize_t safe_read (int fd, char *buffer, size_t count)
{
ssize_t len;
@ -250,10 +249,8 @@ CLEANUP:
* Convert the network address into either a dotted-decimal or an IPv6
* hex string.
*/
const char *get_ip_string (struct sockaddr *sa, char *buf, size_t buflen)
char *get_ip_string (struct sockaddr *sa, char *buf, size_t buflen)
{
const char *result;
assert (sa != NULL);
assert (buf != NULL);
assert (buflen != 0);
@ -264,8 +261,7 @@ const char *get_ip_string (struct sockaddr *sa, char *buf, size_t buflen)
{
struct sockaddr_in *sa_in = (struct sockaddr_in *) sa;
result = inet_ntop (AF_INET, &sa_in->sin_addr, buf,
buflen);
inet_ntop (AF_INET, &sa_in->sin_addr, buf, buflen);
break;
}
case AF_INET6:
@ -273,8 +269,7 @@ const char *get_ip_string (struct sockaddr *sa, char *buf, size_t buflen)
struct sockaddr_in6 *sa_in6 =
(struct sockaddr_in6 *) sa;
result = inet_ntop (AF_INET6, &sa_in6->sin6_addr, buf,
buflen);
inet_ntop (AF_INET6, &sa_in6->sin6_addr, buf, buflen);
break;
}
default:
@ -282,7 +277,7 @@ const char *get_ip_string (struct sockaddr *sa, char *buf, size_t buflen)
return NULL;
}
return result;
return buf;
}
/*

View File

@ -21,13 +21,13 @@
#ifndef TINYPROXY_NETWORK_H
#define TINYPROXY_NETWORK_H
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 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 int write_message (int fd, const char *fmt, ...);
extern ssize_t readline (int fd, char **whole_buffer);
extern const char *get_ip_string (struct sockaddr *sa, char *buf, size_t len);
extern char *get_ip_string (struct sockaddr *sa, char *buf, size_t len);
extern int full_inet_pton (const char *ip, void *dst);
#endif

View File

@ -1,115 +0,0 @@
#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;
}

View File

@ -1,20 +0,0 @@
#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

1032
src/reqs.c

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -34,7 +34,6 @@ 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,
@ -66,17 +65,8 @@ void reversepath_add (const char *path, const char *url,
if (!path)
reverse->path = safestrdup ("/");
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;
}
}
else
reverse->path = safestrdup (path);
reverse->url = safestrdup (url);
@ -93,16 +83,10 @@ 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) {
lu = strlen (url);
lp = strlen (reverse->path);
if ((
(l = lu) == lp-1 ||
(l = lp) <= lu
) &&
!memcmp(url, reverse->path, l))
if (strstr (url, reverse->path) == url)
return reverse;
reverse = reverse->next;
}
@ -127,41 +111,35 @@ void free_reversepath_list (struct reversepath *reverse)
/*
* Rewrite the URL for reverse proxying.
*/
char *reverse_rewrite_url (struct conn_s *connptr, orderedmap hashofheaders,
char *url, int *status)
char *reverse_rewrite_url (struct conn_s *connptr, hashmap_t hashofheaders,
char *url)
{
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) {
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"))) {
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) {
/* 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
@ -178,14 +156,20 @@ char *reverse_rewrite_url (struct conn_s *connptr, orderedmap hashofheaders,
}
}
if (rewrite_url == NULL) {
/* 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);
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,7 +22,6 @@
#define TINYPROXY_REVERSE_PROXY_H
#include "conns.h"
#include "orderedmap.h"
struct reversepath {
struct reversepath *next;
@ -38,7 +37,6 @@ 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,
orderedmap hashofheaders, char *url,
int *status);
hashmap_t hashofheaders, char *url);
#endif

View File

@ -1,80 +0,0 @@
#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--;
}
}

View File

@ -1,92 +0,0 @@
#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,29 +33,6 @@
#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
@ -66,7 +43,6 @@ 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);
@ -75,13 +51,9 @@ bind_socket (int sockfd, const char *addr, int family)
hints.ai_family = family;
hints.ai_socktype = SOCK_STREAM;
/* 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));
/* The local port it not important */
if (getaddrinfo (addr, NULL, &hints, &res) != 0)
return -1;
}
ressave = res;
@ -98,36 +70,6 @@ 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
@ -142,9 +84,6 @@ 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;
@ -154,13 +93,10 @@ 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 address info for %s:%d: %s", host, port, get_gai_error (n));
"opensock: Could not retrieve info for %s", host);
return -1;
}
log_message(LOG_INFO,
"opensock: getaddrinfo returned for %s:%d", host, port);
ressave = res;
do {
sockfd =
@ -175,27 +111,16 @@ int opensock (const char *host, int port, const char *bind_to)
close (sockfd);
continue; /* can't bind, so try again */
}
} else if (config->bind_addrs) {
if (bind_socket_list (sockfd, config->bind_addrs,
res->ai_family) < 0) {
} else if (config.bind_address) {
if (bind_socket (sockfd, config.bind_address,
res->ai_family) < 0) {
close (sockfd);
continue; /* can't bind, so try again */
}
}
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);
}
if (connect (sockfd, res->ai_addr, res->ai_addrlen) == 0)
break; /* success */
}
close (sockfd);
} while ((res = res->ai_next) != NULL);
@ -203,9 +128,8 @@ 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:%d",
host,
port);
"opensock: Could not establish a connection to %s",
host);
return -1;
}
@ -256,7 +180,8 @@ static int listen_on_one_socket(struct addrinfo *ad)
ret = getnameinfo(ad->ai_addr, ad->ai_addrlen,
numerichost, NI_MAXHOST, NULL, 0, flags);
if (ret != 0) {
log_message(LOG_ERR, "getnameinfo failed: %s", get_gai_error (ret));
log_message(LOG_ERR, "error calling getnameinfo: %s",
gai_strerror(errno));
return -1;
}
@ -320,12 +245,11 @@ static int listen_on_one_socket(struct addrinfo *ad)
* 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)
int listen_sock (const char *addr, uint16_t port, vector_t listen_fds)
{
struct addrinfo hints, *result, *rp;
char portstr[6];
int ret = -1;
int n;
assert (port > 0);
assert (listen_fds != NULL);
@ -340,13 +264,10 @@ int listen_sock (const char *addr, uint16_t port, sblist* listen_fds)
snprintf (portstr, sizeof (portstr), "%d", port);
n = getaddrinfo (addr, portstr, &hints, &result);
if (n != 0) {
if (getaddrinfo (addr, portstr, &hints, &result) != 0) {
log_message (LOG_ERR,
"Unable to getaddrinfo() for %s:%d because of %s",
addr,
port,
get_gai_error (n));
"Unable to getaddrinfo() because of %s",
strerror (errno));
return -1;
}
@ -358,7 +279,7 @@ int listen_sock (const char *addr, uint16_t port, sblist* listen_fds)
continue;
}
sblist_add (listen_fds, &listenfd);
vector_append (listen_fds, &listenfd, sizeof(int));
/* success */
ret = 0;
@ -407,9 +328,27 @@ int getsock_ip (int fd, char *ipaddr)
/*
* Return the peer's socket information.
*/
void getpeer_information (union sockaddr_union* addr, char *ipaddr, size_t ipaddr_len)
int getpeer_information (int fd, char *ipaddr, char *string_addr)
{
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);
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);
}

View File

@ -28,37 +28,15 @@
#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;
};
#include "vector.h"
extern int opensock (const char *host, int port, const char *bind_to);
extern int listen_sock (const char *addr, uint16_t port, sblist* listen_fds);
extern int listen_sock (const char *addr, uint16_t port, vector_t 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 void getpeer_information (union sockaddr_union *addr, char *ipaddr, size_t ipaddr_len);
extern int getpeer_information (int fd, char *ipaddr, char *string_addr);
#endif

View File

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

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 *) saferealloc (*url, len);
*url = (char *) safemalloc (len);
if (*url == NULL)
return -1;
@ -53,27 +53,22 @@ static int build_url (char **url, const char *host, int port, const char *path)
}
int
do_transparent_proxy (struct conn_s *connptr, orderedmap hashofheaders,
do_transparent_proxy (struct conn_s *connptr, hashmap_t hashofheaders,
struct request_s *request, struct config_s *conf,
char **url)
{
socklen_t length;
char *data;
size_t ulen = strlen (*url);
size_t i;
ssize_t i;
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);
length = hashmap_entry_by_key (hashofheaders, "host", (void **) &data);
if (length <= 0) {
struct sockaddr_in dest_addr;
if (getsockname
(connptr->client_fd, (void *) &dest_addr,
&length) < 0 || length > sizeof(dest_addr)) {
addr_err:;
(connptr->client_fd, (struct sockaddr *) &dest_addr,
&length) < 0) {
log_message (LOG_ERR,
"process_request: cannot get destination IP for %d",
connptr->client_fd);
@ -83,14 +78,10 @@ do_transparent_proxy (struct conn_s *connptr, orderedmap hashofheaders,
return 0;
}
af = SOCKADDR_UNION_AF(&dest_addr);
dest_inaddr = SOCKADDR_UNION_ADDRESS(&dest_addr);
request->host = (char *) safemalloc (17);
strlcpy (request->host, inet_ntoa (dest_addr.sin_addr), 17);
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->port = ntohs (dest_addr.sin_port);
request->path = (char *) safemalloc (ulen + 1);
strlcpy (request->path, *url, ulen + 1);
@ -100,7 +91,6 @@ do_transparent_proxy (struct conn_s *connptr, orderedmap hashofheaders,
"process_request: trans IP %s %s for %d",
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) {
@ -121,12 +111,12 @@ do_transparent_proxy (struct conn_s *connptr, orderedmap hashofheaders,
return 1;
}
for (i = 0; i < sblist_getsize(conf->listen_addrs); i++) {
char **addr;
for (i = 0; i < vector_length(conf->listen_addrs); i++) {
const char *addr;
addr = sblist_get(conf->listen_addrs, i);
addr = (char *)vector_getentry(conf->listen_addrs, i, NULL);
if (addr && *addr && strcmp(request->host, *addr) == 0) {
if (addr && strcmp(request->host, addr) == 0) {
log_message(LOG_ERR,
"transparent: destination IP %s is local "
"on socket fd %d",

View File

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

View File

@ -27,120 +27,89 @@
#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, char *domain,
const char *user, const char *pass,
proxy_type type, enum upstream_build_error *ube)
static struct upstream *upstream_build (const char *host, int port, const char *domain)
{
char *ptr;
struct upstream *up;
*ube = UBE_SUCCESS;
up = (struct upstream *) safemalloc (sizeof (struct upstream));
if (!up) {
*ube = UBE_OOM;
log_message (LOG_ERR,
"Unable to allocate memory in upstream_build()");
return NULL;
}
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);
}
}
up->host = up->domain = NULL;
up->ip = up->mask = 0;
if (domain == NULL) {
if (type == PT_NONE) {
e_nonedomain:;
*ube = UBE_EDOMAIN;
goto fail;
}
if (!host || !host[0] || port < 1) {
*ube = UBE_INVHOST;
if (!host || host[0] == '\0' || port < 1) {
log_message (LOG_WARNING,
"Nonsense upstream rule: invalid host or port");
goto fail;
}
up->host = safestrdup (host);
up->port = port;
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;
}
up->host = safestrdup (host);
up->port = port;
}
if (hostspec_parse(domain, &up->target)
|| up->target.type == HST_NONE) {
*ube = UBE_NETMASK;
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");
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);
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);
}
return up;
fail:
safefree (up->ua.user);
safefree (up->pass);
safefree (up->host);
if(up->target.type == HST_STRING)
safefree (up->target.address.string);
safefree (up->domain);
safefree (up);
return NULL;
@ -149,24 +118,21 @@ fail:
/*
* Add an entry to the 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)
void upstream_add (const char *host, int port, const char *domain,
struct upstream **upstream_list)
{
struct upstream *up;
enum upstream_build_error ube;
up = upstream_build (host, port, domain, user, pass, type, &ube);
up = upstream_build (host, port, domain);
if (up == NULL) {
return ube;
return;
}
if (up->target.type == HST_NONE) { /* always add default to end */
if (!up->domain && !up->ip) { /* always add default to end */
struct upstream *tmp = *upstream_list;
while (tmp) {
if (tmp->target.type == HST_NONE) {
if (!tmp->domain && !tmp->ip) {
log_message (LOG_WARNING,
"Duplicate default upstream");
goto upstream_cleanup;
@ -175,7 +141,7 @@ enum upstream_build_error upstream_add (
if (!tmp->next) {
up->next = NULL;
tmp->next = up;
return ube;
return;
}
tmp = tmp->next;
@ -185,15 +151,14 @@ enum upstream_build_error upstream_add (
up->next = *upstream_list;
*upstream_list = up;
return ube;
return;
upstream_cleanup:
safefree (up->host);
if(up->target.type == HST_STRING)
safefree (up->target.address.string);
safefree (up->domain);
safefree (up);
return ube;
return;
}
/*
@ -201,22 +166,44 @@ upstream_cleanup:
*/
struct upstream *upstream_get (char *host, struct upstream *up)
{
while (up) {
if (up->target.type == HST_NONE)
break;
in_addr_t my_ip = INADDR_NONE;
if (hostspec_match(host, &up->target))
break;
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)
break;
} else {
break; /* No domain or IP, default upstream */
}
up = up->next;
}
if (up && (!up->host))
if (up && (!up->host || !up->port))
up = NULL;
if (up)
log_message (LOG_INFO, "Found upstream proxy %s %s:%d for %s",
proxy_type_name(up->type), up->host, up->port, host);
log_message (LOG_INFO, "Found upstream proxy %s:%d for %s",
up->host, up->port, host);
else
log_message (LOG_INFO, "No upstream proxy for %s", host);
@ -228,8 +215,7 @@ void free_upstream_list (struct upstream *up)
while (up) {
struct upstream *tmp = up;
up = up->next;
if(tmp->target.type == HST_STRING)
safefree (tmp->target.address.string);
safefree (tmp->domain);
safefree (tmp->host);
safefree (tmp);
}

View File

@ -26,51 +26,24 @@
#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;
struct hostspec target;
proxy_type type;
in_addr_t ip, mask;
};
#ifdef UPSTREAM_SUPPORT
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 void upstream_add (const char *host, int port, const char *domain,
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,
"Server: " PACKAGE "/" VERSION,
"Content-type: text/html",
"Connection: close"
};

214
src/vector.c Normal file
View File

@ -0,0 +1,214 @@
/* 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;
}

75
src/vector.h Normal file
View File

@ -0,0 +1,75 @@
/* 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=$(cd $(dirname $0) && pwd)
SCRIPTS_DIR=$(pwd)/$(dirname $0)
BASEDIR=$SCRIPTS_DIR/../..
TESTS_DIR=$SCRIPTS_DIR/..
TESTENV_DIR=$TESTS_DIR/env
@ -30,7 +30,6 @@ 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
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
@ -80,32 +79,26 @@ 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_FILE"
Logfile "$TINYPROXY_LOG_DIR/tinyproxy.log"
PidFile "$TINYPROXY_PID_FILE"
LogLevel Info
MaxClients 100
MinSpareServers 5
MaxSpareServers 20
StartServers 10
MaxRequestsPerChild 0
Allow 127.0.0.0/8
ViaProxyName "tinyproxy"
#DisableViaHeader Yes
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
cat << 'EOF' > $TINYPROXY_FILTER_FILE
.*\.google-analytics\.com$
EOF
touch $TINYPROXY_FILTER_FILE
}
start_tinyproxy() {
@ -114,26 +107,13 @@ 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..."
pid=$(cat $TINYPROXY_PID_FILE)
kill $pid
kill $(cat $TINYPROXY_PID_FILE)
if test "x$?" = "x0" ; then
echo " ok"
else
echo " error killing pid $pid"
ps aux | grep tinyproxy
echo "### printing logfile"
cat $TINYPROXY_LOG_FILE
echo "### printing stderr logfile"
cat $TINYPROXY_STDERR_LOG
echo " error"
fi
}
@ -174,7 +154,7 @@ 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"
@ -182,31 +162,11 @@ run_basic_webclient_request() {
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"
provision_initial
@ -216,11 +176,10 @@ provision_webserver
start_webserver
start_tinyproxy
wait_for_some_seconds 1
wait_for_some_seconds 3
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))
@ -232,30 +191,6 @@ 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))
}
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"

View File

@ -24,5 +24,5 @@ TESTS_DIR=$SCRIPTS_DIR/..
TESTENV_DIR=$TESTS_DIR/env
LOG_DIR=$TESTENV_DIR/var/log
VALGRIND="valgrind --track-origins=yes --show-leak-kinds=all --tool=memcheck --leak-check=full --log-file=$LOG_DIR/valgrind.log" $SCRIPTS_DIR/run_tests.sh
VALGRIND="valgrind -q --tool=memcheck --leak-check=full --log-file=$LOG_DIR/valgrind.log" $SCRIPTS_DIR/run_tests.sh

View File

@ -26,8 +26,9 @@ use Pod::Usage;
my $EOL = "\015\012";
my $VERSION = "0.1";
my $NAME = "Tinyproxy-Web-Client";
my $user_agent = "$NAME";
my $user_agent = "$NAME/$VERSION";
my $user_agent_header = "User-Agent: $user_agent$EOL";
my $http_version = "1.0";
my $method = "GET";
@ -121,16 +122,11 @@ foreach my $document (@ARGV) {
print $remote $request;
$_ = <$remote>;
print; # /* HTTP/1.0 400 Bad Request */
my($errn) = ($_ =~ /HTTP\/\d\.\d (\d{3})/);
while (<$remote>) {
print;
}
close $remote;
exit($errn - 399) if($errn > 399);
}
exit(0);

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