From eefada62bd06b3320d54bca61810b47f588b8e21 Mon Sep 17 00:00:00 2001 From: basil00 Date: Thu, 4 Sep 2014 18:19:30 +0800 Subject: [PATCH] - TorWall reborn as Tallow. --- Makefile | 19 +- README.md | 81 +- build.sh | 49 +- config/config.txt | 24 - config/default.action | 1424 ---------------------------------- config/default.filter | 813 ------------------- config/match-all.action | 34 - config/tor_wall.exe.manifest | 12 - config/trust.txt | 2 - config/user.action | 197 ----- config/user.filter | 75 -- domain.c | 357 +++++++++ domain.h | 40 + hosts.deny | 22 + install.nsi | 65 +- main.c | 511 ++++++++++++ main.h | 86 ++ main.rc | 25 + manifest.xml | 7 + redirect.c | 757 ++++++++++++++++++ redirect.h | 26 + tallow.ico | Bin 0 -> 3629 bytes tor.ico | Bin 0 -> 4724 bytes tor_wall.c | 501 ------------ 24 files changed, 1926 insertions(+), 3201 deletions(-) delete mode 100644 config/config.txt delete mode 100644 config/default.action delete mode 100644 config/default.filter delete mode 100644 config/match-all.action delete mode 100644 config/tor_wall.exe.manifest delete mode 100644 config/trust.txt delete mode 100644 config/user.action delete mode 100644 config/user.filter create mode 100644 domain.c create mode 100644 domain.h create mode 100644 hosts.deny create mode 100644 main.c create mode 100644 main.h create mode 100644 main.rc create mode 100644 manifest.xml create mode 100644 redirect.c create mode 100644 redirect.h create mode 100644 tallow.ico create mode 100644 tor.ico delete mode 100644 tor_wall.c diff --git a/Makefile b/Makefile index 60f1d18..0acc0d5 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,16 @@ -CC = x86_64-w64-mingw32-gcc -CFLAGS = --std=c99 -I contrib/WinDivert-1.0.5-MINGW/include/ -CLIBS = -lws2_32 -lkernel32 -L contrib/WinDivert-1.0.5-MINGW/amd64/ -l WinDivert -OBJS = tor_wall.o +CC = i686-w64-mingw32-gcc +WINDRES = i686-w64-mingw32-windres +CFLAGS = --std=c99 -O2 -I contrib/WinDivert-1.1.5-MINGW/include/ -mwindows \ + -mthreads -m32 -Wall +CLIBS = -lws2_32 -lkernel32 -L contrib/WinDivert-1.1.5-MINGW/x86/ \ + -lWinDivert -lcomctl32 -mwindows +OBJS = main.o redirect.o domain.o +PROG = tallow.exe -tor_wall: $(OBJS) - $(CC) -s -o tor_wall.exe $(OBJS) $(CLIBS) +$(PROG): $(OBJS) + $(WINDRES) main.rc -O coff -o main.res + $(CC) -s -o $(PROG) $(OBJS) main.res $(CLIBS) clean: - rm -rf $(OBJS) tor_wall.exe + rm -rf $(OBJS) $(PROG) diff --git a/README.md b/README.md index 1074127..22f95f6 100644 --- a/README.md +++ b/README.md @@ -1,64 +1,67 @@ -TorWall -======= +Tallow (formally TorWall) - Transparent Tor for Windows +======================================================= -TorWall -- transparent Tor for Windows. +Tallow is a small program that redirects all outbound traffic from a Windows +machine via the Tor anonymity network. Any traffic that cannot be handled by +Tor, e.g. UDP, is blocked. Tallow also intercepts and handles DNS requests +preventing potential leaks. -In a nutshell, TorWall sets up the following configuration: +Tallow has several applications, including: - +-----------+ +-----------+ +-----------+ +----------+ - | BROWSER |------->| PRIVOXY |------->| TOR |------->| SERVER | - | a.b.c.d |<-------| a.b.c.d |<-------| a.b.c.d |<-------| x.y.z.w | - +-----------+ +-----------+ +-----------+ +----------+ +* "Tor-ifying" applications there were never designed to use Tor +* Filter circumvention -- if you wish to bypass a local filter and are + not so concerned about anonymity +* *Better-than-nothing-Tor* -- Some Tor may be better than no Tor. -Here (a.b.c.d) represents the local address, and (x.y.z.w) represents a remote -web server. +Note that, by itself, Tallow is not designed to be a complete strong anonymity +solution. See the warnings below. -TorWall works by redirecting your web traffic to a local instance of -Privoxy/Tor. This configuration is completely transparent to the web browser, -i.e. the web browser sees a normal internet connection (albeit slower). +Usage +===== -Unlike the TorBrowserBundle, TorWall works with any browser, including Chrome, -Safari, Opera, Firefox, Internet Explorer, etc. This may have some -disadvantages too, e.g. some browsers may leak information. To help mitigate -this risk, TorWall routes all web traffic through Privoxy. +Using the Tallow GUI, simply press the big "Tor" button to start redirecting +traffic via the Tor network. Press the button again to stop Tor redirection. +Note that your Internet connection may be temporarily interrupted each time +you toggle the button. -To prevent non-browser related leaks, TorWall also blocks all non-Tor traffic -when it is running. Furthermore, TorWall poisons DNS traffic with fake -responses to prevent leaks whilst still maintaining transparency for the -browser. +To test if Tor redirection is working, please visit the following site: +[https://check.torproject.org](https://check.torproject.org). -Limitations -=========== +History +======= -Currently HTTPS (port 443) is not supported. This is because Privoxy cannot -intercept such traffic. +Tallow was derived from the TorWall prototype (where "tallow" is an +anagram of "torwall" minus the 'r'). -Support for HTTPS in "passthru" mode may be added later. +Tallow works slightly differently, and aims to redirect all traffic rather +than just HTTP port 80. Also, unlike the prototype, Tallow does *not* use +Privoxy nor does it alter the content of any TCP streams in any way (see +warnings below). Building ======== -To build TorWall you need MinGW w64 cross-compiler for Linux. +To build Tallow you need the MinGW cross-compiler for Linux. You also need to download and place the following external dependencies and place them in the contrib/ directory: -* [WinDivert-1.0.5-MINGW.zip](http://reqrypt.org/windivert.html). -* The following files extracted from the [Privoxy](http://www.privoxy.org/) - installation package: - - privoxy.exe - - mgwz.dll +* [WinDivert-1.1.5-MINGW.zip](http://reqrypt.org/windivert.html). * The following files extracted from the - [Tor Browser Bundle](https://www.torproject.org/): + [Tor Expert Bundle](https://www.torproject.org/): - tor.exe - - libaey32.dll - - sslea32.dll Then simply run the build.sh script. -Warning -======= +Warnings +======== + +Tallow is beta quality software. There may be bugs. -TorWall is *experimental* software. It should not be relied on if you need -strong anonymity. For this you should still use the TorBrowserBundle. +Currently Tallow makes no attempt to anonymize the content of traffic sent +*through* the Tor network. This information may be used to de-anonymize you. +See [this +link](https://trac.torproject.org/projects/tor/wiki/doc/TransparentProxyLeaks) +for more information. Tallow *should not be relied on for strong anonymity* +unless you know what you are doing. diff --git a/build.sh b/build.sh index 2fb254b..4bf32b5 100644 --- a/build.sh +++ b/build.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# (C) 2013, all rights reserved, +# (C) 2014, all rights reserved, # # 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 @@ -20,10 +20,8 @@ set -e -WINDIVERT=WinDivert-1.0.5-MINGW -PRIVOXY=privoxy +WINDIVERT=WinDivert-1.1.5-MINGW TOR=tor -MGWZ=mgwz LIBEAY32=libeay32 SSLEAY32=ssleay32 @@ -35,21 +33,12 @@ then "(http://reqrypt.org/windivert.html)" 2>&1 exit 1 fi -for FILE in "$PRIVOXY.exe" "$MGWZ.dll" -do - if [ ! -e "$FILE" ] - then - echo "ERROR: missing \"$FILE\"; download and extract from the" \ - "Privoxy Windows installation package (http://www.privoxy.org/)" 2&1 - exit 1 - fi -done -for FILE in "$TOR.exe" "$LIBEAY32.dll" "$SSLEAY32.dll" +for FILE in "$TOR.exe" do if [ ! -e "$FILE" ] then echo "ERROR: missing \"$FILE\"; download and extract from the Tor" \ - "Browser Bundle for Windows (https://www.torproject.org/)" 2>&1 + "Expert Bundle for Windows (https://www.torproject.org/)" 2>&1 exit 1; fi done @@ -57,36 +46,32 @@ done echo "Extracting WinDivert..." unzip -o $WINDIVERT.zip -echo "Building TorWall..." +echo "Building Tallow..." cd .. make -echo "Copying \"tor_wall.exe\"..." -cp tor_wall.exe install/. +echo "Copying \"tallow.exe\"..." +cp tallow.exe install/. +echo "Copying \"hosts.deny\"..." +cp hosts.deny install/. -for FILE in "$PRIVOXY.exe" "$MGWZ.dll" "$TOR.exe" "$LIBEAY32.dll" \ - "$SSLEAY32.dll" \ - "$WINDIVERT/amd64/WinDivert.sys" \ - "$WINDIVERT/amd64/WinDivert.inf" \ - "$WINDIVERT/amd64/WinDivert.dll" \ - "$WINDIVERT/amd64/WdfCoInstaller01009.dll" +for FILE in "$TOR.exe" \ + "$WINDIVERT/amd64/WinDivert64.sys" \ + "$WINDIVERT/x86/WinDivert32.sys" \ + "$WINDIVERT/x86/WinDivert.dll" do echo "Copying \"$FILE\"..." cp contrib/"$FILE" install/. done -cd config -for FILE in * -do - echo "Copying \"$FILE\"..." - cp "$FILE" ../install/. -done -cd .. echo "Building installation package..." cd install + +zip -r ../TallowBundle-files.zip * cp ../install.nsi . + makensis install.nsi -mv TorWall-install.exe .. +mv TallowBundle-install.exe .. cd .. echo "Cleaning up..." diff --git a/config/config.txt b/config/config.txt deleted file mode 100644 index ceb311d..0000000 --- a/config/config.txt +++ /dev/null @@ -1,24 +0,0 @@ -confdir . -logdir . -actionsfile match-all.action -actionsfile default.action -actionsfile user.action -filterfile default.filter -filterfile user.filter -logfile privoxy.log -listen-address :9049 # TorWall SET (do not change) -toggle 1 -enable-remote-toggle 0 -enable-remote-http-toggle 0 -enable-edit-actions 1 -enforce-blocks 0 -buffer-limit 4096 -enable-proxy-authentication-forwarding 0 -forward-socks5 / 127.0.0.1:9050 . # TorWall SET (do not change) -forwarded-connect-retries 0 -accept-intercepted-requests 1 # TorWall SET (do not change) -allow-cgi-request-crunching 0 -split-large-forms 0 -keep-alive-timeout 5 -tolerate-pipelining 1 -socket-timeout 300 diff --git a/config/default.action b/config/default.action deleted file mode 100644 index 3302925..0000000 --- a/config/default.action +++ /dev/null @@ -1,1424 +0,0 @@ -############################################################################# -# Settings -- Don't change. -############################################################################# -{{settings}} -############################################################################# -for-privoxy-version=3.0.11 - -############################################################################# -# Aliases -############################################################################# -{{alias}} -############################################################################# -# -# You can define a short form for a list of permissions - e.g., instead -# of "-crunch-incoming-cookies -crunch-outgoing-cookies -filter -fast-redirects", -# you can just write "shop". This is called an alias. -# -# Currently, an alias can contain any character except space, tab, '=', '{' -# or '}'. -# But please use only 'a'-'z', '0'-'9', '+', and '-'. -# -# Alias names are not case sensitive. -# -# Aliases beginning with '+' or '-' may be used for system action names -# in future releases - so try to avoid alias names like this. (e.g. -# "+crunch-all-cookies" below is not a good name) -# -# Aliases must be defined before they are used. -# - -# These aliases just save typing later: -# -+crunch-all-cookies = +crunch-incoming-cookies +crunch-outgoing-cookies --crunch-all-cookies = -crunch-incoming-cookies -crunch-outgoing-cookies - allow-all-cookies = -crunch-all-cookies -session-cookies-only - allow-popups = -filter{all-popups} -filter{unsolicited-popups} -+block-as-image = +block{Blocked image request.} +handle-as-image --block-as-image = -block - -# These aliases define combinations of actions -# that are useful for certain types of sites: -# -fragile = -block -crunch-all-cookies -filter -fast-redirects -hide-referer -shop = -crunch-all-cookies allow-popups - -# Your favourite blend of filters: -# -myfilters = +filter{html-annoyances} +filter{js-annoyances} +filter{all-popups}\ - +filter{webbugs} +filter{banners-by-size} - -# Allow ads for selected useful free sites: -# -allow-ads = -block -filter{banners-by-size} -filter{banners-by-link} - -################ -# -# Cautious settings -- safe for all sites, but offer little privacy protection -# -{ \ -+change-x-forwarded-for{block} \ -+client-header-tagger{css-requests} \ -+client-header-tagger{image-requests} \ -+hide-from-header{block} \ -+set-image-blocker{pattern} \ -} -standard.Cautious - -################ -# -# Medium settings -- safe for most sites, with reasonable protection/damage tradeoff -# -{ \ -+change-x-forwarded-for{block} \ -+client-header-tagger{css-requests} \ -+client-header-tagger{image-requests} \ -+deanimate-gifs{last} \ -+filter{refresh-tags} \ -+filter{img-reorder} \ -+filter{banners-by-size} \ -+filter{webbugs} \ -+filter{jumping-windows} \ -+filter{ie-exploits} \ -+hide-from-header{block} \ -+hide-referrer{conditional-block} \ -+session-cookies-only \ -+set-image-blocker{pattern} \ -} -standard.Medium - -################ -# -# Advanced settings -- reasonable privacy protection but -# require some exceptions for trusted sites, most likely -# because of cookies or SSL. Also testing ground for -# new options. -# -# CAUTION: These settings can still be subverted by a -# misconfigured client that executes code from untrusted -# sources. -# -{ \ -+change-x-forwarded-for{block} \ -+client-header-tagger{css-requests} \ -+client-header-tagger{image-requests} \ -+crunch-if-none-match \ -+crunch-outgoing-cookies \ -+crunch-incoming-cookies \ -+deanimate-gifs{last} \ -+fast-redirects{check-decoded-url} \ -+filter{html-annoyances} \ -+filter{content-cookies} \ -+filter{refresh-tags} \ -+filter{img-reorder} \ -+filter{banners-by-size} \ -+filter{banners-by-link} \ -+filter{webbugs} \ -+filter{jumping-windows} \ -+filter{frameset-borders} \ -+filter{quicktime-kioskmode} \ -+hide-if-modified-since{-60} \ -+hide-from-header{block} \ -+hide-referrer{conditional-block} \ -+limit-connect{,} \ -+overwrite-last-modified{randomize} \ -+set-image-blocker{pattern} \ -} -standard.Advanced - -############################################################################# -# These extensions belong to images: -############################################################################# -{+handle-as-image -filter} -############################################################################# -/.*\.(gif|jpe?g|png|bmp|ico)($|\?) - -############################################################################# -# These don't: -############################################################################# -{-handle-as-image} -/.*\.(js|php|css|.?html?) - -############################################################################# -# These belong to multimedia files of which Firefox occasionally only -# requests parts. #2816708 -############################################################################# -{-filter -deanimate-gifs} -# Sticky Actions = -filter -deanimate-gifs -# URL = http://www.example.org/foo/bar.ogg -# URL = http://www.example.net/bar.ogv -/.*\.og[gv]$ - -############################################################################# -# Generic block patterns by host: -############################################################################# -{+block{Host matches generic block pattern.}} -ad*. -.*ads. -.ad.?. -.ad.[a-ik-z][a-oq-z]. -.ad.jp.*. -.ad.???*. -# Blocked URL = http://alternativos.iw-advertising.com/ -.*advert*. -*banner*. -count*. -*counter. -promotions. -# Blocked URL = http://metrics.performancing.com/ -metrics. - -############################################################################# -# Generic unblockers by host: -############################################################################# -{-block} -# Sticky Actions = -block -adsl. -ad[udmw]*. -adbl*. -adam*. -adapt*. -adob*. -adrenaline. -adtp*. -adv[oia]*. -adventure*. -.*road*. -.olympiad*. -.*load*. -.*[epu]ad*. -county*. -countr*. -# URL = http://metrics.torproject.org/consensus-graphs.html -metrics.torproject.org/ -# URL = http://linuxcounter.net/ -linuxcounter.net/ - -############################################################################# -# Generic block patterns by path: -############################################################################# -{+block{Path matches generic block pattern.}} -/(.*/)?ad(\?|/|s|v|_?(image|se?rv|box)|cycle|rotate|mentor|click|f[ra]m|script|stream|fetch|log|space) -# Blocked URL = http://www.example.org/adimage -# Blocked URL = http://www.example.org/adspace -/phpads(new)?/ -/(.*/)?(ad|all|nn|db|promo(tion)?)?[-_]?banner -/(.*/)?(publicite|werbung|rekla(me|am)|annonse|maino(kset|nta|s)?/) -/.*(count|track|compteur|(? context as in: -# -# s/()/$1replacement/sigU -# -# but that would make them match only the first occurrence of -# nasty-item in each )|$1never|sigU - -# If we allow window.open, we want normal window features: -# Test: http://www.htmlgoodies.com/beyond/notitle.html -# -s/(open\s*\([^\)]+resizable=)(["']?)(?:no|0)\2/$1$2yes$2/sigU -s/(open\s*\([^\)]+location=)(["']?)(?:no|0)\2/$1$2yes$2/sigU -s/(open\s*\([^\)]+status=)(["']?)(?:no|0)\2/$1$2yes$2/sigU -s/(open\s*\([^\)]+scroll(?:ing|bars)=)(["']?)(?:no|0)\2/$1$2auto$2/sigU -s/(open\s*\([^\)]+menubar=)(["']?)(?:no|0)\2/$1$2yes$2/sigU -s/(open\s*\([^\)]+toolbar=)(["']?)(?:no|0)\2/$1$2yes$2/sigU -s/(open\s*\([^\)]+directories=)(["']?)(?:no|0)\2/$1$2yes$2/sigU -s/(open\s*\([^\)]+fullscreen=)(["']?)(?:yes|1)\2/$1$2no$2/sigU -s/(open\s*\([^\)]+always(?:raised|lowered)=)(["']?)(?:yes|1)\2/$1$2no$2/sigU -s/(open\s*\([^\)]+z-?lock=)(["']?)(?:yes|1)\2/$1$2no$2/sigU -s/(open\s*\([^\)]+hotkeys=)(["']?)(?:yes|1)\2/$1$2no$2/sigU -s/(open\s*\([^\)]+titlebar=)(["']?)(?:no|0)\2/$1$2yes$2/sigU -s/(open\s*\([^\)]+always(?:raised|lowered)=)(["']?)(?:yes|1)\2/$1$2no$2/sigU - - -################################################################################# -# -# js-events: Kill JavaScript event bindings and timers (Radically destructive! Only for extra nasty sites). -# -################################################################################# -FILTER: js-events Kill JavaScript event bindings and timers (Radically destructive! Only for extra nasty sites). - -s/(on|event\.)((mouse(over|out|down|up|move))|(un)?load|contextmenu|selectstart)/never/ig -# Not events, but abused on the same type of sites: -s/(alert|confirm)\s*\(/concat(/ig -s/set(timeout|interval)\(/concat(/ig - -################################################################################# -# -# html-annoyances: Get rid of particularly annoying HTML abuse. -# -################################################################################# -FILTER: html-annoyances Get rid of particularly annoying HTML abuse. - -# New browser windows (if allowed -- see no-popups filter below) should be -# resizeable and have a location and status bar -# -s/(]+resizable=)(['"]?)(?:no|0)\2/$1$2yes$2/igU -s/(]+location=)(['"]?)(?:no|0)\2/$1$2yes$2/igU -s/(]+status=)(['"]?)(?:no|0)\2/$1$2yes1$2/igU -s/(]+scrolling=)(['"]?)(?:no|0)\2/$1$2auto$2/igU -s/(]+menubar=)(['"]?)(?:no|0)\2/$1$2yes$2/igU - -# The and tags were crimes! -# -s---sigU - - -################################################################################# -# -# content-cookies: Kill cookies that come in the HTML or JS content. -# -################################################################################# -FILTER: content-cookies Kill cookies that come in the HTML or JS content. - -# JS cookies, except those used by antiadbuster.com to detect us: -# -s|(\w+\.)+cookie(?=[ \t\r\n]*=)(?!='aab)|ZappedCookie|ig - -# HTML cookies: -# -s|||igU - - -################################################################################# -# -# refresh-tags: Kill automatic refresh tags if refresh time is larger than 9 seconds. -# -################################################################################# -FILTER: refresh-tags Kill automatic refresh tags if refresh time is larger than 9 seconds. - -# Note: Only deactivates refreshes with more than 9 seconds delay to -# preserve monster-stupid but common redirections via meta tags. -# -s@\2]*))?\2@)(?=\s*[^'"])+$1+isU -s@([^\w\s.]\s*)((?:map)?(window|this|parent)\.?)?open\s*\(@$1PrivoxyWindowOpen(@ig -s+([^'"]\s*)(?!\s*(\\n|'|"))+$1+iU - - -################################################################################## -# -# all-popups: Kill all popups in JavaScript and HTML. -# -################################################################################# -FILTER: all-popups Kill all popups in JavaScript and HTML. - -s@((\W\s*)(?:map)?(window|this|parent)\.?)open\s*\\?\(@$1concat(@ig # JavaScript -#s/\starget\s*=\s*(['"]?)_?(blank|new)\1?/ notarget/ig # HTML -s/\starget\s*=\s*(['"]?)_?(blank|new)\1?/ /ig # (X)HTML - -################################################################################## -# -# img-reorder: Reorder attributes in tags to make the banners-by-* filters more effective. -# -################################################################################# -FILTER: img-reorder Reorder attributes in tags to make the banners-by-* filters more effective. - -# In the first step src is moved to the start, then width is moved to the second -# place to guarantee an order of src, width, height. Also does some white-space -# normalization. -# -# This makes banners-by-size more effective and allows both banners-by-size -# and banners-by-link to preserve the original image URL in the title attribute. - -s|]*)\ssrc\s*=\s*(['"])([^>\\\2]+)\2|]*)\ssrc\s*=\s*([^'">\\\s]+)|]+height)\s*=\s*|$1=|sig - -s|\\\\2]*\2\|[^'">\\\s]+?))([^>]*)\s+width\s*=\s*((["']?)\d+?\5)(?=[\s>])|\\\1\s]+)\1)?[^>]*?(width=(['"]?)88\4)[^>]*?(height=(['"]?)31\6)[^>]*?(?=/?>)@\ - \\\1\s]+)\1)?[^>]*?(width=(['"]?)120\4)[^>]*?(height=(['"]?)(?:600?|90|240)\6)[^>]*?(?=/?>)@\ - \\\1\s]+)\1)?[^>]*?(width=(['"]?)125\4)[^>]*?(height=(['"]?)125\6)[^>]*?(?=/?>)@\ - \\\1\s]+)\1)?[^>]*?(width=(['"]?)160\4)[^>]*?(height=(['"]?)600\6)[^>]*?(?=/?>)@\ - \\\1\s]+)\1)?[^>]*?(width=(['"]?)180\4)[^>]*?(height=(['"]?)150\6)[^>]*?(?=/?>)@\ - \\\1\s]+)\1)?[^>]*?(width=(['"]?)(?:234|468)\4)[^>]*?(height=(['"]?)60\6)[^>]*?(?=/?>)@\ - \\\1\s]+)\1)?[^>]*?(width=(['"]?)240\4)[^>]*?(height=(['"]?)400\6)[^>]*?(?=/?>)@\ - \\\1\s]+)\1)?[^>]*?(width=(['"]?)(?:250|300)\4)[^>]*?(height=(['"]?)250\6)[^>]*?(?=/?>)@\ - \\\1\s]+)\1)?[^>]*?(width=(['"]?)336\4)[^>]*?(height=(['"]?)280\6)[^>]*?(?=/?>)@\ - \\\1\s]+)\1)?[^>]*?(width=(['"]?)200\4)[^>]*?(height=(['"]?)50\6)[^>]*?(?=/?>)@\ -# \1\s]*?(?:\ - adclick # See www.dn.se \ -| advert # see dict.leo.org \ -| atwola\.com/(?:link|redir) # see www.cnn.com \ -| doubleclick\.net/jump/ # redirs for doublecklick.net ads \ -| counter # common \ -| (?\1\s]*)\1[^>]*>\s*\\\3\s]+)\3)?[^>]*((?:width|height)\s*=\s*(['"]?)\d+?\6)[^>]*((?:width|height)\s*=\s*(['"]?)\d+?\8)[^>]*?(?=/?>)\ -@\1\s]*?(?:ad(?:click|vert)|atwola\.com/(?:link|redir)|doubleclick\.net/jump/|(?\1\s]*)\1[^>]*>\s*\\\3\s]+)\3)?[^>]*?(?=/?>)@]*\s(?:width|height)\s*=\s*['"]?[01](?=\D)[^>]*\s(?:width|height)\s*=\s*['"]?[01](?=\D)[^>]*?>@@siUg - - -################################################################################# -# -# tiny-textforms: Extend those tiny textareas up to 40x80 and kill the hard wrap. -# -################################################################################# -FILTER: tiny-textforms Extend those tiny textareas up to 40x80 and kill the hard wrap. - -s/(]*?)(?:\s*(?:rows|cols)=(['"]?)\d+\2)+/$1 rows=$2\40$2 cols=$2\80$2/ig -s/(]*?)wrap=(['"]?)hard\2/$1/ig - - -################################################################################# -# -# jumping-windows: Prevent windows from resizing and moving themselves. -# -################################################################################# -FILTER: jumping-windows Prevent windows from resizing and moving themselves. - -s/(?<=[\W])(?:window|this|self)\.(?:move|resize)(?:to|by)\(/''.concat(/ig - -################################################################################# -# -# frameset-borders: Give frames a border, make them resizable and scrollable. -# -################################################################################# -FILTER: frameset-borders Give frames a border and make them resizable. - -s/(]*)framespacing=(['"]?)(no|0)\2/$1/igU -s/(]*)frameborder=(['"]?)(no|0)\2/$1/igU -s/(]*)border=(['"]?)(no|0)\2/$1/igU -s/(]*)noresize/$1/igU -s/(]*)frameborder=(['"]?)(no|0)\2/$1/igU -s/(]*)scrolling=(['"]?)(no|0)\2/$1/igU - - -################################################################################# -# -# iframes: Remove all detected iframes. Should only be enabled for -# individual sites after testing that the iframes are optional. -# -################################################################################# -FILTER: iframes Removes all detected iframes. Should only be enabled for individual sites. -s@@@Uisg - - -################################################################################# -# -# demoronizer: Correct Microsoft's abuse of standardized character sets, which -# leave the browser to (mis)-interpret unknown characters, with -# sometimes bizarre results on non-MS platforms. -# -# credit: ripped from the demoroniser.pl script by: -# John Walker -- January 1998, http://www.fourmilab.ch/webtools/demoroniser -# -################################################################################# -FILTER: demoronizer Fix MS's non-standard use of standard charsets. - -s/(&\#[0-2]\d\d)\s/$1; /g -# per Robert Lynch: http://slate.msn.com//?id=2067547, just a guess. -# Must come before x94 below. -s/\xE2\x80\x94/ -- /g -s/\x82/,/g -#s-\x83-f-g -s/\x84/,,/g -s/\x85/.../g -#s/\x88/^/g -#s-\x89- °/°°-g -s/\x8B/~-g -#s-\x99-TM-g -# per Robert Lynch. -s/\x9B/>/g # 155 - - -################################################################################# -# -# shockwave-flash: Kill embedded Shockwave Flash objects. -# Note: Better just block "/.*\.swf$"! -# -################################################################################# -FILTER: shockwave-flash Kill embedded Shockwave Flash objects. - -s|]*macromedia.*||sigU -s|]*(application/x-shockwave-flash\|\.swf).*>(.*)?||sigU - - -################################################################################# -# -# quicktime-kioskmode: Make Quicktime movies saveable. -# -################################################################################# -FILTER: quicktime-kioskmode Make Quicktime movies saveable. - -s/(]*)kioskmode\s*=\s*(["']?)true\2/$1/ig - - -################################################################################# -# -# fun: Text replacements for subversive browsing fun! -# -################################################################################# -FILTER: fun Text replacements for subversive browsing fun! - -# SCNR -# -s/microsoft(?!\.[^\s])/MicroSuck/ig - -# Buzzword Bingo (example for extended regex syntax) -# -s* (?:industry|world)[ -]leading \ -| cutting[ -]edge \ -| customer[ -]focused \ -| market[ -]driven \ -| award[ -]winning # Comments are OK, too! \ -| high[ -]performance \ -| solutions[ -]based \ -| unmatched \ -| unparalleled \ -| unrivalled \ -*$0Bingo! \ -*igx - -# For Germans only -# -s/(M|m)edien(?![^<]*>)/$1ädchen/Ug - -################################################################################# -# -# crude-parental: Crude parental filtering. Use with a suitable blocklist. -# Pages are "blocked" based on keyword matching. -# -################################################################################# -FILTER: crude-parental Crude parental filtering. Note that this filter doesn't work reliably. - -# (Note: Middlesex, Sussex and Essex are counties in the UK, not rude words) -# (Note #2: Is 'sex' a rude word?!) - -s%^.*(?Blocked\ -

Blocked by Privoxy's crude-parental filter due to possible adult content.

%is - -s+^.*warez.*$+No Warez

You're not searching for illegal stuff, are you?

+is - -# Remove by description -s/^.*\ -(?:(suck|lick|tongue|rub|fuck|fingering|finger|chicks?)\s*)?\ -(?:(her|your|my|hard|with|big|wet|tight|pink|hot|moist|young|teen)\s*)+\ -(dicks?|penis|cocks?|balls?|tits?|pussy|cunt|clit|ass|mouth).*$\ -/This page has been blocked by Privoxy's crude-parental content filter\ -/is - -#Remove by link text -s/^.*\ -(download|broadband|view|watch|free|get|extreem)?\s*\ -(sex|xxx|porn|cumshot|fuck(ing|s)?|anal|ass|asian|adult|Amateur|org(y|ies)|close ups?|hand?job|nail(ed)?)+\s*\ -(movies?|pics?|videos?|dvds?|dvd's|links?).*$\ -/This page has been blocked by Privoxy's crude-parental content filter\ -/is - -#Remove by age disclaimer -s/^.*\ -(models?|chicks?|girls?|women|persons)\s*\ -(who|are|were)+ (over|at least) (16|18|21) years (old|of age).*$\ -/This page has been blocked by Privoxy's crude-parental content filter\ -/is - -#Remove by regulations -s/^.*(Section 2257|18 U.?S.?C.? 2257).*$\ -/This page has been blocked by Privoxy's crude-parental content filter\ -/is - - -################################################################################# -# -# IE-Exploits: Disable some known Internet Explorer bug exploits. -# -################################################################################# -FILTER: ie-exploits Disable some known Internet Explorer bug exploits. - -# Note: This is basically a demo and waits for someone more interested in IE -# security (sic!) to take over. - -# Cross-site-scripting: -# -s%f\("javascript:location.replace\('mk:@MSITStore:C:'\)"\);%alert\("This page looks like it tries to use a vulnerability described here:\n http://online.securityfocus.com/archive/1/298748/2002-11-02/2002-11-08/2"\);%siU - -# Address bar spoofing (http://www.secunia.com/advisories/10395/): -# -s/(]*href[^>]*)(?:\x01|\x02|\x03|%0[012])@/$1MALICIOUS-LINK@/ig - -# Nimda: -# -s%%
WARNING: This Server is infected with Nimda!%g - - -################################################################################# -# -# -# site-specifics: Cure for site-specific problems. Don't apply generally! -# -# Note: The fixes contained here are so specific to the problems of the -# particular web sites they are designed for that they would be a -# waste of CPU cycles (or even destructive!) on 99.9% of the web -# sites where they don't apply. -# -################################################################################# -FILTER: site-specifics Cure for site-specific problems. Don't apply generally! - -# www.spiegel.de excludes X11 users from viewing Flash5 objects - shame. -# Apply to: www.spiegel.de/static/js/flash-plugin.js -# -s/indexOf\("x11"\)/indexOf("x13")/ - -# www.quelle-bausparkasse.de uses a very stupid redirect mechanism that -# relies on a webbug being present. Can we tolerate that? No! -# Apply to: www.quelle-bausparkasse.de/$ -# -s/mylogfunc()//g - -# groups.yahoo.com has splash pages that one needs to click through in -# order to access the actual messages. Let the browser do that. Thanks -# to Paul Jobson for this one: -# -s|(?:Continue to message\|Weiter zu Nachricht)||ig - -# monster.com has two very similar gimmicks: -# -s|||i - -s|||i - -# nytimes.com triggers popups through the onload handler of dummy images -# to fool popup-blockers. -# -s|(]*)onload|$1never|sig - -# Pre-check all the "Discard" buttons in GNU Mailman's web interface. -# (This saves a lot of mouse aiming practice when flushing spamtraps) -# -s|( and tags. -# -################################################################################# -FILTER: no-ping Removes non-standard ping attributes in and tags. -s@(]*?)\sping=(['"]?)([^"'>]+)\2([>\s]?)@\ -PING!\n$1$4@ig - -################################################################################# -# -# google: CSS-based block for Google text ads. Also removes -# a width limitation and the toolbar advertisement. -# -################################################################################# -FILTER: google CSS-based block for Google text ads. Also removes a width limitation and the toolbar advertisement. - -s@[^\\]@\n$0@ -s@
@
@ -s@(
@\n\n$0\n@ - -s@(]*)width:545px;@$1width:70%;@isU - -################################################################################# -# -# msn: CSS-based block for MSN text ads. Also removes tracking URLs -# and a width limitation. -# -################################################################################# -FILTER: msn CSS-based block for MSN text ads. Also removes tracking URLs and a width limitation. - -s@@\n$0@ -# Are these ids still in use? -s@(]*) id=(["']?)ads_[^\2]*\2@$1 class="msn_ads"@Uig -s@(]*) class=(["']?)sb_ads[^\2]*\2@$1 class="msn_ads"@Uig -s@(]*href=\")http://g.msn.com/.*\?(http://.*)(&&DI=.*)(\")@$1$2$4@Ug -s@(]*)gping=\".*\"@$1 title="URL cleaned up by Privoxy's msn filter"@Ug - -################################################################################# -# -# blogspot: Cleans up some Blogspot blogs. Read the fine print before using this. -# -# This filter also intentionally removes some navigation stuff and -# sets the page width to 100%. As a result, some rounded "corners" would -# appear to early or not at all and as fixing this would require a browser -# that understands background-size (CSS3), they are removed instead. -# -# When applied to feeds, it removes comment titles that -# only contain the beginning of the actual comment. -# -################################################################################# -FILTER: blogspot Cleans up some Blogspot blogs. Read the fine print before using this. - -s@@\n$0@ -s@|(
([^<]*)(?:\.\.\.)?\s*\s*\ -(\s*\1)@$2@ig - -################################################################################# -# -# x-httpd-php-to-html: Changes the Content-Type header from -# x-httpd-php to html. "Content-Type: x-httpd-php" -# is set by clueless PHP users and causes many -# browsers do open a download menu instead of -# rendering the page. -# -################################################################################# -SERVER-HEADER-FILTER: x-httpd-php-to-html Changes the Content-Type header from x-httpd-php to html. - -s@^(Content-Type:)\s*application/x-httpd-php@$1 text/html@i - -################################################################################# -# -# html-to-xml: Changes the Content-Type header from html to xml. -# -################################################################################# -SERVER-HEADER-FILTER: html-to-xml Changes the Content-Type header from html to xml. - -s@^(Content-Type:)\s*text/html(;.*)?$@$1 application/xhtml+xml$2@i - -################################################################################# -# -# xml-to-html: Changes the Content-Type header from xml to html. -# -################################################################################# -SERVER-HEADER-FILTER: xml-to-html Changes the Content-Type header from xml to html. - -s@^(Content-Type:)\s*(?:application|text)/(?:xhtml\+)?xml(;.*)?$@$1 text/html$2@i - -################################################################################# -# -# hide-tor-exit-notation: Remove the Tor exit node notation in Host and Referer headers. -# -# Note: If Privoxy and Tor are chained and Privoxy is configured to -# use socks4a, one can use http://www.example.org.foobar.exit/ -# to access the host www.example.org through Tor exit node foobar. -# -# As the HTTP client isn't aware of this notation, it treats the -# whole string "www.example.org.foobar.exit" as host and uses it -# for the "Host" and "Referer" headers. From the server's point of -# view the resulting headers are invalid and can cause problems. -# -# An invalid "Referer" header can trigger "hot-linking" protections, -# an invalid "Host" header will make it impossible for the server to -# find the right vhost (several domains hosted on the same IP address). -# -# This filter removes the "foo.exit" part in those headers -# to prevent the mentioned problems. Note that it only modifies -# the HTTP headers, it doesn't make it impossible for the server -# to detect your Tor exit node based on the IP address the request is -# coming from. -# -################################################################################# -CLIENT-HEADER-FILTER: hide-tor-exit-notation Removes the Tor exit node notation in Host and Referer headers. - -s@^((?:Referer|Host):\s*(?:https?://)?[^/]*)\.[^\./]*?\.exit@$1@i - -################################################################################# -# -# less-download-windows: Prevents annoying download windows for content types -# the browser can handle itself. -# -################################################################################# -SERVER-HEADER-FILTER: less-download-windows Prevent annoying download windows for content types the browser can handle itself. - -s@^Content-Disposition:.*filename=(["']?).*\.(png|gif|jpe?g|diff?|d?patch|c|h|pl|shar)\1.*$@@i -s@^(Content-Type:)\s*(?:message/(?:news|rfc822)|text/x-.*|application/x-sh(?:\s|$))\s*@$1 text/plain@i - -################################################################################# -# -# image-requests: Tags detected image requests as "IMAGE-REQUEST". Whether -# or not the detection actually works depends on the browser. -# -################################################################################# -CLIENT-HEADER-TAGGER: image-requests Tags detected image requests as "IMAGE-REQUEST". - -s@^Accept:\s*image/.*@IMAGE-REQUEST@i - -################################################################################# -# -# css-requests: Tags detected CSS requests as "CSS-REQUEST". Whether -# or not the detection actually works depends on the browser. -# -################################################################################# -CLIENT-HEADER-TAGGER: css-requests Tags detected CSS requests as "CSS-REQUEST". - -s@^Accept:\s*text/css.*@CSS-REQUEST@i - -################################################################################# -# -# range-requests: Tags range requests as "RANGE-REQUEST". -# -# By default Privoxy removes Range headers for requests to -# ressources that will be filtered to make sure the filters -# get the whole picture. Otherwise Range requests could be -# intentionally used to circumvent filters or, less likely, -# filtering a partial response may damage it because it matched -# a pattern that the ressource as a whole wouldn't. -# -# Range requests can be useful and save bandwidth so instead -# of removing Range headers for requests to ressources that -# will be filtered, you may prefer to simply disable filtering -# for those requests. -# -# That's what this tagger is all about. After enabling it, -# you can disable filtering for range requests using the following -# action section: -# -# {-filter -deanimate-gifs} -# TAG:^RANGE-REQUEST -# -################################################################################# -CLIENT-HEADER-TAGGER: range-requests Tags range requests as "RANGE-REQUEST". - -s@^Range:.*@RANGE-REQUEST@i - -################################################################################# -# -# client-ip-address: Tags the request with the client's IP address. -# -################################################################################# -CLIENT-HEADER-TAGGER: client-ip-address Tags the request with the client's IP address. - -s@^\w*\s+.*\s+HTTP/\d\.\d\s*@IP-ADDRESS: $origin@D - -################################################################################# -# -# http-method: Tags the request with its HTTP method. -# -################################################################################# -CLIENT-HEADER-TAGGER: http-method Tags the request with its HTTP method. - -s@^(\w*).*HTTP/\d\.\d\s*$@$1@i - -################################################################################# -# -# allow-post: Tags POST requests as "ALLOWED-POST". -# -################################################################################# -CLIENT-HEADER-TAGGER: allow-post Tags POST requests as "ALLOWED-POST". - -s@^(?:POST)\s+.*\s+HTTP/\d\.\d\s*@ALLOWED-POST@i - -################################################################################# -# -# complete-url: Tags the request with the whole request URL. -# -################################################################################# -CLIENT-HEADER-TAGGER: complete-url Tags the request with the whole request URL. - -s@^\w*\s+(.*)\s+HTTP/\d\.\d\s*$@$1@i - -################################################################################# -# -# user-agent: Tags the request with the complete User-Agent header. -# -################################################################################# -CLIENT-HEADER-TAGGER: user-agent Tags the request with the complete User-Agent header. - -s@^User-Agent:.*@$0@i - -################################################################################# -# -# referer: Tags the request with the complete Referer header. -# -################################################################################# -CLIENT-HEADER-TAGGER: referer Tags the request with the complete Referer header. - -s@^Referer:.*@$0@i - -################################################################################# -# -# content-type: Tags the request with the content type declared by the server. -# -################################################################################# -SERVER-HEADER-TAGGER: content-type Tags the request with the content type declared by the server. - -s@^Content-Type:\s*([^;]+).*@$1@i - -################################################################################# -# -# privoxy-control: The taggers create tags with the content of X-Privoxy-Control -# headers, the filters remove said headers. -# -################################################################################# -CLIENT-HEADER-TAGGER: privoxy-control Creates tags with the content of X-Privoxy-Control headers. - -s@^X-Privoxy-Control:\s*@@i - -CLIENT-HEADER-FILTER: privoxy-control Removes X-Privoxy-Control headers. - -s@^X-Privoxy-Control:.*@@i - -SERVER-HEADER-TAGGER: privoxy-control Creates tags with the content of X-Privoxy-Control headers. - -s@^X-Privoxy-Control:\s*@@i - -SERVER-HEADER-FILTER: privoxy-control Removes X-Privoxy-Control headers. - -s@^X-Privoxy-Control:.*@@i diff --git a/config/match-all.action b/config/match-all.action deleted file mode 100644 index 885b797..0000000 --- a/config/match-all.action +++ /dev/null @@ -1,34 +0,0 @@ -############################################################################# -# $Id: match-all.action,v 1.3 2010/03/27 18:48:38 fabiankeil Exp $ -# -# This file contains the actions that are applied to all requests and -# may be overruled later on by other actions files. Less experienced -# users should only edit this file through the actions file editor. -# -############################################################################# -{+change-x-forwarded-for{block} \ - +client-header-tagger{css-requests} \ - +client-header-tagger{image-requests} \ - +crunch-if-none-match \ - +crunch-incoming-cookies \ - +crunch-outgoing-cookies \ - +deanimate-gifs{last} \ - +fast-redirects{check-decoded-url} \ - +filter{html-annoyances} \ - +filter{content-cookies} \ - +filter{refresh-tags} \ - +filter{img-reorder} \ - +filter{banners-by-size} \ - +filter{banners-by-link} \ - +filter{webbugs} \ - +filter{jumping-windows} \ - +filter{frameset-borders} \ - +filter{quicktime-kioskmode} \ - +hide-from-header{block} \ - +hide-if-modified-since{-60} \ - +hide-referrer{conditional-block} \ - +limit-connect{,} \ - +overwrite-last-modified{randomize} \ - +set-image-blocker{pattern} \ -} -/ # Match all URLs diff --git a/config/tor_wall.exe.manifest b/config/tor_wall.exe.manifest deleted file mode 100644 index f0597b5..0000000 --- a/config/tor_wall.exe.manifest +++ /dev/null @@ -1,12 +0,0 @@ - - - -TorWall - - - - - - - - diff --git a/config/trust.txt b/config/trust.txt deleted file mode 100644 index edb0d55..0000000 --- a/config/trust.txt +++ /dev/null @@ -1,2 +0,0 @@ -~config.privoxy.org -~p.p diff --git a/config/user.action b/config/user.action deleted file mode 100644 index 472930d..0000000 --- a/config/user.action +++ /dev/null @@ -1,197 +0,0 @@ -###################################################################### -# -# File : $Source: /cvsroot/ijbswa/current/user.action,v $ -# -# $Id: user.action,v 1.13 2011/11/06 11:36:01 fabiankeil Exp $ -# -# Purpose : User-maintained actions file, see -# http://www.privoxy.org/user-manual/actions-file.html -# -###################################################################### - -# This is the place to add your personal exceptions and additions to -# the general policies as defined in default.action. (Here they will be -# safe from updates to default.action.) Later defined actions always -# take precedence, so anything defined here should have the last word. - -# See http://www.privoxy.org/user-manual/actions-file.html, or the -# comments in default.action, for an explanation of what an "action" is -# and what each action does. - -# The examples included here either use bogus sites, or have the actual -# rules commented out (with the '#' character). Useful aliases are -# included in the top section as a convenience. - -############################################################################# -# Aliases -############################################################################# -{{alias}} -############################################################################# -# -# You can define a short form for a list of permissions - e.g., instead -# of "-crunch-incoming-cookies -crunch-outgoing-cookies -filter -fast-redirects", -# you can just write "shop". This is called an alias. -# -# Currently, an alias can contain any character except space, tab, '=', '{' -# or '}'. -# But please use only 'a'-'z', '0'-'9', '+', and '-'. -# -# Alias names are not case sensitive. -# -# Aliases beginning with '+' or '-' may be used for system action names -# in future releases - so try to avoid alias names like this. (e.g. -# "+crunch-all-cookies" below is not a good name) -# -# Aliases must be defined before they are used. -# -# These aliases just save typing later: -# -+crunch-all-cookies = +crunch-incoming-cookies +crunch-outgoing-cookies --crunch-all-cookies = -crunch-incoming-cookies -crunch-outgoing-cookies - allow-all-cookies = -crunch-all-cookies -session-cookies-only -filter{content-cookies} - allow-popups = -filter{all-popups} -filter{unsolicited-popups} -+block-as-image = +block{Blocked image request.} +handle-as-image --block-as-image = -block - -# These aliases define combinations of actions -# that are useful for certain types of sites: -# -fragile = -block -crunch-all-cookies -filter -fast-redirects -hide-referer -prevent-compression -shop = -crunch-all-cookies allow-popups - -# Your favourite blend of filters: -# -myfilters = +filter{html-annoyances} +filter{js-annoyances} +filter{all-popups}\ - +filter{webbugs} +filter{banners-by-size} - -# Allow ads for selected useful free sites: -# -allow-ads = -block -filter{banners-by-size} -filter{banners-by-link} -#... etc. Customize to your heart's content. - -## end aliases ######################################################## -####################################################################### - -# Begin examples: ##################################################### - -# Say you have accounts on some sites that you visit regularly, and you -# don't want to have to log in manually each time. So you'd like to allow -# persistent cookies for these sites. The allow-all-cookies alias defined -# above does exactly that, i.e. it disables crunching of cookies in any -# direction, and the processing of cookies to make them only temporary. -# -{ allow-all-cookies } -#.sourceforge.net -#sunsolve.sun.com -#slashdot.org -#.yahoo.com -#.msdn.microsoft.com -#.redhat.com - -# Say the site where you do your homebanking needs to open popup -# windows, but you have chosen to kill popups uncoditionally by default. -# This will allow it for your-example-bank.com: -# -{ -filter{all-popups} } -.banking.example.com - -# Some hosts and some file types you may not want to filter for -# various reasons: -# -{ -filter } - -# Technical documentation is likely to contain strings that might -# erroneously get altered by the JavaScript-oriented filters: -# -#.tldp.org -#/(.*/)?selfhtml/ - -# And this stupid host sends streaming video with a wrong MIME type, -# so that Privoxy thinks it is getting HTML and starts filtering: -# -stupid-server.example.com/ - - -# Example of a simple "block" action. Say you've seen an ad on your -# favourite page on example.com that you want to get rid of. You have -# right-clicked the image, selected "copy image location" and pasted -# the URL below while removing the leading http://, into a { +block{reason} } -# section. Note that { +handle-as-image } need not be specified, since -# all URLs ending in .gif will be tagged as images by the general rules -# as set in default.action anyway: -# -{ +block{Nasty ads.} } -www.example.com/nasty-ads/sponsor.gif - -# The URLs of dynamically generated banners, especially from large banner -# farms, often don't use the well-known image file name extensions, which -# makes it impossible for Privoxy to guess the file type just by looking -# at the URL. -# You can use the +block-as-image alias defined above for these cases. -# Note that objects which match this rule but then turn out NOT to be an -# image are typically rendered as a "broken image" icon by the browser. -# Use cautiously. -# -{ +block-as-image } -#.doubleclick.net -#/Realmedia/ads/ -#ar.atwola.com/ - -# Now you noticed that the default configuration breaks Forbes -# Magazine, but you were too lazy to find out which action is the -# culprit, and you were again too lazy to give feedback, so you just -# used the fragile alias on the site, and -- whoa! -- it worked. The -# 'fragile' aliases disables those actions that are most likely to break -# a site. Also, good for testing purposes to see if it is Privoxy that -# is causing the problem or not. -# -{ fragile } -#.forbes.com - -# Here are some sites we wish to support, and we will allow their ads -# through. -# -{ allow-ads } -#.sourceforge.net -#.slashdot.org -#.osdn.net - -# user.action is generally the best place to define exceptions and -# additions to the default policies of default.action. Some actions are -# safe to have their default policies set here though. So let's set a -# default policy to have a 'blank' image as opposed to the checkerboard -# pattern for ALL sites. '/' of course matches all URLs. -# patterns: -# -{ +set-image-blocker{blank} } -#/ - -# Enable the following section (not the regression-test directives) -# to rewrite and redirect click-tracking URLs on news.google.com. -# Disabling JavaScript should work as well and probably works more reliably. -# -# Redirected URL = http://news.google.com/news/url?ct2=us%2F0_0_s_1_1_a&sa=t&usg=AFQjCNHJWPc7ffoSXPSqBRz55jDA0KgxOQ&cid=8797762374160&url=http%3A%2F%2Fonline.wsj.com%2Farticle%2FSB10001424052970204485304576640791304008536.html&ei=YcqeTsymCIjxggf8uQE&rt=HOMEPAGE&vm=STANDARD&bvm=section&did=-6537064229385238098 -# Redirect Destination = http://online.wsj.com/article/SB10001424052970204485304576640791304008536.html -# Ignore = Yes -# -#{+fast-redirects{check-decoded-url}} -#news.google.com/news/url.*&url=http.*& - -# Enable the following section (not the regression-test directives) -# to block various Facebook "like" and similar tracking URLs. At the -# time this section was added it was reported to not break Facebook -# itself but this may have changed by the time you read this. This URL -# list is probably incomplete and if you don't have an account anyway, -# you may prefer to block the whole domain. -# -# Blocked URL = http://www.facebook.com/plugins/likebox.php?href=http%3A%2F%2Ffacebook.com%2Farstechnica&width=300&colorscheme=light&show_faces=false&stream=false&header=false&height=62&border_color=%23FFFFFF -# Ignore = Yes -# Blocked URL = http://www.facebook.com/plugins/activity.php?site=arstechnica.com&width=300&height=370&header=false&colorscheme=light&recommendations=false&border_color=%23FFFFFF -# Ignore = Yes -# Blocked URL = http://www.facebook.com/plugins/fan.php?api_key=368513495882&connections=10&height=250&id=8304333127&locale=en_US&sdk=joey&stream=false&width=377 -# Ignore = Yes -# Blocked URL = http://www.facebook.com/plugins/like.php?api_key=368513495882&channel_url=http%3A%2F%2Fstatic.ak.fbcdn.net%2Fconnect%2Fxd_proxy.php%3Fversion%3D3%23cb%3Df13997452c%26origin%3Dhttp%253A%252F%252Fonline.wsj.com%252Ff1b037e354%26relation%3Dparent.parent%26transport%3Dpostmessage&extended_social_context=false&href=http%3A%2F%2Fonline.wsj.com%2Farticle%2FSB10001424052970204485304576640791304008536.html&layout=button_count&locale=en_US&node_type=link&ref=wsj_share_FB&sdk=joey&send=false&show_faces=false&width=90 -# Ignore = Yes -# -#{+block{Facebook "like" and similar tracking URLs.}} -#www.facebook.com/(extern|plugins)/(login_status|like(box)?|activity|fan)\.php diff --git a/config/user.filter b/config/user.filter deleted file mode 100644 index c62d6bc..0000000 --- a/config/user.filter +++ /dev/null @@ -1,75 +0,0 @@ -# ******************************************************************** -# -# File : $Source: /cvsroot/ijbswa/current/user.filter,v $ -# -# $Id: user.filter,v 1.3 2008/05/21 20:17:03 fabiankeil Exp $ -# -# Purpose : Rules to process the content of web pages -# -# Copyright : Written by and Copyright (C) 2006-2008 the -# Privoxy team. http://www.privoxy.org/ -# -# We value your feedback. However, to provide you with the best support, -# please note: -# -# * Use the support forum to get help: -# http://sourceforge.net/tracker/?group_id=11118&atid=211118 -# * Submit bugs only thru our bug forum: -# http://sourceforge.net/tracker/?group_id=11118&atid=111118 -# Make sure that the bug has not already been submitted. Please try -# to verify that it is a Privoxy bug, and not a browser or site -# bug first. If you are using your own custom configuration, please -# try the stock configs to see if the problem is a configuration -# related bug. And if not using the latest development snapshot, -# please try the latest one. Or even better, CVS sources. -# * Submit feature requests only thru our feature request forum: -# http://sourceforge.net/tracker/?atid=361118&group_id=11118&func=browse -# -# For any other issues, feel free to use the mailing lists: -# http://sourceforge.net/mail/?group_id=11118 -# -# Anyone interested in actively participating in development and related -# discussions can join the appropriate mailing list here: -# http://sourceforge.net/mail/?group_id=11118. Archives are available -# here too. -# -################################################################################# -# -# Syntax: -# -# Generally filters start with a line like "FILTER: name description". -# They are then referrable from the actionsfile with +filter{name} -# -# FILTER marks a filter as content filter, other filter -# types are CLIENT-HEADER-FILTER, CLIENT-HEADER-TAGGER, -# SERVER-HEADER-FILTER and SERVER-HEADER-TAGGER. -# -# Inside the filters, write one Perl-Style substitution (job) per line. -# Jobs that precede the first FILTER: line are ignored. -# -# For Details see the pcrs manpage contained in this distribution. -# (and the perlre, perlop and pcre manpages) -# -# Note that you are free to choose the delimiter as you see fit. -# -# Note2: In addition to the Perl options gimsx, the following nonstandard -# options are supported: -# -# 'U' turns the default to ungreedy matching. Add ? to quantifiers to -# switch back to greedy. -# -# 'T' (trivial) prevents parsing for backreferences in the substitute. -# Use if you want to include text like '$&' in your substitute without -# quoting. -# -# 'D' (Dynamic) allows the use of variables. Supported variables are: -# $host, $origin (the IP address the request came from), $path and $url. -# -# Note that '$' is a bad choice as delimiter for dynamic filters as you -# might end up with unintended variables if you use a variable name -# directly after the delimiter. Variables will be resolved without -# escaping anything, therefore you also have to be careful not to chose -# delimiters that appear in the replacement text. For example '<' should -# be save, while '?' will sooner or later cause conflicts with $url. -# -################################################################################# diff --git a/domain.c b/domain.c new file mode 100644 index 0000000..b84788e --- /dev/null +++ b/domain.c @@ -0,0 +1,357 @@ +/* + * domain.c + * Copyright (C) 2014, basil + * + * 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 3 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, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "domain.h" +#include "main.h" + +#define RATE_LIMIT 8000 + +#define rand16() \ + (rand() & 0xFF) | ((rand() & 0xFF) << 8) + +// Domain blacklist: +struct blacklist +{ + size_t size; + size_t len; + char **names; +}; +static struct blacklist *blacklist = NULL; + +// State: +static struct name *names[UINT16_MAX] = {NULL}; +static uint8_t sbox[UINT8_MAX] = {0}; +static uint8_t sbox1[UINT8_MAX] = {0}; +static uint16_t key[4] = {0}; +static LONGLONG counter = 0; +static LONGLONG counter_0 = 0; +static LONGLONG counter_1 = 0; +static LONGLONG rate = 0; + +static HANDLE names_lock = NULL; + +// Prototypes: +static uint16_t domain_encrypt(uint16_t count); +static void domain_ref(struct name *name); +static struct blacklist *domain_blacklist_read(void); +static bool domain_blacklist_lookup(struct blacklist *blacklist, + const char *name); +static int __cdecl domain_blacklist_compare_0(const void *x, const void *y); +static int domain_blacklist_compare(const char *name0, size_t len, + const char *name1); + +// "Encrypt" the count to give the IPs a random look-and-feel. Not security +// critical. +static uint16_t domain_encrypt(uint16_t count) +{ + for (size_t i = 0; i < sizeof(key) / sizeof(uint16_t); i++) + { + count = (count << 3) | (count >> 13); // Mix + count ^= key[i]; // Round-key + count = (uint16_t)sbox[count & 0xFF] | // S-Box + ((uint16_t)sbox[count >> 8] << 8); + } + return count; +} + +// Initialize this module: +extern void domain_init(void) +{ + // Load the domain blacklist. + blacklist = domain_blacklist_read(); + + // Init S-BOXes: + for (size_t i = 1; i <= UINT8_MAX; i++) + { + uint8_t idx = (uint8_t)rand16(); + while (sbox[idx] != 0) + idx += 23; + sbox[idx] = (uint8_t)i; + if (i % 16 == 0) + srand(random()); + } + srand(random()); + for (size_t i = 1; i <= UINT8_MAX; i++) + { + uint8_t idx = (uint8_t)rand16(); + while (sbox1[idx] != 0) + idx += 23; + sbox1[idx] = (uint8_t)i; + } + + // Init the key sched. + for (size_t i = 0; i < sizeof(key) / sizeof(uint16_t); i++) + key[i] = (uint16_t)random(); + + counter = rand16(); + counter <<= 8; + counter |= (LONGLONG)rand16(); + counter_1 = counter_0 = counter; + names_lock = create_lock(); +} + +// Lookup an address given a domain name. If the name does not exist then +// create one. +extern uint32_t domain_lookup_addr(const char *name0) +{ + if (name0[0] == '.') + name0++; + + if (domain_blacklist_lookup(blacklist, name0)) + { + debug("Block %s\n", name0); + return 0; // Blocked! + } + + if (InterlockedIncrement64(&rate) >= RATE_LIMIT) + { + debug("Block (rate limit)\n"); + return 0; + } + + uint64_t idx0 = (uint64_t)InterlockedIncrement64(&counter); + uint16_t idx = domain_encrypt((uint16_t)idx0); + uint8_t msb = sbox1[(idx0 >> 16) & 0xFF]; + uint32_t addr = ADDR_BASE | ((uint32_t)msb << 16) | (uint32_t)idx; + + if (names[idx] != NULL) + { + // Name table is full! + debug("Block %s (name entry is full)\n", name0); + return 0; + } + + size_t len = strlen(name0); + size_t size = sizeof(struct name) + (len + 1) * sizeof(char); + struct name *name = (struct name *)malloc(size); + if (name == NULL) + { + warning("failed to allocate %u bytes for domain name", size); + exit(EXIT_FAILURE); + } + + name->ref_count = 1; + name->msb = msb; + memcpy(name->name, name0, len+1); + + names[idx] = name; + + return addr; +} + +// Lookup a name based on the address. +extern struct name *domain_lookup_name(uint32_t addr) +{ + if (addr < ADDR_BASE || addr > ADDR_MAX) + return NULL; + uint8_t msb = (uint8_t)(addr >> 16); + uint16_t idx = (uint16_t)addr; + lock(names_lock); + struct name *name = names[idx]; + if (name == NULL || name->msb != msb) + { + unlock(names_lock); + return NULL; + } + domain_ref(name); + unlock(names_lock); + return name; +} + +// Reference counting: +static void domain_ref(struct name *name) +{ + if (name == NULL) + return; + name->ref_count++; +} +extern void domain_deref(struct name *name) +{ + if (name == NULL) + return; + LONG old = InterlockedDecrement(&name->ref_count); + if (old == 0) + free(name); +} + +// Cleanup old names. +extern void domain_cleanup(size_t count) +{ + InterlockedExchange64(&rate, 0); + + if (count % 16 != 0) + return; + + LONGLONG start = counter_1, end = counter_0; + + counter_1 = counter_0; + counter_0 = counter; + + for (; start < end; start++) + { + uint16_t idx = (uint16_t)start; + idx = domain_encrypt(idx); + + lock(names_lock); + struct name *old_name = names[idx]; + names[idx] = NULL; + unlock(names_lock); + + if (old_name != NULL) + debug("Cleanup name %s\n", old_name->name); + domain_deref(old_name); + } +} + +// Read the blacklist file: +static struct blacklist *domain_blacklist_read(void) +{ + struct blacklist *blacklist = + (struct blacklist *)malloc(sizeof(struct blacklist)); + if (blacklist == NULL) + { + warning("failed to allocate %u bytes for domain blacklist", + sizeof(struct blacklist)); + exit(EXIT_FAILURE); + } + blacklist->size = 0; + blacklist->len = 0; + blacklist->names = NULL; + + const char *filename = "hosts.deny"; + FILE *stream = fopen(filename, "r"); + if (stream == NULL) + { + warning("failed to open \"%s\" for reading", filename); + return blacklist; + } + + // Read blocked domains: + int c; + char buf[256]; + while (true) + { + while (isspace(c = getc(stream))) + ; + if (c == EOF) + break; + if (c == '#') + { + while ((c = getc(stream)) != '\n' && c != EOF) + ; + continue; + } + size_t i = 0; + while (i < sizeof(buf)-1 && (c == '-' || c == '.' || isalnum(c))) + { + buf[i++] = c; + c = getc(stream); + } + if (i >= sizeof(buf)-1 || !isspace(c)) + { + warning("failed to parse domain blacklist from the \"%s\" file", + filename); + exit(EXIT_FAILURE); + } + buf[i] = '\0'; + if (blacklist->len >= blacklist->size) + { + blacklist->size = (blacklist->size == 0? 32: 2 * blacklist->size); + blacklist->names = (char **)realloc(blacklist->names, + blacklist->size * sizeof(char *)); + if (blacklist->names == NULL) + { + warning("failed to (re)allocate %u bytes for domain blacklist", + blacklist->size * sizeof(char *)); + exit(EXIT_FAILURE); + } + } + size_t size = (i+1) * sizeof(char); + char *name = (char *)malloc(size); + if (name == NULL) + { + warning("failed to allocate %u bytes for domain blacklist entry", + size); + exit(EXIT_FAILURE); + } + for (size_t j = 0; j < i; j++) + name[j] = buf[i - 1 - j]; + name[i] = '\0'; + blacklist->names[blacklist->len++] = name; + } + + fclose(stream); + + qsort(blacklist->names, blacklist->len, sizeof(char *), + domain_blacklist_compare_0); + return blacklist; +} + +// Check if a domain matches the blacklist or not: +static bool domain_blacklist_lookup(struct blacklist *blacklist, + const char *name) +{ + if (blacklist->len == 0) + return false; + + size_t len = strlen(name); + ssize_t lo = 0, hi = blacklist->len-1; + while (lo <= hi) + { + ssize_t mid = (lo + hi) / 2; + int cmp = domain_blacklist_compare(name, len, blacklist->names[mid]); + if (cmp > 0) + hi = mid-1; + else if (cmp < 0) + lo = mid+1; + else + return true; + } + return false; +} + +// Domain compare function(s): +static int __cdecl domain_blacklist_compare_0(const void *x, const void *y) +{ + const char *name0 = *(const char **)x; + const char *name1 = *(const char **)y; + return strcmp(name0, name1); +} +static int domain_blacklist_compare(const char *name0, size_t len, + const char *name1) +{ + size_t i = 0; + ssize_t j = (ssize_t)len - 1; + for (; j >= 0 && name1[i] != '\0'; i++, j--) + { + int cmp = (int)name1[i] - (int)name0[j]; + if (cmp != 0) + return cmp; + } + if (j < 0 && name1[i] != '\0') + return 1; + return 0; +} + diff --git a/domain.h b/domain.h new file mode 100644 index 0000000..fe84b07 --- /dev/null +++ b/domain.h @@ -0,0 +1,40 @@ +/* + * domain.h + * Copyright (C) 2014, basil + * + * 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 3 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, see . + */ + +#ifndef __DOMAIN_H +#define __DOMAIN_H + +#include + +#define ADDR_BASE 0x2C000000 // 44.0.0.0/24 (AMPRNet) +#define ADDR_MAX 0x2CFFFFFF + +struct name +{ + LONG ref_count; + uint8_t msb; + char name[]; +} __attribute__((__packed__)); + +extern void domain_init(void); +extern uint32_t domain_lookup_addr(const char *name); +extern struct name *domain_lookup_name(uint32_t addr); +extern void domain_deref(struct name *name); +extern void domain_cleanup(size_t count); + +#endif diff --git a/hosts.deny b/hosts.deny new file mode 100644 index 0000000..6136032 --- /dev/null +++ b/hosts.deny @@ -0,0 +1,22 @@ +# This file contains a list of domains that are blocked via Tor. +# NOTE: These domains are *NOT* blocked when Tor diversion is disabled! +# The primary motivation for this list is to minimize Tor bandwidth. + +# Windows update: +download.windowsupdate.com +download.microsoft.com +update.microsoft.com +windowsupdate.com +ntservicepack.microsoft.com +wustat.windows.com + +# Windows "phone-home" related: +# NOTE: This list is probably not comprehensive. +spynet2.microsoft.com +spynettest.microsoft.com +sqm.microsoft.com +watson.microsoft.com + +# Common ad servers: +doubleclick.net + diff --git a/install.nsi b/install.nsi index c224963..cbc2bca 100644 --- a/install.nsi +++ b/install.nsi @@ -1,5 +1,5 @@ ; install.nsi -; (C) 2013, all rights reserved, +; (C) 2014, all rights reserved, ; ; 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 @@ -18,10 +18,10 @@ SetCompressor /SOLID /FINAL lzma -Name "TorWall" -OutFile "TorWall-install.exe" +Name "Tallow" +OutFile "TallowBundle-install.exe" -InstallDir "$PROGRAMFILES\TorWall\" +InstallDir "$PROGRAMFILES\Tallow\" RequestExecutionLevel admin @@ -35,50 +35,33 @@ RequestExecutionLevel admin Section "" SetOutPath $INSTDIR - File "tor_wall.exe" - File "tor_wall.exe.manifest" - File "privoxy.exe" - File "mgwz.dll" + File "tallow.exe" File "tor.exe" - File "libeay32.dll" - File "ssleay32.dll" - File "WinDivert.sys" - File "WinDivert.inf" + File "WinDivert32.sys" + File "WinDivert64.sys" File "WinDivert.dll" - File "WdfCoInstaller01009.dll" - File "config.txt" - File "default.action" - File "default.filter" - File "match-all.action" - File "trust.txt" - File "user.action" - File "user.filter" - WriteUninstaller "TorWall-uninstall.exe" - CreateShortCut "$DESKTOP\TorWall.lnk" "$INSTDIR\tor_wall.exe" "" + File "hosts.deny" + WriteUninstaller "TallowBundle-uninstall.exe" + WriteRegStr HKLM \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tallow" \ + "DisplayName" "TallowBundle" + WriteRegStr HKLM \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tallow" \ + "UninstallString" "$\"$INSTDIR\TallowBundle-uninstall.exe$\"" + CreateShortCut "$DESKTOP\Tallow.lnk" "$INSTDIR\tallow.exe" "" SectionEnd Section "Uninstall" - Delete "$INSTDIR\tor_wall.exe" - Delete "$INSTDIR\tor_wall.exe.manifest" - Delete "$INSTDIR\privoxy.exe" - Delete "$INSTDIR\mgwz.dll" + Delete "$INSTDIR\tallow.exe" Delete "$INSTDIR\tor.exe" - Delete "$INSTDIR\libeay32.dll" - Delete "$INSTDIR\ssleay32.dll" - Delete "$INSTDIR\WinDivert.sys" - Delete "$INSTDIR\WinDivert.inf" + Delete "$INSTDIR\WinDivert32.sys" + Delete "$INSTDIR\WinDivert64.sys" Delete "$INSTDIR\WinDivert.dll" - Delete "$INSTDIR\WdfCoInstaller01009.dll" - Delete "$INSTDIR\config.txt" - Delete "$INSTDIR\default.action" - Delete "$INSTDIR\default.filter" - Delete "$INSTDIR\match-all.action" - Delete "$INSTDIR\trust.txt" - Delete "$INSTDIR\user.action" - Delete "$INSTDIR\user.filter" - Delete "$INSTDIR\privoxy.log" - Delete "$INSTDIR\TorWall-uninstall.exe" + Delete "$INSTDIR\hosts.deny" + Delete "$INSTDIR\TallowBundle-uninstall.exe" RMDir "$INSTDIR\" - Delete "$DESKTOP\TorWall.lnk" + DeleteRegKey HKLM \ + "Software\Microsoft\Windows\CurrentVersion\Uninstall\Tallow" + Delete "$DESKTOP\Tallow.lnk" SectionEnd diff --git a/main.c b/main.c new file mode 100644 index 0000000..4d555d6 --- /dev/null +++ b/main.c @@ -0,0 +1,511 @@ +/* + * main.c + * Copyright (C) 2014, basil + * + * 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 3 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, see . + */ + +#include +#include +#include + +#include +#include + +#include "domain.h" +#include "main.h" +#include "redirect.h" + +// GUI parameters: +#define STATUS_TOR_ON_COLOR RGB(196, 255, 196) +#define STATUS_TOR_OFF_COLOR RGB(255, 196, 196) +#define BUTTON_OFFSET_X 10 +#define BUTTON_OFFSET_Y 10 +#define BUTTON_SIZE_X 138 +#define BUTTON_SIZE_Y 138 +#define WINDOW_SIZE_X \ + (10 + 2 * BUTTON_SIZE_X + 3 * BUTTON_OFFSET_X) +#define WINDOW_SIZE_Y \ + (50 + BUTTON_SIZE_Y + 2 * BUTTON_OFFSET_Y) +#define TOR_ON_MESSAGE "Tor divert is ON" +#define TOR_OFF_MESSAGE "Tor divert is OFF" + +// Prototypes: +static DWORD WINAPI tor_thread(LPVOID arg); +static DWORD WINAPI cleanup_thread(DWORD arg); + +// The GUI: +static HWND status_bar = NULL; +static HWND status_label = NULL; + +// Tor state: +static bool state = false; + +// Options: +bool option_force_socks4a = true; + +// Start/stop Tor: +static void start_tor(void) +{ + state = true; + redirect_start(); + SetWindowText(status_label, TOR_ON_MESSAGE); +} + +static void stop_tor(void) +{ + state = false; + redirect_stop(); + SetWindowText(status_label, TOR_OFF_MESSAGE); +} + +// Tor SOCKS4a config control: +static WNDPROC config_proc0 = NULL; +static LRESULT CALLBACK config_proc(HWND hwnd, UINT msg, WPARAM wparam, + LPARAM lparam) +{ + switch (msg) + { + case WM_COMMAND: + { + int event = HIWORD(wparam); + if (event == BN_CLICKED) + { + LRESULT state = SendMessage((HWND)lparam, BM_GETCHECK, 0, 0); + option_force_socks4a = (state == BST_CHECKED); + } + break; + } + default: + break; + } + return CallWindowProc(config_proc0, hwnd, msg, wparam, lparam); +} + +// Tor status control: +static WNDPROC status_proc0 = NULL; +static LRESULT CALLBACK status_proc(HWND hwnd, UINT msg, WPARAM wparam, + LPARAM lparam) +{ + switch (msg) + { + case WM_CTLCOLORSTATIC: + { + HDC hdc = (HDC)wparam; + if (state) + { + SetBkColor(hdc, STATUS_TOR_ON_COLOR); + SetDCBrushColor(hdc, STATUS_TOR_ON_COLOR); + } + else + { + SetBkColor(hdc, STATUS_TOR_OFF_COLOR); + SetDCBrushColor(hdc, STATUS_TOR_OFF_COLOR); + } + return (LRESULT)GetStockObject(DC_BRUSH); + } + default: + break; + } + return CallWindowProc(status_proc0, hwnd, msg, wparam, lparam); +} + +// Window control: +LRESULT CALLBACK window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + HWND button = NULL; + + switch(msg) + { + case WM_CREATE: + { + InitCommonControls(); + + // (1) Create the big Tor button: + HINSTANCE instance = (HINSTANCE)GetWindowLong(hwnd, + GWLP_HINSTANCE); + button = CreateWindow( + "BUTTON", "Tor", + BS_ICON | BS_AUTOCHECKBOX | BS_PUSHLIKE | WS_CHILD | WS_VISIBLE, + BUTTON_OFFSET_X, BUTTON_OFFSET_Y, + BUTTON_SIZE_X, BUTTON_SIZE_Y, + hwnd, NULL, instance, NULL); + if (button == NULL) + goto gui_init_failed; + HICON image = LoadImage(GetModuleHandle(NULL), "TOR_ICON", + IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR); + if (image != NULL) + SendMessage(button, BM_SETIMAGE, (WPARAM)IMAGE_ICON, + (LPARAM)image); + + // (2) Create the status bar: + status_bar = CreateWindow( + STATUSCLASSNAME, "Starting...", + WS_CHILD | WS_VISIBLE | SBT_TOOLTIPS, + 0, 0, 0, 0, hwnd, NULL, instance, NULL); + if (status_bar == NULL) + goto gui_init_failed; + + // (3) Create the status box: + size_t status_offset_x = 2 * BUTTON_OFFSET_X + BUTTON_SIZE_X, + status_offset_y = BUTTON_OFFSET_Y; + size_t status_size_x = BUTTON_SIZE_X, status_size_y = 45; + HWND status_box = CreateWindow( + "BUTTON", "Status", + WS_CHILD | WS_VISIBLE | BS_GROUPBOX, + status_offset_x, status_offset_y, + status_size_x, status_size_y, hwnd, NULL, instance, NULL); + if (status_box == NULL) + goto gui_init_failed; + status_proc0 = (WNDPROC)SetWindowLongPtr(status_box, GWLP_WNDPROC, + (LONG_PTR)status_proc); + HGDIOBJ font = GetStockObject(DEFAULT_GUI_FONT); + SendMessage(status_box, WM_SETFONT, (WPARAM)font, 0); + status_label = CreateWindow( + WC_STATIC, TOR_OFF_MESSAGE, + WS_VISIBLE | WS_CHILD | SS_CENTER, + 15, 20, status_size_x - 30, 15, + status_box, NULL, instance, NULL); + if (status_label == NULL) + goto gui_init_failed; + SendMessage(status_label, WM_SETFONT, (WPARAM)font, 0); + + // (4) Create the config box: + size_t config_offset_x = status_offset_x, + config_offset_y = status_offset_y + status_size_y + + BUTTON_OFFSET_Y; + size_t config_size_x = status_size_x, + config_size_y = BUTTON_SIZE_X - status_size_y - + BUTTON_OFFSET_Y; + HWND config_box = CreateWindow( + "BUTTON", "Config (Advanced)", + WS_CHILD | WS_VISIBLE | BS_GROUPBOX, + config_offset_x, config_offset_y, + config_size_x, config_size_y, hwnd, NULL, instance, NULL); + if (config_box == NULL) + goto gui_init_failed; + SendMessage(config_box, WM_SETFONT, (WPARAM)font, 0); + + HWND direct_check = CreateWindow( + "BUTTON", "Force SOCKS4a", + BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE, + 15, 20, config_size_x - 30, 15, + config_box, NULL, instance, NULL); + if (direct_check == NULL) + goto gui_init_failed; + SendMessage(direct_check, WM_SETFONT, (WPARAM)font, 0); + SendMessage(direct_check, BM_SETCHECK, (WPARAM)BST_CHECKED, 0); + config_proc0 = (WNDPROC)SetWindowLongPtr(config_box, + GWLP_WNDPROC, (LONG_PTR)config_proc); + + break; + } + case WM_COMMAND: + { + int event = HIWORD(wparam); + if (event == BN_CLICKED) + { + LRESULT state = SendMessage((HWND)lparam, BM_GETCHECK, 0, 0); + if (state == BST_CHECKED) + start_tor(); + else + stop_tor(); + } + break; + } + case WM_CLOSE: + DestroyWindow(hwnd); + break; + case WM_DESTROY: + PostQuitMessage(0); + break; + default: + return DefWindowProc(hwnd, msg, wparam, lparam); + } + return 0; + +gui_init_failed: + warning("failed to create the GUI"); + exit(EXIT_FAILURE); +} + +// Entry point: +int WINAPI WinMain(HINSTANCE instance, HINSTANCE prev_instance, + LPSTR cmd_line, int cmd_show) +{ + // Attach to the parent console if it exists. + if (AttachConsole(ATTACH_PARENT_PROCESS)) + { + freopen("conout$", "w", stdout); + freopen("conout$", "w", stderr); + putchar('\n'); + } + + WNDCLASSEX class; + HWND window; + + // (0) Init stuff: + srand(random()); + domain_init(); + redirect_init(); + + // (1) Register the window class: + memset(&class, 0, sizeof(class)); + class.cbSize = sizeof(WNDCLASSEX); + class.lpfnWndProc = window_proc; + class.hInstance = instance; + class.hIcon = LoadIcon(NULL, IDI_APPLICATION); + class.hCursor = LoadCursor(NULL, IDC_ARROW); + class.hbrBackground = (HBRUSH)(COLOR_WINDOW); + class.lpszClassName = PROGNAME "_WINDOW"; + class.hIconSm = LoadIcon(NULL, IDI_APPLICATION); + + if (!RegisterClassEx(&class)) + { + warning("failed to register window class; cannot display the GUI"); + return EXIT_FAILURE; + } + + // (2) Create the window: + window = CreateWindow(PROGNAME "_WINDOW", PROGNAME, + WS_OVERLAPPEDWINDOW & (~WS_THICKFRAME), + CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_SIZE_X, WINDOW_SIZE_Y, + NULL, NULL, instance, NULL); + if (window == NULL) + { + warning("failed to create the main window"); + return EXIT_FAILURE; + } + + // (3) Start Tor: + HANDLE thread = CreateThread(NULL, 0, + (LPTHREAD_START_ROUTINE)tor_thread, NULL, 0, NULL); + if (thread == NULL) + { + warning("failed to create Tor thread"); + return EXIT_FAILURE; + } + + // (4) Start clean-up thread: + thread = CreateThread(NULL, 0, + (LPTHREAD_START_ROUTINE)cleanup_thread, NULL, 0, NULL); + if (thread == NULL) + { + warning("failed to create cleanup thread"); + return EXIT_FAILURE; + } + + ShowWindow(window, cmd_show); + UpdateWindow(window); + + // (5) Handle messages: + MSG message; + for (size_t i = 0; GetMessage(&message, NULL, 0, 0) > 0; i++) + { + TranslateMessage(&message); + DispatchMessage(&message); + } + return message.wParam; +} + +// Tor thread: +static DWORD WINAPI tor_thread(LPVOID arg) +{ + // (1) Create the Tor process: + status("starting Tor"); + + HANDLE job = CreateJobObject(NULL, NULL); + if (job == NULL) + { + warning("failed to create Tor job object"); + exit(EXIT_FAILURE); + } + JOBOBJECT_EXTENDED_LIMIT_INFORMATION ji; + memset(&ji, 0, sizeof(ji)); + ji.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + if (!SetInformationJobObject(job, JobObjectExtendedLimitInformation, &ji, + sizeof(ji))) + { + warning("failed to configure Tor job object"); + exit(EXIT_FAILURE); + } + + HANDLE out, in; + SECURITY_ATTRIBUTES attr; + memset(&attr, 0, sizeof(attr)); + attr.nLength = sizeof(SECURITY_ATTRIBUTES); + attr.bInheritHandle = TRUE; + attr.lpSecurityDescriptor = NULL; + if (!CreatePipe(&out, &in, &attr, 0)) + { + warning("failed to create Tor pipe"); + exit(EXIT_FAILURE); + } + if (!SetHandleInformation(out, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) + { + warning("failed to set handle information for Tor pipe"); + exit(EXIT_FAILURE); + } + + STARTUPINFO si; + PROCESS_INFORMATION pi; + memset(&si, 0, sizeof(si)); + si.cb = sizeof(STARTUPINFO); + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.hStdOutput = in; + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; + const char *tor_path = ".\\tor.exe"; + if (!CreateProcess(tor_path, + "tor.exe --SOCKSListenAddress 0.0.0.0:" STR(TOR_PORT), + NULL, NULL, TRUE, CREATE_BREAKAWAY_FROM_JOB, NULL, NULL, &si, &pi)) + { + warning("failed to start Tor"); + exit(EXIT_FAILURE); + } + + if (!AssignProcessToJobObject(job, pi.hProcess)) + { + TerminateProcess(pi.hProcess, 0); + warning("failed to assign Tor process to Tor job object"); + exit(EXIT_FAILURE); + } + + // Forward Tor messages to the status bar: + while (TRUE) + { + char buf[BUFSIZ]; + DWORD len; + + if (!ReadFile(out, buf, sizeof(buf)-1, &len, NULL)) + { + warning("failed to read Tor output"); + continue; + } + if (len <= 2) + continue; + buf[len-2] = '\0'; + + // Tidy-up the Tor message a bit: + size_t i = 0; + while (buf[i] != ']' && buf[i] != '\0') + i++; + if (buf[i] != ']' && buf[i+1] != ' ') + continue; + status("%s", buf+i+2); + } +} + +// Cleanup thread: +static DWORD WINAPI cleanup_thread(DWORD arg) +{ + size_t count = 0; + while (true) + { + Sleep(8000 + random() % 1024); + + domain_cleanup(count); + redirect_cleanup(count); + count++; + } + return 0; +} + +// Status handling: +#define MAX_STATUS_LEN 256 +extern void status(const char *message, ...) +{ + va_list args; + va_start(args, message); + char buf[MAX_STATUS_LEN+8]; + + int n = vsnprintf(buf, MAX_STATUS_LEN, message, args); + if (n < 0 || n >= MAX_STATUS_LEN) + { + // Do nothing if something goes wrong... + return; + } + + if (islower(buf[0])) + buf[0] = toupper(buf[0]); + + if (n < 3 || buf[n-1] != '.' || buf[n-2] != '.' || buf[n-3] != '.') + { + buf[n++] = '.'; + buf[n++] = '.'; + buf[n++] = '.'; + buf[n++] = '\0'; + } + + printf("%s\n", buf); + + if (status_bar != NULL) + { + SendMessage(status_bar, SB_SETTEXT, 0, (LPARAM)buf); + SendMessage(status_bar, SB_SETTIPTEXT, 0, (LPARAM)buf); + } +} + +// Error handling: +#define MAX_WARNING_LEN 1024 +extern void warning(const char *message, ...) +{ + // (1) Construct the message: + int err = GetLastError(); + va_list args; + va_start(args, message); + char buf[MAX_WARNING_LEN+1]; + + int n = vsnprintf(buf, MAX_WARNING_LEN, message, args); + if (n < 0 || n >= MAX_WARNING_LEN) + { +warning_failed: + MessageBox(NULL, "failed to display warning message", NULL, + MB_ICONERROR | MB_OK); + exit(EXIT_FAILURE); + } + + if (islower(buf[0])) + buf[0] = toupper(buf[0]); + + if (err != 0) + { + // Compare FormatMessage() vs. strerror() -- no wonder people do not + // use it... + LPTSTR err_str = NULL; + DWORD err_len = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, 0, err, 0, (LPTSTR)&err_str, 0, 0); + if (err_len != 0) + { + if (n + 3 + err_len >= MAX_WARNING_LEN) + goto warning_failed; + buf[n++] = ':'; + buf[n++] = ' '; + for (int i = 0; i < err_len; i++) + buf[n++] = err_str[i]; + buf[n++] = '\0'; + LocalFree(err_str); + } + } + + // (2) Display the message. + HANDLE console = GetStdHandle(STD_ERROR_HANDLE); + SetConsoleTextAttribute(console, FOREGROUND_RED); + fprintf(stderr, "warning: %s\n", buf); + SetConsoleTextAttribute(console, + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + MessageBox(NULL, buf, PROGNAME " - Warning", MB_ICONWARNING | MB_OK); +} + diff --git a/main.h b/main.h new file mode 100644 index 0000000..2fafdce --- /dev/null +++ b/main.h @@ -0,0 +1,86 @@ +/* + * main.h + * Copyright (C) 2014, basil + * + * 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 3 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, see . + */ +#ifndef __MAIN_H +#define __MAIN_H + +#define PROGNAME "Tallow" + +#define STR2(s) #s +#define STR(s) STR2(s) + +#define TOR_PORT 49097 +#define TOR_ICON 49001 + +#define ADDR0(a) ((a) >> 24) +#define ADDR1(a) (((a) >> 16) & 0xFF) +#define ADDR2(a) (((a) >> 8) & 0xFF) +#define ADDR3(a) ((a) & 0xFF) + +extern void status(const char *message, ...); +extern void warning(const char *message, ...); + +// Options: +extern bool option_force_socks4a; + +// Locking functions: +static inline HANDLE create_lock(void) +{ + HANDLE lock = CreateMutex(NULL, FALSE, NULL); + if (lock == NULL) + { + warning("failed to create lock"); + exit(EXIT_FAILURE); + } + return lock; +} +static inline void lock(HANDLE lock) +{ + DWORD result = WaitForSingleObject(lock, INFINITE); + if (result != WAIT_OBJECT_0) + { + warning("failed to acquire lock"); + exit(EXIT_FAILURE); + } +} +static inline void unlock(HANDLE lock) +{ + if (!ReleaseMutex(lock)) + { + warning("failed to release lock"); + exit(EXIT_FAILURE); + } +} + +// (Strong) random number: +errno_t __cdecl rand_s(unsigned int *); +static inline unsigned random(void) +{ + unsigned r; + if (rand_s(&r) != 0) + { + warning("failed to get random number"); + exit(EXIT_FAILURE); + } + return r; +} + +// Debugging: +#define debug(msg, ...) \ + fprintf(stderr, msg, ## __VA_ARGS__) + +#endif diff --git a/main.rc b/main.rc new file mode 100644 index 0000000..f106039 --- /dev/null +++ b/main.rc @@ -0,0 +1,25 @@ +/* + * main.rc + * Copyright (C) 2014, basil + * + * 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 3 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, see . + */ + +#include "winuser.h" + +IDI_APPLICATION ICON "tallow.ico" +TOR_ICON ICON "tor.ico" + +1 RT_MANIFEST "manifest.xml" + diff --git a/manifest.xml b/manifest.xml new file mode 100644 index 0000000..01204b7 --- /dev/null +++ b/manifest.xml @@ -0,0 +1,7 @@ + + + +Tallow: Transparent Tor for Windows. + + + diff --git a/redirect.c b/redirect.c new file mode 100644 index 0000000..4fe524b --- /dev/null +++ b/redirect.c @@ -0,0 +1,757 @@ +/* + * redirect.c + * Copyright (C) 2014, basil + * + * 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 3 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, see . + */ + +#include +#include +#include +#include + +#include "windivert.h" + +#include "domain.h" +#include "main.h" +#include "redirect.h" + +#define MAX_PACKET 0xFFFF +#define NUM_WORKERS 4 + +// SOCKS4a headers +#define SOCKS_USERID_SIZE (256 + 8) +struct socks4a_req +{ + uint8_t vn; + uint8_t cd; + uint16_t dst_port; + uint32_t dst_addr; + char userid[SOCKS_USERID_SIZE]; +} __attribute__((__packed__)); + +struct socks4a_rep +{ + uint8_t vn; + uint8_t cd; + uint16_t port; + uint32_t addr; +} __attribute__((__packed__)); + +// DNS headers +#define DNS_MAX_NAME 254 +struct dnshdr +{ + uint16_t id; + uint16_t options; + uint16_t qdcount; + uint16_t ancount; + uint16_t nscount; + uint16_t arcount; +} __attribute__((__packed__)); + +struct dnsq +{ + uint16_t type; + uint16_t class; +} __attribute__((__packed__)); + +struct dnsa +{ + uint16_t name; + uint16_t type; + uint16_t class; + uint32_t ttl; + uint16_t length; + uint32_t addr; +} __attribute__((__packed__)); + +// Connections: +#define STATE_NOT_CONNECTED 0 +#define STATE_SYN_SEEN 1 +#define STATE_SYNACK_SEEN 2 +#define STATE_ESTABLISHED 3 +#define STATE_FIN_SEEN 4 +struct conn +{ + uint16_t port; + uint8_t state; +} __attribute__((__packed__)); + +// Cleanup. +struct cleanup +{ + uint32_t addr; + uint16_t port; + struct cleanup *next; +}; + +// Prototypes: +static void flush_dns_cache(void); +static DWORD redirect_worker(LPVOID arg); +static void redirect_tcp(HANDLE handle, PWINDIVERT_ADDRESS addr, + PWINDIVERT_IPHDR iphdr, PWINDIVERT_TCPHDR tcphdr, char *packet, + size_t packet_len, char *data, size_t data_len); +static void handle_dns(HANDLE handle, PWINDIVERT_ADDRESS addr, + PWINDIVERT_IPHDR iphdr, PWINDIVERT_UDPHDR udphdr, char *data, + size_t data_len); +static void socks4a_connect_1_of_2(struct conn *conn, HANDLE handle, + PWINDIVERT_ADDRESS addr, PWINDIVERT_IPHDR iphdr, PWINDIVERT_TCPHDR tcphdr); +static void socks4a_connect_2_of_2(struct conn *conn, HANDLE handle, + PWINDIVERT_ADDRESS addr, PWINDIVERT_IPHDR iphdr, PWINDIVERT_TCPHDR tcphdr, + struct socks4a_rep *sockshdr); +static void queue_cleanup(uint32_t addr, uint16_t port); + +// State: +static bool redirect_on = false; +static HANDLE handle = INVALID_HANDLE_VALUE; +static HANDLE handle_drop = INVALID_HANDLE_VALUE; +static HANDLE workers[NUM_WORKERS] = {NULL}; // Worker threads +static struct conn conns[UINT16_MAX] = {{0}}; +static struct cleanup *queue = NULL; // Cleanup queue. +static struct cleanup *queue_0 = NULL; + +// Flush the DNS cache: +static void flush_dns_cache(void) +{ + debug("Flush DNS cache\n"); + char dllname[MAX_PATH]; + UINT len = GetSystemDirectory(dllname, sizeof(dllname)); + if (len == 0) + { +dir_error: + warning("failed to get the system directory"); + exit(EXIT_FAILURE); + } + const char filename[] = "dnsapi.dll"; + if (sizeof(dllname) - len <= sizeof(filename) + 2) + goto dir_error; + dllname[len] = '\\'; + strcpy(dllname + len + 1, filename); + HMODULE lib = LoadLibrary(dllname); + if (lib == NULL) + { + warning("failed to load library \"%s\"", dllname); + exit(EXIT_FAILURE); + } + BOOL WINAPI (*DnsFlushResolverCache)(void); + DnsFlushResolverCache = + (BOOL WINAPI (*)(void))GetProcAddress(lib, "DnsFlushResolverCache"); + if (DnsFlushResolverCache == NULL || !DnsFlushResolverCache()) + warning("failed to flush DNS cache"); + FreeLibrary(lib); +} + +// Send a packet asynchronously: +static void send_packet(HANDLE handle, void *packet, size_t packet_len, + PWINDIVERT_ADDRESS addr) +{ + addr->Direction = WINDIVERT_DIRECTION_INBOUND; + WinDivertHelperCalcChecksums(packet, packet_len, 0); + if (!WinDivertSend(handle, packet, packet_len, addr, NULL)) + debug("Send packet failed (err=%d)\n", (int)GetLastError()); +} + +// Init this module: +extern void redirect_init(void) +{ + // This does two things: + // (1) Stops external connections to Tor; and + // (2) Prevents "fake" IPs leaking to the internet (which may indicate the + // use of this program). + HANDLE handle = WinDivertOpen( + "(inbound and tcp.DstPort == " STR(TOR_PORT) ") or " + "(outbound and ip.DstAddr >= " STR(ADDR_BASE) " and ip.DstAddr <= " + STR(ADDR_MAX) ")", + WINDIVERT_LAYER_NETWORK, -755, WINDIVERT_FLAG_DROP); + if (handle == INVALID_HANDLE_VALUE) + { + warning("failed to open WinDivert filter"); + exit(EXIT_FAILURE); + } +} + +// Start traffic redirect through Tor: +extern void redirect_start(void) +{ + debug("Tor divert START\n"); + + if (handle != INVALID_HANDLE_VALUE) + return; + + // Drop traffic we cannot handle: + handle_drop = WinDivertOpen( + "ip.DstAddr != 127.0.0.1 and (ipv6 or (not tcp and udp.DstPort != 53))", + WINDIVERT_LAYER_NETWORK, -753, WINDIVERT_FLAG_DROP); + if (handle_drop == INVALID_HANDLE_VALUE) + { +redirect_start_error: + warning("failed to open WinDivert filter"); + exit(EXIT_FAILURE); + } + + handle = WinDivertOpen( + "(ipv6 or ip.DstAddr != 127.0.0.1) and " + "(not tcp or (tcp and tcp.DstPort != 9001 and " + "tcp.SrcPort != 9001 and " + "tcp.DstPort != 9030 and " + "tcp.SrcPort != 9030))", + WINDIVERT_LAYER_NETWORK, -752, WINDIVERT_FLAG_NO_CHECKSUM); + if (handle == INVALID_HANDLE_VALUE) + goto redirect_start_error; + + flush_dns_cache(); + + // Max-out the packet queue: + WinDivertSetParam(handle, WINDIVERT_PARAM_QUEUE_LEN, 8192); + WinDivertSetParam(handle, WINDIVERT_PARAM_QUEUE_TIME, 1024); + + // Launch threads: + redirect_on = true; + memset(conns, 0, sizeof(conns)); + for (size_t i = 0; i < NUM_WORKERS; i++) + { + workers[i] = CreateThread(NULL, MAX_PACKET*3, + (LPTHREAD_START_ROUTINE)redirect_worker, (LPVOID)handle, 0, NULL); + if (workers[i] == NULL) + { + warning("failed to create WinDivert worker thread"); + exit(EXIT_FAILURE); + } + } +} + +// Stop traffic redirect through Tor: +extern void redirect_stop(void) +{ + debug("Tor divert STOP\n"); + + if (handle == INVALID_HANDLE_VALUE) + return; + + // Close the WinDivert handle; will cause the workers to exit. + redirect_on = false; + if (!WinDivertClose(handle)) + { + warning("failed to close WinDivert filter"); + exit(EXIT_FAILURE); + } + handle = INVALID_HANDLE_VALUE; + + for (size_t i = 0; i < NUM_WORKERS; i++) + { + WaitForSingleObject(workers[i], INFINITE); + workers[i] = NULL; + } + + flush_dns_cache(); +} + +// Redirect worker thread: +static DWORD redirect_worker(LPVOID arg) +{ + HANDLE handle = (HANDLE)arg; + + // Packet processing loop: + char packet[MAX_PACKET]; + UINT packet_len; + WINDIVERT_ADDRESS addr; + + while (redirect_on) + { + if (!WinDivertRecv(handle, packet, sizeof(packet), &addr, &packet_len)) + { + // Silently ignore any error. + continue; + } + + PWINDIVERT_IPHDR iphdr = NULL; + PWINDIVERT_TCPHDR tcphdr = NULL; + PWINDIVERT_UDPHDR udphdr = NULL; + PVOID data = NULL; + UINT data_len; + WinDivertHelperParsePacket(packet, packet_len, &iphdr, NULL, NULL, + NULL, &tcphdr, &udphdr, &data, &data_len); + + if (addr.Direction == WINDIVERT_DIRECTION_INBOUND) + { + // All inbound traffic is dropped: + continue; + } + if (udphdr != NULL && ntohs(udphdr->DstPort) == 53) + handle_dns(handle, &addr, iphdr, udphdr, data, data_len); + else if (tcphdr != NULL) + redirect_tcp(handle, &addr, iphdr, tcphdr, packet, packet_len, + data, data_len); + } + return 0; +} + +// Redirect TCP: +static void redirect_tcp(HANDLE handle, PWINDIVERT_ADDRESS addr, + PWINDIVERT_IPHDR iphdr, PWINDIVERT_TCPHDR tcphdr, char *packet, + size_t packet_len, char *data, size_t data_len) +{ + struct conn *conn; + + bool drop = false; + uint16_t port; + if (ntohs(tcphdr->SrcPort) == TOR_PORT) + { + // Tor ---> PC + port = tcphdr->DstPort; + conn = conns + port; + + switch (conn->state) + { + case STATE_NOT_CONNECTED: + return; + case STATE_SYN_SEEN: + if (!tcphdr->Syn || !tcphdr->Ack) + { + drop = true; + break; + } + + // SYN-ACK + socks4a_connect_1_of_2(conn, handle, addr, iphdr, + tcphdr); + conn->state = STATE_SYNACK_SEEN; + return; + + case STATE_SYNACK_SEEN: + if (data_len != sizeof(struct socks4a_rep)) + { + drop = true; + break; + } + conn->state = STATE_ESTABLISHED; + struct socks4a_rep *rep = (struct socks4a_rep *)data; + socks4a_connect_2_of_2(conn, handle, addr, iphdr, tcphdr, rep); + return; + + default: + break; + } + + tcphdr->SrcPort = conn->port; + } + else + { + // PC ---> Tor + port = tcphdr->SrcPort; + conn = conns + port; + + switch (conn->state) + { + case STATE_SYN_SEEN: + case STATE_SYNACK_SEEN: + drop = true; + break; + + case STATE_NOT_CONNECTED: + if (tcphdr->Syn && !tcphdr->Ack && !tcphdr->Fin && + !tcphdr->Rst) + { + // SYN + tcphdr->SeqNum = htonl(ntohl(tcphdr->SeqNum) - + sizeof(struct socks4a_req)); + conn->state = STATE_SYN_SEEN; + conn->port = tcphdr->DstPort; + queue_cleanup(ntohl(iphdr->DstAddr), port); + break; + } + return; + + default: + break; + } + + tcphdr->DstPort = htons(TOR_PORT); + } + + if (conn->state != STATE_FIN_SEEN && (tcphdr->Fin || tcphdr->Rst)) + { + drop = false; + conn->state = STATE_FIN_SEEN; + queue_cleanup((ntohs(tcphdr->SrcPort) == TOR_PORT? + ntohl(iphdr->SrcAddr): ntohl(iphdr->DstAddr)), port); + } + + if (!drop) + { + uint32_t dst_addr = iphdr->DstAddr; + iphdr->DstAddr = iphdr->SrcAddr; + iphdr->SrcAddr = dst_addr; + send_packet(handle, packet, packet_len, addr); + } +} + +// Glue a normal TCP conn to SOCKS4a +static void socks4a_connect_1_of_2(struct conn *conn, HANDLE handle, + PWINDIVERT_ADDRESS addr, PWINDIVERT_IPHDR iphdr, PWINDIVERT_TCPHDR tcphdr) +{ + uint32_t srcaddr = ntohl(iphdr->SrcAddr), dstaddr = ntohl(iphdr->DstAddr); + struct name *name = domain_lookup_name(ntohl(iphdr->DstAddr)); + if (name == NULL && option_force_socks4a) + { + debug("Ignoring non-SOCKs4a connect %u.%u.%u.%u:%u ---> " + "%u.%u.%u.%u:%u\n", + ADDR0(srcaddr), ADDR1(srcaddr), ADDR2(srcaddr), ADDR3(srcaddr), + ntohs(tcphdr->DstPort), + ADDR0(dstaddr), ADDR1(dstaddr), ADDR2(dstaddr), ADDR3(dstaddr), + ntohs(conn->port)); + + // No corresponding name -- ignore + return; + } + + // ACK to complete 3-way handshake (Tor-side): + struct + { + WINDIVERT_IPHDR iphdr; + WINDIVERT_TCPHDR tcphdr; + } ack; + + memset(&ack.iphdr, 0, sizeof(ack.iphdr)); + ack.iphdr.Version = 4; + ack.iphdr.HdrLength = sizeof(ack.iphdr) / sizeof(uint32_t); + ack.iphdr.Id = htons(0xF001); + WINDIVERT_IPHDR_SET_DF(&ack.iphdr, 1); + ack.iphdr.Length = htons(sizeof(ack)); + ack.iphdr.TTL = 64; + ack.iphdr.Protocol = IPPROTO_TCP; + ack.iphdr.SrcAddr = iphdr->DstAddr; + ack.iphdr.DstAddr = iphdr->SrcAddr; + + memset(&ack.tcphdr, 0, sizeof(ack.tcphdr)); + ack.tcphdr.SrcPort = tcphdr->DstPort; + ack.tcphdr.DstPort = tcphdr->SrcPort; + ack.tcphdr.SeqNum = tcphdr->AckNum; + ack.tcphdr.AckNum = htonl(ntohl(tcphdr->SeqNum) + 1); + ack.tcphdr.HdrLength = sizeof(ack.tcphdr) / sizeof(uint32_t); + ack.tcphdr.Ack = 1; + ack.tcphdr.Window = htons(8192); + + send_packet(handle, &ack, sizeof(ack), addr); + + // SOCKS4a CONNECT request: + struct + { + WINDIVERT_IPHDR iphdr; + WINDIVERT_TCPHDR tcphdr; + struct socks4a_req sockshdr; + } req; + + memset(&req.iphdr, 0, sizeof(req.iphdr)); + req.iphdr.Version = 4; + req.iphdr.HdrLength = sizeof(req.iphdr) / sizeof(uint32_t); + req.iphdr.Id = htons(0xF002); + WINDIVERT_IPHDR_SET_DF(&req.iphdr, 1); + req.iphdr.Length = htons(sizeof(req)); + req.iphdr.TTL = 64; + req.iphdr.Protocol = IPPROTO_TCP; + req.iphdr.SrcAddr = iphdr->DstAddr; + req.iphdr.DstAddr = iphdr->SrcAddr; + + memset(&req.tcphdr, 0, sizeof(req.tcphdr)); + req.tcphdr.SrcPort = tcphdr->DstPort; + req.tcphdr.DstPort = tcphdr->SrcPort; + req.tcphdr.SeqNum = ack.tcphdr.SeqNum; + req.tcphdr.AckNum = ack.tcphdr.AckNum; + req.tcphdr.HdrLength = sizeof(req.tcphdr) / sizeof(uint32_t); + req.tcphdr.Psh = 1; + req.tcphdr.Ack = 1; + req.tcphdr.Window = htons(8192); + + req.sockshdr.vn = 0x04; // SOCKS4a + req.sockshdr.cd = 0x01; // Stream connection + req.sockshdr.dst_port = conn->port; + + if (name != NULL) + { + debug("Connect %u.%u.%u.%u:%u ---> %s:%u\n", + ADDR0(srcaddr), ADDR1(srcaddr), ADDR2(srcaddr), ADDR3(srcaddr), + ntohs(tcphdr->DstPort), name->name, ntohs(conn->port)); + + req.sockshdr.dst_addr = ntohl(0x00000001); + + // Write fake userid: + size_t name_len = strlen(name->name); + size_t userid_len = SOCKS_USERID_SIZE - 1 - name_len - 1; + size_t i; + for (i = 0; i < userid_len; i++) + req.sockshdr.userid[i] = 'A'; + req.sockshdr.userid[i++] = '\0'; + + // Write domain: + for (size_t j = 0; j < name_len; j++) + req.sockshdr.userid[i + j] = name->name[j]; + domain_deref(name); + } + else + { + debug("Connect %u.%u.%u.%u:%u ---> %u.%u.%u.%u:%u\n", + ADDR0(srcaddr), ADDR1(srcaddr), ADDR2(srcaddr), ADDR3(srcaddr), + ntohs(tcphdr->DstPort), + ADDR0(dstaddr), ADDR1(dstaddr), ADDR2(dstaddr), ADDR3(dstaddr), + ntohs(conn->port)); + + req.sockshdr.dst_addr = iphdr->DstAddr; + + // SOCKS4 direct connection: + for (size_t i = 0; i < SOCKS_USERID_SIZE - 1; i++) + req.sockshdr.userid[i] = '4'; + } + req.sockshdr.userid[SOCKS_USERID_SIZE-1] = '\0'; + + send_packet(handle, &req, sizeof(req), addr); +} + +static void socks4a_connect_2_of_2(struct conn *conn, HANDLE handle, + PWINDIVERT_ADDRESS addr, PWINDIVERT_IPHDR iphdr, PWINDIVERT_TCPHDR tcphdr, + struct socks4a_rep *sockshdr) +{ + if (sockshdr->vn != 0 || sockshdr->cd != 0x5A) + { + // Something went wrong; close the connnection: + struct + { + WINDIVERT_IPHDR iphdr; + WINDIVERT_TCPHDR tcphdr; + } rst; + + memset(&rst.iphdr, 0, sizeof(rst.iphdr)); + rst.iphdr.Version = 4; + rst.iphdr.HdrLength = sizeof(rst.iphdr) / sizeof(uint32_t); + rst.iphdr.Id = htons(0xF003); + WINDIVERT_IPHDR_SET_DF(&rst.iphdr, 1); + rst.iphdr.Length = htons(sizeof(rst)); + rst.iphdr.TTL = 64; + rst.iphdr.Protocol = IPPROTO_TCP; + rst.iphdr.SrcAddr = iphdr->DstAddr; + rst.iphdr.DstAddr = iphdr->SrcAddr; + + memset(&rst.tcphdr, 0, sizeof(rst.tcphdr)); + rst.tcphdr.SrcPort = conn->port; + rst.tcphdr.DstPort = tcphdr->DstPort; + rst.tcphdr.SeqNum = htonl(0); + rst.tcphdr.AckNum = tcphdr->AckNum; + rst.tcphdr.HdrLength = sizeof(rst.tcphdr) / sizeof(uint32_t); + rst.tcphdr.Rst = 1; + + send_packet(handle, &rst, sizeof(rst), addr); + return; + } + + // SYN-ACK to complete 3-way handshake (PC-side): + struct + { + WINDIVERT_IPHDR iphdr; + WINDIVERT_TCPHDR tcphdr; + } synack; + + memset(&synack.iphdr, 0, sizeof(synack.iphdr)); + synack.iphdr.Version = 4; + synack.iphdr.HdrLength = sizeof(synack.iphdr) / sizeof(uint32_t); + synack.iphdr.Id = htons(0xF004); + WINDIVERT_IPHDR_SET_DF(&synack.iphdr, 1); + synack.iphdr.Length = htons(sizeof(synack)); + synack.iphdr.TTL = 64; + synack.iphdr.Protocol = IPPROTO_TCP; + synack.iphdr.SrcAddr = iphdr->DstAddr; + synack.iphdr.DstAddr = iphdr->SrcAddr; + + memset(&synack.tcphdr, 0, sizeof(synack.tcphdr)); + synack.tcphdr.SrcPort = conn->port; + synack.tcphdr.DstPort = tcphdr->DstPort; + synack.tcphdr.SeqNum = htonl(ntohl(tcphdr->SeqNum) + + sizeof(struct socks4a_rep) - 1); + synack.tcphdr.AckNum = tcphdr->AckNum; + synack.tcphdr.HdrLength = sizeof(synack.tcphdr) / sizeof(uint32_t); + synack.tcphdr.Syn = 1; + synack.tcphdr.Ack = 1; + synack.tcphdr.Window = tcphdr->Window; + + send_packet(handle, &synack, sizeof(synack), addr); +} + +// Handle DNS requests. +// NOTES: +// - If anything goes wrong, we simply drop the packet without error. +// - An alternative approach would be to let Tor resolve the address, however, +// this would be slow. +static void handle_dns(HANDLE handle, PWINDIVERT_ADDRESS addr, + PWINDIVERT_IPHDR iphdr, PWINDIVERT_UDPHDR udphdr, char *data, + size_t data_len) +{ + // We only handle standard DNS queries. + + if (data_len <= sizeof(struct dnshdr)) + return; + if (data_len > 512) // Max DNS packet size. + return; + + struct dnshdr *dnshdr = (struct dnshdr *)data; + data += sizeof(struct dnshdr); + data_len -= sizeof(struct dnshdr); + + // Check request: + if (ntohs(dnshdr->options) != 0x0100) // Standard query + return; + if (ntohs(dnshdr->qdcount) != 1) // Only 1 req-per-packet supported + return; + if (ntohs(dnshdr->ancount) != 0) + return; + if (ntohs(dnshdr->nscount) != 0) + return; + if (ntohs(dnshdr->arcount) != 0) + return; + + char name[DNS_MAX_NAME + 8]; // 8 bytes extra. + size_t i = 0; + while (i < data_len && data[i] != 0) + { + size_t len = data[i]; + if (i + len >= DNS_MAX_NAME) + return; + name[i++] = '.'; + for (size_t j = 0; j < len; j++, i++) + name[i] = data[i]; + } + if (i >= data_len) + return; + name[i++] = '\0'; + if (data_len - i != sizeof(struct dnsq)) + return; + + // Generate a fake IP address and associate it with this domain name: + uint32_t fake_addr = domain_lookup_addr(name); + if (fake_addr == 0) + { + // This domain is blocked; so ignore the request. + return; + } + + debug("Intercept DNS %s\n", (name[0] == '.'? name+1: name)); + + // Construct a query response: + size_t len = sizeof(struct dnshdr) + data_len + sizeof(struct dnsa); + if (len > 512) // Max DNS packet size. + return; + len += sizeof(WINDIVERT_IPHDR) + sizeof(WINDIVERT_UDPHDR) + len; + + char buf[len + 8]; // 8 bytes extra. + PWINDIVERT_IPHDR riphdr = (PWINDIVERT_IPHDR)buf; + PWINDIVERT_UDPHDR rudphdr = (PWINDIVERT_UDPHDR)(riphdr + 1); + struct dnshdr *rdnshdr = (struct dnshdr *)(rudphdr + 1); + char *rdata = (char *)(rdnshdr + 1); + + memset(riphdr, 0, sizeof(WINDIVERT_IPHDR)); + riphdr->Version = 4; + riphdr->HdrLength = sizeof(WINDIVERT_IPHDR) / sizeof(uint32_t); + riphdr->Length = htons(len); + riphdr->Id = htons(0xF00D); + WINDIVERT_IPHDR_SET_DF(riphdr, 1); + riphdr->TTL = 64; + riphdr->Protocol = IPPROTO_UDP; + riphdr->SrcAddr = iphdr->DstAddr; + riphdr->DstAddr = iphdr->SrcAddr; + + memset(rudphdr, 0, sizeof(WINDIVERT_UDPHDR)); + rudphdr->SrcPort = htons(53); // DNS + rudphdr->DstPort = udphdr->SrcPort; + rudphdr->Length = htons(len - sizeof(WINDIVERT_IPHDR)); + + rdnshdr->id = dnshdr->id; + rdnshdr->options = htons(0x8180); // Standard DNS response. + rdnshdr->qdcount = htons(1); + rdnshdr->ancount = htons(1); + rdnshdr->nscount = 0; + rdnshdr->arcount = 0; + + memcpy(rdata, data, data_len); + struct dnsa *rdnsa = (struct dnsa *)(rdata + data_len); + rdnsa->name = htons(0xC00C); + rdnsa->type = htons(0x0001); // (A) + rdnsa->class = htons(0x0001); // (IN) + rdnsa->ttl = htonl(1) ; // 1 second + rdnsa->length = htons(4); + rdnsa->addr = htonl(fake_addr); // Fake address + + send_packet(handle, &buf, len, addr); +} + +// Queue a cleanup operation. +static void queue_cleanup(uint32_t addr, uint16_t port) +{ + struct cleanup *entry = (struct cleanup *)malloc( + sizeof(struct cleanup)); + if (entry == NULL) + { + warning("failed to allocate %u bytes for cleanup entry", + sizeof(struct cleanup)); + exit(EXIT_FAILURE); + } + entry->addr = addr; + entry->port = port; + + while (true) + { + entry->next = queue; + if (InterlockedCompareExchangePointer((PVOID *)&queue, (PVOID)entry, + (PVOID)entry->next) == entry->next) + break; + } +} + +// Cleanup stale connections. +extern void redirect_cleanup(size_t count) +{ + if (count % 2 != 0) + return; + + struct cleanup *q0 = (struct cleanup *)InterlockedExchangePointer( + (PVOID)&queue, NULL); + struct cleanup *q = queue_0; + queue_0 = q0; + + while (q != NULL) + { + struct conn *conn = conns + q->port; + if (conn->state != STATE_ESTABLISHED && + conn->state != STATE_NOT_CONNECTED) + { + debug("Cleanup %s connection ", + (conn->state == STATE_FIN_SEEN? "closed": "stalled")); + struct name *name = domain_lookup_name(q->addr); + if (name != NULL) + { + debug("%s:%u\n", name->name, ntohs(q->port)); + domain_deref(name); + } + else + debug("%u.%u.%u.%u:%u\n", + ADDR0(q->addr), ADDR1(q->addr), ADDR2(q->addr), + ADDR3(q->addr), ntohs(q->port)); + + conn->state = STATE_NOT_CONNECTED; + conn->port = 0; + } + q0 = q; + q = q->next; + free(q0); + } +} + diff --git a/redirect.h b/redirect.h new file mode 100644 index 0000000..29aa9c0 --- /dev/null +++ b/redirect.h @@ -0,0 +1,26 @@ +/* + * redirect.h + * Copyright (C) 2014, basil + * + * 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 3 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, see . + */ +#ifndef __REDIRECT_H +#define __REDIRECT_H + +extern void redirect_init(void); +extern void redirect_start(void); +extern void redirect_stop(void); +extern void redirect_cleanup(size_t count); + +#endif diff --git a/tallow.ico b/tallow.ico new file mode 100644 index 0000000000000000000000000000000000000000..0e6ef44577ae34a5aad3080b13636cd1669af7c0 GIT binary patch literal 3629 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. - */ - -#include -#include -#include -#include -#include - -#include -#include - -#include "divert.h" - -#define STR2(s) #s -#define STR(s) STR2(s) - -/* - * Config. - */ -#define PROXY 9049 - -#define MAX_PACKET 0xFFFF -#define NUM_THREADS 4 - -/* - * Error Handling. - */ -#define error(message, ...) \ - do { \ - SetConsoleTextAttribute(console, FOREGROUND_RED); \ - fprintf(stderr, "error"); \ - SetConsoleTextAttribute(console, \ - FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); \ - fprintf(stderr, ": " message " [error=%d]\n", ##__VA_ARGS__, \ - GetLastError()); \ - cleanup(0); \ - } while (false) -#define warning(message, ...) \ - do { \ - SetConsoleTextAttribute(console, \ - FOREGROUND_RED | FOREGROUND_GREEN); \ - fprintf(stderr, "warning"); \ - SetConsoleTextAttribute(console, \ - FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); \ - fprintf(stderr, ": " message " [error=%d]\n", ##__VA_ARGS__, \ - GetLastError()); \ - } while (false) - -/* - * DNS related. - */ -struct dnshdr_s -{ - uint16_t id; - uint16_t option; - uint16_t qdcount; - uint16_t ancount; - uint16_t nscount; - uint16_t arcount; -} __attribute__((__packed__)); -typedef struct dnshdr_s *dnshdr_t; - -struct dnsq_s -{ - uint16_t type; - uint16_t class; -} __attribute__((__packed__)); -typedef struct dnsq_s *dnsq_t; - -struct dnsa_s -{ - uint16_t name; - uint16_t type; - uint16_t class; - uint32_t ttl; - uint16_t length; -} __attribute__((__packed__)); -typedef struct dnsa_s *dnsa_t; - -/* - * Prototypes. - */ -static void redirect(void); -static DWORD redirect_worker(LPVOID arg); -static void dns_handle_query(HANDLE handle, PDIVERT_ADDRESS addr, - PDIVERT_IPHDR iphdr, PDIVERT_UDPHDR udphdr, char *data, size_t data_len); -static void cleanup(int sig); - -/* - * Global handles. - */ -static HANDLE console = INVALID_HANDLE_VALUE; -static HANDLE privoxy = INVALID_HANDLE_VALUE; -static HANDLE tor = INVALID_HANDLE_VALUE; - -/* - * Main. - */ -int main(void) -{ - /* - * Welcome banner. - */ - console = GetStdHandle(STD_OUTPUT_HANDLE); - SetConsoleTextAttribute(console, FOREGROUND_GREEN); - printf(" _ _ _\n"); - printf("| |_ ___ _ ____ ____ _| | |\n"); - printf("| __/ _ \\| '__\\ \\ /\\ / / _` | | |\n"); - printf("| || (_) | | \\ V V / (_| | | |\n"); - printf(" \\__\\___/|_| \\_/\\_/ \\__,_|_|_|\n"); - printf("\n"); - SetConsoleTextAttribute(console, - FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); - printf("TorWall.exe: Copyright (C) 2013, basil\n"); - printf("License GPLv3+: GNU GPL version 3 or later " - ".\n"); - printf("This is free software: you are free to change and redistribute " - "it.\n"); - printf("There is NO WARRANTY, to the extent permitted by law.\n\n"); - SetConsoleTextAttribute(console, FOREGROUND_RED | FOREGROUND_GREEN); - printf(">>> Press CONTROL-C to exit <<<\n\n"); - SetConsoleTextAttribute(console, FOREGROUND_RED); - printf("WARNING"); - SetConsoleTextAttribute(console, - FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); - printf(": This is prototype software; use at your own risk!\n\n"); - - /* - * Set-up cleanup. - */ - signal(SIGINT, cleanup); - - /* - * Start Privoxy (minimized): - */ - STARTUPINFO si; - memset(&si, 0, sizeof(si)); - si.cb = sizeof(STARTUPINFO); - si.dwFlags = STARTF_USESHOWWINDOW; - si.wShowWindow = SW_MINIMIZE; - PROCESS_INFORMATION pi; - - printf("Starting Privoxy..."); - if (!CreateProcess("privoxy.exe", NULL, NULL, NULL, FALSE, 0, NULL, - NULL, &si, &pi)) - error("failed to start Privoxy"); - privoxy = pi.hProcess; - printf("Done.\n"); - - printf("Starting Tor"); - HANDLE out, in; - SECURITY_ATTRIBUTES attr; - memset(&attr, 0, sizeof(attr)); - attr.nLength = sizeof(SECURITY_ATTRIBUTES); - attr.bInheritHandle = TRUE; - attr.lpSecurityDescriptor = NULL; - if (!CreatePipe(&out, &in, &attr, 0)) - error("failed to create pipe"); - if (!SetHandleInformation(out, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) - error("failed to search handle information"); - memset(&si, 0, sizeof(si)); - si.cb = sizeof(STARTUPINFO); - si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); - si.hStdOutput = in; - si.hStdError = GetStdHandle(STD_ERROR_HANDLE); - si.dwFlags = STARTF_USESTDHANDLES; - if (!CreateProcess("tor.exe", NULL, NULL, NULL, TRUE, 0, NULL, - NULL, &si, &pi)) - error("failed to start Tor"); - tor = pi.hProcess; - - /* - * Wait for Tor to start: - */ - // Crude but effective: - while (TRUE) - { - char buf[BUFSIZ]; - DWORD len; - if (!ReadFile(out, buf, sizeof(buf)-1, &len, NULL) || len == 0) - error("failed to read Tor output"); - buf[len] = '\0'; - if (strstr(buf, "Bootstrapped 100%: Done.") != NULL) - break; - if (strchr(buf, '%') != NULL) - putchar('.'); - - } - printf("Done.\n"); - - /* - * Re-direct packets: - */ - redirect(); - - cleanup(0); - - return 0; -} - -/* - * Start the packet redirection. - */ -extern void redirect(void) -{ - printf("Starting TorWall..."); - - /* - * We only allow some Tor ports (9001 and 9030) and local traffic. - * Everything else will be blocked or redirected. - */ - HANDLE handle = DivertOpen( - "(ipv6 or ip.DstAddr != 127.0.0.1) and " - "(not tcp or (tcp and tcp.DstPort != 9001 and " - "tcp.SrcPort != 9001 and " - "tcp.DstPort != 9030 and " - "tcp.SrcPort != 9030))", - DIVERT_LAYER_NETWORK, -101, 0); - if (handle == INVALID_HANDLE_VALUE) - error("failed to open the WinDivert device"); - - /* - * Extra protection against inbound TCP connections using Tor ports: - */ - HANDLE handle_drop = DivertOpen( - "inbound and tcp.Syn and tcp.DstPort == " STR(PROXY), - DIVERT_LAYER_NETWORK, -30, DIVERT_FLAG_DROP); - if (handle_drop == INVALID_HANDLE_VALUE) - error("failed to open the WinDivert device"); - - // Max-out the packet queue: - if (!DivertSetParam(handle, DIVERT_PARAM_QUEUE_LEN, 8192)) - error("failed to set packet queue length"); - if (!DivertSetParam(handle, DIVERT_PARAM_QUEUE_TIME, 1024)) - error("failed to set packet queue time"); - - printf("Done.\n"); - printf("TorWall is now running.\n"); - - // Create worker threads: - for (size_t i = 0; i < NUM_THREADS-1; i++) - { - HANDLE thread = CreateThread(NULL, MAX_PACKET*2, - (LPTHREAD_START_ROUTINE)redirect_worker, (LPVOID)handle, 0, NULL); - if (thread == NULL) - error("failed to start redirect worker thread"); - } - redirect_worker((LPVOID)handle); -} - -/* - * Packet redirection (worker). - * - * TCP: - * - * This function implements part of the following traffic flow: - * - * +-----------+ (1) +-----------+ (2) +-----------+ (3) +----------+ - * | BROWSER |------->| PRIVOXY |------->| TOR |------->| SERVER | - * | a.b.c.d |<-------| a.b.c.d |<-------| a.b.c.d |<-------| x.y.z.w | - * +-----------+ (6) +-----------+ (5) +-----------+ (4) +----------+ - * - * Specifically, this function implements: - * (1) [a.b.c.d:port, x.y.z.w:80] ---> [x.y.z.w:port, a.b.c.d:PROXY] - * (6) [a.b.c.d:PROXY, x.y.z.w:port] ---> [x.y.z.w:80, a.b.c.d:port] - * where: - * a.b.c.d = local (source) IP address - * x.y.z.w = destination IP address - * [src, dest] = a packet. - * - * DNS: - * - * Since all proxying is transparent, the browser will still issue DNS - * requests in the usual way. However the results of these queries are - * irrelevant: Privoxy retrieves the domain from the Host header and forwards - * it (via SOCKSv5 and Tor) to the exit node that does the real DNS lookup. - * To account for this we intercept DNS queries and send fake replies in - * the 10.x.x.x (local) address block. - * - */ -static DWORD redirect_worker(LPVOID arg) -{ - HANDLE handle = (HANDLE)arg; - - // Re-direct loop: - char packet[MAX_PACKET]; - UINT packet_len; - DIVERT_ADDRESS addr; - while (true) - { - // Process a packet: - if (!DivertRecv(handle, packet, sizeof(packet), &addr, &packet_len)) - { - warning("failed to redirect packet; divert failed"); - continue; - } - - SetLastError(0); - - PDIVERT_IPHDR iphdr = NULL; - PDIVERT_TCPHDR tcphdr = NULL; - PDIVERT_UDPHDR udphdr = NULL; - PVOID data = NULL; - UINT data_len; - DivertHelperParsePacket(packet, packet_len, &iphdr, NULL, NULL, - NULL, &tcphdr, &udphdr, &data, &data_len); - - if (iphdr != NULL && udphdr != NULL) - { - if (ntohs(udphdr->DstPort) != 53 || data == NULL || data_len == 0) - { - // DROP non DNS UDP traffic. - continue; - } - - // HANDLE DNS request. - dns_handle_query(handle, &addr, iphdr, udphdr, data, data_len); - continue; - } - - if (iphdr == NULL || tcphdr == NULL) - { - // DROP non TCP/IP traffic. - continue; - } - - if (addr.Direction == DIVERT_DIRECTION_INBOUND) - { - // DROP all in-bound traffic. - continue; - } - - if (ntohs(tcphdr->DstPort) == 80) - { - // REDIRECT out-bound HTTP traffic to Privoxy: - uint32_t dst_addr = iphdr->DstAddr; - iphdr->DstAddr = iphdr->SrcAddr; - iphdr->SrcAddr = dst_addr; - tcphdr->DstPort = htons(PROXY); - addr.Direction = DIVERT_DIRECTION_INBOUND; - } - else if (ntohs(tcphdr->SrcPort) == PROXY) - { - // REDIRECT out-bound PROXY traffic to HTTP: - uint32_t dst_addr = iphdr->DstAddr; - iphdr->DstAddr = iphdr->SrcAddr; - iphdr->SrcAddr = dst_addr; - tcphdr->SrcPort = htons(80); - addr.Direction = DIVERT_DIRECTION_INBOUND; - } - else - { - // DROP non-HTTP, non-PROXY traffic: - continue; - } - - // Re-inject the packet: - DivertHelperCalcChecksums(packet, packet_len, 0); - if (!DivertSend(handle, packet, packet_len, &addr, NULL)) - warning("failed to redirect packet; injection failed"); - } -} - -/* - * Send a fake DNS reply for every DNS request. We can use a fake reply - * because the domain will be forwarded through SOCKSv5 via Tor, so the - * value returned is irrelevant. - */ -static void dns_handle_query(HANDLE handle, PDIVERT_ADDRESS addr, - PDIVERT_IPHDR iphdr, PDIVERT_UDPHDR udphdr, char *data, size_t data_len) -{ - if (data_len <= sizeof(struct dnshdr_s)) - return; - if (data_len >= 512) - return; - - dnshdr_t dnshdr = (dnshdr_t)data; - data += sizeof(struct dnshdr_s); - data_len -= sizeof(struct dnshdr_s); - - // Check request: - if (ntohs(dnshdr->option) != 0x0100) - return; - if (ntohs(dnshdr->qdcount) != 1) - return; - if (ntohs(dnshdr->ancount) != 0) - return; - if (ntohs(dnshdr->nscount) != 0) - return; - if (ntohs(dnshdr->arcount) != 0) - return; - size_t i = 0; - while (i < data_len && data[i] != 0) - { - size_t len = data[i]; - i += len + 1; - } - i++; - if (i >= data_len) - return; - if (data_len - i != sizeof(struct dnsq_s)) - return; - dnsq_t dnsq = (dnsq_t)(data + i); - if (ntohs(dnsq->type) != 0x0001) - { - warning("ignoring DNS type=(0x%.4X) request", ntohs(dnsq->type)); - return; - } - if (ntohs(dnsq->class) != 0x0001) - { - warning("ignoring DNS class=(0x%.4X) request", ntohs(dnsq->class)); - return; - } - - // Construct response: - char buf[1024]; - PDIVERT_IPHDR r_iphdr = (PDIVERT_IPHDR)buf; - PDIVERT_UDPHDR r_udphdr = (PDIVERT_UDPHDR)(r_iphdr + 1); - dnshdr_t r_dnshdr = (dnshdr_t)(r_udphdr + 1); - void *r_data = (void *)(r_dnshdr + 1); - - memset(r_iphdr, 0, sizeof(DIVERT_IPHDR)); - r_iphdr->Version = 4; - r_iphdr->HdrLength = sizeof(DIVERT_IPHDR) / sizeof(uint32_t); - r_iphdr->Id = (UINT16)rand(); - DIVERT_IPHDR_SET_DF(r_iphdr, 1); - r_iphdr->TTL = 32; - r_iphdr->Protocol = 17; // IP_PROTO_UDP - memcpy(&r_iphdr->SrcAddr, &iphdr->DstAddr, sizeof(r_iphdr->SrcAddr)); - memcpy(&r_iphdr->DstAddr, &iphdr->SrcAddr, sizeof(r_iphdr->DstAddr)); - - r_udphdr->SrcPort = htons(53); // DNS - r_udphdr->DstPort = udphdr->SrcPort; - - r_dnshdr->id = dnshdr->id; - r_dnshdr->option = htons(0x8180); // Standard DNS response. - r_dnshdr->qdcount = htons(1); - r_dnshdr->ancount = htons(1); - r_dnshdr->nscount = 0; - r_dnshdr->arcount = 0; - - memcpy(r_data, data, data_len); - dnsa_t r_dnsa = (dnsa_t)(r_data + data_len); - r_dnsa->name = htons(0xC00C); - r_dnsa->type = htons(0x0001); // (A) - r_dnsa->class = htons(0x0001); // (IN) - r_dnsa->ttl = htonl(3); // 3 seconds - r_dnsa->length = htons(4); - - // Generate a dummy IP address 10.x.x.x - uint32_t res = 0x0A000000 | ((uint32_t)rand() << 8) | ((uint32_t)rand()); - uint32_t *r_dnsa_res = (uint32_t *)(r_dnsa + 1); - *r_dnsa_res = htonl(res); - - size_t len = sizeof(DIVERT_IPHDR) + sizeof(DIVERT_UDPHDR) + - sizeof(struct dnshdr_s) + data_len + sizeof(struct dnsa_s) + - sizeof(uint32_t); - r_iphdr->Length = htons((uint16_t)len); - r_udphdr->Length = htons((uint16_t)len - sizeof(DIVERT_IPHDR)); - - // Send response: - DivertHelperCalcChecksums(buf, len, 0); - addr->Direction = DIVERT_DIRECTION_INBOUND; - if (!DivertSend(handle, buf, len, addr, NULL)) - warning("failed to send DNS response; injection failed"); -} - -/* - * Clean-up and exit. - */ -static void cleanup(int sig) -{ - printf("Stopping Privoxy...\n"); - if (privoxy != INVALID_HANDLE_VALUE) - TerminateProcess(privoxy, 0); - - printf("Stopping Tor...\n"); - if (tor != INVALID_HANDLE_VALUE) - TerminateProcess(tor, 0); - - printf("Finished!\n"); - Sleep(1500); - exit(EXIT_SUCCESS); -} -