diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 43541078..067105da 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,6 +15,7 @@ jobs: - run: ./configure - run: make - run: make test + - run: cd docs/web ; make test-macos: runs-on: macos-latest steps: @@ -27,8 +28,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: install valgrind - run: sudo apt-get install --assume-yes valgrind + - run: sudo apt update + - run: sudo apt install --assume-yes valgrind - run: ./autogen.sh - run: ./configure --enable-debug --enable-transparent --enable-reverse - run: make diff --git a/.github/workflows/release_tarball.yml b/.github/workflows/release_tarball.yml new file mode 100644 index 00000000..7999f179 --- /dev/null +++ b/.github/workflows/release_tarball.yml @@ -0,0 +1,40 @@ +name: Generate Source Tarball + +# Trigger whenever a release is created +on: + release: + types: + - created + +jobs: + build: + name: build + runs-on: ubuntu-latest + steps: + + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: archive + id: archive + run: | + sudo apt install -y gperf + rm -rf .git + autoreconf -i + VERSION=$(cat VERSION) + PKGNAME="tinyproxy-$VERSION" + ./configure + make dist + echo "tarball_xz=${PKGNAME}.tar.xz" >> "$GITHUB_OUTPUT" + echo "tarball_gz=${PKGNAME}.tar.gz" >> "$GITHUB_OUTPUT" + echo "tarball_bz2=${PKGNAME}.tar.bz2" >> "$GITHUB_OUTPUT" + + - name: upload tarballs + uses: softprops/action-gh-release@v2 + with: + files: | + ${{ steps.archive.outputs.tarball_xz }} + ${{ steps.archive.outputs.tarball_gz }} + ${{ steps.archive.outputs.tarball_bz2 }} + diff --git a/.github/workflows/shellcheck.yaml b/.github/workflows/shellcheck.yaml new file mode 100644 index 00000000..7e5edc7b --- /dev/null +++ b/.github/workflows/shellcheck.yaml @@ -0,0 +1,33 @@ +name: shellcheck +on: + push: + branches: + - master + pull_request: + paths-ignore: + branches: + - master + +# cancel the in-progress workflow when PR is refreshed. +concurrency: + group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + shellcheck: + name: Shellcheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: install shellcheck + run: | + sudo apt-get update + sudo apt-get install -y shellcheck + - name: Run autogen + run: ./autogen.sh + - name: Run ShellCheck + run: make shellcheck + diff --git a/Makefile.am b/Makefile.am index 4a3ead6f..f721d58e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -27,6 +27,10 @@ EXTRA_DIST = \ test: all ./tests/scripts/run_tests.sh +.PHONY: shellcheck +shellcheck: + @shellcheck `find . -name '*.sh'` + test-wait: TINYPROXY_TESTS_WAIT=yes $(MAKE) test diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..93ef8148 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,28 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| --------- | ------------------ | +| 1.11.x | :white_check_mark: | +| <= 1.10.x | :x: | + +## Reporting a Vulnerability + +Open a public issue on github. The issue will most likely be fixed +within a day, unless all maintainers happen to just be taking a +vacation at the same time, which is unlikely. + +Even then, having the bug publicly known will allow competent people +to come up with custom patches for distros, most likely quicker +than black hats can craft a remote execution exploit. + +If you really really do not want to make the issue public, come +to the tinyproxy IRC channel and ask for a maintainer, which you +can then contact via private messages. + +Do not, however, like ["TALOS Intelligence"](https://talosintelligence.com/vulnerability_reports/TALOS-2023-1889) +pull a random email address out of git log, then send an email +nobody reads or responds to, and wait for 6 months for publication. +this only gives black hats plenty time to sell, use and circulate +zero days and get the best possible ROI. diff --git a/VERSION b/VERSION index 1cac385c..0a5af26d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.11.0 +1.11.3 diff --git a/autogen.sh b/autogen.sh index fb6da78b..b3011ccd 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,24 +1,31 @@ #!/bin/sh -srcdir=`dirname $0` +srcdir=$(dirname "$0") test -z "$srcdir" && srcdir=. -ORIGDIR=`pwd` +ORIGDIR=$(pwd) set -x -cd $srcdir +cd "$srcdir" || { + echo "error changing to dir '$srcdir'" + exit +} aclocal -I m4macros \ && autoheader \ && automake --gnu --add-missing \ && autoconf -cd $ORIGDIR +cd "$ORIGDIR" || { + echo "error changing to idir '$ORIGDIR'" + exit + +} set - -echo $srcdir/configure "$@" -$srcdir/configure "$@" +echo "$srcdir"/configure "$@" +"$srcdir"/configure "$@" RC=$? if test $RC -ne 0; then echo diff --git a/configure.ac b/configure.ac index d904c788..329c5633 100644 --- a/configure.ac +++ b/configure.ac @@ -146,7 +146,7 @@ AC_CHECK_HEADERS([sys/ioctl.h alloca.h memory.h malloc.h sysexits.h \ dnl Checks for libary functions AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK -AC_CHECK_FUNCS([strlcpy strlcat setgroups]) +AC_CHECK_FUNCS([strlcpy setgroups]) dnl Enable extra warnings DESIRED_FLAGS="-fdiagnostics-show-option -Wall -Wextra -Wno-unused-parameter -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations -Wfloat-equal -Wundef -Wformat=2 -Wlogical-op -Wmissing-include-dirs -Wformat-nonliteral -Wold-style-definition -Wpointer-arith -Waggregate-return -Winit-self -Wpacked --std=c89 -ansi -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" @@ -173,6 +173,9 @@ 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) @@ -194,20 +197,21 @@ fi #manpage_support_enabled AM_CONDITIONAL(HAVE_POD2MAN, test "x$POD2MAN" != "x" -a "x$POD2MAN" != "xno") AC_PATH_PROG(GPERF, gperf, no) -AM_CONDITIONAL(HAVE_GPERF, test "x$GPERF" != "x" -a "x$GPERF" != "xno") 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 - AM_CONDITIONAL(HAVE_GPERF, false) AS_ECHO("no") fi fi +AM_CONDITIONAL(HAVE_GPERF, $tmp_gperf) AC_CONFIG_FILES([ Makefile @@ -219,7 +223,6 @@ docs/Makefile docs/man5/Makefile docs/man5/tinyproxy.conf.txt docs/man8/Makefile -docs/man8/tinyproxy.txt m4macros/Makefile tests/Makefile tests/scripts/Makefile @@ -244,3 +247,7 @@ if test "x$POD2MAN" = "xno" ; 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 diff --git a/data/templates/debug.html b/data/templates/debug.html index 6ee33674..0e7f0549 100644 --- a/data/templates/debug.html +++ b/data/templates/debug.html @@ -30,9 +30,6 @@

{cause}

clienthost
{clienthost}
-
version
-
{version}
-
package
{package}
@@ -49,7 +46,7 @@

{cause}


-

Generated by {package} version {version}.

+

Generated by {package}.

diff --git a/data/templates/default.html b/data/templates/default.html index 67354b7a..8a9c8f6c 100644 --- a/data/templates/default.html +++ b/data/templates/default.html @@ -16,7 +16,7 @@

{cause}


-

Generated by {package} version {version}.

+

Generated by {package}.

diff --git a/data/templates/stats.html b/data/templates/stats.html index 71798592..f039c970 100644 --- a/data/templates/stats.html +++ b/data/templates/stats.html @@ -1,69 +1,95 @@ - - - - - - -{package} version {version} run-time statistics - - - - - - - - -

{package} version {version} run-time statistics

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameValue
Number of open connections{opens}
Number of requests{reqs}
Number of bad connections{badconns}
Number of denied connections{deniedconns}
Number of refused connections due to high load{refusedconns}
- -
- -

Generated by {package} version {version}.

- - - + + + + + Stats [{package}] + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{package} statistics
Open connections{opens}
Bad connections{badconns}
Denied connections{deniedconns}
Refused (high load){refusedconns}
Total requests{reqs}
+
+
+ diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in index 758382cf..9938ce19 100644 --- a/docs/man5/tinyproxy.conf.txt.in +++ b/docs/man5/tinyproxy.conf.txt.in @@ -22,8 +22,8 @@ configuration file. The Tinyproxy configuration file contains key-value pairs, one per line. Lines starting with `#` and empty lines are comments and are ignored. Keywords are case-insensitive, whereas values are -case-sensitive. Values may be enclosed in double-quotes (") if they -contain spaces. +case-sensitive. Some string values must be enclosed in double +quotes (") as noted below. The possible keywords and their descriptions are as follows: @@ -57,7 +57,7 @@ only on one specific address. =item B This allows you to specify which address Tinyproxy will bind -to for outgoing connections to web servers or upstream proxies. +to for outgoing connections. This parameter may be specified multiple times, then Tinyproxy will try all the specified addresses in order. @@ -76,29 +76,29 @@ allowed to have before it is closed by Tinyproxy. This parameter controls which HTML file Tinyproxy returns when a given HTTP error occurs. It takes two arguments, the error number -and the location of the HTML error file. +and the location of the HTML error file. Enclose the file location +in double quotes. =item B -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. =item B -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@`. =item B -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 L manual page -for details. +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 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 @@ -109,9 +109,9 @@ manual page contains a description of all template variables. =item B -This controls the location of the file to which Tinyproxy -writes its debug output. Alternatively, Tinyproxy can log -to syslog -- see the Syslog option. +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. =item B @@ -144,8 +144,8 @@ and below would be suppressed. Allowed values are: =item B -This option controls the location of the file where the main -Tinyproxy process stores its process ID for signaling purposes. +The location of the file where the main Tinyproxy process stores its +process ID for signaling purposes. Enclose in double quotes. =item B @@ -173,12 +173,20 @@ turns on the upstream proxy for the sites matching `site_spec`. `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 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: @@ -218,7 +226,7 @@ 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 +of a client host, like `127.0.0.1` or `::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 @@ -235,6 +243,14 @@ access is only granted for authenticated users. BasicAuth user password +=item B + +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 Configure one or more HTTP request headers to be added to outgoing @@ -250,7 +266,8 @@ 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. +Otherwise, the server's host name will be used. Enclose in double +quotes. =item B @@ -267,7 +284,7 @@ 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 -FilterExtended is activated. +another FilterType is specified. Comment lines start with a `#` character. Example filter file contents: @@ -287,6 +304,20 @@ Example filter file contents: # filter any domain that starts with adserver ^adserver +=item B + +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 If this boolean option is set to `Yes` or `On`, filtering is @@ -300,6 +331,7 @@ recommended not to use this option. =item B +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. @@ -308,7 +340,11 @@ The default is to use basic POSIX regular expressions. 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. +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 @@ -325,7 +361,7 @@ 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. +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. @@ -396,7 +432,7 @@ This manpage was written by the Tinyproxy project team. =head1 COPYRIGHT -Copyright (c) 1998-2020 the Tinyproxy authors. +Copyright (c) 1998-2024 the Tinyproxy authors. This program is distributed under the terms of the GNU General Public License version 2 or above. See the COPYING file for additional diff --git a/docs/man8/Makefile.am b/docs/man8/Makefile.am index d2d7e19b..28e361ef 100644 --- a/docs/man8/Makefile.am +++ b/docs/man8/Makefile.am @@ -9,6 +9,17 @@ M_NAME=TINYPROXY man_MANS = \ $(MAN8_FILES:.txt=.8) +edit = sed \ + -e 's|@localstatedir[@]|$(localstatedir)|g' \ + -e 's|@runstatedir[@]|$(runstatedir)|g' \ + -e 's|@sysconfdir[@]|$(sysconfdir)|g' \ + -e 's|@TINYPROXY_STATHOST[@]|$(TINYPROXY_STATHOST)|g' + +tinyproxy.txt: $(top_srcdir)/docs/man8/tinyproxy.txt.in Makefile + @rm -f $@ $@.tmp + $(AM_V_GEN) $(edit) $(top_srcdir)/docs/man8/$@.in > $@.tmp + @mv $@.tmp $@ + .txt.8: if HAVE_POD2MAN $(AM_V_GEN) $(POD2MAN) --center="Tinyproxy manual" \ @@ -18,8 +29,12 @@ else @echo "*** pod2man is required to regenerate $(@) ***"; exit 1; endif +CLEANFILES = \ + tinyproxy.txt + MAINTAINERCLEANFILES = \ $(MAN8_FILES:.txt=.8) EXTRA_DIST = \ - $(MAN8_FILES:.txt=.8) + $(MAN8_FILES:.txt=.8) \ + tinyproxy.txt.in diff --git a/docs/man8/tinyproxy.txt.in b/docs/man8/tinyproxy.txt.in index 7fa420f6..9cf2d426 100644 --- a/docs/man8/tinyproxy.txt.in +++ b/docs/man8/tinyproxy.txt.in @@ -156,7 +156,11 @@ configuration variable `StatFile`. =head1 FILES -`/etc/tinyproxy/tinyproxy.conf`, `/var/run/tinyproxy/tinyproxy.pid`, `/var/log/tinyproxy/tinyproxy.log` +F<@sysconfdir@/tinyproxy/tinyproxy.conf> + +F<@runstatedir@/tinyproxy/tinyproxy.pid> + +F<@localstatedir@/log/tinyproxy/tinyproxy.log> =head1 BUGS diff --git a/docs/web/tp.html.head b/docs/web/tp.html.head index 6ce2b0b1..351564ce 100644 --- a/docs/web/tp.html.head +++ b/docs/web/tp.html.head @@ -41,25 +41,27 @@

