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 0000000..0e6ef44 Binary files /dev/null and b/tallow.ico differ diff --git a/tor.ico b/tor.ico new file mode 100644 index 0000000..2e080d1 Binary files /dev/null and b/tor.ico differ diff --git a/tor_wall.c b/tor_wall.c deleted file mode 100644 index b573e11..0000000 --- a/tor_wall.c +++ /dev/null @@ -1,501 +0,0 @@ -/* - * tor_wall.c - * Copyright (C) 2013, 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 - -#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); -} -