Tinyproxy requires only a minimal POSIX environment to build and operate. It can use additional libraries to add functionality though.

-

Tinyproxy allows forwarding of HTTPS connections without modifying traffic in any way through the CONNECT method (see the ConnectPort directive).

+

Tinyproxy allows forwarding of HTTPS connections without modifying traffic in any way through the CONNECT method (see the ConnectPort directive, which you should disable, unless you want to restrict the users).

Tinyproxy supports being configured as a transparent proxy, so that a proxy can be used without requiring any client-side configuration. You can also use it as a reverse proxy front-end to your websites.

-

Using the AddHeader directive, you can add/insert HTTP headers to outgoing traffic.

+

Using the AddHeader directive, you can add/insert HTTP headers to outgoing traffic (HTTP only).

If you're looking to build a custom web proxy, Tinyproxy is easy to modify 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.

-

Tinyproxy has privacy features 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).

+

Tinyproxy has privacy features 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.

Using the remote monitoring facility, you can access proxy statistics from afar, letting you know exactly how busy the proxy is.

You can configure Tinyproxy to control access 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.

-

With a bit of configuration (specifically, making Tinyproxy created files owned by a non-root user and running it on a port greater than 1024), Tinyproxy can be made to run without any special privileges, thus minimizing the chance of system compromise. Furthermore, it was designed with an eye towards preventing buffer overflows. The simplicity of the code ensures it remains easy to spot such bugs.

+

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 recommended 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.

Downloads

+

Note that many distributions ship horribly outdated versions of tinyproxy, therefore it is recommended to compile it from source.

+
  • On Red Hat Enterprise Linux, or its derivatives such as CentOS, install Tinyproxy from the EPEL repository by running yum install tinyproxy.
  • On Fedora, install Tinyproxy by running yum install tinyproxy.
  • @@ -70,7 +72,7 @@
  • Mac OS X users can check MacPorts to see if the Tinyproxy port there is recent enough.
-

If you feel that the Tinyproxy binary package in your operating system is not recent, please contact the package maintainer for that particular operating system. If this fails, you can always compile the latest stable version from source code.

+

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.

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 releases page. 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.

@@ -78,5 +80,19 @@

git clone https://github.com/tinyproxy/tinyproxy.git

+

+Quickstart

+ +

The quickest way to get started is using a minimal config file like the below:

+ +

+Port 8888
+Listen 127.0.0.1
+Timeout 600
+Allow 127.0.0.1
+
+ +

And then simply run tinyproxy -d -c tinyproxy.conf as your current user. This starts tinyproxy in foreground mode with tinyproxy.conf 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 http_proxy=127.0.0.1:8888 curl example.com.

+

Documentation

diff --git a/etc/Makefile.am b/etc/Makefile.am index 57a5c010..045baac3 100644 --- a/etc/Makefile.am +++ b/etc/Makefile.am @@ -12,6 +12,7 @@ edit = sed \ -e 's|@datarootdir[@]|$(datarootdir)|g' \ -e 's|@pkgsysconfdir[@]|$(pkgsysconfdir)|g' \ -e 's|@localstatedir[@]|$(localstatedir)|g' \ + -e 's|@runstatedir[@]|$(runstatedir)|g' \ -e 's|@pkgdatadir[@]|$(pkgdatadir)|g' \ -e 's|@prefix[@]|$(prefix)|g' \ -e 's|@TINYPROXY_STATHOST[@]|$(TINYPROXY_STATHOST)|g' diff --git a/etc/tinyproxy.conf.in b/etc/tinyproxy.conf.in index ce27f7ec..b7d46a71 100644 --- a/etc/tinyproxy.conf.in +++ b/etc/tinyproxy.conf.in @@ -3,7 +3,7 @@ ## ## This example tinyproxy.conf file contains example settings ## with explanations in comments. For decriptions of all -## parameters, see the tinproxy.conf(5) manual page. +## parameters, see the tinyproxy.conf(5) manual page. ## # @@ -124,7 +124,7 @@ LogLevel Info # can be used for signalling purposes. # If not specified, no pidfile will be written. # -#PidFile "@localstatedir@/run/tinyproxy/tinyproxy.pid" +#PidFile "@runstatedir@/tinyproxy/tinyproxy.pid" # # XTinyproxy: Tell Tinyproxy to include the X-Tinyproxy header, which @@ -205,6 +205,13 @@ Allow ::1 # 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 # Tinyproxy makes. Note that this option will not work for HTTPS @@ -240,10 +247,9 @@ ViaProxyName "tinyproxy" #FilterURLs On # -# FilterExtended: Use POSIX Extended regular expressions rather than -# basic. +# FilterType: Use bre (default), ere, or fnmatch for filtering. # -#FilterExtended On +#FilterType fnmatch # # FilterCaseSensitive: Use case sensitive regular expressions. @@ -321,6 +327,3 @@ ViaProxyName "tinyproxy" # If not set then no rewriting occurs. # #ReverseBaseURL "http://localhost:8888/" - - - diff --git a/scripts/version.sh b/scripts/version.sh index 03fb3aa0..4ad08b61 100755 --- a/scripts/version.sh +++ b/scripts/version.sh @@ -5,7 +5,7 @@ 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) + gitstr=$(git describe --tags --match '[0-9]*.[0-9]*.*' 2>/dev/null) if test "x$?" != x0 ; then sed 's/$/-git/' < VERSION else diff --git a/src/Makefile.am b/src/Makefile.am index d132a75f..f9597b5a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -50,7 +50,7 @@ tinyproxy_SOURCES = \ base64.c base64.h \ sblist.c sblist.h \ hsearch.c hsearch.h \ - orderedmap.c orderedmap.h \ + pseudomap.c pseudomap.h \ loop.c loop.h \ mypoll.c mypoll.h \ connect-ports.c connect-ports.h @@ -67,5 +67,5 @@ conf-tokens-gperf.inc: conf-tokens.gperf $(GPERF) $< > $@ endif -EXTRA_DIST = conf-tokens.gperf +EXTRA_DIST = conf-tokens.gperf conf-tokens-gperf.inc diff --git a/src/buffer.c b/src/buffer.c index b3381838..4cf15a0f 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -241,13 +241,6 @@ ssize_t read_buffer (int fd, struct buffer_s * buffptr) bytesin = -1; } else { switch (errno) { -#ifdef EWOULDBLOCK - case EWOULDBLOCK: -#else -# ifdef EAGAIN - case EAGAIN: -# endif -#endif case EINTR: bytesin = 0; break; @@ -295,13 +288,6 @@ ssize_t write_buffer (int fd, struct buffer_s * buffptr) return bytessent; } else { switch (errno) { -#ifdef EWOULDBLOCK - case EWOULDBLOCK: -#else -# ifdef EAGAIN - case EAGAIN: -# endif -#endif case EINTR: return 0; case ENOBUFS: diff --git a/src/child.c b/src/child.c index 985357da..8bae89b2 100644 --- a/src/child.c +++ b/src/child.c @@ -81,7 +81,7 @@ void child_main_loop (void) int connfd; union sockaddr_union cliaddr_storage; struct sockaddr *cliaddr = (void*) &cliaddr_storage; - socklen_t clilen = sizeof(cliaddr_storage); + socklen_t clilen; int nfds = sblist_getsize(listen_fds); pollfd_struct *fds = safecalloc(nfds, sizeof *fds); ssize_t i; @@ -167,6 +167,7 @@ void child_main_loop (void) * Continue handling this connection. */ + clilen = sizeof(cliaddr_storage); connfd = accept (listenfd, cliaddr, &clilen); diff --git a/src/conf-tokens.c b/src/conf-tokens.c index bad70136..4463d230 100644 --- a/src/conf-tokens.c +++ b/src/conf-tokens.c @@ -18,7 +18,7 @@ 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}, + {"",CD_NIL}, {"allow", CD_allow}, {"stathost", CD_stathost}, {"listen", CD_listen}, @@ -34,6 +34,7 @@ config_directive_find (register const char *str, register size_t len) {"defaulterrorfile", CD_defaulterrorfile}, {"startservers", CD_startservers}, {"filtercasesensitive", CD_filtercasesensitive}, + {"filtertype", CD_filtertype}, {"filterurls", CD_filterurls}, {"filter", CD_filter}, {"reversemagic", CD_reversemagic}, @@ -56,6 +57,7 @@ config_directive_find (register const char *str, register size_t len) {"connectport", CD_connectport}, {"logfile", CD_logfile}, {"basicauth", CD_basicauth}, + {"basicauthrealm", CD_basicauthrealm}, {"addheader", CD_addheader}, {"maxrequestsperchild", CD_maxrequestsperchild} }; diff --git a/src/conf-tokens.gperf b/src/conf-tokens.gperf index ef93245c..1013d591 100644 --- a/src/conf-tokens.gperf +++ b/src/conf-tokens.gperf @@ -44,6 +44,7 @@ allow, CD_allow deny, CD_deny bind, CD_bind basicauth, CD_basicauth +basicauthrealm, CD_basicauthrealm errorfile, CD_errorfile addheader, CD_addheader filter, CD_filter @@ -51,6 +52,7 @@ filterurls, CD_filterurls filterextended, CD_filterextended filterdefaultdeny, CD_filterdefaultdeny filtercasesensitive, CD_filtercasesensitive +filtertype, CD_filtertype reversebaseurl, CD_reversebaseurl reverseonly, CD_reverseonly reversemagic, CD_reversemagic diff --git a/src/conf-tokens.h b/src/conf-tokens.h index d9f03cd7..01c8ccb2 100644 --- a/src/conf-tokens.h +++ b/src/conf-tokens.h @@ -29,10 +29,12 @@ 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, diff --git a/src/conf.c b/src/conf.c index 4e6d19ea..372c73f8 100644 --- a/src/conf.c +++ b/src/conf.c @@ -66,9 +66,10 @@ #define PASSWORD "([^@]*)" #define IP "((([0-9]{1,3})\\.){3}[0-9]{1,3})" #define IPMASK "(" IP "(/" DIGIT "+)?)" +#define IPV6SCOPE "((%[^ \t\\/]{1,16})?)" #define IPV6 "(" \ - "(([0-9a-f:]{2,39}))|" \ - "(([0-9a-f:]{0,29}:" IP "))" \ + "([0-9a-f:]{2,39})" IPV6SCOPE "|" \ + "([0-9a-f:]{0,29}:" IP ")" IPV6SCOPE \ ")" #define IPV6MASK "(" IPV6 "(/" DIGIT "+)?)" @@ -80,7 +81,7 @@ * number. Given the usual structure of the configuration file, sixteen * substring matches should be plenty. */ -#define RE_MAX_MATCHES 24 +#define RE_MAX_MATCHES 33 #define CP_WARN(FMT, ...) \ log_message (LOG_WARNING, "line %lu: " FMT, lineno, __VA_ARGS__) @@ -121,6 +122,7 @@ static HANDLE_FUNC (handle_disabled_feature) static HANDLE_FUNC (handle_allow); static HANDLE_FUNC (handle_basicauth); +static HANDLE_FUNC (handle_basicauthrealm); static HANDLE_FUNC (handle_anonymous); static HANDLE_FUNC (handle_bind); static HANDLE_FUNC (handle_bindsame); @@ -135,6 +137,7 @@ static HANDLE_FUNC (handle_filtercasesensitive); static HANDLE_FUNC (handle_filterdefaultdeny); static HANDLE_FUNC (handle_filterextended); static HANDLE_FUNC (handle_filterurls); +static HANDLE_FUNC (handle_filtertype); #endif static HANDLE_FUNC (handle_group); static HANDLE_FUNC (handle_listen); @@ -191,6 +194,7 @@ struct { regex_t *cre; } directives[] = { /* string arguments */ + STDCONF (basicauthrealm, STR, handle_basicauthrealm), STDCONF (logfile, STR, handle_logfile), STDCONF (pidfile, STR, handle_pidfile), STDCONF (anonymous, STR, handle_anonymous), @@ -223,7 +227,7 @@ struct { handle_deny), STDCONF (bind, "(" IP "|" IPV6 ")", handle_bind), /* other */ - STDCONF (basicauth, ALNUM WS ALNUM, handle_basicauth), + STDCONF (basicauth, USERNAME WS PASSWORD, handle_basicauth), STDCONF (errorfile, INT WS STR, handle_errorfile), STDCONF (addheader, STR WS STR, handle_addheader), @@ -234,6 +238,7 @@ struct { STDCONF (filterextended, BOOL, handle_filterextended), STDCONF (filterdefaultdeny, BOOL, handle_filterdefaultdeny), STDCONF (filtercasesensitive, BOOL, handle_filtercasesensitive), + STDCONF (filtertype, "(bre|ere|fnmatch)", handle_filtertype), #endif #ifdef REVERSE_SUPPORT /* Reverse proxy arguments */ @@ -247,7 +252,7 @@ struct { "(" "(none)" WS STR ")|" \ "(" "(http|socks4|socks5)" WS \ "(" USERNAME /*username*/ ":" PASSWORD /*password*/ "@" ")?" - "(" IP "|" ALNUM ")" + "(" IP "|" "\\[(" IPV6 ")\\]" "|" ALNUM ")" ":" INT "(" WS STR ")?" ")", handle_upstream), #endif /* loglevel */ @@ -291,6 +296,7 @@ void free_config (struct config_s *conf) char *k; htab_value *v; size_t it; + safefree (conf->basicauth_realm); safefree (conf->logf_name); safefree (conf->stathost); safefree (conf->user); @@ -425,7 +431,7 @@ static int config_parse (struct config_s *conf, FILE * f) while(isspace(*p))p++; if(!*p) continue; q = p; - while(!isspace(*q))q++; + while(*q && !isspace(*q))q++; c = *q; *q = 0; e = config_directive_find(p, strlen(p)); @@ -478,6 +484,7 @@ static void initialize_config_defaults (struct config_s *conf) * (FIXME: Should have a better API for all this) */ conf->errorpages = NULL; + conf->basicauth_realm = safestrdup (PACKAGE_NAME); conf->stathost = safestrdup (TINYPROXY_STATHOST); conf->idletimeout = MAX_IDLE_TIME; conf->logf_name = NULL; @@ -631,6 +638,11 @@ set_int_arg (unsigned int *var, const char *line, regmatch_t * match) * ***********************************************************************/ +static HANDLE_FUNC (handle_basicauthrealm) +{ + return set_string_arg (&conf->basicauth_realm, line, &match[2]); +} + static HANDLE_FUNC (handle_logfile) { return set_string_arg (&conf->logf_name, line, &match[2]); @@ -707,6 +719,8 @@ static HANDLE_FUNC (handle_xtinyproxy) #ifdef XTINYPROXY_ENABLE return set_bool_arg (&conf->add_xtinyproxy, line, &match[2]); #else + if(!get_bool_arg(line, &match[2])) + return 0; fprintf (stderr, "XTinyproxy NOT Enabled! Recompile with --enable-xtinyproxy\n"); return 1; @@ -950,6 +964,11 @@ static HANDLE_FUNC (handle_basicauth) } #ifdef FILTER_ENABLE + +static void warn_deprecated(const char *arg, unsigned long lineno) { + CP_WARN ("deprecated option %s", arg); +} + static HANDLE_FUNC (handle_filter) { return set_string_arg (&conf->filter, line, &match[2]); @@ -957,26 +976,53 @@ static HANDLE_FUNC (handle_filter) static HANDLE_FUNC (handle_filterurls) { - return set_bool_arg (&conf->filter_url, line, &match[2]); + conf->filter_opts |= + get_bool_arg (line, &match[2]) * FILTER_OPT_URL; + return 0; } static HANDLE_FUNC (handle_filterextended) { - return set_bool_arg (&conf->filter_extended, line, &match[2]); + warn_deprecated("FilterExtended, use FilterType", lineno); + conf->filter_opts |= + get_bool_arg (line, &match[2]) * FILTER_OPT_TYPE_ERE; + return 0; } static HANDLE_FUNC (handle_filterdefaultdeny) { assert (match[2].rm_so != -1); - - if (get_bool_arg (line, &match[2])) - filter_set_default_policy (FILTER_DEFAULT_DENY); + conf->filter_opts |= + get_bool_arg (line, &match[2]) * FILTER_OPT_DEFAULT_DENY; return 0; } static HANDLE_FUNC (handle_filtercasesensitive) { - return set_bool_arg (&conf->filter_casesensitive, line, &match[2]); + conf->filter_opts |= + get_bool_arg (line, &match[2]) * FILTER_OPT_CASESENSITIVE; + return 0; +} + +static HANDLE_FUNC (handle_filtertype) +{ + static const struct { unsigned short flag; char type[8]; } + ftmap[] = { + {FILTER_OPT_TYPE_ERE, "ere"}, + {FILTER_OPT_TYPE_BRE, "bre"}, + {FILTER_OPT_TYPE_FNMATCH, "fnmatch"}, + }; + char *type; + unsigned i; + type = get_string_arg(line, &match[2]); + if (!type) return -1; + + for(i=0;ifilter_opts |= ftmap[i].flag; + + safefree (type); + return 0; } #endif @@ -1078,10 +1124,13 @@ static HANDLE_FUNC (handle_upstream) pass = get_string_arg (line, &match[mi]); mi++; - ip = get_string_arg (line, &match[mi]); + if (match[mi+4].rm_so != -1) /* IPv6 address in square brackets */ + ip = get_string_arg (line, &match[mi+4]); + else + ip = get_string_arg (line, &match[mi]); if (!ip) return -1; - mi += 5; + mi += 16; port = (int) get_long_arg (line, &match[mi]); mi += 3; diff --git a/src/conf.h b/src/conf.h index 99140492..0b25afa4 100644 --- a/src/conf.h +++ b/src/conf.h @@ -39,6 +39,7 @@ typedef struct { */ struct config_s { sblist *basicauth_list; + char *basicauth_realm; char *logf_name; unsigned int syslog; /* boolean */ unsigned int port; @@ -50,9 +51,7 @@ struct config_s { sblist *listen_addrs; #ifdef FILTER_ENABLE char *filter; - unsigned int filter_url; /* boolean */ - unsigned int filter_extended; /* boolean */ - unsigned int filter_casesensitive; /* boolean */ + unsigned int filter_opts; /* enum filter_options */ #endif /* FILTER_ENABLE */ #ifdef XTINYPROXY_ENABLE unsigned int add_xtinyproxy; /* boolean */ diff --git a/src/filter.c b/src/filter.c index b9b50661..0dbc93d2 100644 --- a/src/filter.c +++ b/src/filter.c @@ -25,6 +25,7 @@ #include "main.h" #include +#include #include "filter.h" #include "heap.h" #include "log.h" @@ -37,15 +38,17 @@ static int err; struct filter_list { - regex_t cpatb; + union { + regex_t cpatb; + char *pattern; + } u; }; static sblist *fl = NULL; static int already_init = 0; -static filter_policy_t default_policy = FILTER_DEFAULT_ALLOW; /* - * Initializes a linked list of strings containing hosts/urls to be filtered + * Initializes a list of strings containing hosts/urls to be filtered */ void filter_init (void) { @@ -66,10 +69,8 @@ void filter_init (void) } cflags = REG_NEWLINE | REG_NOSUB; - if (config->filter_extended) - cflags |= REG_EXTENDED; - if (!config->filter_casesensitive) - cflags |= REG_ICASE; + cflags |= (REG_EXTENDED * !!(config->filter_opts & FILTER_OPT_TYPE_ERE)); + cflags |= (REG_ICASE * !(config->filter_opts & FILTER_OPT_CASESENSITIVE)); while (fgets (buf, FILTER_BUFFER_LEN, fd)) { ++lineno; @@ -107,13 +108,19 @@ void filter_init (void) if (!fl) fl = sblist_new(sizeof(struct filter_list), 4096/sizeof(struct filter_list)); - err = regcomp (&fe.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 (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 (!sblist_add(fl, &fe)) { oom:; @@ -142,7 +149,10 @@ void filter_destroy (void) if (fl) { for (i = 0; i < sblist_getsize(fl); ++i) { p = sblist_get(fl, i); - regfree (&p->cpatb); + if (config->filter_opts & FILTER_OPT_TYPE_FNMATCH) + safefree(p->u.pattern); + else + regfree (&p->u.cpatb); } sblist_free(fl); } @@ -175,11 +185,14 @@ int filter_run (const char *str) for (i = 0; i < sblist_getsize(fl); ++i) { p = sblist_get(fl, i); - result = - regexec (&p->cpatb, str, (size_t) 0, (regmatch_t *) 0, 0); + 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); if (result == 0) { - if (default_policy == FILTER_DEFAULT_ALLOW) + if (!(config->filter_opts & FILTER_OPT_DEFAULT_DENY)) return 1; else return 0; @@ -187,16 +200,8 @@ int filter_run (const char *str) } COMMON_EXIT: - if (default_policy == FILTER_DEFAULT_ALLOW) + if (!(config->filter_opts & FILTER_OPT_DEFAULT_DENY)) return 0; else return 1; } - -/* - * Set the default filtering policy - */ -void filter_set_default_policy (filter_policy_t policy) -{ - default_policy = policy; -} diff --git a/src/filter.h b/src/filter.h index 8a7575bf..e5f34687 100644 --- a/src/filter.h +++ b/src/filter.h @@ -21,16 +21,22 @@ #ifndef _TINYPROXY_FILTER_H_ #define _TINYPROXY_FILTER_H_ -typedef enum { - FILTER_DEFAULT_ALLOW, - FILTER_DEFAULT_DENY -} filter_policy_t; +enum filter_options { + FILTER_OPT_CASESENSITIVE = 1 << 0, + FILTER_OPT_URL = 1 << 1, + FILTER_OPT_DEFAULT_DENY = 1 << 2, + + FILTER_OPT_TYPE_BRE = 1 << 8, + FILTER_OPT_TYPE_ERE = 1 << 9, + FILTER_OPT_TYPE_FNMATCH = 1 << 10, +}; + +#define FILTER_TYPE_MASK \ + (FILTER_OPT_TYPE_BRE | FILTER_OPT_TYPE_ERE | FILTER_OPT_TYPE_FNMATCH) extern void filter_init (void); extern void filter_destroy (void); extern void filter_reload (void); extern int filter_run (const char *str); -extern void filter_set_default_policy (filter_policy_t policy); - #endif diff --git a/src/hsearch.c b/src/hsearch.c index be0434c2..dfe1404a 100644 --- a/src/hsearch.c +++ b/src/hsearch.c @@ -80,9 +80,10 @@ 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 = htab->elems + htab->mask + 1; + struct elem *oldend; if (nel > MAXSIZE) nel = MAXSIZE; @@ -95,6 +96,8 @@ static int resize(struct htab *htab, size_t nel) 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++) { diff --git a/src/html-error.c b/src/html-error.c index 998a6ee7..2b870402 100644 --- a/src/html-error.c +++ b/src/html-error.c @@ -30,6 +30,7 @@ #include "network.h" #include "utils.h" #include "conf.h" +#include "log.h" #include @@ -139,14 +140,14 @@ int send_http_headers ( { const char headers[] = "HTTP/1.%u %d %s\r\n" - "Server: %s/%s\r\n" + "Server: %s\r\n" "Content-Type: text/html\r\n" "%s" "Connection: close\r\n" "\r\n"; return (write_message (connptr->client_fd, headers, connptr->protocol.major != 1 ? 0 : connptr->protocol.minor, - code, message, PACKAGE, VERSION, + code, message, PACKAGE, extra)); } @@ -168,33 +169,47 @@ int send_http_error_message (struct conn_s *connptr) "

%s

\n" "

%s

\n" "
\n" - "

Generated by %s version %s.

\n" "\n" + "

Generated by %s.

\n" "\n" "\n"; - const char p_auth_str[] = - "Proxy-Authenticate: Basic realm=\"" - PACKAGE_NAME "\"\r\n"; - - const char w_auth_str[] = - "WWW-Authenticate: Basic realm=\"" - PACKAGE_NAME "\"\r\n"; - /* according to rfc7235, the 407 error must be accompanied by a Proxy-Authenticate header field. */ - const char *add = connptr->error_number == 407 ? p_auth_str : - (connptr->error_number == 401 ? w_auth_str : ""); + 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, add); + connptr->error_string, auth_str_add ? auth_str_add : ""); + + if (auth_str_add) safefree (auth_str_add); error_file = get_html_file (connptr->error_number); - if (!(infile = fopen (error_file, "r"))) { - char *detail = lookup_variable (connptr->error_variables, "detail"); + if (!error_file || !(infile = fopen (error_file, "r"))) { + char *detail; + if (error_file) log_message (LOG_ERR, + "Error opening error file '%s' (%s)", + error_file, strerror (errno)); + detail = lookup_variable (connptr->error_variables, "detail"); return (write_message (connptr->client_fd, fallback_error, connptr->error_number, connptr->error_string, connptr->error_string, - detail, PACKAGE, VERSION)); + detail, PACKAGE)); } ret = send_html_file (infile, connptr); diff --git a/src/log.c b/src/log.c index 14fc3fe9..a9ff71d2 100644 --- a/src/log.c +++ b/src/log.c @@ -165,12 +165,8 @@ void log_message (int level, const char *fmt, ...) if (config->syslog) { pthread_mutex_lock(&log_mutex); -#ifdef HAVE_VSYSLOG_H - vsyslog (level, fmt, args); -#else vsnprintf (str, STRING_LENGTH, fmt, args); syslog (level, "%s", str); -#endif pthread_mutex_unlock(&log_mutex); } else { char *p; diff --git a/src/main.c b/src/main.c index 7ea55408..268255f1 100644 --- a/src/main.c +++ b/src/main.c @@ -257,7 +257,7 @@ int reload_config (int reload_logging) int ret, ret2; struct config_s *c_next = get_next_config(); - log_message (LOG_NOTICE, "Reloading config file"); + log_message (LOG_NOTICE, "Reloading config file (%s)", config_file); if (reload_logging) shutdown_logging (); diff --git a/src/orderedmap.c b/src/orderedmap.c deleted file mode 100644 index 1818e276..00000000 --- a/src/orderedmap.c +++ /dev/null @@ -1,115 +0,0 @@ -#ifdef HAVE_CONFIG_H -# include -#else -# define _ALL_SOURCE -# define _GNU_SOURCE -#endif -#include -#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; -} diff --git a/src/orderedmap.h b/src/orderedmap.h deleted file mode 100644 index e3f4b0eb..00000000 --- a/src/orderedmap.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef ORDEREDMAP_H -#define ORDEREDMAP_H - -#include -#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 diff --git a/src/pseudomap.c b/src/pseudomap.c new file mode 100644 index 00000000..3b62e5bb --- /dev/null +++ b/src/pseudomap.c @@ -0,0 +1,99 @@ +#include "config.h" +#include "pseudomap.h" +#include +#include +#include + +/* this data structure implements a pseudo hashmap. + tinyproxy originally used a hashmap for keeping the key/value pairs + in HTTP requests; however later it turned out that items need to be + returned in order - so we implemented an "orderedmap". + again, later it turned out that there are are special case headers, + namely Set-Cookie that can happen more than once, so a hashmap isn't the + right structure to hold the key-value pairs in HTTP headers. + it's expected that: + 1) the number of headers in a HTTP request we have to process is + not big enough to cause a noticable performance drop when we have + to iterate through our list to find the right header; and + 2) use of plain HTTP is getting exceedingly extinct by the day, so + in most usecases CONNECT method is used anyway. +*/ + +/* restrict the number of headers to 256 to prevent an attacker from + launching a denial of service attack. */ +#define MAX_SIZE 256 + +pseudomap *pseudomap_create(void) { + return sblist_new(sizeof(struct pseudomap_entry), 64); +} + +void pseudomap_destroy(pseudomap *o) { + if(!o) return; + while(sblist_getsize(o)) { + /* retrieve latest element, and "shrink" list in place, + so we don't have to constantly rearrange list items + by using sblist_delete(). */ + struct pseudomap_entry *e = sblist_get(o, sblist_getsize(o)-1); + free(e->key); + free(e->value); + --o->count; + } + sblist_free(o); +} + +int pseudomap_append(pseudomap *o, const char *key, char *value ) { + struct pseudomap_entry e; + if(sblist_getsize(o) >= MAX_SIZE) return 0; + e.key = strdup(key); + e.value = strdup(value); + if(!e.key || !e.value) goto oom; + if(!sblist_add(o, &e)) goto oom; + return 1; +oom: + free(e.key); + free(e.value); + return 0; +} + +static size_t pseudomap_find_index(pseudomap *o, const char *key) { + size_t i; + struct pseudomap_entry *e; + for(i = 0; i < sblist_getsize(o); ++i) { + e = sblist_get(o, i); + if(!strcasecmp(key, e->key)) return i; + } + return (size_t)-1; +} + +char* pseudomap_find(pseudomap *o, const char *key) { + struct pseudomap_entry *e; + size_t i = pseudomap_find_index(o, key); + if(i == (size_t)-1) return 0; + e = sblist_get(o, i); + return e->value; +} + +/* remove *all* entries that match key, to mimic behaviour of hashmap */ +int pseudomap_remove(pseudomap *o, const char *key) { + struct pseudomap_entry *e; + size_t i; + int ret = 0; + while((i = pseudomap_find_index(o, key)) != (size_t)-1) { + e = sblist_get(o, i); + free(e->key); + free(e->value); + sblist_delete(o, i); + ret = 1; + } + return ret; +} + +size_t pseudomap_next(pseudomap *o, size_t iter, char** key, char** value) { + struct pseudomap_entry *e; + if(iter >= sblist_getsize(o)) return 0; + e = sblist_get(o, iter); + *key = e->key; + *value = e->value; + return iter + 1; +} + diff --git a/src/pseudomap.h b/src/pseudomap.h new file mode 100644 index 00000000..24c67b7d --- /dev/null +++ b/src/pseudomap.h @@ -0,0 +1,22 @@ +#ifndef PSEUDOMAP_H +#define PSEUDOMAP_H + +#include +#include "sblist.h" + +struct pseudomap_entry { + char *key; + char *value; +}; + +typedef sblist pseudomap; + +pseudomap *pseudomap_create(void); +void pseudomap_destroy(pseudomap *o); +int pseudomap_append(pseudomap *o, const char *key, char *value ); +char* pseudomap_find(pseudomap *o, const char *key); +int pseudomap_remove(pseudomap *o, const char *key); +size_t pseudomap_next(pseudomap *o, size_t iter, char** key, char** value); + +#endif + diff --git a/src/reqs.c b/src/reqs.c index 50f69144..2e7542cb 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -33,7 +33,7 @@ #include "conns.h" #include "filter.h" #include "hsearch.h" -#include "orderedmap.h" +#include "pseudomap.h" #include "heap.h" #include "html-error.h" #include "log.h" @@ -174,7 +174,7 @@ static int strip_return_port (char *host) { char *ptr1; char *ptr2; - int port; + unsigned port; ptr1 = strrchr (host, ':'); if (ptr1 == NULL) @@ -186,8 +186,11 @@ static int strip_return_port (char *host) return 0; *ptr1++ = '\0'; - if (sscanf (ptr1, "%d", &port) != 1) /* one conversion required */ - return 0; + + port = atoi(ptr1); + /* check that port string is in the valid range 1-0xffff) */ + if(strlen(ptr1) > 5 || (port & 0xffff0000)) return 0; + return port; } @@ -301,21 +304,27 @@ establish_http_connection (struct conn_s *connptr, struct request_s *request) } /* - * These two defines are for the SSL tunnelling. - */ -#define SSL_CONNECTION_RESPONSE "HTTP/1.0 200 Connection established" -#define PROXY_AGENT "Proxy-agent: " PACKAGE "/" VERSION - -/* - * Send the appropriate response to the client to establish a SSL - * connection. + * Send the appropriate response to the client to establish a + * connection via CONNECT method. */ -static int send_ssl_response (struct conn_s *connptr) +static int send_connect_method_response (struct conn_s *connptr) { return write_message (connptr->client_fd, - "%s\r\n" - "%s\r\n" - "\r\n", SSL_CONNECTION_RESPONSE, PROXY_AGENT); + "HTTP/1.%u 200 Connection established\r\n" + "Proxy-agent: " PACKAGE "\r\n" + "\r\n", connptr->protocol.major != 1 ? 0 : + connptr->protocol.minor); +} + +/* determine whether a hostname with optional trailing colon/port is the + stathost */ +static int is_stathost (const char* host) +{ + const char *p = config->stathost; + const char *q = host; + if (!p || !q) return 0; + while (*p && *(p++) == *(q++)); + return *p == 0 && (*q == 0 || *q == ':'); } /* @@ -323,13 +332,15 @@ static int send_ssl_response (struct conn_s *connptr) * build a new request line. Finally connect to the remote server. */ static struct request_s *process_request (struct conn_s *connptr, - orderedmap hashofheaders) + pseudomap *hashofheaders) { char *url; struct request_s *request; - int ret; + int ret, skip_trans; size_t request_len; + skip_trans = 0; + /* NULL out all the fields so frees don't cause segfaults. */ request = (struct request_s *) safecalloc (1, sizeof (struct request_s)); @@ -346,8 +357,12 @@ static struct request_s *process_request (struct conn_s *connptr, goto fail; } + /* zero-terminate the strings so they don't contain junk in error page */ + request->method[0] = url[0] = request->protocol[0] = 0; + ret = sscanf (connptr->request_line, "%[^ ] %[^ ] %[^ ]", request->method, url, request->protocol); + if (ret == 2 && !strcasecmp (request->method, "GET")) { request->protocol[0] = 0; @@ -380,6 +395,16 @@ static struct request_s *process_request (struct conn_s *connptr, goto fail; } + /* + * Check to see if they're requesting the stat host + */ + if (is_stathost (pseudomap_find (hashofheaders, "host"))) { +got_stathost: + log_message (LOG_NOTICE, "Request for the stathost."); + connptr->show_stats = TRUE; + goto fail; + } + #ifdef REVERSE_SUPPORT if (config->reversepath_list != NULL) { /* @@ -402,6 +427,7 @@ static struct request_s *process_request (struct conn_s *connptr, } safefree (url); url = reverse_url; + skip_trans = 1; } else if (config->reverseonly) { log_message (LOG_ERR, "Bad request, no mapping for '%s' found", @@ -451,11 +477,13 @@ static struct request_s *process_request (struct conn_s *connptr, connptr->connect_method = TRUE; } else { #ifdef TRANSPARENT_PROXY - if (!do_transparent_proxy - (connptr, hashofheaders, request, config, &url)) { - goto fail; - } -#else + if (!skip_trans) { + if (!do_transparent_proxy + (connptr, hashofheaders, request, config, &url)) + goto fail; + } else +#endif + { indicate_http_error (connptr, 501, "Not Implemented", "detail", "Unknown method or unsupported protocol.", @@ -463,7 +491,7 @@ static struct request_s *process_request (struct conn_s *connptr, log_message (LOG_INFO, "Unknown method (%s) or protocol (%s)", request->method, url); goto fail; -#endif + } } #ifdef FILTER_ENABLE @@ -471,22 +499,16 @@ static struct request_s *process_request (struct conn_s *connptr, * Filter restricted domains/urls */ if (config->filter) { - if (config->filter_url) - ret = filter_run (url); - else - ret = filter_run (request->host); + int fu = config->filter_opts & FILTER_OPT_URL; + ret = filter_run (fu ? url : request->host); if (ret) { update_stats (STAT_DENIED); - if (config->filter_url) - log_message (LOG_NOTICE, - "Proxying refused on filtered url \"%s\"", - url); - else - log_message (LOG_NOTICE, - "Proxying refused on filtered domain \"%s\"", - request->host); + log_message (LOG_NOTICE, + "Proxying refused on filtered %s \"%s\"", + fu ? "url" : "domain", + fu ? url : request->host); indicate_http_error (connptr, 403, "Filtered", "detail", @@ -496,19 +518,11 @@ static struct request_s *process_request (struct conn_s *connptr, } } #endif - - - /* - * Check to see if they're requesting the stat host - */ - if (config->stathost && strcmp (config->stathost, request->host) == 0) { - log_message (LOG_NOTICE, "Request for the stathost."); - connptr->show_stats = TRUE; - goto fail; - } + /* check whether hostname from url is the stathost */ + if (is_stathost (request->host)) + goto got_stathost; safefree (url); - return request; fail: @@ -523,11 +537,10 @@ static struct request_s *process_request (struct conn_s *connptr, * server headers can be processed. * - rjkaes */ -static int pull_client_data (struct conn_s *connptr, long int length, int iehack) +static int pull_client_data (struct conn_s *connptr, long int length) { char *buffer; ssize_t len; - int ret; buffer = (char *) safemalloc (min (MAXBUFFSIZE, (unsigned long int) length)); @@ -548,43 +561,6 @@ static int pull_client_data (struct conn_s *connptr, long int length, int iehack length -= len; } while (length > 0); - if (iehack) { - /* - * BUG FIX: Internet Explorer will leave two bytes (carriage - * return and line feed) at the end of a POST message. These - * need to be eaten for tinyproxy to work correctly. - */ - ret = socket_nonblocking (connptr->client_fd); - if (ret != 0) { - log_message(LOG_ERR, "Failed to set the client socket " - "to non-blocking: %s", strerror(errno)); - goto ERROR_EXIT; - } - - len = recv (connptr->client_fd, buffer, 2, MSG_PEEK); - - ret = socket_blocking (connptr->client_fd); - if (ret != 0) { - log_message(LOG_ERR, "Failed to set the client socket " - "to blocking: %s", strerror(errno)); - goto ERROR_EXIT; - } - - if (len < 0 && errno != EAGAIN) - goto ERROR_EXIT; - - if ((len == 2) && CHECK_CRLF (buffer, len)) { - ssize_t bytes_read; - - bytes_read = read (connptr->client_fd, buffer, 2); - if (bytes_read == -1) { - log_message - (LOG_WARNING, - "Could not read two bytes from POST message"); - } - } - } - safefree (buffer); return 0; @@ -612,8 +588,10 @@ static int pull_client_data_chunked (struct conn_s *connptr) { } chunklen = strtol (buffer, (char**)0, 16); + /* prevent negative or huge values causing overflow */ + if (chunklen < 0 || chunklen > 0x0fffffff) goto ERROR_EXIT; - if (pull_client_data (connptr, chunklen+2, 0) < 0) + if (pull_client_data (connptr, chunklen+2) < 0) goto ERROR_EXIT; if(!chunklen) break; @@ -647,7 +625,7 @@ static int add_xtinyproxy_header (struct conn_s *connptr) * can be retrieved and manipulated later. */ static int -add_header_to_connection (orderedmap hashofheaders, char *header, size_t len) +add_header_to_connection (pseudomap *hashofheaders, char *header, size_t len) { char *sep; @@ -665,7 +643,7 @@ add_header_to_connection (orderedmap hashofheaders, char *header, size_t len) /* Calculate the new length of just the data */ len -= sep - header - 1; - return orderedmap_append (hashofheaders, header, sep); + return pseudomap_append (hashofheaders, header, sep); } /* @@ -678,7 +656,7 @@ add_header_to_connection (orderedmap hashofheaders, char *header, size_t len) /* * Read all the headers from the stream */ -static int get_all_headers (int fd, orderedmap hashofheaders) +static int get_all_headers (int fd, pseudomap *hashofheaders) { char *line = NULL; char *header = NULL; @@ -771,7 +749,7 @@ static int get_all_headers (int fd, orderedmap hashofheaders) * Extract the headers to remove. These headers were listed in the Connection * and Proxy-Connection headers. */ -static int remove_connection_headers (orderedmap hashofheaders) +static int remove_connection_headers (pseudomap *hashofheaders) { static const char *headers[] = { "connection", @@ -781,11 +759,11 @@ static int remove_connection_headers (orderedmap hashofheaders) char *data; char *ptr; ssize_t len; - int i; + int i,j,df; for (i = 0; i != (sizeof (headers) / sizeof (char *)); ++i) { /* Look for the connection header. If it's not found, return. */ - data = orderedmap_find(hashofheaders, headers[i]); + data = pseudomap_find(hashofheaders, headers[i]); if (!data) return 0; @@ -806,7 +784,12 @@ static int remove_connection_headers (orderedmap hashofheaders) */ ptr = data; while (ptr < data + len) { - orderedmap_remove (hashofheaders, ptr); + df = 0; + /* check that ptr isn't one of headers to prevent + double-free (CVE-2023-49606) */ + for (j = 0; j != (sizeof (headers) / sizeof (char *)); ++j) + if(!strcasecmp(ptr, headers[j])) df = 1; + if (!df) pseudomap_remove (hashofheaders, ptr); /* Advance ptr to the next token */ ptr += strlen (ptr) + 1; @@ -815,7 +798,7 @@ static int remove_connection_headers (orderedmap hashofheaders) } /* Now remove the connection header it self. */ - orderedmap_remove (hashofheaders, headers[i]); + pseudomap_remove (hashofheaders, headers[i]); } return 0; @@ -825,12 +808,12 @@ static int remove_connection_headers (orderedmap hashofheaders) * If there is a Content-Length header, then return the value; otherwise, return * -1. */ -static long get_content_length (orderedmap hashofheaders) +static long get_content_length (pseudomap *hashofheaders) { char *data; long content_length = -1; - data = orderedmap_find (hashofheaders, "content-length"); + data = pseudomap_find (hashofheaders, "content-length"); if (data) content_length = atol (data); @@ -838,11 +821,11 @@ static long get_content_length (orderedmap hashofheaders) return content_length; } -static int is_chunked_transfer (orderedmap hashofheaders) +static int is_chunked_transfer (pseudomap *hashofheaders) { char *data; - data = orderedmap_find (hashofheaders, "transfer-encoding"); - return data ? !strcmp (data, "chunked") : 0; + data = pseudomap_find (hashofheaders, "transfer-encoding"); + return data ? !strcasecmp (data, "chunked") : 0; } /* @@ -853,7 +836,7 @@ static int is_chunked_transfer (orderedmap hashofheaders) * purposes. */ static int -write_via_header (int fd, orderedmap hashofheaders, +write_via_header (int fd, pseudomap *hashofheaders, unsigned int major, unsigned int minor) { char hostname[512]; @@ -875,29 +858,23 @@ write_via_header (int fd, orderedmap hashofheaders, * See if there is a "Via" header. If so, again we need to do a bit * of processing. */ - data = orderedmap_find (hashofheaders, "via"); + data = pseudomap_find (hashofheaders, "via"); if (data) { ret = write_message (fd, - "Via: %s, %hu.%hu %s (%s/%s)\r\n", - data, major, minor, hostname, PACKAGE, - VERSION); + "Via: %s, %hu.%hu %s (%s)\r\n", + data, major, minor, hostname, PACKAGE); - orderedmap_remove (hashofheaders, "via"); + pseudomap_remove (hashofheaders, "via"); } else { ret = write_message (fd, - "Via: %hu.%hu %s (%s/%s)\r\n", - major, minor, hostname, PACKAGE, VERSION); + "Via: %hu.%hu %s (%s)\r\n", + major, minor, hostname, PACKAGE); } done: return ret; } -/* - * Number of buckets to use internally in the hashmap. - */ -#define HEADER_BUCKETS 32 - /* * Here we loop through all the headers the client is sending. If we * are running in anonymous mode, we will _only_ send the headers listed @@ -905,7 +882,7 @@ write_via_header (int fd, orderedmap hashofheaders, * - rjkaes */ static int -process_client_headers (struct conn_s *connptr, orderedmap hashofheaders) +process_client_headers (struct conn_s *connptr, pseudomap *hashofheaders) { static const char *skipheaders[] = { "host", @@ -953,7 +930,7 @@ process_client_headers (struct conn_s *connptr, orderedmap hashofheaders) * Delete the headers listed in the skipheaders list */ for (i = 0; i != (sizeof (skipheaders) / sizeof (char *)); i++) { - orderedmap_remove (hashofheaders, skipheaders[i]); + pseudomap_remove (hashofheaders, skipheaders[i]); } /* Send, or add the Via header */ @@ -974,7 +951,7 @@ process_client_headers (struct conn_s *connptr, orderedmap hashofheaders) * Output all the remaining headers to the remote machine. */ iter = 0; - while((iter = orderedmap_next(hashofheaders, iter, &data, &header))) { + while((iter = pseudomap_next(hashofheaders, iter, &data, &header))) { if (!is_anonymous_enabled (config) || anonymous_search (config, data) > 0) { ret = @@ -1007,7 +984,7 @@ process_client_headers (struct conn_s *connptr, orderedmap hashofheaders) PULL_CLIENT_DATA: if (connptr->content_length.client > 0) { ret = pull_client_data (connptr, - connptr->content_length.client, 1); + connptr->content_length.client); } else if (connptr->content_length.client == -2) ret = pull_client_data_chunked (connptr); @@ -1029,7 +1006,7 @@ static int process_server_headers (struct conn_s *connptr) char *response_line; - orderedmap hashofheaders; + pseudomap *hashofheaders; size_t iter; char *data, *header; ssize_t len; @@ -1059,7 +1036,7 @@ static int process_server_headers (struct conn_s *connptr) goto retry; } - hashofheaders = orderedmap_create (HEADER_BUCKETS); + hashofheaders = pseudomap_create (); if (!hashofheaders) { safefree (response_line); return -1; @@ -1071,7 +1048,7 @@ static int process_server_headers (struct conn_s *connptr) if (get_all_headers (connptr->server_fd, hashofheaders) < 0) { log_message (LOG_WARNING, "Could not retrieve all the headers from the remote server."); - orderedmap_destroy (hashofheaders); + pseudomap_destroy (hashofheaders); safefree (response_line); indicate_http_error (connptr, 503, @@ -1090,7 +1067,7 @@ static int process_server_headers (struct conn_s *connptr) * Instead we'll free all the memory and return. */ if (connptr->protocol.major < 1) { - orderedmap_destroy (hashofheaders); + pseudomap_destroy (hashofheaders); safefree (response_line); return 0; } @@ -1117,7 +1094,7 @@ static int process_server_headers (struct conn_s *connptr) * Delete the headers listed in the skipheaders list */ for (i = 0; i != (sizeof (skipheaders) / sizeof (char *)); i++) { - orderedmap_remove (hashofheaders, skipheaders[i]); + pseudomap_remove (hashofheaders, skipheaders[i]); } /* Send, or add the Via header */ @@ -1139,7 +1116,7 @@ static int process_server_headers (struct conn_s *connptr) /* Rewrite the HTTP redirect if needed */ if (config->reversebaseurl && - (header = orderedmap_find (hashofheaders, "location"))) { + (header = pseudomap_find (hashofheaders, "location"))) { /* Look for a matching entry in the reversepath list */ while (reverse) { @@ -1164,7 +1141,7 @@ static int process_server_headers (struct conn_s *connptr) "Rewriting HTTP redirect: %s -> %s%s%s", header, config->reversebaseurl, (reverse->path + 1), (header + len)); - orderedmap_remove (hashofheaders, "location"); + pseudomap_remove (hashofheaders, "location"); } } #endif @@ -1173,14 +1150,14 @@ static int process_server_headers (struct conn_s *connptr) * All right, output all the remaining headers to the client. */ iter = 0; - while ((iter = orderedmap_next(hashofheaders, iter, &data, &header))) { + while ((iter = pseudomap_next(hashofheaders, iter, &data, &header))) { ret = write_message (connptr->client_fd, "%s: %s\r\n", data, header); if (ret < 0) goto ERROR_EXIT; } - orderedmap_destroy (hashofheaders); + pseudomap_destroy (hashofheaders); /* Write the final blank line to signify the end of the headers */ if (safe_write (connptr->client_fd, "\r\n", 2) < 0) @@ -1189,13 +1166,13 @@ static int process_server_headers (struct conn_s *connptr) return 0; ERROR_EXIT: - orderedmap_destroy (hashofheaders); + pseudomap_destroy (hashofheaders); return -1; } /* - * Switch the sockets into nonblocking mode and begin relaying the bytes - * between the two connections. We continue to use the buffering code + * Begin relaying the bytes between the two connections. + * We continue to use the buffering code * since we want to be able to buffer a certain amount for slower * connections (as this was the reason why I originally modified * tinyproxy oh so long ago...) @@ -1268,14 +1245,6 @@ static void relay_connection (struct conn_s *connptr) /* * Try to send any remaining data to the server if we can. */ - ret = socket_blocking (connptr->server_fd); - if (ret != 0) { - log_message(LOG_ERR, - "Failed to set server socket to blocking: %s", - strerror(errno)); - return; - } - while (buffer_size (connptr->cbuffer) > 0) { if (write_buffer (connptr->server_fd, connptr->cbuffer) < 0) break; @@ -1284,6 +1253,7 @@ static void relay_connection (struct conn_s *connptr) return; } +#ifdef UPSTREAM_SUPPORT static int connect_to_upstream_proxy(struct conn_s *connptr, struct request_s *request) { @@ -1400,7 +1370,7 @@ connect_to_upstream_proxy(struct conn_s *connptr, struct request_s *request) return establish_http_connection(connptr, request); } - +#endif /* * Establish a connection to the upstream proxy server. @@ -1556,18 +1526,24 @@ static void handle_connection_failure(struct conn_s *connptr, int got_headers) } } +static void auth_error(struct conn_s *connptr, int code) { + const char *tit = code == 401 ? "Unauthorized" : "Proxy Authentication Required"; + const char *msg = code == 401 ? + "The administrator of this proxy has not configured it to service requests from you." : + "This proxy requires authentication."; -/* - * This is the main drive for each connection. As you can tell, for the - * first few steps we are using a blocking socket. If you remember the - * older tinyproxy code, this use to be a very confusing state machine. - * Well, no more! :) The sockets are only switched into nonblocking mode - * when we start the relay portion. This makes most of the original - * tinyproxy code, which was confusing, redundant. Hail progress. - * - rjkaes + update_stats (STAT_DENIED); + log_message (LOG_INFO, + "Failed auth attempt (file descriptor: %d), ip %s", + connptr->client_fd, + connptr->client_ip_addr); + indicate_http_error (connptr, code, tit, "detail", msg, NULL); +} +/* + * This is the main drive for each connection. * this function is called directly from child_thread() with the newly - * received fd from accept(). + * received fd from accept(). */ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) { @@ -1579,7 +1555,7 @@ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) int got_headers = 0, fd = connptr->client_fd; size_t i; struct request_s *request = NULL; - orderedmap hashofheaders = NULL; + pseudomap *hashofheaders = NULL; char sock_ipaddr[IP_LENGTH]; char peer_ipaddr[IP_LENGTH]; @@ -1628,17 +1604,13 @@ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) if (read_request_line (connptr) < 0) { update_stats (STAT_BADCONN); - indicate_http_error (connptr, 408, "Timeout", - "detail", - "Server timeout waiting for the HTTP request " - "from the client.", NULL); - HC_FAIL(); + goto done; } /* * The "hashofheaders" store the client's headers. */ - hashofheaders = orderedmap_create (HEADER_BUCKETS); + hashofheaders = pseudomap_create (); if (hashofheaders == NULL) { update_stats (STAT_BADCONN); indicate_http_error (connptr, 503, "Internal error", @@ -1667,23 +1639,18 @@ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) if (config->basicauth_list != NULL) { char *authstring; int failure = 1, stathost_connect = 0; - authstring = orderedmap_find (hashofheaders, "proxy-authorization"); + authstring = pseudomap_find (hashofheaders, "proxy-authorization"); if (!authstring && config->stathost) { - authstring = orderedmap_find (hashofheaders, "host"); - if (authstring && !strncmp(authstring, config->stathost, strlen(config->stathost))) { - authstring = orderedmap_find (hashofheaders, "authorization"); + authstring = pseudomap_find (hashofheaders, "host"); + if (authstring && is_stathost(authstring)) { + authstring = pseudomap_find (hashofheaders, "authorization"); stathost_connect = 1; } else authstring = 0; } if (!authstring) { - if (stathost_connect) goto e401; - update_stats (STAT_DENIED); - indicate_http_error (connptr, 407, "Proxy Authentication Required", - "detail", - "This proxy requires authentication.", - NULL); + auth_error(connptr, stathost_connect ? 401 : 407); HC_FAIL(); } if ( /* currently only "basic" auth supported */ @@ -1692,16 +1659,10 @@ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) basicauth_check (config->basicauth_list, authstring + 6) == 1) failure = 0; if(failure) { -e401: - update_stats (STAT_DENIED); - indicate_http_error (connptr, 401, "Unauthorized", - "detail", - "The administrator of this proxy has not configured " - "it to service requests from you.", - NULL); + auth_error(connptr, stathost_connect ? 401 : 407); HC_FAIL(); } - orderedmap_remove (hashofheaders, "proxy-authorization"); + pseudomap_remove (hashofheaders, "proxy-authorization"); } /* @@ -1712,7 +1673,7 @@ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) for (i = 0; i < sblist_getsize (config->add_headers); i++) { http_header_t *header = sblist_get (config->add_headers, i); - orderedmap_append (hashofheaders, header->name, header->value); + pseudomap_append (hashofheaders, header->name, header->value); } request = process_request (connptr, hashofheaders); @@ -1772,10 +1733,10 @@ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) HC_FAIL(); } } else { - if (send_ssl_response (connptr) < 0) { + if (send_connect_method_response (connptr) < 0) { log_message (LOG_ERR, - "handle_connection: Could not send SSL greeting " - "to client."); + "handle_connection: Could not send CONNECT" + " method greeting to client."); update_stats (STAT_BADCONN); HC_FAIL(); } @@ -1790,7 +1751,7 @@ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) done: free_request_struct (request); - orderedmap_destroy (hashofheaders); + pseudomap_destroy (hashofheaders); conn_destroy_contents (connptr); return; #undef HC_FAIL diff --git a/src/reverse-proxy.c b/src/reverse-proxy.c index 68f04a69..98b0963e 100644 --- a/src/reverse-proxy.c +++ b/src/reverse-proxy.c @@ -127,7 +127,7 @@ void free_reversepath_list (struct reversepath *reverse) /* * Rewrite the URL for reverse proxying. */ -char *reverse_rewrite_url (struct conn_s *connptr, orderedmap hashofheaders, +char *reverse_rewrite_url (struct conn_s *connptr, pseudomap *hashofheaders, char *url, int *status) { char *rewrite_url = NULL; @@ -153,7 +153,7 @@ char *reverse_rewrite_url (struct conn_s *connptr, orderedmap hashofheaders, sprintf (rewrite_url, "%s%s", reverse->url, url + lrp); } } else if (config->reversemagic - && (cookie = orderedmap_find (hashofheaders, + && (cookie = pseudomap_find (hashofheaders, "cookie"))) { /* No match - try the magical tracking cookie next */ diff --git a/src/reverse-proxy.h b/src/reverse-proxy.h index a9a5bdd5..7ded66d4 100644 --- a/src/reverse-proxy.h +++ b/src/reverse-proxy.h @@ -22,7 +22,7 @@ #define TINYPROXY_REVERSE_PROXY_H #include "conns.h" -#include "orderedmap.h" +#include "pseudomap.h" struct reversepath { struct reversepath *next; @@ -38,7 +38,7 @@ extern struct reversepath *reversepath_get (char *url, struct reversepath *reverse); void free_reversepath_list (struct reversepath *reverse); extern char *reverse_rewrite_url (struct conn_s *connptr, - orderedmap hashofheaders, char *url, + pseudomap *hashofheaders, char *url, int *status); #endif diff --git a/src/sock.c b/src/sock.c index b3b0920d..ec8ad4c8 100644 --- a/src/sock.c +++ b/src/sock.c @@ -212,33 +212,6 @@ int opensock (const char *host, int port, const char *bind_to) return sockfd; } -/* - * Set the socket to non blocking -rjkaes - */ -int socket_nonblocking (int sock) -{ - int flags; - - assert (sock >= 0); - - flags = fcntl (sock, F_GETFL, 0); - return fcntl (sock, F_SETFL, flags | O_NONBLOCK); -} - -/* - * Set the socket to blocking -rjkaes - */ -int socket_blocking (int sock) -{ - int flags; - - assert (sock >= 0); - - flags = fcntl (sock, F_GETFL, 0); - return fcntl (sock, F_SETFL, flags & ~O_NONBLOCK); -} - - /** * Try to listen on one socket based on the addrinfo * as returned from getaddrinfo. diff --git a/src/sock.h b/src/sock.h index 70c44739..9763490d 100644 --- a/src/sock.h +++ b/src/sock.h @@ -53,9 +53,6 @@ union sockaddr_union { 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 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); diff --git a/src/stats.c b/src/stats.c index 1228aa33..19607336 100644 --- a/src/stats.c +++ b/src/stats.c @@ -87,9 +87,9 @@ showstats (struct conn_s *connptr) "\n" "\n" - "%s version %s run-time statistics\n" + "%s run-time statistics\n" "\n" - "

%s version %s run-time statistics

\n" + "

%s run-time statistics

\n" "

\n" "Number of open connections: %lu
\n" "Number of requests: %lu
\n" @@ -98,13 +98,13 @@ showstats (struct conn_s *connptr) "Number of refused connections due to high load: %lu\n" "

\n" "
\n" - "

Generated by %s version %s.

\n" "\n" + "

Generated by %s.

\n" "\n" "\n", - PACKAGE, VERSION, PACKAGE, VERSION, + PACKAGE, PACKAGE, stats->num_open, stats->num_reqs, stats->num_badcons, stats->num_denied, - stats->num_refused, PACKAGE, VERSION); + stats->num_refused, PACKAGE); if (send_http_message (connptr, 200, "OK", message_buffer) < 0) { diff --git a/src/text.c b/src/text.c index 6bd46d22..e4bae39d 100644 --- a/src/text.c +++ b/src/text.c @@ -47,30 +47,6 @@ size_t strlcpy (char *dst, const char *src, size_t size) } #endif -#ifndef HAVE_STRLCAT -/* - * Function API taken from OpenBSD. Like strncat(), but does not 0 fill the - * buffer, and always NULL terminates the buffer. size is the length of the - * buffer, which should be one more than the maximum resulting string - * length. - */ -size_t strlcat (char *dst, const char *src, size_t size) -{ - size_t len1 = strlen (dst); - size_t len2 = strlen (src); - size_t ret = len1 + len2; - - if (len1 + len2 >= size) - len2 = size - len1 - 1; - if (len2 > 0) { - memcpy (dst + len1, src, len2); - dst[len1 + len2] = '\0'; - } - - return ret; -} -#endif - /* * Removes any new-line or carriage-return characters from the end of the * string. This function is named after the same function in Perl. diff --git a/src/text.h b/src/text.h index 0beb9b11..cfe631ab 100644 --- a/src/text.h +++ b/src/text.h @@ -21,10 +21,6 @@ #ifndef TINYPROXY_TEXT_H #define TINYPROXY_TEXT_H -#ifndef HAVE_STRLCAT -extern size_t strlcat (char *dst, const char *src, size_t size); -#endif /* HAVE_STRLCAT */ - #ifndef HAVE_STRLCPY extern size_t strlcpy (char *dst, const char *src, size_t size); #endif /* HAVE_STRLCPY */ diff --git a/src/transparent-proxy.c b/src/transparent-proxy.c index d090ae37..d0ba2c86 100644 --- a/src/transparent-proxy.c +++ b/src/transparent-proxy.c @@ -53,7 +53,7 @@ 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, pseudomap *hashofheaders, struct request_s *request, struct config_s *conf, char **url) { @@ -62,7 +62,7 @@ do_transparent_proxy (struct conn_s *connptr, orderedmap hashofheaders, size_t ulen = strlen (*url); size_t i; - data = orderedmap_find (hashofheaders, "host"); + data = pseudomap_find (hashofheaders, "host"); if (!data) { union sockaddr_union dest_addr; const void *dest_inaddr; diff --git a/src/transparent-proxy.h b/src/transparent-proxy.h index b56c446e..1167aed2 100644 --- a/src/transparent-proxy.h +++ b/src/transparent-proxy.h @@ -26,11 +26,11 @@ #ifdef TRANSPARENT_PROXY #include "conns.h" -#include "orderedmap.h" +#include "pseudomap.h" #include "reqs.h" extern int do_transparent_proxy (struct conn_s *connptr, - orderedmap hashofheaders, + pseudomap *hashofheaders, struct request_s *request, struct config_s *config, char **url); diff --git a/src/utils.c b/src/utils.c index ef2e6732..69faef6f 100644 --- a/src/utils.c +++ b/src/utils.c @@ -39,7 +39,7 @@ send_http_message (struct conn_s *connptr, int http_code, const char *error_title, const char *message) { static const char *headers[] = { - "Server: " PACKAGE "/" VERSION, + "Server: " PACKAGE, "Content-type: text/html", "Connection: close" }; diff --git a/tests/scripts/run_tests.sh b/tests/scripts/run_tests.sh index 5a8ecd8f..7b76002b 100755 --- a/tests/scripts/run_tests.sh +++ b/tests/scripts/run_tests.sh @@ -18,7 +18,7 @@ # this program; if not, see . -SCRIPTS_DIR=$(cd $(dirname $0) && pwd) +SCRIPTS_DIR=$(cd "$(dirname "$0")" && pwd) BASEDIR=$SCRIPTS_DIR/../.. TESTS_DIR=$SCRIPTS_DIR/.. TESTENV_DIR=$TESTS_DIR/env @@ -51,27 +51,27 @@ WEBCLIENT_LOG=$LOG_DIR/webclient.log WEBCLIENT_BIN=$SCRIPTS_DIR/webclient.pl provision_initial() { - if test -e $TESTENV_DIR ; then + if test -e "$TESTENV_DIR" ; then TESTENV_DIR_OLD=$TESTENV_DIR.old - if test -e $TESTENV_DIR_OLD ; then - rm -rf $TESTENV_DIR_OLD + if test -e "$TESTENV_DIR_OLD" ; then + rm -rf "$TESTENV_DIR_OLD" fi - mv $TESTENV_DIR $TESTENV_DIR.old + mv "$TESTENV_DIR" "$TESTENV_DIR".old fi - mkdir -p $LOG_DIR + mkdir -p "$LOG_DIR" } provision_tinyproxy() { - mkdir -p $TINYPROXY_DATA_DIR - cp $BASEDIR/data/templates/default.html $TINYPROXY_DATA_DIR - cp $BASEDIR/data/templates/debug.html $TINYPROXY_DATA_DIR - cp $BASEDIR/data/templates/stats.html $TINYPROXY_DATA_DIR - mkdir -p $TINYPROXY_PID_DIR - mkdir -p $TINYPROXY_LOG_DIR - mkdir -p $TINYPROXY_CONF_DIR - - cat >>$TINYPROXY_CONF_FILE<>"$TINYPROXY_CONF_FILE"< $TINYPROXY_FILTER_FILE +cat << 'EOF' > "$TINYPROXY_FILTER_FILE" .*\.google-analytics\.com$ EOF } start_tinyproxy() { - echo -n "starting tinyproxy..." - $VALGRIND $TINYPROXY_BIN -c $TINYPROXY_CONF_FILE 2> $TINYPROXY_STDERR_LOG + printf "starting tinyproxy..." + $VALGRIND "$TINYPROXY_BIN" -c "$TINYPROXY_CONF_FILE" 2> "$TINYPROXY_STDERR_LOG" echo " done (listening on $TINYPROXY_IP:$TINYPROXY_PORT)" } reload_config() { - echo -n "signaling tinyproxy to reload config..." - pid=$(cat $TINYPROXY_PID_FILE) + printf "signaling tinyproxy to reload config..." + pid=$(cat "$TINYPROXY_PID_FILE") #1: SIGHUP - kill -1 $pid && echo "ok" || echo "fail" + kill -1 "$pid" && echo "ok" || echo "fail" } stop_tinyproxy() { - echo -n "killing tinyproxy..." - pid=$(cat $TINYPROXY_PID_FILE) - kill $pid - if test "x$?" = "x0" ; then + printf "killing tinyproxy..." + pid=$(cat "$TINYPROXY_PID_FILE") + kill "$pid" + if test "$?" = "0" ; then echo " ok" else echo " error killing pid $pid" - ps aux | grep tinyproxy + pgrep tinyproxy echo "### printing logfile" - cat $TINYPROXY_LOG_FILE + cat "$TINYPROXY_LOG_FILE" echo "### printing stderr logfile" - cat $TINYPROXY_STDERR_LOG + cat "$TINYPROXY_STDERR_LOG" fi } provision_webserver() { - mkdir -p $WEBSERVER_PID_DIR - mkdir -p $WEBSERVER_LOG_DIR + mkdir -p "$WEBSERVER_PID_DIR" + mkdir -p "$WEBSERVER_LOG_DIR" } start_webserver() { - echo -n "starting web server..." - $WEBSERVER_BIN --port $WEBSERVER_PORT --log-dir $WEBSERVER_LOG_DIR --pid-file $WEBSERVER_PID_FILE - echo " done (listening on $WEBSERVER_IP:$WEBSERVER_PORT)" + printf "starting web server..." + "$WEBSERVER_BIN" --port "$WEBSERVER_PORT" --log-dir "$WEBSERVER_LOG_DIR" --pid-file "$WEBSERVER_PID_FILE" + echo " done. listening on $WEBSERVER_IP:$WEBSERVER_PORT" } stop_webserver() { - echo -n "killing webserver..." - kill $(cat $WEBSERVER_PID_FILE) - if test "x$?" = "x0" ; then + printf "killing webserver..." + kill "$(cat "$WEBSERVER_PID_FILE")" + if test "$?" = "0" ; then echo " ok" else echo " error" - fi + fi } wait_for_some_seconds() { - SECONDS=$1 - if test "x$SECONDS" = "x" ; then - SECONDS=1 + seconds=$1 + if test "$seconds" = "" ; then + seconds=1 fi - echo -n "waiting for $SECONDS seconds." + printf 'waiting for %s seconds.' "$seconds" - for COUNT in $(seq 1 $SECONDS) ; do + for COUNT in $(seq 1 "$seconds") ; do sleep 1 - echo -n "." + printf ' %s ' "$COUNT" done - echo " done" + echo ' done' } 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 + if test "$WEBCLIENT_EXIT_CODE" = "0" ; then echo " ok" else - echo "ERROR ($WEBCLIENT_EXIT_CODE)" + echo "ERROR: $WEBCLIENT_EXIT_CODE" echo "webclient output:" - cat $WEBCLIENT_LOG + cat "$WEBCLIENT_LOG" echo "######################################" fi @@ -192,15 +192,15 @@ run_failure_webclient_request() { ec=$1 expected_error=$(($1 - 399)) shift - $WEBCLIENT_BIN "$1" "$2" "$3" "$4" > $WEBCLIENT_LOG 2>&1 + "$WEBCLIENT_BIN" "$1" "$2" "$3" "$4" > "$WEBCLIENT_LOG" 2>&1 WEBCLIENT_EXIT_CODE=$? - if test "x$WEBCLIENT_EXIT_CODE" = "x$expected_error" ; then + if test "$WEBCLIENT_EXIT_CODE" = "$expected_error" ; then echo " ok, got expected error code $ec" return 0 else - echo "ERROR ($WEBCLIENT_EXIT_CODE)" + echo "ERROR: $WEBCLIENT_EXIT_CODE" echo "webclient output:" - cat $WEBCLIENT_LOG + cat "$WEBCLIENT_LOG" echo "######################################" fi @@ -221,35 +221,35 @@ wait_for_some_seconds 1 FAILED=0 basic_test() { -echo -n "checking direct connection to web server..." +printf "checking direct connection to web server..." run_basic_webclient_request "$WEBSERVER_IP:$WEBSERVER_PORT" / -test "x$?" = "x0" || FAILED=$((FAILED + 1)) +test "$?" = "0" || FAILED=$((FAILED + 1)) -echo -n "testing connection through tinyproxy..." +printf "testing connection through tinyproxy..." run_basic_webclient_request "$TINYPROXY_IP:$TINYPROXY_PORT" "http://$WEBSERVER_IP:$WEBSERVER_PORT/" -test "x$?" = "x0" || FAILED=$((FAILED + 1)) +test "$?" = "0" || FAILED=$((FAILED + 1)) -echo -n "requesting statspage via stathost url..." +printf "requesting statspage via stathost url..." run_basic_webclient_request "$TINYPROXY_IP:$TINYPROXY_PORT" "http://$TINYPROXY_STATHOST_IP" -test "x$?" = "x0" || FAILED=$((FAILED + 1)) +test "$?" = "0" || FAILED=$((FAILED + 1)) } ext_test() { -echo -n "checking bogus request..." +printf "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)) +test "$?" = "0" || FAILED=$((FAILED + 1)) -echo -n "testing connection to filtered domain..." +printf "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)) +test "$?" = "0" || FAILED=$((FAILED + 1)) -echo -n "requesting connect method to denied port..." +printf "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)) +test "$?" = "0" || FAILED=$((FAILED + 1)) -echo -n "testing unavailable backend..." +printf "testing unavailable backend..." run_failure_webclient_request 502 "$TINYPROXY_IP:$TINYPROXY_PORT" "http://bogus.invalid" -test "x$?" = "x0" || FAILED=$((FAILED + 1)) +test "$?" = "0" || FAILED=$((FAILED + 1)) } basic_test @@ -259,10 +259,11 @@ ext_test echo "$FAILED errors" -if test "x$TINYPROXY_TESTS_WAIT" = "xyes"; then +if test "$TINYPROXY_TESTS_WAIT" = "yes"; then echo "You can continue using the webserver and tinyproxy." - echo -n "hit to stop the servers and exit: " - read READ + rintf "hit to stop the servers and exit: " + read -r READ + echo "$READ" > /dev/null fi stop_tinyproxy diff --git a/tests/scripts/run_tests_valgrind.sh b/tests/scripts/run_tests_valgrind.sh index 72b21154..1ebb4f71 100755 --- a/tests/scripts/run_tests_valgrind.sh +++ b/tests/scripts/run_tests_valgrind.sh @@ -18,11 +18,10 @@ # this program; if not, see . -SCRIPTS_DIR=$(dirname $0) -BASEDIR=$SCRIPTS_DIR/../.. +SCRIPTS_DIR=$(dirname "$0") 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 --track-origins=yes --show-leak-kinds=all --tool=memcheck --leak-check=full --log-file=$LOG_DIR/valgrind.log" "$SCRIPTS_DIR"/run_tests.sh diff --git a/tests/scripts/webclient.pl b/tests/scripts/webclient.pl index 4dddb69e..94df08b2 100755 --- a/tests/scripts/webclient.pl +++ b/tests/scripts/webclient.pl @@ -26,9 +26,8 @@ my $EOL = "\015\012"; -my $VERSION = "0.1"; my $NAME = "Tinyproxy-Web-Client"; -my $user_agent = "$NAME/$VERSION"; +my $user_agent = "$NAME"; my $user_agent_header = "User-Agent: $user_agent$EOL"; my $http_version = "1.0"; my $method = "GET"; diff --git a/tests/scripts/webserver.pl b/tests/scripts/webserver.pl index aa5eb137..2a6860fb 100755 --- a/tests/scripts/webserver.pl +++ b/tests/scripts/webserver.pl @@ -31,9 +31,8 @@ use Pod::Usage; use Fcntl ':flock'; # import LOCK_* constants -my $VERSION = "0.1"; my $NAME = "Tinyproxy-Test-Web-Server"; -my $server_header = "Server: $NAME/$VERSION"; +my $server_header = "Server: $NAME"; my $EOL = "\015\012";